/* 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.1.1.1.2.1 2004/07/16 23:32:30 vlad_r Exp $ */ package com.vladium.emma.report.txt; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import com.vladium.util.Files; import com.vladium.util.IProperties; 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.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; // ---------------------------------------------------------------------------- /** * @author Vlad Roubtsov, (C) 2003 */ public final class ReportGenerator extends AbstractReportGenerator implements IAppErrorCodes { // public: ................................................................ // TODO: this is prototype quality, needs major cleanup // IReportGenerator: public 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); long start = 0, end; final boolean trace1 = m_log.atTRACE1 (); if (trace1) start = System.currentTimeMillis (); { m_queue = new LinkedList (); for (m_queue.add (m_view.getRoot ()); ! m_queue.isEmpty (); ) { final IItem head = (IItem) m_queue.removeFirst (); head.accept (this, null); } line (); close (); } if (trace1) { end = System.currentTimeMillis (); m_log.trace1 ("process", "[" + getType () + "] report generated in " + (end - start) + " ms"); } } public void cleanup () { m_queue = null; close (); super.cleanup (); } // IItemVisitor: public Object visit (final AllItem item, final Object ctx) { File outFile = m_settings.getOutFile (); if (outFile == null) { outFile = new File ("coverage.txt"); m_settings.setOutFile (outFile); } final File fullOutFile = Files.newFile (m_settings.getOutDir (), outFile); m_log.info ("writing [" + getType () + "] report to [" + fullOutFile.getAbsolutePath () + "] ..."); openOutFile (fullOutFile, m_settings.getOutEncoding (), true); // build ID stamp: try { final StringBuffer label = new StringBuffer (101); label.append ("["); label.append (IAppConstants.APP_NAME); label.append (" v"); label.append (IAppConstants.APP_VERSION_WITH_BUILD_ID_AND_TAG); label.append (" report, generated "); label.append (new Date (EMMAProperties.getTimeStamp ())); label.append ("]"); m_out.write (label.toString ()); m_out.newLine (); m_out.flush (); } catch (IOException ioe) { throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); } final int [] columns = m_settings.getColumnOrder (); line (); // [all] coverage summary row: addTitleRow ("OVERALL COVERAGE SUMMARY", 0, 1); { // header row: addHeaderRow (item, columns); // coverage row: addItemRow (item, columns); } // [all] stats summary table ([all] only): addTitleRow ("OVERALL STATS SUMMARY", 1, 1); { row ("total packages:" + m_separator + item.getChildCount ()); row ("total classes:" + m_separator + item.getAggregate (IItem.TOTAL_CLASS_COUNT)); row ("total methods:" + m_separator + item.getAggregate (IItem.TOTAL_METHOD_COUNT)); if (m_srcView && m_hasSrcFileInfo) { row ("total executable files:" + m_separator + item.getAggregate (IItem.TOTAL_SRCFILE_COUNT)); if (m_hasLineNumberInfo) row ("total executable lines:" + m_separator + item.getAggregate (IItem.TOTAL_LINE_COUNT)); } } final boolean deeper = (m_settings.getDepth () > item.getMetadata ().getTypeID ()); // render package summary rows: addTitleRow ("COVERAGE BREAKDOWN BY PACKAGE", 1, 1); { boolean headerDone = false; final ItemComparator order = m_typeSortComparators [PackageItem.getTypeMetadata ().getTypeID ()]; for (Iterator packages = item.getChildren (order); packages.hasNext (); ) { final IItem pkg = (IItem) packages.next (); if (! headerDone) { // header row: addHeaderRow (pkg, columns); headerDone = true; } // coverage row: addItemRow (pkg, columns); if (deeper) m_queue.addLast (pkg); } } return ctx; } public Object visit (final PackageItem item, final Object ctx) { if (m_verbose) m_log.verbose (" report: processing package [" + item.getName () + "] ..."); final int [] columns = m_settings.getColumnOrder (); line (); // coverage summary row: addTitleRow ("COVERAGE SUMMARY FOR PACKAGE [".concat (item.getName ()).concat ("]"), 0, 1); { // header row: addHeaderRow (item, columns); // coverage row: addItemRow (item, columns); } final boolean deeper = (m_settings.getDepth () > item.getMetadata ().getTypeID ()); // render child summary rows: final String summaryTitle = m_srcView ? "COVERAGE BREAKDOWN BY SOURCE FILE" : "COVERAGE BREAKDOWN BY CLASS"; addTitleRow (summaryTitle, 1, 1); { boolean headerDone = false; final ItemComparator order = m_typeSortComparators [m_srcView ? SrcFileItem.getTypeMetadata ().getTypeID () : ClassItem.getTypeMetadata ().getTypeID ()]; for (Iterator srcORclsFiles = item.getChildren (order); srcORclsFiles.hasNext (); ) { final IItem srcORcls = (IItem) srcORclsFiles.next (); if (! headerDone) { // header row: addHeaderRow (srcORcls, columns); headerDone = true; } // coverage row: addItemRow (srcORcls, columns); if (deeper) m_queue.addLast (srcORcls); } } return ctx; } public Object visit (final SrcFileItem item, final Object ctx) { final int [] columns = m_settings.getColumnOrder (); line (); // coverage summary row: addTitleRow ("COVERAGE SUMMARY FOR SOURCE FILE [".concat (item.getName ()).concat ("]"), 0, 1); { // header row: addHeaderRow (item, columns); // coverage row: addItemRow (item, columns); } // render child summary rows: addTitleRow ("COVERAGE BREAKDOWN BY CLASS AND METHOD", 1, 1); { boolean headerDone = false; final ItemComparator order = m_typeSortComparators [ClassItem.getTypeMetadata ().getTypeID ()]; for (Iterator classes = item.getChildren (order); classes.hasNext (); ) { final IItem cls = (IItem) classes.next (); if (! headerDone) { // header row: addHeaderRow (cls, columns); headerDone = true; } // coverage row: //addItemRow (child, columns); addTitleRow ("class [" + cls.getName () + "] methods", 0, 0); // TODO: select the right comparator here final ItemComparator order2 = m_typeSortComparators [MethodItem.getTypeMetadata ().getTypeID ()]; for (Iterator methods = cls.getChildren (order2); methods.hasNext (); ) { final MethodItem method = (MethodItem) methods.next (); addItemRow (method, columns); } } } return ctx; } public Object visit (final ClassItem item, final Object ctx) { final int [] columns = m_settings.getColumnOrder (); line (); // coverage summary row: addTitleRow ("COVERAGE SUMMARY FOR CLASS [".concat (item.getName ()).concat ("]"), 0, 1); { // header row: addHeaderRow (item, columns); // coverage row: addItemRow (item, columns); } // render child summary rows: addTitleRow ("COVERAGE BREAKDOWN BY METHOD", 1, 1); { final ItemComparator order = m_typeSortComparators [MethodItem.getTypeMetadata ().getTypeID ()]; for (Iterator methods = item.getChildren (order); methods.hasNext (); ) { final IItem method = (IItem) methods.next (); // coverage row: addItemRow (method, columns); } } return ctx; } // protected: ............................................................. // package: ............................................................... // private: ............................................................... private void addTitleRow (final String text, final int hlines, final int flines) { for (int i = 0; i < hlines; ++ i) eol (); row (new StringBuffer (text).append (":")); for (int i = 0; i < flines; ++ i) eol (); } private void addHeaderRow (final IItem item, final int [] columns) { if ($assert.ENABLED) { $assert.ASSERT (item != null, "null input: item"); $assert.ASSERT (columns != null, "null input: columns"); } // header row: final StringBuffer buf = new StringBuffer (); for (int c = 0, cLimit = columns.length; c < cLimit; ++ c) { final int attrID = columns [c]; final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); if (attr != null) { buf.append ('['); buf.append (attr.getName ()); buf.append (']'); } if (c != cLimit - 1) buf.append (m_separator); } row (buf); } /* * No header row, just data rows. */ private void addItemRow (final IItem item, final int [] columns) { if ($assert.ENABLED) { $assert.ASSERT (item != null, "null input: item"); $assert.ASSERT (columns != null, "null input: columns"); } final StringBuffer buf = new StringBuffer (11); // TODO: reuse a buffer for (int c = 0, cLimit = columns.length; c < cLimit; ++ c) { final int attrID = columns [c]; final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); if (attr != null) { boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]); if (fail) { //buf.append ('('); //buf.append ("! "); attr.format (item, buf); buf.append ('!'); //buf.append (')'); } else { attr.format (item, buf); } } if (c != cLimit - 1) buf.append (m_separator); } row (buf); } private void row (final StringBuffer str) { if ($assert.ENABLED) $assert.ASSERT (str != null, "str = null"); try { m_out.write (str.toString ()); m_out.newLine (); } catch (IOException ioe) { throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); } } private void row (final String str) { if ($assert.ENABLED) $assert.ASSERT (str != null, "str = null"); try { m_out.write (str); m_out.newLine (); } catch (IOException ioe) { throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); } } private void line () { try { m_out.write (LINE); m_out.newLine (); } catch (IOException ioe) { throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); } } private void eol () { try { m_out.newLine (); } catch (IOException ioe) { throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); } } private void close () { if (m_out != null) { try { m_out.flush (); m_out.close (); } catch (IOException ioe) { throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); } finally { m_out = null; } } } private void openOutFile (final File file, final String encoding, final boolean mkdirs) { try { if (mkdirs) { final File parent = file.getParentFile (); if (parent != null) parent.mkdirs (); } m_out = new BufferedWriter (new OutputStreamWriter (new FileOutputStream (file), encoding), IO_BUF_SIZE); } catch (UnsupportedEncodingException uee) { // TODO: error code throw new EMMARuntimeException (uee); } // note: in J2SDK 1.3 FileOutputStream constructor's throws clause // was narrowed to FileNotFoundException: catch (IOException fnfe) // FileNotFoundException { // TODO: error code throw new EMMARuntimeException (fnfe); } } private char m_separator = '\t'; // TODO: set this private LinkedList /* IITem */ m_queue; private BufferedWriter m_out; private static final String TYPE = "txt"; private static final String LINE = "-------------------------------------------------------------------------------"; private static final int IO_BUF_SIZE = 32 * 1024; } // end of class // ----------------------------------------------------------------------------