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: AbstractReportGenerator.java,v 1.1.1.1.2.4 2005/04/24 23:51:37 vlad_r Exp $
8 */
9package com.vladium.emma.report;
10
11import java.util.Iterator;
12import java.util.Set;
13import java.util.TreeSet;
14
15import com.vladium.logging.Logger;
16import com.vladium.util.Descriptors;
17import com.vladium.util.IProperties;
18import com.vladium.util.IntIntMap;
19import com.vladium.util.IntVector;
20import com.vladium.util.ObjectIntMap;
21import com.vladium.emma.EMMARuntimeException;
22import com.vladium.emma.data.ClassDescriptor;
23import com.vladium.emma.data.ICoverageData;
24import com.vladium.emma.data.IMetaData;
25
26// ----------------------------------------------------------------------------
27/**
28 * @author Vlad Roubtsov, (C) 2003
29 */
30public
31abstract class AbstractReportGenerator extends AbstractItemVisitor
32                                       implements IReportGenerator
33{
34    // public: ................................................................
35
36
37    public static IReportGenerator create (final String type)
38    {
39        if ((type == null) || (type.length () == 0))
40            throw new IllegalArgumentException ("null/empty input: type");
41
42        // TODO: proper pluggability pattern here
43
44        if ("html".equals (type))
45            return new com.vladium.emma.report.html.ReportGenerator ();
46        else if ("lcov".equals (type))
47            return new com.vladium.emma.report.lcov.ReportGenerator ();
48        else if ("txt".equals (type))
49            return new com.vladium.emma.report.txt.ReportGenerator ();
50        else if ("xml".equals (type))
51            return new com.vladium.emma.report.xml.ReportGenerator ();
52        else // TODO: error code
53            throw new EMMARuntimeException ("no report generator class found for type [" + type + "]");
54    }
55
56
57    public void initialize (final IMetaData mdata, final ICoverageData cdata,
58                            final SourcePathCache cache, final IProperties properties)
59        throws EMMARuntimeException
60    {
61        m_log = Logger.getLogger ();
62        m_verbose = m_log.atVERBOSE ();
63
64        m_settings = ReportProperties.parseProperties (properties, getType ());
65
66        m_cache = cache;
67
68        m_hasSrcFileInfo = mdata.hasSrcFileData ();
69        m_hasLineNumberInfo = mdata.hasLineNumberData ();
70
71        boolean debugInfoWarning = false;
72        boolean bailOut = false;
73
74        // src view is not possible if 'm_hasSrcFileInfo' is false:
75        if (! mdata.hasSrcFileData () && (m_settings.getViewType () == IReportDataView.HIER_SRC_VIEW))
76        {
77            debugInfoWarning = true;
78
79            m_log.warning ("not all instrumented classes were compiled with source file");
80            m_log.warning ("debug data: no sources will be embedded in the report.");
81
82            m_settings.setViewType (IReportDataView.HIER_CLS_VIEW);
83        }
84
85        // line coverage column must be removed if 'm_hasLineNumberInfo' is false:
86        if (! m_hasLineNumberInfo)
87        {
88            final int [] userColumnIDs = m_settings.getColumnOrder ();
89            final IntVector columnIDs = new IntVector ();
90
91            boolean removed = false;
92            for (int c = 0; c < userColumnIDs.length; ++ c)
93            {
94                if (userColumnIDs [c] == IItemAttribute.ATTRIBUTE_LINE_COVERAGE_ID)
95                    removed = true;
96                else
97                    columnIDs.add (userColumnIDs [c]);
98            }
99
100            // at this point it is possible that there are no columns left: bail out
101            if (removed)
102            {
103                debugInfoWarning = true;
104
105                if (columnIDs.size () == 0)
106                {
107                    m_log.warning ("line coverage requested in a report of type [" + getType () + "] but");
108                    m_log.warning ("not all instrumented classes were compiled with line number");
109                    m_log.warning ("debug data: since this was the only requested column, no report will be generated.");
110
111                    bailOut = true;
112                }
113                else
114                {
115                    m_log.warning ("line coverage requested in a report of type [" + getType () + "] but");
116                    m_log.warning ("not all instrumented classes were compiled with line number");
117                    m_log.warning ("debug data: this column will be removed from the report.");
118
119                    m_settings.setColumnOrder (columnIDs.values ());
120
121                    final int [] userSort = m_settings.getSortOrder ();
122                    final IntVector sort = new IntVector ();
123
124                    for (int c = 0; c < userSort.length; c += 2)
125                    {
126                        if (Math.abs (userSort [c]) != IItemAttribute.ATTRIBUTE_LINE_COVERAGE_ID)
127                        {
128                            sort.add (userSort [c]);
129                            sort.add (userSort [c + 1]);
130                        }
131                    }
132
133                    m_settings.setSortOrder (sort.values ());
134                }
135            }
136        }
137        // note: no need to adjust m_metrics due to possible column removal above
138
139        // SF FR 971176: provide user with sample classes that caused the above warnings
140        if (debugInfoWarning && m_log.atINFO ())
141        {
142            final Set /* String */ sampleClassNames = new TreeSet ();
143            final ObjectIntMap /* packageVMName:String -> count:int */ countMap = new ObjectIntMap ();
144            final int [] _count = new int [1];
145
146            for (Iterator /* ClassDescriptor */ descriptors = mdata.iterator (); descriptors.hasNext (); )
147            {
148                final ClassDescriptor cls = (ClassDescriptor) descriptors.next ();
149
150                // SF BUG 979717: this check was incorrectly absent in the initial FR impl:
151                if (! cls.hasCompleteLineNumberInfo () || ! cls.hasSrcFileInfo ())
152                {
153                    final String packageVMName = cls.getPackageVMName ();
154                    final int count = countMap.get (packageVMName, _count)
155                        ? _count [0]
156                        : 0;
157
158                    if (count < MAX_DEBUG_INFO_WARNING_COUNT)
159                    {
160                        sampleClassNames.add (Descriptors.vmNameToJavaName (cls.getClassVMName ()));
161                        countMap.put (packageVMName, count + 1);
162                    }
163                }
164            }
165
166            m_log.info ("showing up to " + MAX_DEBUG_INFO_WARNING_COUNT + " classes without full debug info per package:");
167            for (Iterator /* String */ names = sampleClassNames.iterator (); names.hasNext (); )
168            {
169                m_log.info ("  " + names.next ());
170            }
171        }
172
173        if (bailOut)
174        {
175            // TODO: error code
176            throw new EMMARuntimeException ("BAILED OUT");
177        }
178
179        final IItemMetadata [] allTypes = IItemMetadata.Factory.getAllTypes ();
180        m_typeSortComparators = new ItemComparator [allTypes.length];
181
182        for (int t = 0; t < allTypes.length; ++ t)
183        {
184            final IntVector orderedAttrIDsWithDir = new IntVector ();
185            final long typeAttrIDSet = allTypes [t].getAttributeIDs ();
186
187            for (int s = 0; s < m_settings.getSortOrder ().length; s += 2)
188            {
189                final int attrID = m_settings.getSortOrder () [s];
190
191                if ((typeAttrIDSet & (1 << attrID)) != 0)
192                {
193                    orderedAttrIDsWithDir.add (attrID);
194
195                    final int dir = m_settings.getSortOrder () [s + 1];
196                    orderedAttrIDsWithDir.add (dir);
197                }
198            }
199
200            m_typeSortComparators [t] = ItemComparator.Factory.create (orderedAttrIDsWithDir.values (), m_settings.getUnitsType ());
201        }
202
203        m_metrics = new int [allTypes.length];
204        final IntIntMap metrics = m_settings.getMetrics ();
205        for (int t = 0; t < m_metrics.length; ++ t)
206        {
207            m_metrics [t] = -1;
208            metrics.get (t, m_metrics, t);
209        }
210
211        final IReportDataModel model = IReportDataModel.Factory.create (mdata, cdata);
212        m_view = model.getView (m_settings.getViewType ());
213
214        m_srcView = (m_settings.getViewType () == IReportDataView.HIER_SRC_VIEW);
215    }
216
217    public void cleanup ()
218    {
219        reset ();
220    }
221
222    // protected: .............................................................
223
224
225    protected void reset ()
226    {
227        m_settings = null;
228        m_cache = null;
229        m_view = null;
230        m_srcView = false;
231
232        //m_typeSortIDs = null;
233        m_typeSortComparators = null;
234        m_metrics = null;
235
236        m_log = null;
237    }
238
239
240    protected ReportProperties.ParsedProperties m_settings;
241    protected SourcePathCache m_cache;
242    protected IReportDataView m_view;
243    protected boolean m_srcView;
244
245    protected boolean m_hasSrcFileInfo, m_hasLineNumberInfo;
246    protected ItemComparator [] m_typeSortComparators; // m_typeSortComparators [t] is a comparator representing the sort order for item type t
247    protected int [] m_metrics; // -1 means no pass/fail check for this attribute
248
249    protected Logger m_log; // every report generator is used on a single thread but the logger needs to be run()-scoped
250    protected boolean m_verbose;
251
252    // package: ...............................................................
253
254    // private: ...............................................................
255
256
257    private static final int MAX_DEBUG_INFO_WARNING_COUNT = 3; // per package
258
259} // end of class
260// ----------------------------------------------------------------------------
261