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: SrcFileItem.java,v 1.1.1.1.2.1 2004/06/20 20:07:22 vlad_r Exp $
8 */
9package com.vladium.emma.report;
10
11import java.util.Iterator;
12
13import com.vladium.util.IntObjectMap;
14import com.vladium.util.asserts.$assert;
15import com.vladium.emma.data.ClassDescriptor;
16import com.vladium.emma.data.MethodDescriptor;
17
18// ----------------------------------------------------------------------------
19/**
20 * @author Vlad Roubtsov, (C) 2003
21 */
22public
23final class SrcFileItem extends Item
24{
25    // public: ................................................................
26
27
28    public final class LineCoverageData
29    {
30        public static final int LINE_COVERAGE_ZERO = 0;
31        public static final int LINE_COVERAGE_PARTIAL = 1;
32        public static final int LINE_COVERAGE_COMPLETE = 2;
33
34        public final int m_coverageStatus;
35        public final int [/* units mode */][/* total, coverage */] m_coverageRatio; // not null in LINE_COVERAGE_PARTIAL status only]
36
37        LineCoverageData (final int coverageStatus, final int [][] coverageRatio)
38        {
39            m_coverageStatus = coverageStatus;
40            m_coverageRatio = coverageRatio;
41        }
42
43    } // end of nested class
44
45
46    public SrcFileItem (final IItem parent, final String name, final String fullVMName)
47    {
48        super (parent);
49
50        m_name = name;
51        m_fullVMName = fullVMName;
52    }
53
54    public String getName ()
55    {
56        return m_name;
57    }
58
59    public String getFullVMName ()
60    {
61        return m_fullVMName;
62    }
63
64    public int getFirstLine ()
65    {
66        // TODO: state validation
67
68        if (m_firstLine == 0)
69        {
70            getAggregate (TOTAL_LINE_COUNT); // fault line coverage calculation
71        }
72
73        return m_firstLine;
74    }
75
76
77
78
79    public IntObjectMap /* line_no:int -> LineCoverageData */ getLineCoverage ()
80    {
81        if (m_lineCoverage == null)
82        {
83            getAggregate (TOTAL_LINE_COUNT); // fault line coverage calculation
84        }
85
86        return m_lineCoverage;
87    }
88
89
90    public int getAggregate (final int type)
91    {
92        final int [] aggregates = m_aggregates;
93
94        int value = aggregates [type];
95
96        if (value < 0)
97        {
98            switch (type)
99            {
100                case COVERAGE_CLASS_COUNT:
101                case    TOTAL_CLASS_COUNT:
102                {
103                    aggregates [TOTAL_CLASS_COUNT] = getChildCount ();
104
105                    value = 0;
106                    for (Iterator children = getChildren (); children.hasNext (); )
107                    {
108                        // SF BUG 972725: this was incorrectly using 'type' instead
109                        // of the COVERAGE_CLASS_COUNT aggregate type, making class
110                        // coverage computation dependent on the order of how item
111                        // nodes were traversed in report generators
112                        value += ((IItem) children.next ()).getAggregate (COVERAGE_CLASS_COUNT);
113                    }
114                    aggregates [COVERAGE_CLASS_COUNT] = value;
115
116                    return aggregates [type];
117                }
118                //break;
119
120
121                case TOTAL_SRCFILE_COUNT:
122                {
123                    return aggregates [TOTAL_SRCFILE_COUNT] = 1;
124                }
125                //break;
126
127
128                case COVERAGE_LINE_COUNT:
129                case    TOTAL_LINE_COUNT:
130
131                case COVERAGE_LINE_INSTR:
132                {
133                    // line aggregate types are special when used on srcfile items:
134                    // unlike all others, they do not simply add up when the line
135                    // info is available; instead, lines from all classes belonging
136                    // to the same srcfile parent are set-merged
137
138                    final IntObjectMap /* line -> int[2] */ fldata = new IntObjectMap ();
139
140                    for (Iterator classes = getChildren (); classes.hasNext (); )
141                    {
142                        final ClassItem cls = (ClassItem) classes.next ();
143
144                        final boolean [][] ccoverage = cls.getCoverage (); // this can be null
145                        final ClassDescriptor clsdesc = cls.getClassDescriptor ();
146                        final MethodDescriptor [] methoddescs = clsdesc.getMethods ();
147
148                        for (Iterator methods = cls.getChildren (); methods.hasNext (); )
149                        {
150                            final MethodItem method = (MethodItem) methods.next ();
151                            final int methodID = method.getID ();
152
153                            final boolean [] mcoverage = ccoverage == null ? null : ccoverage [methodID];
154
155                            final MethodDescriptor methoddesc = methoddescs [methodID];
156                            final int [] mbsizes = methoddesc.getBlockSizes ();
157                            final IntObjectMap mlineMap = methoddesc.getLineMap ();
158                            if ($assert.ENABLED) $assert.ASSERT (mlineMap != null);
159
160                            final int [] mlines = mlineMap.keys ();
161                            for (int ml = 0, mlLimit = mlines.length; ml < mlLimit; ++ ml)
162                            {
163                                final int mline = mlines [ml];
164
165                                int [] data = (int []) fldata.get (mline);
166                                if (data == null)
167                                {
168                                    data = new int [4]; // { totalcount, totalinstr, coveragecount, coverageinstr }
169                                    fldata.put (mline, data);
170                                }
171
172                                final int [] lblocks = (int []) mlineMap.get (mline);
173
174                                final int bCount = lblocks.length;
175                                data [0] += bCount;
176
177                                for (int bID = 0; bID < bCount; ++ bID)
178                                {
179                                    final int block = lblocks [bID];
180
181                                    final boolean bcovered = mcoverage != null && mcoverage [block];
182                                    final int instr = mbsizes [block];
183
184                                    data [1] += instr;
185                                    if (bcovered)
186                                    {
187                                        ++ data [2];
188                                        data [3] += instr;
189                                    }
190                                }
191                            }
192                        }
193                    }
194
195                    final int lineCount = fldata.size ();
196
197                    aggregates [TOTAL_LINE_COUNT] = lineCount;
198
199                    int coverageLineCount = 0;
200                    int coverageLineInstr = 0;
201
202                    final IntObjectMap /* line_no:int -> LineCoverageData */ lineCoverage = new IntObjectMap (lineCount);
203                    int firstLine = Integer.MAX_VALUE;
204
205                    final int [] clines = fldata.keys ();
206
207                    for (int cl = 0; cl < lineCount; ++ cl)
208                    {
209                        final int cline = clines [cl];
210                        final int [] data = (int []) fldata.get (cline);
211
212                        final int ltotalCount = data [0];
213                        final int ltotalInstr = data [1];
214                        final int lcoverageCount = data [2];
215                        final int lcoverageInstr = data [3];
216
217                        if (lcoverageInstr > 0)
218                        {
219                            coverageLineCount += (PRECISION * lcoverageCount) / ltotalCount;
220                            coverageLineInstr += (PRECISION * lcoverageInstr) / ltotalInstr;
221                        }
222
223                        // side effect: populate line coverage data map [used by getLineCoverage()]
224
225                        final int lcoverageStatus;
226                        int [][] lcoverageRatio = null;
227
228                        if (lcoverageInstr == 0)
229                            lcoverageStatus = LineCoverageData.LINE_COVERAGE_ZERO;
230                        else if (lcoverageInstr == ltotalInstr)
231                            lcoverageStatus = LineCoverageData.LINE_COVERAGE_COMPLETE;
232                        else
233                        {
234                            lcoverageStatus = LineCoverageData.LINE_COVERAGE_PARTIAL;
235                            lcoverageRatio = new int [][] {{ltotalCount, lcoverageCount}, {ltotalInstr, lcoverageInstr}}; // note: ordering depends on IItemAttribute.UNITS_xxx
236                        }
237
238                        lineCoverage.put (cline, new LineCoverageData (lcoverageStatus, lcoverageRatio));
239
240                        // side effect: compute m_firstLine
241
242                        if (cline < firstLine) firstLine = cline;
243                    }
244
245                    m_lineCoverage = lineCoverage; // side effect
246                    m_firstLine = firstLine; // side effect
247
248                    aggregates [COVERAGE_LINE_COUNT] = coverageLineCount;
249                    aggregates [COVERAGE_LINE_INSTR] = coverageLineInstr;
250
251                    return aggregates [type];
252                }
253                //break;
254
255
256                default: return super.getAggregate (type);
257            }
258        }
259
260        return value;
261    }
262
263
264    public void accept (final IItemVisitor visitor, final Object ctx)
265    {
266        visitor.visit (this, ctx);
267    }
268
269    public final IItemMetadata getMetadata ()
270    {
271        return METADATA;
272    }
273
274    public static IItemMetadata getTypeMetadata ()
275    {
276        return METADATA;
277    }
278
279    // protected: .............................................................
280
281    // package: ...............................................................
282
283    // private: ...............................................................
284
285
286    private final String m_name, m_fullVMName;
287    private IntObjectMap /* line_no:int -> LineCoverageData */ m_lineCoverage;
288    private int m_firstLine;
289
290    private static final Item.ItemMetadata METADATA; // set in <clinit>
291
292    static
293    {
294        METADATA = new Item.ItemMetadata (IItemMetadata.TYPE_ID_SRCFILE, "srcfile",
295            1 << IItemAttribute.ATTRIBUTE_NAME_ID |
296            1 << IItemAttribute.ATTRIBUTE_CLASS_COVERAGE_ID |
297            1 << IItemAttribute.ATTRIBUTE_METHOD_COVERAGE_ID |
298            1 << IItemAttribute.ATTRIBUTE_BLOCK_COVERAGE_ID |
299            1 << IItemAttribute.ATTRIBUTE_LINE_COVERAGE_ID);
300    }
301
302} // end of class
303// ----------------------------------------------------------------------------