/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. * * This program and the accompanying materials are made available under * the terms of the Common Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/cpl-v10.html * * $Id: ReportGenerator.java,v 1.2.2.1 2004/07/16 23:32:04 vlad_r Exp $ */ package com.vladium.emma.report.html; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.text.DecimalFormat; import java.text.FieldPosition; import java.text.NumberFormat; import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import com.vladium.util.Descriptors; import com.vladium.util.Files; import com.vladium.util.IProperties; import com.vladium.util.IntObjectMap; import com.vladium.util.IntVector; import com.vladium.util.ObjectIntMap; import com.vladium.util.Property; import com.vladium.util.asserts.$assert; import com.vladium.emma.IAppConstants; import com.vladium.emma.IAppErrorCodes; import com.vladium.emma.EMMAProperties; import com.vladium.emma.EMMARuntimeException; import com.vladium.emma.data.ICoverageData; import com.vladium.emma.data.IMetaData; import com.vladium.emma.report.AbstractReportGenerator; import com.vladium.emma.report.AllItem; import com.vladium.emma.report.ClassItem; import com.vladium.emma.report.IItem; import com.vladium.emma.report.IItemAttribute; import com.vladium.emma.report.IItemMetadata; import com.vladium.emma.report.ItemComparator; import com.vladium.emma.report.MethodItem; import com.vladium.emma.report.PackageItem; import com.vladium.emma.report.SourcePathCache; import com.vladium.emma.report.SrcFileItem; import com.vladium.emma.report.html.doc.*; // ---------------------------------------------------------------------------- /** * @author Vlad Roubtsov, (C) 2003 */ public final class ReportGenerator extends AbstractReportGenerator implements IAppErrorCodes { // public: ................................................................ // TODO: make sure relative file names are converted to relative URLs in all anchors/hrefs public ReportGenerator () { m_format = (DecimalFormat) NumberFormat.getPercentInstance (); // TODO: locale m_fieldPosition = new FieldPosition (DecimalFormat.INTEGER_FIELD); m_format.setMaximumFractionDigits (0); } // IReportGenerator: public final String getType () { return TYPE; } public void process (final IMetaData mdata, final ICoverageData cdata, final SourcePathCache cache, final IProperties properties) throws EMMARuntimeException { initialize (mdata, cdata, cache, properties); m_pageTitle = null; m_footerBottom = null; File outDir = m_settings.getOutDir (); if ((outDir == null) /* this should never happen */ || (outDir.equals (new File (Property.getSystemProperty ("user.dir", ""))))) { outDir = new File ("coverage"); m_settings.setOutDir (outDir); } long start = 0, end; final boolean trace1 = m_log.atTRACE1 (); if (trace1) start = System.currentTimeMillis (); { m_queue = new LinkedList (); m_reportIDNamespace = new IDGenerator (mdata.size ()); for (m_queue.add (m_view.getRoot ()); ! m_queue.isEmpty (); ) { final IItem head = (IItem) m_queue.removeFirst (); head.accept (this, null); } m_reportIDNamespace = null; } if (trace1) { end = System.currentTimeMillis (); m_log.trace1 ("process", "[" + getType () + "] report generated in " + (end - start) + " ms"); } } public void cleanup () { m_queue = null; m_reportIDNamespace = null; super.cleanup (); } // IItemVisitor: public Object visit (final AllItem item, final Object ctx) { HTMLWriter out = null; try { File outFile = m_settings.getOutFile (); if (outFile == null) { outFile = new File ("index".concat (FILE_EXTENSION)); m_settings.setOutFile (outFile); } final File fullOutFile = Files.newFile (m_settings.getOutDir (), outFile); m_log.info ("writing [" + getType () + "] report to [" + fullOutFile.getAbsolutePath () + "] ..."); out = openOutFile (fullOutFile, m_settings.getOutEncoding (), true); final int [] columns = m_settings.getColumnOrder (); final StringBuffer buf = new StringBuffer (); final String title; { final StringBuffer _title = new StringBuffer (REPORT_HEADER_TITLE); _title.append (" (generated "); _title.append (new Date (EMMAProperties.getTimeStamp ())); _title.append (')'); title = _title.toString (); } final HTMLDocument page = createPage (title); { final IItem [] path = getParentPath (item); addPageHeader (page, item, path); addPageFooter (page, item, path); } // [all] coverage summary table: page.addH (1, "OVERALL COVERAGE SUMMARY", null); final HTMLTable summaryTable = new HTMLTable ("100%", null, null, "0"); { // header row: final HTMLTable.IRow header = summaryTable.newTitleRow (); // coverage row: final HTMLTable.IRow coverage = summaryTable.newRow (); for (int c = 0; c < columns.length; ++ c) { final int attrID = columns [c]; final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); final HTMLTable.ICell headercell = header.newCell (); headercell.setText (attr.getName (), true); if (attr != null) { boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]); buf.setLength (0); attr.format (item, buf); final HTMLTable.ICell cell = coverage.newCell (); cell.setText (buf.toString (), true); if (fail) cell.setClass (CSS_DATA_HIGHLIGHT); } } } page.add (summaryTable); // [all] stats summary table ([all] only): page.addH (2, "OVERALL STATS SUMMARY", null); final HTMLTable statsTable = new HTMLTable (null, null, null, "0"); statsTable.setClass (CSS_INVISIBLE_TABLE); { HTMLTable.IRow row = statsTable.newRow (); row.newCell ().setText ("total packages:", true); row.newCell ().setText ("" + item.getChildCount (), false); if (m_srcView && m_hasSrcFileInfo) { row = statsTable.newRow (); row.newCell ().setText ("total executable files:", true); row.newCell ().setText ("" + item.getAggregate (IItem.TOTAL_SRCFILE_COUNT), false); } row = statsTable.newRow (); row.newCell ().setText ("total classes:", true); row.newCell ().setText ("" + item.getAggregate (IItem.TOTAL_CLASS_COUNT), true); row = statsTable.newRow (); row.newCell ().setText ("total methods:", true); row.newCell ().setText ("" + item.getAggregate (IItem.TOTAL_METHOD_COUNT), true); if (m_srcView && m_hasSrcFileInfo && m_hasLineNumberInfo) { row = statsTable.newRow (); row.newCell ().setText ("total executable lines:", true); row.newCell ().setText ("" + item.getAggregate (IItem.TOTAL_LINE_COUNT), true); } } /* { final HTMLTable.IRow first = statsTable.newRow (); // stats always available first.newCell ().setText ("total packages: " + item.getChildCount (), true); first.newCell ().setText ("total classes: " + item.getAggregate (IItem.TOTAL_CLASS_COUNT), true); first.newCell ().setText ("total methods: " + item.getAggregate (IItem.TOTAL_METHOD_COUNT), true); if (m_srcView && m_hasSrcFileInfo) { final HTMLTable.IRow second = statsTable.newRow (); final HTMLTable.ICell cell1 = second.newCell (); cell1.setText ("total source files: " + item.getAggregate (IItem.TOTAL_SRCFILE_COUNT), true); if (m_hasLineNumberInfo) { final HTMLTable.ICell cell2 = second.newCell (); cell2.setText ("total executable source lines: " + item.getAggregate (IItem.TOTAL_LINE_COUNT), true); cell2.getAttributes ().set (Attribute.COLSPAN, "2"); } else { cell1.getAttributes ().set (Attribute.COLSPAN, "3"); } } } */ page.add (statsTable); final boolean deeper = (m_settings.getDepth () > item.getMetadata ().getTypeID ()); // render package summary tables on the same page: page.addH (2, "COVERAGE BREAKDOWN BY PACKAGE", null); final HTMLTable childSummaryTable = new HTMLTable ("100%", null, null, "0"); { int [] headerColumns = null; boolean odd = true; final ItemComparator order = m_typeSortComparators [PackageItem.getTypeMetadata ().getTypeID ()]; for (Iterator packages = item.getChildren (order); packages.hasNext (); odd = ! odd) { final IItem pkg = (IItem) packages.next (); if (headerColumns == null) { // header row: headerColumns = addHeaderRow (pkg, childSummaryTable, columns); } // coverage row: String childHREF = null; if (deeper) { childHREF = getItemHREF (item, pkg); } addItemRow (pkg, odd, childSummaryTable, headerColumns, childHREF, false); if (deeper) m_queue.addLast (pkg); } } page.add (childSummaryTable); page.emit (out); out.flush (); } finally { if (out != null) out.close (); out = null; } return ctx; } public Object visit (final PackageItem item, final Object ctx) { HTMLWriter out = null; try { if (m_verbose) m_log.verbose (" report: processing package [" + item.getName () + "] ..."); final File outFile = getItemFile (NESTED_ITEMS_PARENT_DIR, m_reportIDNamespace.getID (getItemKey (item))); out = openOutFile (Files.newFile (m_settings.getOutDir (), outFile), m_settings.getOutEncoding (), true); final int [] columns = m_settings.getColumnOrder (); final StringBuffer buf = new StringBuffer (); // TODO: set title [from a prop?] final HTMLDocument page = createPage (REPORT_HEADER_TITLE); { final IItem [] path = getParentPath (item); addPageHeader (page, item, path); addPageFooter (page, item, path); } // summary table: { final IElement itemname = IElement.Factory.create (Tag.SPAN); itemname.setText (item.getName (), true); itemname.setClass (CSS_ITEM_NAME); final IElementList title = new ElementList (); title.add (new Text ("COVERAGE SUMMARY FOR PACKAGE [", true)); title.add (itemname); title.add (new Text ("]", true)); page.addH (1, title, null); } final HTMLTable summaryTable = new HTMLTable ("100%", null, null, "0"); { // header row: final HTMLTable.IRow header = summaryTable.newTitleRow (); // coverage row: final HTMLTable.IRow coverage = summaryTable.newRow (); for (int c = 0; c < columns.length; ++ c) { final int attrID = columns [c]; final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); final HTMLTable.ICell headercell = header.newCell (); headercell.setText (attr.getName (), true); if (attr != null) { boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]); buf.setLength (0); attr.format (item, buf); final HTMLTable.ICell cell = coverage.newCell (); cell.setText (buf.toString (), true); if (fail) cell.setClass (CSS_DATA_HIGHLIGHT); } } } page.add (summaryTable); final boolean deeper = (m_settings.getDepth () > item.getMetadata ().getTypeID ()); // render child summary tables on the same page: final String summaryTitle = m_srcView ? "COVERAGE BREAKDOWN BY SOURCE FILE" : "COVERAGE BREAKDOWN BY CLASS"; page.addH (2, summaryTitle, null); final HTMLTable childSummaryTable = new HTMLTable ("100%", null, null, "0"); { int [] headerColumns = null; boolean odd = true; final ItemComparator order = m_typeSortComparators [m_srcView ? SrcFileItem.getTypeMetadata ().getTypeID () : ClassItem.getTypeMetadata ().getTypeID ()]; for (Iterator srcORclsFiles = item.getChildren (order); srcORclsFiles.hasNext (); odd = ! odd) { final IItem srcORcls = (IItem) srcORclsFiles.next (); if (headerColumns == null) { // header row: headerColumns = addHeaderRow (srcORcls, childSummaryTable, columns); } // coverage row: String childHREF = null; if (deeper) { childHREF = getItemHREF (item, srcORcls); } addItemRow (srcORcls, odd, childSummaryTable, headerColumns, childHREF, false); if (deeper) m_queue.addLast (srcORcls); } } page.add (childSummaryTable); page.emit (out); out.flush (); } finally { if (out != null) out.close (); out = null; } return ctx; } public Object visit (final SrcFileItem item, final Object ctx) { // this visit only takes place in src views HTMLWriter out = null; try { final File outFile = getItemFile (NESTED_ITEMS_PARENT_DIR, m_reportIDNamespace.getID (getItemKey (item))); out = openOutFile (Files.newFile (m_settings.getOutDir (), outFile), m_settings.getOutEncoding (), true); final int [] columns = m_settings.getColumnOrder (); final StringBuffer buf = new StringBuffer (); // TODO: set title [from a prop?] final HTMLDocument page = createPage (REPORT_HEADER_TITLE); { final IItem [] path = getParentPath (item); addPageHeader (page, item, path); addPageFooter (page, item, path); } // summary table: { final IElement itemname = IElement.Factory.create (Tag.SPAN); itemname.setText (item.getName (), true); itemname.setClass (CSS_ITEM_NAME); final IElementList title = new ElementList (); title.add (new Text ("COVERAGE SUMMARY FOR SOURCE FILE [", true)); title.add (itemname); title.add (new Text ("]", true)); page.addH (1, title, null); } final HTMLTable summaryTable = new HTMLTable ("100%", null, null, "0"); { // header row: final HTMLTable.IRow header = summaryTable.newTitleRow (); // coverage row: final HTMLTable.IRow coverage = summaryTable.newRow (); for (int c = 0; c < columns.length; ++ c) { final int attrID = columns [c]; final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); final HTMLTable.ICell headercell = header.newCell (); headercell.setText (attr.getName (), true); if (attr != null) { boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]); buf.setLength (0); attr.format (item, buf); final HTMLTable.ICell cell = coverage.newCell (); cell.setText (buf.toString (), true); if (fail) cell.setClass (CSS_DATA_HIGHLIGHT); } } } page.add (summaryTable); final boolean deeper = (m_settings.getDepth () > ClassItem.getTypeMetadata ().getTypeID ()); final boolean embedSrcFile = deeper && srcFileAvailable (item, m_cache); final boolean createAnchors = embedSrcFile && m_hasLineNumberInfo; final IDGenerator pageIDNamespace = createAnchors ? new IDGenerator () : null; // child summary table is special for srcfile items: page.addH (2, "COVERAGE BREAKDOWN BY CLASS AND METHOD", null); final IntObjectMap lineAnchorIDMap = embedSrcFile ? new IntObjectMap () : null; final HTMLTable childSummaryTable = new HTMLTable ("100%", null, null, "0"); childSummaryTable.setClass (CSS_CLS_NOLEFT); { int [] headerColumns = null; final ItemComparator order = m_typeSortComparators [ClassItem.getTypeMetadata ().getTypeID ()]; int clsIndex = 0; for (Iterator classes = item.getChildren (order); classes.hasNext (); ++ clsIndex) { final ClassItem cls = (ClassItem) classes.next (); if (headerColumns == null) { // header row: headerColumns = addHeaderRow (cls, childSummaryTable, columns); } String HREFname = null; // special class subheader: if (createAnchors) { if ($assert.ENABLED) { $assert.ASSERT (lineAnchorIDMap != null); $assert.ASSERT (pageIDNamespace != null); } final String childKey = getItemKey (cls); HREFname = addLineAnchorID (cls.getFirstLine (), pageIDNamespace.getID (childKey), lineAnchorIDMap); } addClassRow (cls, clsIndex, childSummaryTable, headerColumns, HREFname, createAnchors); // // row to separate this class's methods: // final HTMLTable.IRow subheader = childSummaryTable.newTitleRow (); // final HTMLTable.ICell cell = subheader.newCell (); // // TODO: cell.setColspan (???) // cell.setText ("class " + child.getName () + " methods:", true); boolean odd = false; final ItemComparator order2 = m_typeSortComparators [MethodItem.getTypeMetadata ().getTypeID ()]; for (Iterator methods = cls.getChildren (order2); methods.hasNext (); odd = ! odd) { final MethodItem method = (MethodItem) methods.next (); HREFname = null; if (createAnchors) { if ($assert.ENABLED) { $assert.ASSERT (lineAnchorIDMap != null); $assert.ASSERT (pageIDNamespace != null); } final String child2Key = getItemKey (method); HREFname = addLineAnchorID (method.getFirstLine (), pageIDNamespace.getID (child2Key), lineAnchorIDMap); } addClassItemRow (method, odd, childSummaryTable, headerColumns, HREFname, createAnchors); } } } page.add (childSummaryTable); // embed source file: if (deeper) { //page.addHR (1); page.addEmptyP (); { embedSrcFile (item, page, lineAnchorIDMap, m_cache); } //page.addHR (1); } page.emit (out); out.flush (); } finally { if (out != null) out.close (); out = null; } return ctx; } public Object visit (final ClassItem item, final Object ctx) { // this visit only takes place in class views HTMLWriter out = null; try { final File outFile = getItemFile (NESTED_ITEMS_PARENT_DIR, m_reportIDNamespace.getID (getItemKey (item))); // TODO: deal with overwrites out = openOutFile (Files.newFile (m_settings.getOutDir (), outFile), m_settings.getOutEncoding (), true); final int [] columns = m_settings.getColumnOrder (); final StringBuffer buf = new StringBuffer (); // TODO: set title [from a prop?] final HTMLDocument page = createPage (REPORT_HEADER_TITLE); { final IItem [] path = getParentPath (item); addPageHeader (page, item, path); addPageFooter (page, item, path); } // summary table: { final IElement itemname = IElement.Factory.create (Tag.SPAN); itemname.setText (item.getName (), true); itemname.setClass (CSS_ITEM_NAME); final IElementList title = new ElementList (); title.add (new Text ("COVERAGE SUMMARY FOR CLASS [", true)); title.add (itemname); title.add (new Text ("]", true)); page.addH (1, title, null); } final HTMLTable summaryTable = new HTMLTable ("100%", null, null, "0"); { // header row: final HTMLTable.IRow header = summaryTable.newTitleRow (); // coverage row: final HTMLTable.IRow coverage = summaryTable.newRow (); for (int c = 0; c < columns.length; ++ c) { final int attrID = columns [c]; final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); final HTMLTable.ICell headercell = header.newCell (); headercell.setText (attr.getName (), true); if (attr != null) { boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]); buf.setLength (0); attr.format (item, buf); final HTMLTable.ICell cell = coverage.newCell (); cell.setText (buf.toString (), true); if (fail) cell.setClass (CSS_DATA_HIGHLIGHT); } } } page.add (summaryTable); // child summary table: page.addH (2, "COVERAGE BREAKDOWN BY METHOD", null); final HTMLTable childSummaryTable = new HTMLTable ("100%", null, null, "0"); { int [] headerColumns = null; boolean odd = true; final ItemComparator order = m_typeSortComparators [MethodItem.getTypeMetadata ().getTypeID ()]; for (Iterator methods = item.getChildren (order); methods.hasNext (); odd = ! odd) { final MethodItem method = (MethodItem) methods.next (); if (headerColumns == null) { // header row: headerColumns = addHeaderRow (method, childSummaryTable, columns); } addItemRow (method, odd, childSummaryTable, headerColumns, null, false); } } page.add (childSummaryTable); page.emit (out); out.flush (); } finally { if (out != null) out.close (); out = null; } return ctx; } // protected: ............................................................. // package: ............................................................... // private: ............................................................... private static final class IDGenerator { IDGenerator () { m_namespace = new ObjectIntMap (101); m_out = new int [1]; } IDGenerator (final int initialCapacity) { m_namespace = new ObjectIntMap (initialCapacity); m_out = new int [1]; } String getID (final String key) { final int [] out = m_out; final int ID; if (m_namespace.get (key, out)) ID = out [0]; else { ID = m_namespace.size (); m_namespace.put (key, ID); } return Integer.toHexString (ID); } private final ObjectIntMap /* key:String->ID */ m_namespace; private final int [] m_out; } // end of nested class private HTMLDocument createPage (final String title) { final HTMLDocument page = new HTMLDocument (title, m_settings.getOutEncoding ()); page.addStyle (CSS); // TODO: split by visit type return page; } private IElement addPageHeader (final HTMLDocument page, final IItem item, final IItem [] path) { // TODO: merge header and footer in the same method if ($assert.ENABLED) { $assert.ASSERT (page != null); } final HTMLTable header = new HTMLTable ("100%", null, null, "0"); header.setClass (CSS_HEADER_FOOTER); // header row: addPageHeaderTitleRow (header); // nav row: { final HTMLTable.IRow navRow = header.newRow (); final HTMLTable.ICell cell = navRow.newCell (); cell.setClass (CSS_NAV); final int lLimit = path.length > 1 ? path.length - 1 : path.length; for (int l = 0; l < lLimit; ++ l) { cell.add (LEFT_BRACKET); final String name = path [l].getName (); final String HREF = getItemHREF (item, path [l]); cell.add (new HyperRef (HREF, name, true)); cell.add (RIGHT_BRACKET); } } page.setHeader (header); return header; } private IElement addPageFooter (final HTMLDocument page, final IItem item, final IItem [] path) { if ($assert.ENABLED) { $assert.ASSERT (page != null); } final HTMLTable footerTable = new HTMLTable ("100%", null, null, "0"); footerTable.setClass (CSS_HEADER_FOOTER); // nav row: { final HTMLTable.IRow navRow = footerTable.newRow (); final HTMLTable.ICell cell = navRow.newCell (); cell.setClass (CSS_NAV); final int lLimit = path.length > 1 ? path.length - 1 : path.length; for (int l = 0; l < lLimit; ++ l) { cell.add (LEFT_BRACKET); final String name = path [l].getName (); final String HREF = getItemHREF (item, path [l]); cell.add (new HyperRef (HREF, name, true)); cell.add (RIGHT_BRACKET); } } // title row: { final HTMLTable.IRow titleRow = footerTable.newRow (); final HTMLTable.ICell cell = titleRow.newCell (); cell.setClass (CSS_TITLE); cell.add (getFooterBottom ()); } final ElementList footer = new ElementList (); footer.add (IElement.Factory.create (Tag.P)); // spacer footer.add (footerTable); page.setFooter (footer); return footerTable; } private int [] addHeaderRow (final IItem item, final HTMLTable table, final int [] columns) { if ($assert.ENABLED) { $assert.ASSERT (item != null, "null input: item"); $assert.ASSERT (table != null, "null input: table"); $assert.ASSERT (columns != null, "null input: columns"); } // header row: final HTMLTable.IRow header = table.newTitleRow (); // determine the set of columns actually present in the header [may be narrower than 'columns']: final IntVector headerColumns = new IntVector (columns.length); for (int c = 0; c < columns.length; ++ c) { final int attrID = columns [c]; final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); if (attr != null) { final HTMLTable.ICell cell = header.newCell (); cell.setText (attr.getName (), true);//.getAttributes ().set (Attribute.WIDTH, "20%"); cell.setClass (headerCellStyle (c)); headerColumns.add (attrID); } // note: by design this does not create columns for nonexistent attribute types } return headerColumns.values (); } /* * No header row, just data rows. */ private void addItemRow (final IItem item, final boolean odd, final HTMLTable table, final int [] columns, final String nameHREF, final boolean anchor) { if ($assert.ENABLED) { $assert.ASSERT (item != null, "null input: item"); $assert.ASSERT (table != null, "null input: table"); $assert.ASSERT (columns != null, "null input: columns"); } final HTMLTable.IRow row = table.newRow (); if (odd) row.setClass (CSS_ODDROW); final StringBuffer buf = new StringBuffer (11); // TODO: reuse a buffer for (int c = 0; c < columns.length; ++ c) { final int attrID = columns [c]; final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); if (attr != null) { final HTMLTable.ICell cell = row.newCell (); if ((nameHREF != null) && (attrID == IItemAttribute.ATTRIBUTE_NAME_ID)) { buf.setLength (0); attr.format (item, buf); trimForDisplay (buf); final String fullHREFName = anchor ? "#".concat (nameHREF) : nameHREF; cell.add (new HyperRef (fullHREFName, buf.toString (), true)); } else { final boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]); buf.setLength (0); attr.format (item, buf); trimForDisplay (buf); cell.setText (buf.toString (), true); if (fail) cell.setClass (CSS_DATA_HIGHLIGHT); } } else { // note: by design this puts empty cells for nonexistent attribute types final HTMLTable.ICell cell = row.newCell (); cell.setText (" ", true); } } } private void addClassRow (final ClassItem item, final int clsIndex, final HTMLTable table, final int [] columns, final String itemHREF, final boolean isAnchor) { if ($assert.ENABLED) { $assert.ASSERT (item != null, "null input: item"); $assert.ASSERT (table != null, "null input: table"); $assert.ASSERT (columns != null, "null input: columns"); } final HTMLTable.IRow blank = table.newRow (); final HTMLTable.IRow row = table.newRow (); row.setClass (CSS_CLASS_ITEM_SPECIAL); final StringBuffer buf = new StringBuffer (11); for (int c = 0; c < columns.length; ++ c) { final int attrID = columns [c]; final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); if (attr != null) { buf.setLength (0); attr.format (item, buf); final HTMLTable.ICell blankcell = blank.newCell (); blankcell.setClass (clsIndex == 0 ? CSS_BLANK : CSS_BOTTOM); blankcell.setText (" ", true); final HTMLTable.ICell cell = row.newCell (); boolean fail = false; if (attrID == IItemAttribute.ATTRIBUTE_NAME_ID) { if (itemHREF != null) { final String fullItemHREF = isAnchor ? "#".concat (itemHREF) : itemHREF; cell.add (new Text ("class ", true)); cell.add (new HyperRef (fullItemHREF, buf.toString (), true)); } else { cell.setText ("class " + buf.toString (), true); } } else { fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]); cell.setText (buf.toString (), true); } cell.setClass (dataCellStyle (c, fail)); } else { final HTMLTable.ICell cell = row.newCell (); cell.setText (" ", true); cell.setClass (dataCellStyle (c, false)); } } } private void addClassItemRow (final IItem item, final boolean odd, final HTMLTable table, final int [] columns, final String nameHREF, final boolean anchor) { if ($assert.ENABLED) { $assert.ASSERT (item != null, "null input: item"); $assert.ASSERT (table != null, "null input: table"); $assert.ASSERT (columns != null, "null input: columns"); } final HTMLTable.IRow row = table.newRow (); if (odd) row.setClass (CSS_ODDROW); final StringBuffer buf = new StringBuffer (11); // TODO: reuse a buffer for (int c = 0; c < columns.length; ++ c) { final int attrID = columns [c]; final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); if (attr != null) { final HTMLTable.ICell cell = row.newCell (); boolean fail = false; if ((nameHREF != null) && (attrID == IItemAttribute.ATTRIBUTE_NAME_ID)) { buf.setLength (0); attr.format (item, buf); trimForDisplay (buf); final String fullHREFName = anchor ? "#".concat (nameHREF) : nameHREF; cell.add (new HyperRef (fullHREFName, buf.toString (), true)); } else { fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]); buf.setLength (0); attr.format (item, buf); trimForDisplay (buf); cell.setText (buf.toString (), true); } cell.setClass (dataCellStyle (c, fail)); } else { // note: by design this puts empty cells for nonexistent attribute types final HTMLTable.ICell cell = row.newCell (); cell.setText (" ", true); cell.setClass (dataCellStyle (c, false)); } } } private boolean srcFileAvailable (final SrcFileItem item, final SourcePathCache cache) { if (cache == null) return false; if ($assert.ENABLED) $assert.ASSERT (item != null, "null input: item"); final String fileName = item.getName (); if ($assert.ENABLED) $assert.ASSERT (fileName.endsWith (".java"), "cache only handles .java extensions"); // TODO: should I keep VM names in package items? final String packageVMName = ((PackageItem) item.getParent ()).getVMName (); return (cache.find (packageVMName, fileName) != null); } // private boolean srcFileAvailable (final ClassItem item, final SourcePathCache cache) // { // if (cache == null) return false; // // if ($assert.ENABLED) $assert.ASSERT (item != null, "null input: item"); // // final String fileName = item.getSrcFileName (); // if ($assert.ENABLED) $assert.ASSERT (fileName.endsWith (".java"), "cache only handles .java extensions"); // // // TODO: should I keep VM names in package items? // final String packageVMName = ((PackageItem) item.getParent ()).getVMName (); // // return (cache.find (packageVMName, fileName) != null); // } private void embedSrcFile (final SrcFileItem item, final HTMLDocument page, final IntObjectMap /* line num:int->anchor name:String */anchorMap, final SourcePathCache cache) { if ($assert.ENABLED) { $assert.ASSERT (item != null, "null input: item"); $assert.ASSERT (page != null, "null input: page"); } final String fileName = item.getName (); if ($assert.ENABLED) $assert.ASSERT (fileName.endsWith (".java"), "cache only handles .java extensions"); // TODO: should I keep VM names in package items? final String packageVMName = ((PackageItem) item.getParent ()).getVMName (); boolean success = false; final HTMLTable srcTable = new HTMLTable ("100%", null, null, "0"); if (cache != null) // TODO: do this check earlier, in outer scope { srcTable.setClass (CSS_SOURCE); final File srcFile = cache.find (packageVMName, fileName); if (srcFile != null) { BufferedReader in = null; try { in = new BufferedReader (new FileReader (srcFile), IO_BUF_SIZE); final boolean markupCoverage = m_hasLineNumberInfo; final int unitsType = m_settings.getUnitsType (); IntObjectMap /* line num:int -> SrcFileItem.LineCoverageData */ lineCoverageMap = null; StringBuffer tooltipBuffer = null; if (markupCoverage) { lineCoverageMap = item.getLineCoverage (); $assert.ASSERT (lineCoverageMap != null, "null: lineCoverageMap"); tooltipBuffer = new StringBuffer (64); } int l = 1; for (String line; (line = in.readLine ()) != null; ++ l) { final HTMLTable.IRow srcline = srcTable.newRow (); final HTMLTable.ICell lineNumCell = srcline.newCell (); lineNumCell.setClass (CSS_LINENUM); if (anchorMap != null) { final int adjustedl = l < SRC_LINE_OFFSET ? l : l + SRC_LINE_OFFSET; final String anchor = (String) anchorMap.get (adjustedl); if (anchor != null) { final IElement a = IElement.Factory.create (Tag.A); //a.getAttributes ().set (Attribute.ID, anchor); ID anchoring does not work in NS 4.0 a.getAttributes ().set (Attribute.NAME, anchor); a.setText (Integer.toString (l), true); lineNumCell.add (a); } else { lineNumCell.setText (Integer.toString (l), true); } } else { lineNumCell.setText (Integer.toString (l), true); } final HTMLTable.ICell lineTxtCell = srcline.newCell (); lineTxtCell.setText (line.length () > 0 ? line : " ", true); if (markupCoverage) { final SrcFileItem.LineCoverageData lCoverageData = (SrcFileItem.LineCoverageData) lineCoverageMap.get (l); if (lCoverageData != null) { switch (lCoverageData.m_coverageStatus) { case SrcFileItem.LineCoverageData.LINE_COVERAGE_ZERO: srcline.setClass (CSS_COVERAGE_ZERO); break; case SrcFileItem.LineCoverageData.LINE_COVERAGE_PARTIAL: { srcline.setClass (CSS_COVERAGE_PARTIAL); if (USE_LINE_COVERAGE_TOOLTIPS) { tooltipBuffer.setLength (0); final int [] coverageRatio = lCoverageData.m_coverageRatio [unitsType]; final int d = coverageRatio [0]; final int n = coverageRatio [1]; m_format.format ((double) n / d, tooltipBuffer, m_fieldPosition); tooltipBuffer.append (" line coverage ("); tooltipBuffer.append (n); tooltipBuffer.append (" out of "); tooltipBuffer.append (d); switch (unitsType) { case IItemAttribute.UNITS_COUNT: tooltipBuffer.append (" basic blocks)"); break; case IItemAttribute.UNITS_INSTR: tooltipBuffer.append (" instructions)"); break; } // [Opera does not display TITLE tooltios on