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