/* 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: AbstractReportGenerator.java,v 1.1.1.1.2.4 2005/04/24 23:51:37 vlad_r Exp $ */ package com.vladium.emma.report; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import com.vladium.logging.Logger; import com.vladium.util.Descriptors; import com.vladium.util.IProperties; import com.vladium.util.IntIntMap; import com.vladium.util.IntVector; import com.vladium.util.ObjectIntMap; import com.vladium.emma.EMMARuntimeException; import com.vladium.emma.data.ClassDescriptor; import com.vladium.emma.data.ICoverageData; import com.vladium.emma.data.IMetaData; // ---------------------------------------------------------------------------- /** * @author Vlad Roubtsov, (C) 2003 */ public abstract class AbstractReportGenerator extends AbstractItemVisitor implements IReportGenerator { // public: ................................................................ public static IReportGenerator create (final String type) { if ((type == null) || (type.length () == 0)) throw new IllegalArgumentException ("null/empty input: type"); // TODO: proper pluggability pattern here if ("html".equals (type)) return new com.vladium.emma.report.html.ReportGenerator (); else if ("lcov".equals (type)) return new com.vladium.emma.report.lcov.ReportGenerator (); else if ("txt".equals (type)) return new com.vladium.emma.report.txt.ReportGenerator (); else if ("xml".equals (type)) return new com.vladium.emma.report.xml.ReportGenerator (); else // TODO: error code throw new EMMARuntimeException ("no report generator class found for type [" + type + "]"); } public void initialize (final IMetaData mdata, final ICoverageData cdata, final SourcePathCache cache, final IProperties properties) throws EMMARuntimeException { m_log = Logger.getLogger (); m_verbose = m_log.atVERBOSE (); m_settings = ReportProperties.parseProperties (properties, getType ()); m_cache = cache; m_hasSrcFileInfo = mdata.hasSrcFileData (); m_hasLineNumberInfo = mdata.hasLineNumberData (); boolean debugInfoWarning = false; boolean bailOut = false; // src view is not possible if 'm_hasSrcFileInfo' is false: if (! mdata.hasSrcFileData () && (m_settings.getViewType () == IReportDataView.HIER_SRC_VIEW)) { debugInfoWarning = true; m_log.warning ("not all instrumented classes were compiled with source file"); m_log.warning ("debug data: no sources will be embedded in the report."); m_settings.setViewType (IReportDataView.HIER_CLS_VIEW); } // line coverage column must be removed if 'm_hasLineNumberInfo' is false: if (! m_hasLineNumberInfo) { final int [] userColumnIDs = m_settings.getColumnOrder (); final IntVector columnIDs = new IntVector (); boolean removed = false; for (int c = 0; c < userColumnIDs.length; ++ c) { if (userColumnIDs [c] == IItemAttribute.ATTRIBUTE_LINE_COVERAGE_ID) removed = true; else columnIDs.add (userColumnIDs [c]); } // at this point it is possible that there are no columns left: bail out if (removed) { debugInfoWarning = true; if (columnIDs.size () == 0) { m_log.warning ("line coverage requested in a report of type [" + getType () + "] but"); m_log.warning ("not all instrumented classes were compiled with line number"); m_log.warning ("debug data: since this was the only requested column, no report will be generated."); bailOut = true; } else { m_log.warning ("line coverage requested in a report of type [" + getType () + "] but"); m_log.warning ("not all instrumented classes were compiled with line number"); m_log.warning ("debug data: this column will be removed from the report."); m_settings.setColumnOrder (columnIDs.values ()); final int [] userSort = m_settings.getSortOrder (); final IntVector sort = new IntVector (); for (int c = 0; c < userSort.length; c += 2) { if (Math.abs (userSort [c]) != IItemAttribute.ATTRIBUTE_LINE_COVERAGE_ID) { sort.add (userSort [c]); sort.add (userSort [c + 1]); } } m_settings.setSortOrder (sort.values ()); } } } // note: no need to adjust m_metrics due to possible column removal above // SF FR 971176: provide user with sample classes that caused the above warnings if (debugInfoWarning && m_log.atINFO ()) { final Set /* String */ sampleClassNames = new TreeSet (); final ObjectIntMap /* packageVMName:String -> count:int */ countMap = new ObjectIntMap (); final int [] _count = new int [1]; for (Iterator /* ClassDescriptor */ descriptors = mdata.iterator (); descriptors.hasNext (); ) { final ClassDescriptor cls = (ClassDescriptor) descriptors.next (); // SF BUG 979717: this check was incorrectly absent in the initial FR impl: if (! cls.hasCompleteLineNumberInfo () || ! cls.hasSrcFileInfo ()) { final String packageVMName = cls.getPackageVMName (); final int count = countMap.get (packageVMName, _count) ? _count [0] : 0; if (count < MAX_DEBUG_INFO_WARNING_COUNT) { sampleClassNames.add (Descriptors.vmNameToJavaName (cls.getClassVMName ())); countMap.put (packageVMName, count + 1); } } } m_log.info ("showing up to " + MAX_DEBUG_INFO_WARNING_COUNT + " classes without full debug info per package:"); for (Iterator /* String */ names = sampleClassNames.iterator (); names.hasNext (); ) { m_log.info (" " + names.next ()); } } if (bailOut) { // TODO: error code throw new EMMARuntimeException ("BAILED OUT"); } final IItemMetadata [] allTypes = IItemMetadata.Factory.getAllTypes (); m_typeSortComparators = new ItemComparator [allTypes.length]; for (int t = 0; t < allTypes.length; ++ t) { final IntVector orderedAttrIDsWithDir = new IntVector (); final long typeAttrIDSet = allTypes [t].getAttributeIDs (); for (int s = 0; s < m_settings.getSortOrder ().length; s += 2) { final int attrID = m_settings.getSortOrder () [s]; if ((typeAttrIDSet & (1 << attrID)) != 0) { orderedAttrIDsWithDir.add (attrID); final int dir = m_settings.getSortOrder () [s + 1]; orderedAttrIDsWithDir.add (dir); } } m_typeSortComparators [t] = ItemComparator.Factory.create (orderedAttrIDsWithDir.values (), m_settings.getUnitsType ()); } m_metrics = new int [allTypes.length]; final IntIntMap metrics = m_settings.getMetrics (); for (int t = 0; t < m_metrics.length; ++ t) { m_metrics [t] = -1; metrics.get (t, m_metrics, t); } final IReportDataModel model = IReportDataModel.Factory.create (mdata, cdata); m_view = model.getView (m_settings.getViewType ()); m_srcView = (m_settings.getViewType () == IReportDataView.HIER_SRC_VIEW); } public void cleanup () { reset (); } // protected: ............................................................. protected void reset () { m_settings = null; m_cache = null; m_view = null; m_srcView = false; //m_typeSortIDs = null; m_typeSortComparators = null; m_metrics = null; m_log = null; } protected ReportProperties.ParsedProperties m_settings; protected SourcePathCache m_cache; protected IReportDataView m_view; protected boolean m_srcView; protected boolean m_hasSrcFileInfo, m_hasLineNumberInfo; protected ItemComparator [] m_typeSortComparators; // m_typeSortComparators [t] is a comparator representing the sort order for item type t protected int [] m_metrics; // -1 means no pass/fail check for this attribute protected Logger m_log; // every report generator is used on a single thread but the logger needs to be run()-scoped protected boolean m_verbose; // package: ............................................................... // private: ............................................................... private static final int MAX_DEBUG_INFO_WARNING_COUNT = 3; // per package } // end of class // ----------------------------------------------------------------------------