001    /*
002     * Cobertura - http://cobertura.sourceforge.net/
003     *
004     * Copyright (C) 2003 jcoverage ltd.
005     * Copyright (C) 2005 Mark Doliner <thekingant@users.sourceforge.net>
006     *
007     * Cobertura is free software; you can redistribute it and/or modify
008     * it under the terms of the GNU General Public License as published
009     * by the Free Software Foundation; either version 2 of the License,
010     * or (at your option) any later version.
011     *
012     * Cobertura is distributed in the hope that it will be useful, but
013     * WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * General Public License for more details.
016     *
017     * You should have received a copy of the GNU General Public License
018     * along with Cobertura; if not, write to the Free Software
019     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
020     * USA
021     */
022    
023    package net.sourceforge.cobertura.coverage;
024    
025    import java.io.Serializable;
026    import java.util.Collections;
027    import java.util.HashMap;
028    import java.util.HashSet;
029    import java.util.Iterator;
030    import java.util.Map;
031    import java.util.Set;
032    
033    /**
034     * <p>
035     * CoverageData information is typically serialized to a file. An
036     * instance of this class records coverage information for a single
037     * class that has been instrumented.
038     * </p>
039     *
040     * <p>
041     * This class implements HasBeenInstrumented so that when cobertura
042     * instruments itself, it will omit this class.  It does this to
043     * avoid an infinite recursion problem because instrumented classes
044     * make use of this class.
045     * </p>
046     */
047    public class CoverageData implements HasBeenInstrumented, Serializable
048    {
049    
050            private static final long serialVersionUID = 2;
051    
052            /**
053             * Each key is a line number in this class, stored as an Integer object.
054             * Each value is information about the line, stored as a LineInformation object.
055             */
056            private Map conditionals = new HashMap();
057    
058            /**
059             * Each key is a line number in this class, stored as an Integer object.
060             * Each value is information about the line, stored as a LineInformation object.
061             */
062            private Map lines = new HashMap();
063    
064            private Set methodNamesAndDescriptors = new HashSet();
065    
066            private String sourceFileName = null;
067    
068            public void addLine(int lineNumber, String methodName,
069                            String methodDescriptor)
070            {
071                    LineInformation lineInformation = getLineInformation(lineNumber);
072                    if (lineInformation == null)
073                    {
074                            lineInformation = new LineInformation(lineNumber);
075                            lines.put(new Integer(lineNumber), lineInformation);
076                    }
077                    lineInformation.setMethodNameAndDescriptor(methodName,
078                                    methodDescriptor);
079                    methodNamesAndDescriptors.add(methodName + methodDescriptor);
080            }
081    
082            /**
083             * @return The branch coverage rate for the class.
084             */
085            public double getBranchCoverageRate()
086            {
087                    if (conditionals.size() == 0)
088                    {
089                            // no conditional branches, therefore 100% branch coverage.
090                            return 1d;
091                    }
092                    return (double)getNumberOfCoveredBranches() / conditionals.size();
093            }
094    
095            /**
096             * @return The branch coverage rate for a particular method.
097             */
098            public double getBranchCoverageRate(String methodNameAndDescriptor)
099            {
100                    int total = 0;
101                    int hits = 0;
102    
103                    Iterator iter = conditionals.values().iterator();
104                    while (iter.hasNext())
105                    {
106                            LineInformation next = (LineInformation)iter.next();
107                            if (methodNameAndDescriptor.equals(next.getMethodName()
108                                            + next.getMethodDescriptor()))
109                            {
110                                    total++;
111                                    if (next.getHits() > 0)
112                                    {
113                                            hits++;
114                                    }
115                            }
116                    }
117                    return (double)hits / total;
118            }
119    
120            public Set getConditionals()
121            {
122                    return Collections.unmodifiableSet(conditionals.keySet());
123            }
124    
125            /**
126             * @param lineNumber The source code line number.
127             * @return The number of hits a particular line of code has.
128             */
129            public long getHitCount(int lineNumber)
130            {
131                    Integer lineNum = new Integer(lineNumber);
132                    if (!lines.containsKey(lineNum))
133                    {
134                            return 0;
135                    }
136    
137                    return ((LineInformation)lines.get(lineNum)).getHits();
138            }
139    
140            /**
141             * @return The line coverage rate for the class
142             */
143            public double getLineCoverageRate()
144            {
145                    if (lines.size() == 0)
146                    {
147                            return 1d;
148                    }
149                    return (double)getNumberOfCoveredLines() / lines.size();
150            }
151    
152            /**
153             * @return The line coverage rate for particular method
154             */
155            public double getLineCoverageRate(String methodNameAndDescriptor)
156            {
157                    int total = 0;
158                    int hits = 0;
159    
160                    Iterator iter = lines.values().iterator();
161                    while (iter.hasNext())
162                    {
163                            LineInformation next = (LineInformation)iter.next();
164                            if (methodNameAndDescriptor.equals(next.getMethodName()
165                                            + next.getMethodDescriptor()))
166                            {
167                                    total++;
168                                    if (next.getHits() > 0)
169                                    {
170                                            hits++;
171                                    }
172                            }
173                    }
174                    return (double)hits / total;
175            }
176    
177            private LineInformation getLineInformation(int lineNumber)
178            {
179                    return (LineInformation)lines.get(new Integer(lineNumber));
180            }
181    
182            /**
183             * @return The method name and descriptor of each method found in the
184             *         class represented by this instrumentation.
185             */
186            public Set getMethodNamesAndDescriptors()
187            {
188                    return methodNamesAndDescriptors;
189            }
190    
191            /**
192             * @return The number of branches in this class covered by testing.
193             */
194            public int getNumberOfCoveredBranches()
195            {
196                    int num = 0;
197    
198                    Iterator iter = conditionals.values().iterator();
199                    while (iter.hasNext())
200                    {
201                            if (((LineInformation)iter.next()).getHits() > 0)
202                                    num++;
203                    }
204    
205                    return num;
206            }
207    
208            /**
209             * @return The number of lines in this class covered by testing.
210             */
211            public int getNumberOfCoveredLines()
212            {
213                    int num = 0;
214    
215                    Iterator iter = lines.values().iterator();
216                    while (iter.hasNext())
217                    {
218                            if (((LineInformation)iter.next()).getHits() > 0)
219                                    num++;
220                    }
221    
222                    return num;
223            }
224    
225            /**
226             * @return The number of branches in this class.
227             */
228            public int getNumberOfValidBranches()
229            {
230                    return conditionals.size();
231            }
232    
233            /**
234             * @return The number of lines in this class.
235             */
236            public int getNumberOfValidLines()
237            {
238                    return lines.size();
239            }
240    
241            public String getSourceFileName()
242            {
243                    return sourceFileName;
244            }
245    
246            /**
247             * @return The set of valid source line numbers
248             */
249            public Set getValidLineNumbers()
250            {
251                    return Collections.unmodifiableSet(lines.keySet());
252            }
253    
254            /**
255             * Determine if a given line number is a valid line of code.
256             *
257             * @return True if the line contains executable code.  False
258             *         if the line is empty, or a comment, etc.
259             */
260            public boolean isValidSourceLineNumber(int lineNumber)
261            {
262                    return lines.containsKey(new Integer(lineNumber));
263            }
264    
265            public void markLineAsConditional(int lineNumber)
266            {
267                    LineInformation lineInformation = getLineInformation(lineNumber);
268                    if (lineInformation != null)
269                    {
270                            lineInformation.setConditional(true);
271                            this.conditionals.put(new Integer(lineNumber), lineInformation);
272                    }
273            }
274    
275            /**
276             * Merge some existing instrumentation with this instrumentation.
277             *
278             * @param coverageData Some existing coverage data.
279             */
280            public void merge(CoverageData coverageData)
281            {
282                    lines.putAll(coverageData.lines);
283                    conditionals.putAll(coverageData.conditionals);
284                    methodNamesAndDescriptors.addAll(coverageData
285                                    .getMethodNamesAndDescriptors());
286            }
287    
288            public void removeLine(int lineNumber)
289            {
290                    lines.remove(new Integer(lineNumber));
291            }
292    
293            public void setSourceFileName(String sourceFileName)
294            {
295                    this.sourceFileName = sourceFileName;
296            }
297    
298            /**
299             * Increment the number of hits for a particular line of code.
300             *
301             * @param lineNumber the line of code to increment the number of hits.
302             */
303            public void touch(int lineNumber)
304            {
305                    LineInformation lineInformation = getLineInformation(lineNumber);
306                    if (lineInformation != null)
307                            lineInformation.touch();
308            }
309    
310    }