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 */ 9package com.vladium.emma.report.html; 10 11import java.io.BufferedReader; 12import java.io.BufferedWriter; 13import java.io.File; 14import java.io.FileOutputStream; 15import java.io.FileReader; 16import java.io.IOException; 17import java.io.OutputStreamWriter; 18import java.io.UnsupportedEncodingException; 19import java.text.DecimalFormat; 20import java.text.FieldPosition; 21import java.text.NumberFormat; 22import java.util.Date; 23import java.util.Iterator; 24import java.util.LinkedList; 25 26import com.vladium.util.Descriptors; 27import com.vladium.util.Files; 28import com.vladium.util.IProperties; 29import com.vladium.util.IntObjectMap; 30import com.vladium.util.IntVector; 31import com.vladium.util.ObjectIntMap; 32import com.vladium.util.Property; 33import com.vladium.util.asserts.$assert; 34import com.vladium.emma.IAppConstants; 35import com.vladium.emma.IAppErrorCodes; 36import com.vladium.emma.EMMAProperties; 37import com.vladium.emma.EMMARuntimeException; 38import com.vladium.emma.data.ICoverageData; 39import com.vladium.emma.data.IMetaData; 40import com.vladium.emma.report.AbstractReportGenerator; 41import com.vladium.emma.report.AllItem; 42import com.vladium.emma.report.ClassItem; 43import com.vladium.emma.report.IItem; 44import com.vladium.emma.report.IItemAttribute; 45import com.vladium.emma.report.IItemMetadata; 46import com.vladium.emma.report.ItemComparator; 47import com.vladium.emma.report.MethodItem; 48import com.vladium.emma.report.PackageItem; 49import com.vladium.emma.report.SourcePathCache; 50import com.vladium.emma.report.SrcFileItem; 51import com.vladium.emma.report.html.doc.*; 52 53// ---------------------------------------------------------------------------- 54/** 55 * @author Vlad Roubtsov, (C) 2003 56 */ 57public 58final 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 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 76 public final String getType () 77 { 78 return TYPE; 79 } 80 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 124 public void cleanup () 125 { 126 m_queue = null; 127 m_reportIDNamespace = null; 128 129 super.cleanup (); 130 } 131 132 133 // IItemVisitor: 134 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 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 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 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 { 730 IDGenerator () 731 { 732 m_namespace = new ObjectIntMap (101); 733 m_out = new int [1]; 734 } 735 736 IDGenerator (final int initialCapacity) 737 { 738 m_namespace = new ObjectIntMap (initialCapacity); 739 m_out = new int [1]; 740 } 741 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 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 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 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 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 */ 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 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 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 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 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 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 */ 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 */ 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 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 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 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 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 */ 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 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 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 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 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