• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2  *
3  * This program and the accompanying materials are made available under
4  * the terms of the Common Public License v1.0 which accompanies this distribution,
5  * and is available at http://www.eclipse.org/legal/cpl-v10.html
6  *
7  * $Id: ReportGenerator.java,v 1.2.2.1 2004/07/16 23:32:04 vlad_r Exp $
8  */
9 package com.vladium.emma.report.html;
10 
11 import java.io.BufferedReader;
12 import java.io.BufferedWriter;
13 import java.io.File;
14 import java.io.FileOutputStream;
15 import java.io.FileReader;
16 import java.io.IOException;
17 import java.io.OutputStreamWriter;
18 import java.io.UnsupportedEncodingException;
19 import java.text.DecimalFormat;
20 import java.text.FieldPosition;
21 import java.text.NumberFormat;
22 import java.util.Date;
23 import java.util.Iterator;
24 import java.util.LinkedList;
25 
26 import com.vladium.util.Descriptors;
27 import com.vladium.util.Files;
28 import com.vladium.util.IProperties;
29 import com.vladium.util.IntObjectMap;
30 import com.vladium.util.IntVector;
31 import com.vladium.util.ObjectIntMap;
32 import com.vladium.util.Property;
33 import com.vladium.util.asserts.$assert;
34 import com.vladium.emma.IAppConstants;
35 import com.vladium.emma.IAppErrorCodes;
36 import com.vladium.emma.EMMAProperties;
37 import com.vladium.emma.EMMARuntimeException;
38 import com.vladium.emma.data.ICoverageData;
39 import com.vladium.emma.data.IMetaData;
40 import com.vladium.emma.report.AbstractReportGenerator;
41 import com.vladium.emma.report.AllItem;
42 import com.vladium.emma.report.ClassItem;
43 import com.vladium.emma.report.IItem;
44 import com.vladium.emma.report.IItemAttribute;
45 import com.vladium.emma.report.IItemMetadata;
46 import com.vladium.emma.report.ItemComparator;
47 import com.vladium.emma.report.MethodItem;
48 import com.vladium.emma.report.PackageItem;
49 import com.vladium.emma.report.SourcePathCache;
50 import com.vladium.emma.report.SrcFileItem;
51 import com.vladium.emma.report.html.doc.*;
52 
53 // ----------------------------------------------------------------------------
54 /**
55  * @author Vlad Roubtsov, (C) 2003
56  */
57 public
58 final class ReportGenerator extends AbstractReportGenerator
59                             implements IAppErrorCodes
60 {
61     // public: ................................................................
62 
63     // TODO: make sure relative file names are converted to relative URLs in all anchors/hrefs
64 
ReportGenerator()65     public ReportGenerator ()
66     {
67         m_format = (DecimalFormat) NumberFormat.getPercentInstance (); // TODO: locale
68         m_fieldPosition = new FieldPosition (DecimalFormat.INTEGER_FIELD);
69 
70         m_format.setMaximumFractionDigits (0);
71     }
72 
73 
74     // IReportGenerator:
75 
getType()76     public final String getType ()
77     {
78         return TYPE;
79     }
80 
process(final IMetaData mdata, final ICoverageData cdata, final SourcePathCache cache, final IProperties properties)81     public void process (final IMetaData mdata, final ICoverageData cdata,
82                          final SourcePathCache cache, final IProperties properties)
83         throws EMMARuntimeException
84     {
85         initialize (mdata, cdata, cache, properties);
86 
87         m_pageTitle = null;
88         m_footerBottom = null;
89 
90         File outDir = m_settings.getOutDir ();
91         if ((outDir == null) /* this should never happen */ || (outDir.equals (new File (Property.getSystemProperty ("user.dir", "")))))
92         {
93             outDir = new File ("coverage");
94             m_settings.setOutDir (outDir);
95         }
96 
97         long start = 0, end;
98         final boolean trace1 = m_log.atTRACE1 ();
99 
100         if (trace1) start = System.currentTimeMillis ();
101 
102         {
103             m_queue = new LinkedList ();
104             m_reportIDNamespace = new IDGenerator (mdata.size ());
105 
106             for (m_queue.add (m_view.getRoot ()); ! m_queue.isEmpty (); )
107             {
108                 final IItem head = (IItem) m_queue.removeFirst ();
109 
110                 head.accept (this, null);
111             }
112 
113             m_reportIDNamespace = null;
114         }
115 
116         if (trace1)
117         {
118             end = System.currentTimeMillis ();
119 
120             m_log.trace1 ("process", "[" + getType () + "] report generated in " + (end - start) + " ms");
121         }
122     }
123 
cleanup()124     public void cleanup ()
125     {
126         m_queue = null;
127         m_reportIDNamespace = null;
128 
129         super.cleanup ();
130     }
131 
132 
133     // IItemVisitor:
134 
visit(final AllItem item, final Object ctx)135     public Object visit (final AllItem item, final Object ctx)
136     {
137         HTMLWriter out = null;
138         try
139         {
140             File outFile = m_settings.getOutFile ();
141             if (outFile == null)
142             {
143                 outFile = new File ("index".concat (FILE_EXTENSION));
144                 m_settings.setOutFile (outFile);
145             }
146 
147             final File fullOutFile = Files.newFile (m_settings.getOutDir (), outFile);
148 
149             m_log.info ("writing [" + getType () + "] report to [" + fullOutFile.getAbsolutePath () + "] ...");
150 
151             out = openOutFile (fullOutFile, m_settings.getOutEncoding (), true);
152 
153             final int [] columns = m_settings.getColumnOrder ();
154             final StringBuffer buf = new StringBuffer ();
155 
156             final String title;
157             {
158                 final StringBuffer _title = new StringBuffer (REPORT_HEADER_TITLE);
159 
160                 _title.append (" (generated ");
161                 _title.append (new Date (EMMAProperties.getTimeStamp ()));
162                 _title.append (')');
163 
164                 title = _title.toString ();
165             }
166 
167             final HTMLDocument page = createPage (title);
168             {
169                 final IItem [] path = getParentPath (item);
170 
171                 addPageHeader (page, item, path);
172                 addPageFooter (page, item, path);
173             }
174 
175             // [all] coverage summary table:
176 
177             page.addH (1, "OVERALL COVERAGE SUMMARY", null);
178 
179             final HTMLTable summaryTable = new HTMLTable ("100%", null, null, "0");
180             {
181                 // header row:
182                 final HTMLTable.IRow header = summaryTable.newTitleRow ();
183                 // coverage row:
184                 final HTMLTable.IRow coverage = summaryTable.newRow ();
185 
186                 for (int c = 0; c < columns.length; ++ c)
187                 {
188                     final int attrID = columns [c];
189                     final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ());
190 
191                     final HTMLTable.ICell headercell = header.newCell ();
192                     headercell.setText (attr.getName (), true);
193 
194                     if (attr != null)
195                     {
196                         boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]);
197 
198                         buf.setLength (0);
199                         attr.format (item, buf);
200 
201                         final HTMLTable.ICell cell = coverage.newCell ();
202                         cell.setText (buf.toString (), true);
203                         if (fail) cell.setClass (CSS_DATA_HIGHLIGHT);
204                     }
205                 }
206             }
207             page.add (summaryTable);
208 
209             // [all] stats summary table ([all] only):
210 
211             page.addH (2, "OVERALL STATS SUMMARY", null);
212 
213             final HTMLTable statsTable = new HTMLTable (null, null, null, "0");
214             statsTable.setClass (CSS_INVISIBLE_TABLE);
215             {
216                 HTMLTable.IRow row = statsTable.newRow ();
217                 row.newCell ().setText ("total packages:", true);
218                 row.newCell ().setText ("" + item.getChildCount (), false);
219 
220                 if (m_srcView && m_hasSrcFileInfo)
221                 {
222                     row = statsTable.newRow ();
223                     row.newCell ().setText ("total executable files:", true);
224                     row.newCell ().setText ("" + item.getAggregate (IItem.TOTAL_SRCFILE_COUNT), false);
225                 }
226 
227                 row = statsTable.newRow ();
228                 row.newCell ().setText ("total classes:", true);
229                 row.newCell ().setText ("" + item.getAggregate (IItem.TOTAL_CLASS_COUNT), true);
230                 row = statsTable.newRow ();
231                 row.newCell ().setText ("total methods:", true);
232                 row.newCell ().setText ("" + item.getAggregate (IItem.TOTAL_METHOD_COUNT), true);
233 
234                 if (m_srcView && m_hasSrcFileInfo && m_hasLineNumberInfo)
235                 {
236                     row = statsTable.newRow ();
237                     row.newCell ().setText ("total executable lines:", true);
238                     row.newCell ().setText ("" + item.getAggregate (IItem.TOTAL_LINE_COUNT), true);
239                 }
240             }
241             /*
242             {
243                 final HTMLTable.IRow first = statsTable.newRow (); // stats always available
244 
245                 first.newCell ().setText ("total packages: " + item.getChildCount (), true);
246                 first.newCell ().setText ("total classes: " + item.getAggregate (IItem.TOTAL_CLASS_COUNT), true);
247                 first.newCell ().setText ("total methods: " + item.getAggregate (IItem.TOTAL_METHOD_COUNT), true);
248 
249                 if (m_srcView && m_hasSrcFileInfo)
250                 {
251                     final HTMLTable.IRow second = statsTable.newRow ();
252 
253                     final HTMLTable.ICell cell1 = second.newCell ();
254                     cell1.setText ("total source files: " + item.getAggregate (IItem.TOTAL_SRCFILE_COUNT), true);
255 
256                     if (m_hasLineNumberInfo)
257                     {
258                         final HTMLTable.ICell cell2 = second.newCell ();
259 
260                         cell2.setText ("total executable source lines: " + item.getAggregate (IItem.TOTAL_LINE_COUNT), true);
261                         cell2.getAttributes ().set (Attribute.COLSPAN, "2");
262                     }
263                     else
264                     {
265                         cell1.getAttributes ().set (Attribute.COLSPAN, "3");
266                     }
267                 }
268             }
269             */
270             page.add (statsTable);
271 
272             final boolean deeper = (m_settings.getDepth () > item.getMetadata ().getTypeID ());
273 
274             // render package summary tables on the same page:
275 
276             page.addH (2, "COVERAGE BREAKDOWN BY PACKAGE", null);
277 
278             final HTMLTable childSummaryTable = new HTMLTable ("100%", null, null, "0");
279             {
280                 int [] headerColumns = null;
281 
282                 boolean odd = true;
283                 final ItemComparator order = m_typeSortComparators [PackageItem.getTypeMetadata ().getTypeID ()];
284                 for (Iterator packages = item.getChildren (order); packages.hasNext (); odd = ! odd)
285                 {
286                     final IItem pkg = (IItem) packages.next ();
287 
288                     if (headerColumns == null)
289                     {
290                         // header row:
291                         headerColumns = addHeaderRow (pkg, childSummaryTable, columns);
292                     }
293 
294                     // coverage row:
295                     String childHREF = null;
296                     if (deeper)
297                     {
298                         childHREF = getItemHREF (item, pkg);
299                     }
300                     addItemRow (pkg, odd, childSummaryTable, headerColumns, childHREF, false);
301 
302                     if (deeper) m_queue.addLast (pkg);
303                 }
304             }
305             page.add (childSummaryTable);
306 
307 
308             page.emit (out);
309             out.flush ();
310         }
311         finally
312         {
313             if (out != null) out.close ();
314             out = null;
315         }
316 
317         return ctx;
318     }
319 
visit(final PackageItem item, final Object ctx)320     public Object visit (final PackageItem item, final Object ctx)
321     {
322         HTMLWriter out = null;
323         try
324         {
325             if (m_verbose) m_log.verbose ("  report: processing package [" + item.getName () + "] ...");
326 
327             final File outFile = getItemFile (NESTED_ITEMS_PARENT_DIR, m_reportIDNamespace.getID (getItemKey (item)));
328 
329             out = openOutFile (Files.newFile (m_settings.getOutDir (), outFile), m_settings.getOutEncoding (), true);
330 
331             final int [] columns = m_settings.getColumnOrder ();
332             final StringBuffer buf = new StringBuffer ();
333 
334             // TODO: set title [from a prop?]
335             final HTMLDocument page = createPage (REPORT_HEADER_TITLE);
336             {
337                 final IItem [] path = getParentPath (item);
338 
339                 addPageHeader (page, item, path);
340                 addPageFooter (page, item, path);
341             }
342 
343             // summary table:
344 
345             {
346                 final IElement itemname = IElement.Factory.create (Tag.SPAN);
347                 itemname.setText (item.getName (), true);
348                 itemname.setClass (CSS_ITEM_NAME);
349 
350                 final IElementList title = new ElementList ();
351                 title.add (new Text ("COVERAGE SUMMARY FOR PACKAGE [", true));
352                 title.add (itemname);
353                 title.add (new Text ("]", true));
354 
355                 page.addH (1, title, null);
356             }
357 
358             final HTMLTable summaryTable = new HTMLTable ("100%", null, null, "0");
359             {
360                 // header row:
361                 final HTMLTable.IRow header = summaryTable.newTitleRow ();
362                 // coverage row:
363                 final HTMLTable.IRow coverage = summaryTable.newRow ();
364 
365                 for (int c = 0; c < columns.length; ++ c)
366                 {
367                     final int attrID = columns [c];
368                     final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ());
369 
370                     final HTMLTable.ICell headercell = header.newCell ();
371                     headercell.setText (attr.getName (), true);
372 
373                     if (attr != null)
374                     {
375                         boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]);
376 
377                         buf.setLength (0);
378                         attr.format (item, buf);
379 
380                         final HTMLTable.ICell cell = coverage.newCell ();
381                         cell.setText (buf.toString (), true);
382                         if (fail) cell.setClass (CSS_DATA_HIGHLIGHT);
383                     }
384                 }
385             }
386             page.add (summaryTable);
387 
388             final boolean deeper = (m_settings.getDepth () > item.getMetadata ().getTypeID ());
389 
390             // render child summary tables on the same page:
391 
392             final String summaryTitle = m_srcView ? "COVERAGE BREAKDOWN BY SOURCE FILE" : "COVERAGE BREAKDOWN BY CLASS";
393             page.addH (2, summaryTitle, null);
394 
395             final HTMLTable childSummaryTable = new HTMLTable ("100%", null, null, "0");
396             {
397                 int [] headerColumns = null;
398 
399                 boolean odd = true;
400                 final ItemComparator order = m_typeSortComparators [m_srcView ? SrcFileItem.getTypeMetadata ().getTypeID () : ClassItem.getTypeMetadata ().getTypeID ()];
401                 for (Iterator srcORclsFiles = item.getChildren (order); srcORclsFiles.hasNext (); odd = ! odd)
402                 {
403                     final IItem srcORcls = (IItem) srcORclsFiles.next ();
404 
405                     if (headerColumns == null)
406                     {
407                         // header row:
408                         headerColumns = addHeaderRow (srcORcls, childSummaryTable, columns);
409                     }
410 
411                     // coverage row:
412                     String childHREF = null;
413                     if (deeper)
414                     {
415                         childHREF = getItemHREF (item, srcORcls);
416                     }
417                     addItemRow (srcORcls, odd, childSummaryTable, headerColumns, childHREF, false);
418 
419                     if (deeper) m_queue.addLast (srcORcls);
420                 }
421             }
422             page.add (childSummaryTable);
423 
424 
425             page.emit (out);
426             out.flush ();
427         }
428         finally
429         {
430             if (out != null) out.close ();
431             out = null;
432         }
433 
434         return ctx;
435     }
436 
visit(final SrcFileItem item, final Object ctx)437     public Object visit (final SrcFileItem item, final Object ctx)
438     {
439         // this visit only takes place in src views
440 
441         HTMLWriter out = null;
442         try
443         {
444             final File outFile = getItemFile (NESTED_ITEMS_PARENT_DIR, m_reportIDNamespace.getID (getItemKey (item)));
445 
446             out = openOutFile (Files.newFile (m_settings.getOutDir (), outFile), m_settings.getOutEncoding (), true);
447 
448             final int [] columns = m_settings.getColumnOrder ();
449             final StringBuffer buf = new StringBuffer ();
450 
451             // TODO: set title [from a prop?]
452             final HTMLDocument page = createPage (REPORT_HEADER_TITLE);
453             {
454                 final IItem [] path = getParentPath (item);
455 
456                 addPageHeader (page, item, path);
457                 addPageFooter (page, item, path);
458             }
459 
460             // summary table:
461 
462             {
463                 final IElement itemname = IElement.Factory.create (Tag.SPAN);
464                 itemname.setText (item.getName (), true);
465                 itemname.setClass (CSS_ITEM_NAME);
466 
467                 final IElementList title = new ElementList ();
468                 title.add (new Text ("COVERAGE SUMMARY FOR SOURCE FILE [", true));
469                 title.add (itemname);
470                 title.add (new Text ("]", true));
471 
472                 page.addH (1, title, null);
473             }
474 
475             final HTMLTable summaryTable = new HTMLTable ("100%", null, null, "0");
476             {
477                 // header row:
478                 final HTMLTable.IRow header = summaryTable.newTitleRow ();
479                 // coverage row:
480                 final HTMLTable.IRow coverage = summaryTable.newRow ();
481 
482                 for (int c = 0; c < columns.length; ++ c)
483                 {
484                     final int attrID = columns [c];
485                     final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ());
486 
487                     final HTMLTable.ICell headercell = header.newCell ();
488                     headercell.setText (attr.getName (), true);
489 
490                     if (attr != null)
491                     {
492                         boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]);
493 
494                         buf.setLength (0);
495                         attr.format (item, buf);
496 
497                         final HTMLTable.ICell cell = coverage.newCell ();
498                         cell.setText (buf.toString (), true);
499                         if (fail) cell.setClass (CSS_DATA_HIGHLIGHT);
500                     }
501                 }
502             }
503             page.add (summaryTable);
504 
505             final boolean deeper = (m_settings.getDepth () > ClassItem.getTypeMetadata ().getTypeID ());
506             final boolean embedSrcFile = deeper && srcFileAvailable (item, m_cache);
507             final boolean createAnchors = embedSrcFile && m_hasLineNumberInfo;
508 
509             final IDGenerator pageIDNamespace = createAnchors ? new IDGenerator () : null;
510 
511             // child summary table is special for srcfile items:
512 
513             page.addH (2, "COVERAGE BREAKDOWN BY CLASS AND METHOD", null);
514 
515             final IntObjectMap lineAnchorIDMap = embedSrcFile ? new IntObjectMap () : null;
516             final HTMLTable childSummaryTable = new HTMLTable ("100%", null, null, "0");
517 
518             childSummaryTable.setClass (CSS_CLS_NOLEFT);
519 
520             {
521                 int [] headerColumns = null;
522 
523                 final ItemComparator order = m_typeSortComparators [ClassItem.getTypeMetadata ().getTypeID ()];
524                 int clsIndex = 0;
525                 for (Iterator classes = item.getChildren (order); classes.hasNext (); ++ clsIndex)
526                 {
527                     final ClassItem cls = (ClassItem) classes.next ();
528 
529                     if (headerColumns == null)
530                     {
531                         // header row:
532                         headerColumns = addHeaderRow (cls, childSummaryTable, columns);
533                     }
534 
535                     String HREFname = null;
536 
537                     // special class subheader:
538                     if (createAnchors)
539                     {
540                         if ($assert.ENABLED)
541                         {
542                             $assert.ASSERT (lineAnchorIDMap != null);
543                             $assert.ASSERT (pageIDNamespace != null);
544                         }
545 
546                         final String childKey = getItemKey (cls);
547 
548                         HREFname = addLineAnchorID (cls.getFirstLine (), pageIDNamespace.getID (childKey), lineAnchorIDMap);
549                     }
550 
551                     addClassRow (cls, clsIndex, childSummaryTable, headerColumns, HREFname, createAnchors);
552 
553 //                    // row to separate this class's methods:
554 //                    final HTMLTable.IRow subheader = childSummaryTable.newTitleRow ();
555 //                    final HTMLTable.ICell cell = subheader.newCell ();
556 //                    // TODO: cell.setColspan (???)
557 //                    cell.setText ("class " + child.getName () + " methods:", true);
558 
559                     boolean odd = false;
560                     final ItemComparator order2 = m_typeSortComparators [MethodItem.getTypeMetadata ().getTypeID ()];
561                     for (Iterator methods = cls.getChildren (order2); methods.hasNext (); odd = ! odd)
562                     {
563                         final MethodItem method = (MethodItem) methods.next ();
564 
565                         HREFname = null;
566 
567                         if (createAnchors)
568                         {
569                             if ($assert.ENABLED)
570                             {
571                                 $assert.ASSERT (lineAnchorIDMap != null);
572                                 $assert.ASSERT (pageIDNamespace != null);
573                             }
574 
575                             final String child2Key = getItemKey (method);
576 
577                             HREFname = addLineAnchorID (method.getFirstLine (), pageIDNamespace.getID (child2Key), lineAnchorIDMap);
578                         }
579 
580                         addClassItemRow (method, odd, childSummaryTable, headerColumns, HREFname, createAnchors);
581                     }
582                 }
583             }
584             page.add (childSummaryTable);
585 
586 
587             // embed source file:
588 
589             if (deeper)
590             {
591                 //page.addHR (1);
592                 page.addEmptyP ();
593                 {
594                     embedSrcFile (item, page, lineAnchorIDMap, m_cache);
595                 }
596                 //page.addHR (1);
597             }
598 
599 
600             page.emit (out);
601             out.flush ();
602         }
603         finally
604         {
605             if (out != null) out.close ();
606             out = null;
607         }
608 
609         return ctx;
610     }
611 
visit(final ClassItem item, final Object ctx)612     public Object visit (final ClassItem item, final Object ctx)
613     {
614         // this visit only takes place in class views
615 
616         HTMLWriter out = null;
617         try
618         {
619             final File outFile = getItemFile (NESTED_ITEMS_PARENT_DIR, m_reportIDNamespace.getID (getItemKey (item)));
620 
621             // TODO: deal with overwrites
622             out = openOutFile (Files.newFile (m_settings.getOutDir (), outFile), m_settings.getOutEncoding (), true);
623 
624             final int [] columns = m_settings.getColumnOrder ();
625             final StringBuffer buf = new StringBuffer ();
626 
627             // TODO: set title [from a prop?]
628             final HTMLDocument page = createPage (REPORT_HEADER_TITLE);
629             {
630                 final IItem [] path = getParentPath (item);
631 
632                 addPageHeader (page, item, path);
633                 addPageFooter (page, item, path);
634             }
635 
636 
637             // summary table:
638 
639             {
640                 final IElement itemname = IElement.Factory.create (Tag.SPAN);
641                 itemname.setText (item.getName (), true);
642                 itemname.setClass (CSS_ITEM_NAME);
643 
644                 final IElementList title = new ElementList ();
645                 title.add (new Text ("COVERAGE SUMMARY FOR CLASS [", true));
646                 title.add (itemname);
647                 title.add (new Text ("]", true));
648 
649                 page.addH (1, title, null);
650             }
651 
652             final HTMLTable summaryTable = new HTMLTable ("100%", null, null, "0");
653             {
654                 // header row:
655                 final HTMLTable.IRow header = summaryTable.newTitleRow ();
656                 // coverage row:
657                 final HTMLTable.IRow coverage = summaryTable.newRow ();
658 
659                 for (int c = 0; c < columns.length; ++ c)
660                 {
661                     final int attrID = columns [c];
662                     final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ());
663 
664                     final HTMLTable.ICell headercell = header.newCell ();
665                     headercell.setText (attr.getName (), true);
666 
667                     if (attr != null)
668                     {
669                         boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]);
670 
671                         buf.setLength (0);
672                         attr.format (item, buf);
673 
674                         final HTMLTable.ICell cell = coverage.newCell ();
675                         cell.setText (buf.toString (), true);
676                         if (fail) cell.setClass (CSS_DATA_HIGHLIGHT);
677                     }
678                 }
679             }
680             page.add (summaryTable);
681 
682 
683             // child summary table:
684 
685             page.addH (2, "COVERAGE BREAKDOWN BY METHOD", null);
686 
687             final HTMLTable childSummaryTable = new HTMLTable ("100%", null, null, "0");
688             {
689                 int [] headerColumns = null;
690 
691                 boolean odd = true;
692                 final ItemComparator order = m_typeSortComparators [MethodItem.getTypeMetadata ().getTypeID ()];
693                 for (Iterator methods = item.getChildren (order); methods.hasNext (); odd = ! odd)
694                 {
695                     final MethodItem method = (MethodItem) methods.next ();
696 
697                     if (headerColumns == null)
698                     {
699                         // header row:
700                         headerColumns = addHeaderRow (method, childSummaryTable, columns);
701                     }
702 
703                     addItemRow (method, odd, childSummaryTable, headerColumns, null, false);
704                 }
705             }
706             page.add (childSummaryTable);
707 
708 
709             page.emit (out);
710             out.flush ();
711         }
712         finally
713         {
714             if (out != null) out.close ();
715             out = null;
716         }
717 
718         return ctx;
719     }
720 
721     // protected: .............................................................
722 
723     // package: ...............................................................
724 
725     // private: ...............................................................
726 
727 
728     private static final class IDGenerator
729     {
IDGenerator()730         IDGenerator ()
731         {
732             m_namespace = new ObjectIntMap (101);
733             m_out = new int [1];
734         }
735 
IDGenerator(final int initialCapacity)736         IDGenerator (final int initialCapacity)
737         {
738             m_namespace = new ObjectIntMap (initialCapacity);
739             m_out = new int [1];
740         }
741 
getID(final String key)742         String getID (final String key)
743         {
744             final int [] out = m_out;
745             final int ID;
746 
747             if (m_namespace.get (key, out))
748                 ID = out [0];
749             else
750             {
751                 ID = m_namespace.size ();
752                 m_namespace.put (key, ID);
753             }
754 
755             return Integer.toHexString (ID);
756         }
757 
758         private final ObjectIntMap /* key:String->ID */ m_namespace;
759         private final int [] m_out;
760 
761     } // end of nested class
762 
763 
createPage(final String title)764     private HTMLDocument createPage (final String title)
765     {
766         final HTMLDocument page = new HTMLDocument (title, m_settings.getOutEncoding ());
767         page.addStyle (CSS); // TODO: split by visit type
768 
769         return page;
770     }
771 
addPageHeader(final HTMLDocument page, final IItem item, final IItem [] path)772     private IElement addPageHeader (final HTMLDocument page, final IItem item, final IItem [] path)
773     {
774         // TODO: merge header and footer in the same method
775 
776         if ($assert.ENABLED)
777         {
778             $assert.ASSERT (page != null);
779         }
780 
781         final HTMLTable header = new HTMLTable ("100%", null, null, "0");
782         header.setClass (CSS_HEADER_FOOTER);
783 
784         // header row:
785         addPageHeaderTitleRow (header);
786 
787         // nav row:
788         {
789             final HTMLTable.IRow navRow = header.newRow ();
790 
791             final HTMLTable.ICell cell = navRow.newCell ();
792             cell.setClass (CSS_NAV);
793 
794             final int lLimit = path.length > 1 ? path.length - 1 : path.length;
795             for (int l = 0; l < lLimit; ++ l)
796             {
797                 cell.add (LEFT_BRACKET);
798 
799                 final String name = path [l].getName ();
800                 final String HREF = getItemHREF (item, path [l]);
801                 cell.add (new HyperRef (HREF, name, true));
802 
803                 cell.add (RIGHT_BRACKET);
804             }
805         }
806 
807         page.setHeader (header);
808 
809         return header;
810     }
811 
addPageFooter(final HTMLDocument page, final IItem item, final IItem [] path)812     private IElement addPageFooter (final HTMLDocument page, final IItem item, final IItem [] path)
813     {
814         if ($assert.ENABLED)
815         {
816             $assert.ASSERT (page != null);
817         }
818 
819         final HTMLTable footerTable = new HTMLTable ("100%", null, null, "0");
820         footerTable.setClass (CSS_HEADER_FOOTER);
821 
822         // nav row:
823         {
824             final HTMLTable.IRow navRow = footerTable.newRow ();
825 
826             final HTMLTable.ICell cell = navRow.newCell ();
827             cell.setClass (CSS_NAV);
828 
829             final int lLimit = path.length > 1 ? path.length - 1 : path.length;
830             for (int l = 0; l < lLimit; ++ l)
831             {
832                 cell.add (LEFT_BRACKET);
833 
834                 final String name = path [l].getName ();
835                 final String HREF = getItemHREF (item, path [l]);
836                 cell.add (new HyperRef (HREF, name, true));
837 
838                 cell.add (RIGHT_BRACKET);
839             }
840         }
841 
842         // title row:
843         {
844             final HTMLTable.IRow titleRow = footerTable.newRow ();
845 
846             final HTMLTable.ICell cell = titleRow.newCell ();
847             cell.setClass (CSS_TITLE);
848 
849             cell.add (getFooterBottom ());
850         }
851 
852         final ElementList footer = new ElementList ();
853 
854         footer.add (IElement.Factory.create (Tag.P)); // spacer
855         footer.add (footerTable);
856 
857         page.setFooter (footer);
858 
859         return footerTable;
860     }
861 
addHeaderRow(final IItem item, final HTMLTable table, final int [] columns)862     private int [] addHeaderRow (final IItem item, final HTMLTable table, final int [] columns)
863     {
864         if ($assert.ENABLED)
865         {
866             $assert.ASSERT (item != null, "null input: item");
867             $assert.ASSERT (table != null, "null input: table");
868             $assert.ASSERT (columns != null, "null input: columns");
869         }
870 
871         // header row:
872         final HTMLTable.IRow header = table.newTitleRow ();
873 
874         // determine the set of columns actually present in the header [may be narrower than 'columns']:
875         final IntVector headerColumns = new IntVector (columns.length);
876 
877         for (int c = 0; c < columns.length; ++ c)
878         {
879             final int attrID = columns [c];
880             final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ());
881 
882             if (attr != null)
883             {
884                 final HTMLTable.ICell cell = header.newCell ();
885 
886                 cell.setText (attr.getName (), true);//.getAttributes ().set (Attribute.WIDTH, "20%");
887                 cell.setClass (headerCellStyle (c));
888                 headerColumns.add (attrID);
889             }
890 
891             // note: by design this does not create columns for nonexistent attribute types
892         }
893 
894         return headerColumns.values ();
895     }
896 
897     /*
898      * No header row, just data rows.
899      */
addItemRow(final IItem item, final boolean odd, final HTMLTable table, final int [] columns, final String nameHREF, final boolean anchor)900     private void addItemRow (final IItem item, final boolean odd, final HTMLTable table, final int [] columns, final String nameHREF, final boolean anchor)
901     {
902         if ($assert.ENABLED)
903         {
904             $assert.ASSERT (item != null, "null input: item");
905             $assert.ASSERT (table != null, "null input: table");
906             $assert.ASSERT (columns != null, "null input: columns");
907         }
908 
909         final HTMLTable.IRow row = table.newRow ();
910         if (odd) row.setClass (CSS_ODDROW);
911 
912         final StringBuffer buf = new StringBuffer (11); // TODO: reuse a buffer
913 
914         for (int c = 0; c < columns.length; ++ c)
915         {
916             final int attrID = columns [c];
917             final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ());
918 
919             if (attr != null)
920             {
921                 final HTMLTable.ICell cell = row.newCell ();
922 
923                 if ((nameHREF != null) && (attrID == IItemAttribute.ATTRIBUTE_NAME_ID))
924                 {
925                     buf.setLength (0);
926                     attr.format (item, buf);
927 
928                     trimForDisplay (buf);
929 
930                     final String fullHREFName = anchor ? "#".concat (nameHREF) : nameHREF;
931 
932                     cell.add (new HyperRef (fullHREFName, buf.toString (), true));
933                 }
934                 else
935                 {
936                     final boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]);
937 
938                     buf.setLength (0);
939                     attr.format (item, buf);
940 
941                     trimForDisplay (buf);
942 
943                     cell.setText (buf.toString (), true);
944                     if (fail) cell.setClass (CSS_DATA_HIGHLIGHT);
945                 }
946             }
947             else
948             {
949                 // note: by design this puts empty cells for nonexistent attribute types
950 
951                 final HTMLTable.ICell cell = row.newCell ();
952                 cell.setText (" ", true);
953             }
954         }
955     }
956 
addClassRow(final ClassItem item, final int clsIndex, final HTMLTable table, final int [] columns, final String itemHREF, final boolean isAnchor)957     private void addClassRow (final ClassItem item, final int clsIndex, final HTMLTable table, final int [] columns,
958                               final String itemHREF, final boolean isAnchor)
959     {
960         if ($assert.ENABLED)
961         {
962             $assert.ASSERT (item != null, "null input: item");
963             $assert.ASSERT (table != null, "null input: table");
964             $assert.ASSERT (columns != null, "null input: columns");
965         }
966 
967         final HTMLTable.IRow blank = table.newRow ();
968 
969         final HTMLTable.IRow row = table.newRow ();
970         row.setClass (CSS_CLASS_ITEM_SPECIAL);
971 
972         final StringBuffer buf = new StringBuffer (11);
973 
974         for (int c = 0; c < columns.length; ++ c)
975         {
976             final int attrID = columns [c];
977             final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ());
978 
979             if (attr != null)
980             {
981                 buf.setLength (0);
982                 attr.format (item, buf);
983 
984                 final HTMLTable.ICell blankcell = blank.newCell ();
985                 blankcell.setClass (clsIndex == 0 ? CSS_BLANK : CSS_BOTTOM);
986                 blankcell.setText (" ", true);
987 
988                 final HTMLTable.ICell cell = row.newCell ();
989 
990                 boolean fail = false;
991                 if (attrID == IItemAttribute.ATTRIBUTE_NAME_ID)
992                 {
993                     if (itemHREF != null)
994                     {
995                         final String fullItemHREF = isAnchor ? "#".concat (itemHREF) : itemHREF;
996 
997                         cell.add (new Text ("class ", true));
998                         cell.add (new HyperRef (fullItemHREF, buf.toString (), true));
999                     }
1000                     else
1001                     {
1002                         cell.setText ("class " + buf.toString (), true);
1003                     }
1004                 }
1005                 else
1006                 {
1007                     fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]);
1008 
1009                     cell.setText (buf.toString (), true);
1010                 }
1011 
1012                 cell.setClass (dataCellStyle (c, fail));
1013             }
1014             else
1015             {
1016                 final HTMLTable.ICell cell = row.newCell ();
1017                 cell.setText (" ", true);
1018                 cell.setClass (dataCellStyle (c, false));
1019             }
1020         }
1021     }
1022 
1023 
addClassItemRow(final IItem item, final boolean odd, final HTMLTable table, final int [] columns, final String nameHREF, final boolean anchor)1024     private void addClassItemRow (final IItem item, final boolean odd, final HTMLTable table, final int [] columns, final String nameHREF, final boolean anchor)
1025     {
1026         if ($assert.ENABLED)
1027         {
1028             $assert.ASSERT (item != null, "null input: item");
1029             $assert.ASSERT (table != null, "null input: table");
1030             $assert.ASSERT (columns != null, "null input: columns");
1031         }
1032 
1033         final HTMLTable.IRow row = table.newRow ();
1034         if (odd) row.setClass (CSS_ODDROW);
1035 
1036         final StringBuffer buf = new StringBuffer (11); // TODO: reuse a buffer
1037 
1038         for (int c = 0; c < columns.length; ++ c)
1039         {
1040             final int attrID = columns [c];
1041             final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ());
1042 
1043             if (attr != null)
1044             {
1045                 final HTMLTable.ICell cell = row.newCell ();
1046 
1047                 boolean fail = false;
1048                 if ((nameHREF != null) && (attrID == IItemAttribute.ATTRIBUTE_NAME_ID))
1049                 {
1050                     buf.setLength (0);
1051                     attr.format (item, buf);
1052 
1053                     trimForDisplay (buf);
1054 
1055                     final String fullHREFName = anchor ? "#".concat (nameHREF) : nameHREF;
1056 
1057                     cell.add (new HyperRef (fullHREFName, buf.toString (), true));
1058                 }
1059                 else
1060                 {
1061                     fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]);
1062 
1063                     buf.setLength (0);
1064                     attr.format (item, buf);
1065 
1066                     trimForDisplay (buf);
1067 
1068                     cell.setText (buf.toString (), true);
1069                 }
1070 
1071                 cell.setClass (dataCellStyle (c, fail));
1072             }
1073             else
1074             {
1075                 // note: by design this puts empty cells for nonexistent attribute types
1076 
1077                 final HTMLTable.ICell cell = row.newCell ();
1078                 cell.setText (" ", true);
1079                 cell.setClass (dataCellStyle (c, false));
1080             }
1081         }
1082     }
1083 
1084 
srcFileAvailable(final SrcFileItem item, final SourcePathCache cache)1085     private boolean srcFileAvailable (final SrcFileItem item, final SourcePathCache cache)
1086     {
1087         if (cache == null) return false;
1088 
1089         if ($assert.ENABLED) $assert.ASSERT (item != null, "null input: item");
1090 
1091         final String fileName = item.getName ();
1092         if ($assert.ENABLED) $assert.ASSERT (fileName.endsWith (".java"), "cache only handles .java extensions");
1093 
1094         // TODO: should I keep VM names in package items?
1095         final String packageVMName = ((PackageItem) item.getParent ()).getVMName ();
1096 
1097         return (cache.find (packageVMName, fileName) != null);
1098     }
1099 
1100 //    private boolean srcFileAvailable (final ClassItem item, final SourcePathCache cache)
1101 //    {
1102 //        if (cache == null) return false;
1103 //
1104 //        if ($assert.ENABLED) $assert.ASSERT (item != null, "null input: item");
1105 //
1106 //        final String fileName = item.getSrcFileName ();
1107 //        if ($assert.ENABLED) $assert.ASSERT (fileName.endsWith (".java"), "cache only handles .java extensions");
1108 //
1109 //        // TODO: should I keep VM names in package items?
1110 //        final String packageVMName = ((PackageItem) item.getParent ()).getVMName ();
1111 //
1112 //        return (cache.find (packageVMName, fileName) != null);
1113 //    }
1114 
embedSrcFile(final SrcFileItem item, final HTMLDocument page, final IntObjectMap anchorMap, final SourcePathCache cache)1115     private void embedSrcFile (final SrcFileItem item, final HTMLDocument page,
1116                                final IntObjectMap /* line num:int->anchor name:String */anchorMap,
1117                                final SourcePathCache cache)
1118     {
1119         if ($assert.ENABLED)
1120         {
1121             $assert.ASSERT (item != null, "null input: item");
1122             $assert.ASSERT (page != null, "null input: page");
1123         }
1124 
1125         final String fileName = item.getName ();
1126         if ($assert.ENABLED) $assert.ASSERT (fileName.endsWith (".java"), "cache only handles .java extensions");
1127 
1128         // TODO: should I keep VM names in package items?
1129         final String packageVMName = ((PackageItem) item.getParent ()).getVMName ();
1130 
1131         boolean success = false;
1132 
1133         final HTMLTable srcTable = new HTMLTable ("100%", null, null, "0");
1134 
1135         if (cache != null) // TODO: do this check earlier, in outer scope
1136         {
1137             srcTable.setClass (CSS_SOURCE);
1138             final File srcFile = cache.find (packageVMName, fileName);
1139 
1140             if (srcFile != null)
1141             {
1142                 BufferedReader in = null;
1143                 try
1144                 {
1145                     in = new BufferedReader (new FileReader (srcFile), IO_BUF_SIZE);
1146 
1147                     final boolean markupCoverage = m_hasLineNumberInfo;
1148 
1149                     final int unitsType = m_settings.getUnitsType ();
1150                     IntObjectMap /* line num:int -> SrcFileItem.LineCoverageData */ lineCoverageMap = null;
1151                     StringBuffer tooltipBuffer = null;
1152 
1153 
1154                     if (markupCoverage)
1155                     {
1156                         lineCoverageMap = item.getLineCoverage ();
1157                         $assert.ASSERT (lineCoverageMap != null, "null: lineCoverageMap");
1158 
1159                         tooltipBuffer = new StringBuffer (64);
1160                     }
1161 
1162                     int l = 1;
1163                     for (String line; (line = in.readLine ()) != null; ++ l)
1164                     {
1165                         final HTMLTable.IRow srcline = srcTable.newRow ();
1166                         final HTMLTable.ICell lineNumCell = srcline.newCell ();
1167                         lineNumCell.setClass (CSS_LINENUM);
1168 
1169                         if (anchorMap != null)
1170                         {
1171                             final int adjustedl = l < SRC_LINE_OFFSET ? l : l + SRC_LINE_OFFSET;
1172 
1173                             final String anchor = (String) anchorMap.get (adjustedl);
1174                             if (anchor != null)
1175                             {
1176                                 final IElement a = IElement.Factory.create (Tag.A);
1177                                 //a.getAttributes ().set (Attribute.ID, anchor); ID anchoring does not work in NS 4.0
1178                                 a.getAttributes ().set (Attribute.NAME, anchor);
1179 
1180                                 a.setText (Integer.toString (l), true);
1181 
1182                                 lineNumCell.add (a);
1183                             }
1184                             else
1185                             {
1186                                 lineNumCell.setText (Integer.toString (l), true);
1187                             }
1188                         }
1189                         else
1190                         {
1191                             lineNumCell.setText (Integer.toString (l), true);
1192                         }
1193 
1194                         final HTMLTable.ICell lineTxtCell = srcline.newCell ();
1195                         lineTxtCell.setText (line.length () > 0 ? line : " ", true);
1196 
1197                         if (markupCoverage)
1198                         {
1199                             final SrcFileItem.LineCoverageData lCoverageData = (SrcFileItem.LineCoverageData) lineCoverageMap.get (l);
1200 
1201                             if (lCoverageData != null)
1202                             {
1203                                 switch (lCoverageData.m_coverageStatus)
1204                                 {
1205                                     case SrcFileItem.LineCoverageData.LINE_COVERAGE_ZERO:
1206                                         srcline.setClass (CSS_COVERAGE_ZERO);
1207                                     break;
1208 
1209                                     case SrcFileItem.LineCoverageData.LINE_COVERAGE_PARTIAL:
1210                                     {
1211                                         srcline.setClass (CSS_COVERAGE_PARTIAL);
1212 
1213                                         if (USE_LINE_COVERAGE_TOOLTIPS)
1214                                         {
1215                                             tooltipBuffer.setLength (0);
1216 
1217                                             final int [] coverageRatio = lCoverageData.m_coverageRatio [unitsType];
1218 
1219                                             final int d = coverageRatio [0];
1220                                             final int n = coverageRatio [1];
1221 
1222                                             m_format.format ((double) n / d, tooltipBuffer, m_fieldPosition);
1223 
1224                                             tooltipBuffer.append (" line coverage (");
1225                                             tooltipBuffer.append (n);
1226                                             tooltipBuffer.append (" out of ");
1227                                             tooltipBuffer.append (d);
1228 
1229                                             switch (unitsType)
1230                                             {
1231                                                 case IItemAttribute.UNITS_COUNT:
1232                                                     tooltipBuffer.append (" basic blocks)");
1233                                                 break;
1234 
1235                                                 case IItemAttribute.UNITS_INSTR:
1236                                                     tooltipBuffer.append (" instructions)");
1237                                                 break;
1238                                             }
1239 
1240                                             // [Opera does not display TITLE tooltios on <TR> elements]
1241 
1242                                             lineNumCell.getAttributes ().set (Attribute.TITLE, tooltipBuffer.toString ());
1243                                             lineTxtCell.getAttributes ().set (Attribute.TITLE, tooltipBuffer.toString ());
1244                                         }
1245                                     }
1246                                     break;
1247 
1248                                     case SrcFileItem.LineCoverageData.LINE_COVERAGE_COMPLETE:
1249                                         srcline.setClass (CSS_COVERAGE_COMPLETE);
1250                                     break;
1251 
1252                                     default: $assert.ASSERT (false, "invalid line coverage status: " + lCoverageData.m_coverageStatus);
1253 
1254                                 } // end of switch
1255                             }
1256                         }
1257                     }
1258 
1259                     success = true;
1260                 }
1261                 catch (Throwable t)
1262                 {
1263                     t.printStackTrace (System.out); // TODO: logging
1264                     success = false;
1265                 }
1266                 finally
1267                 {
1268                     if (in != null) try { in.close (); } catch (Throwable ignore) {}
1269                     in = null;
1270                 }
1271             }
1272         }
1273 
1274         if (! success)
1275         {
1276             srcTable.setClass (CSS_INVISIBLE_TABLE);
1277 
1278             final HTMLTable.IRow row = srcTable.newTitleRow ();
1279             row.newCell ().setText ("[source file '" + Descriptors.combineVMName (packageVMName, fileName) + "' not found in sourcepath]", false);
1280         }
1281 
1282         page.add (srcTable);
1283     }
1284 
1285 
addLineAnchorID(final int line, final String anchorID, final IntObjectMap lineAnchorIDMap)1286     private static String addLineAnchorID (final int line, final String anchorID,
1287                                            final IntObjectMap /* line num:int->anchorID:String */lineAnchorIDMap)
1288     {
1289         if (line > 0)
1290         {
1291             final String _anchorID = (String) lineAnchorIDMap.get (line);
1292             if (_anchorID != null)
1293                 return _anchorID;
1294             else
1295             {
1296                 lineAnchorIDMap.put (line, anchorID);
1297 
1298                 return anchorID;
1299             }
1300         }
1301 
1302         return null;
1303     }
1304 
1305     /*
1306      * Always includes AllItem
1307      */
getParentPath(IItem item)1308     private IItem [] getParentPath (IItem item)
1309     {
1310         final LinkedList /* IItem */ _result = new LinkedList ();
1311 
1312         for ( ; item != null; item = item.getParent ())
1313         {
1314             _result.add (item);
1315         }
1316 
1317         final IItem [] result = new IItem [_result.size ()];
1318         int j = result.length - 1;
1319         for (Iterator i = _result.iterator (); i.hasNext (); -- j)
1320         {
1321             result [j] = (IItem) i.next ();
1322         }
1323 
1324         return result;
1325     }
1326 
1327     /*
1328      *
1329      */
getItemHREF(final IItem base, final IItem item)1330     private String getItemHREF (final IItem base, final IItem item)
1331     {
1332         final String itemHREF;
1333         if (item instanceof AllItem)
1334             itemHREF = m_settings.getOutFile ().getName (); // note that this is always a simple filename [no parent path]
1335         else
1336             itemHREF = m_reportIDNamespace.getID (getItemKey (item)).concat (FILE_EXTENSION);
1337 
1338         final String fullHREF;
1339 
1340         if (base == null)
1341             fullHREF = itemHREF;
1342         else
1343         {
1344             final int nesting = NESTING [base.getMetadata ().getTypeID ()] [item.getMetadata ().getTypeID ()];
1345             if (nesting == 1)
1346                 fullHREF = NESTED_ITEMS_PARENT_DIRNAME.concat ("/").concat (itemHREF);
1347             else if (nesting == -1)
1348                 fullHREF = "../".concat (itemHREF);
1349             else
1350                 fullHREF = itemHREF;
1351         }
1352 
1353         return fullHREF;
1354     }
1355 
1356 
getPageTitle()1357     private IContent getPageTitle ()
1358     {
1359         IContent title = m_pageTitle;
1360         if (title == null)
1361         {
1362             final IElementList _title = new ElementList ();
1363 
1364             _title.add (new HyperRef (IAppConstants.APP_HOME_SITE_LINK, IAppConstants.APP_NAME, true));
1365 
1366             final StringBuffer s = new StringBuffer (" Coverage Report (generated ");
1367             s.append (new Date (EMMAProperties.getTimeStamp ()));
1368             s.append (')');
1369 
1370             _title.add (new Text (s.toString (), true));
1371 
1372             m_pageTitle = title = _title;
1373         }
1374 
1375         return title;
1376     }
1377 
getFooterBottom()1378     private IContent getFooterBottom ()
1379     {
1380         IContent bottom = m_footerBottom;
1381         if (bottom == null)
1382         {
1383             final IElementList _bottom = new ElementList ();
1384 
1385             _bottom.add (new HyperRef (IAppConstants.APP_BUG_REPORT_LINK, IAppConstants.APP_NAME + " " + IAppConstants.APP_VERSION_WITH_BUILD_ID_AND_TAG, true));
1386             _bottom.add (new Text (" " + IAppConstants.APP_COPYRIGHT, true));
1387 
1388             m_footerBottom = bottom = _bottom;
1389         }
1390 
1391         return bottom;
1392     }
1393 
addPageHeaderTitleRow(final HTMLTable header)1394     private void addPageHeaderTitleRow (final HTMLTable header)
1395     {
1396         final HTMLTable.IRow titleRow = header.newTitleRow ();
1397 
1398         final HTMLTable.ICell cell = titleRow.newCell ();
1399         cell.setClass (CSS_TITLE);
1400         cell.add (getPageTitle ());
1401     }
1402 
trimForDisplay(final StringBuffer buf)1403     private static void trimForDisplay (final StringBuffer buf)
1404     {
1405         if (buf.length () > MAX_DISPLAY_NAME_LENGTH)
1406         {
1407             buf.setLength (MAX_DISPLAY_NAME_LENGTH - 3);
1408             buf.append ("...");
1409         }
1410     }
1411 
1412     /*
1413      * Assumes relative pathnames.
1414      */
getItemFile(final File parentDir, final String itemKey)1415     private static File getItemFile (final File parentDir, final String itemKey)
1416     {
1417         if (parentDir == null)
1418             return new File (itemKey.concat (FILE_EXTENSION));
1419         else
1420             return new File (parentDir, itemKey.concat (FILE_EXTENSION));
1421     }
1422 
getItemKey(IItem item)1423     private static String getItemKey (IItem item)
1424     {
1425         final StringBuffer result = new StringBuffer ();
1426 
1427         for ( ; item != null; item = item.getParent ())
1428         {
1429             result.append (item.getName ());
1430             result.append (':');
1431         }
1432 
1433         return result.toString ();
1434     }
1435 
openOutFile(final File file, final String encoding, final boolean mkdirs)1436     private static HTMLWriter openOutFile (final File file, final String encoding, final boolean mkdirs)
1437     {
1438         BufferedWriter out = null;
1439         try
1440         {
1441             if (mkdirs)
1442             {
1443                 final File parent = file.getParentFile ();
1444                 if (parent != null) parent.mkdirs ();
1445             }
1446 
1447             out = new BufferedWriter (new OutputStreamWriter (new FileOutputStream (file), encoding), IO_BUF_SIZE);
1448         }
1449         catch (UnsupportedEncodingException uee)
1450         {
1451             // TODO: error code
1452             throw new EMMARuntimeException (uee);
1453         }
1454         // note: in J2SDK 1.3 FileOutputStream constructor's throws clause
1455         // was narrowed to FileNotFoundException:
1456         catch (IOException fnfe) // FileNotFoundException
1457         {
1458             // TODO: error code
1459             throw new EMMARuntimeException (fnfe);
1460         }
1461 
1462         return new HTMLWriter (out);
1463     }
1464 
dataCellStyle(final int column, final boolean highlight)1465     private static String dataCellStyle (final int column, final boolean highlight)
1466     {
1467         if (column == 0)
1468             return highlight ? CSS_DATA_HIGHLIGHT_FIRST : CSS_DATA_FIRST;
1469         else
1470             return highlight ? CSS_DATA_HIGHLIGHT : CSS_DATA;
1471     }
1472 
headerCellStyle(final int column)1473     private static String headerCellStyle (final int column)
1474     {
1475         return (column == 0) ? CSS_HEADER_FIRST : CSS_HEADER;
1476     }
1477 
1478 
1479     private final DecimalFormat m_format;
1480     private final FieldPosition m_fieldPosition;
1481 
1482     private LinkedList /* IITem */ m_queue;
1483     private IDGenerator m_reportIDNamespace;
1484 
1485     private IContent m_pageTitle, m_footerBottom;
1486 
1487     private static final boolean USE_LINE_COVERAGE_TOOLTIPS = true;
1488 
1489     private static final String TYPE = "html";
1490     private static final String REPORT_HEADER_TITLE = IAppConstants.APP_NAME + " Coverage Report";
1491     private static final IContent LEFT_BRACKET = new Text ("[", false);
1492     private static final IContent RIGHT_BRACKET = new Text ("]", false);
1493 
1494     private static final int MAX_DISPLAY_NAME_LENGTH = 80;
1495     private static final int SRC_LINE_OFFSET = 4;
1496 
1497 
1498     private static final String CSS_HEADER_FOOTER       = "hdft";
1499     private static final String CSS_TITLE               = "tl";
1500     private static final String CSS_NAV                 = "nv";
1501 
1502     private static final String CSS_COVERAGE_ZERO       = "z";
1503     private static final String CSS_COVERAGE_PARTIAL    = "p";
1504     private static final String CSS_COVERAGE_COMPLETE   = "c";
1505 
1506     private static final String DARKER_BACKGROUND   = "#F0F0F0";
1507     private static final String TITLE_BACKGROUND    = "#6699CC";
1508     private static final String NAV_BACKGROUND      = "#6633DD";
1509 
1510     private static final String CSS_INVISIBLE_TABLE     = "it";
1511 
1512     private static final String CSS_ITEM_NAME           = "in";
1513 
1514     private static final String CSS_CLASS_ITEM_SPECIAL  = "cis";
1515 
1516     private static final String CSS_SOURCE              = "s";
1517     private static final String CSS_LINENUM             = "l";
1518 
1519     private static final String CSS_BOTTOM              = "bt";
1520     private static final String CSS_ODDROW              = "o";
1521     private static final String CSS_BLANK               = "b";
1522 
1523     private static final String CSS_DATA = "";
1524     private static final String CSS_DATA_HIGHLIGHT = CSS_DATA + "h";
1525     private static final String CSS_DATA_FIRST = CSS_DATA + "f";
1526     private static final String CSS_DATA_HIGHLIGHT_FIRST = CSS_DATA + "hf";
1527     private static final String CSS_HEADER = "";
1528     private static final String CSS_HEADER_FIRST = CSS_HEADER + "f";
1529 
1530     private static final String CSS_CLS_NOLEFT          = "cn";
1531 
1532     // TODO: optimize this
1533 
1534     private static final String CSS =
1535         " TABLE,TD,TH {border-style:solid; border-color:black;}" +
1536         " TD,TH {background:white;margin:0;line-height:100%;padding-left:0.5em;padding-right:0.5em;}" +
1537 
1538         " TD {border-width:0 1px 0 0;}" +
1539         " TH {border-width:1px 1px 1px 0;}" +
1540         " TR TD." + CSS_DATA_HIGHLIGHT + " {color:red;}" +
1541 
1542         " TABLE {border-spacing:0; border-collapse:collapse;border-width:0 0 1px 1px;}" +
1543 
1544         " P,H1,H2,H3,TH {font-family:verdana,arial,sans-serif;font-size:10pt;}" +
1545         " TD {font-family:courier,monospace;font-size:10pt;}" +
1546 
1547         " TABLE." + CSS_HEADER_FOOTER + " {border-spacing:0;border-collapse:collapse;border-style:none;}" +
1548         " TABLE." + CSS_HEADER_FOOTER + " TH,TABLE." + CSS_HEADER_FOOTER + " TD {border-style:none;line-height:normal;}" +
1549 
1550         " TABLE." + CSS_HEADER_FOOTER + " TH." + CSS_TITLE + ",TABLE." + CSS_HEADER_FOOTER + " TD." + CSS_TITLE + " {background:" + TITLE_BACKGROUND + ";color:white;}" +
1551         " TABLE." + CSS_HEADER_FOOTER + " TD." + CSS_NAV + " {background:" + NAV_BACKGROUND + ";color:white;}" +
1552 
1553         " ." + CSS_NAV + " A:link {color:white;}" +
1554         " ." + CSS_NAV + " A:visited {color:white;}" +
1555         " ." + CSS_NAV + " A:active {color:yellow;}" +
1556 
1557         " TABLE." + CSS_HEADER_FOOTER + " A:link {color:white;}" +
1558         " TABLE." + CSS_HEADER_FOOTER + " A:visited {color:white;}" +
1559         " TABLE." + CSS_HEADER_FOOTER + " A:active {color:yellow;}" +
1560 
1561         //" ." + CSS_ITEM_NAME + " {color:#6633FF;}" +
1562         //" ." + CSS_ITEM_NAME + " {color:#C000E0;}" +
1563         " ." + CSS_ITEM_NAME + " {color:#356085;}" +
1564 
1565         //" A:hover {color:#0066FF; text-decoration:underline; font-style:italic}" +
1566 
1567         " TABLE." + CSS_SOURCE + " TD {padding-left:0.25em;padding-right:0.25em;}" +
1568         " TABLE." + CSS_SOURCE + " TD." + CSS_LINENUM + " {padding-left:0.25em;padding-right:0.25em;text-align:right;background:" + DARKER_BACKGROUND + ";}" +
1569         " TABLE." + CSS_SOURCE + " TR." + CSS_COVERAGE_ZERO + " TD {background:#FF9999;}" +
1570         " TABLE." + CSS_SOURCE + " TR." + CSS_COVERAGE_PARTIAL + " TD {background:#FFFF88;}" +
1571         " TABLE." + CSS_SOURCE + " TR." + CSS_COVERAGE_COMPLETE + " TD {background:#CCFFCC;}" +
1572 
1573         " A:link {color:#0000EE;text-decoration:none;}" +
1574         " A:visited {color:#0000EE;text-decoration:none;}" +
1575         " A:hover {color:#0000EE;text-decoration:underline;}" +
1576 
1577         " TABLE." + CSS_CLS_NOLEFT + " {border-width:0 0 1px 0;}" +
1578         " TABLE." + CSS_SOURCE + " {border-width:1px 0 1px 1px;}" +
1579 
1580 //        " TD {border-width: 0px 1px 0px 0px; }" +
1581         " TD." + CSS_DATA_HIGHLIGHT + " {color:red;border-width:0 1px 0 0;}" +
1582         " TD." + CSS_DATA_FIRST + " {border-width:0 1px 0 1px;}" +
1583         " TD." + CSS_DATA_HIGHLIGHT_FIRST + " {color:red;border-width:0 1px 0 1px;}" +
1584 
1585 //        " TH {border-width: 1px 1px 1px 0px; }" +
1586         " TH." + CSS_HEADER_FIRST + " {border-width:1px 1px 1px 1px;}" +
1587 
1588         " TR." + CSS_CLASS_ITEM_SPECIAL + " TD {background:" + DARKER_BACKGROUND + ";}" +
1589         " TR." + CSS_CLASS_ITEM_SPECIAL + " TD {border-width:1px 1px 1px 0;}" +
1590         " TR." + CSS_CLASS_ITEM_SPECIAL + " TD." + CSS_DATA_HIGHLIGHT + " {color:red;border-width:1px 1px 1px 0;}" +
1591         " TR." + CSS_CLASS_ITEM_SPECIAL + " TD." + CSS_DATA_FIRST + " {border-width:1px 1px 1px 1px;}" +
1592         " TR." + CSS_CLASS_ITEM_SPECIAL + " TD." + CSS_DATA_HIGHLIGHT_FIRST + " {color:red;border-width:1px 1px 1px 1px;}" +
1593 
1594         " TD." + CSS_BLANK + " {border-style:none;background:transparent;line-height:50%;} " +
1595         " TD." + CSS_BOTTOM + " {border-width:1px 0 0 0;background:transparent;line-height:50%;}" +
1596         " TR." + CSS_ODDROW + " TD {background:" + DARKER_BACKGROUND + ";}" +
1597 
1598         "TABLE." + CSS_INVISIBLE_TABLE + " {border-style:none;}" +
1599         "TABLE." + CSS_INVISIBLE_TABLE + " TD,TABLE." + CSS_INVISIBLE_TABLE + " TH {border-style:none;}" +
1600 
1601         "";
1602 
1603     private static final String NESTED_ITEMS_PARENT_DIRNAME = "_files";
1604     private static final File NESTED_ITEMS_PARENT_DIR = new File (NESTED_ITEMS_PARENT_DIRNAME);
1605     private static final int [][] NESTING; // set in <clinit>; this reflects the dir structure for the report
1606 
1607     private static final String FILE_EXTENSION = ".html";
1608     private static final int IO_BUF_SIZE = 32 * 1024;
1609 
1610     private static final long [] ATTRIBUTE_SETS; // set in <clinit>
1611 
1612     static
1613     {
1614         final IItemMetadata [] allTypes = IItemMetadata.Factory.getAllTypes ();
1615 
1616         ATTRIBUTE_SETS = new long [allTypes.length];
1617         for (int t = 0; t < allTypes.length; ++ t)
1618         {
1619             ATTRIBUTE_SETS [allTypes [t].getTypeID ()] = allTypes [t].getAttributeIDs ();
1620         }
1621 
1622         NESTING = new int [4][4];
1623 
1624         int base = AllItem.getTypeMetadata().getTypeID ();
1625         NESTING [base][PackageItem.getTypeMetadata ().getTypeID ()] = 1;
1626         NESTING [base][SrcFileItem.getTypeMetadata ().getTypeID ()] = 1;
1627         NESTING [base][ClassItem.getTypeMetadata ().getTypeID ()] = 1;
1628 
1629         base = PackageItem.getTypeMetadata().getTypeID ();
1630         NESTING [base][AllItem.getTypeMetadata ().getTypeID ()] = -1;
1631 
1632         base = SrcFileItem.getTypeMetadata().getTypeID ();
1633         NESTING [base][AllItem.getTypeMetadata ().getTypeID ()] = -1;
1634 
1635         base = ClassItem.getTypeMetadata().getTypeID ();
1636         NESTING [base][AllItem.getTypeMetadata ().getTypeID ()] = -1;
1637     }
1638 
1639 } // end of class
1640 // ----------------------------------------------------------------------------
1641