net.sourceforge.cobertura.coveragedata.ClassData Maven / Gradle / Ivy
/*
* Cobertura - http://cobertura.sourceforge.net/
*
* Copyright (C) 2003 jcoverage ltd.
* Copyright (C) 2005 Mark Doliner
* Copyright (C) 2006 Jiri Mares
*
* Cobertura is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Cobertura is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cobertura; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
package net.sourceforge.cobertura.coveragedata;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
/**
*
* ProjectData information is typically serialized to a file. An
* instance of this class records coverage information for a single
* class that has been instrumented.
*
*
*
* This class implements HasBeenInstrumented so that when cobertura
* instruments itself, it will omit this class. It does this to
* avoid an infinite recursion problem because instrumented classes
* make use of this class.
*
*/
public class ClassData extends CoverageDataContainer
implements Comparable, HasBeenInstrumented
{
private static final long serialVersionUID = 5;
/**
* Each key is a line number in this class, stored as an Integer object.
* Each value is information about the line, stored as a LineData object.
*/
private Map branches = new HashMap();
private boolean containsInstrumentationInfo = false;
private Set methodNamesAndDescriptors = new HashSet();
private String name = null;
private String sourceFileName = null;
/**
* @param name In the format "net.sourceforge.cobertura.coveragedata.ClassData"
*/
public ClassData(String name)
{
if (name == null)
throw new IllegalArgumentException(
"Class name must be specified.");
this.name = name;
}
public LineData addLine(int lineNumber, String methodName,
String methodDescriptor)
{
lock.lock();
try
{
LineData lineData = getLineData(lineNumber);
if (lineData == null)
{
lineData = new LineData(lineNumber);
// Each key is a line number in this class, stored as an Integer object.
// Each value is information about the line, stored as a LineData object.
children.put(new Integer(lineNumber), lineData);
}
lineData.setMethodNameAndDescriptor(methodName, methodDescriptor);
// methodName and methodDescriptor can be null when cobertura.ser with
// no line information was loaded (or was not loaded at all).
if( methodName!=null && methodDescriptor!=null)
methodNamesAndDescriptors.add(methodName + methodDescriptor);
return lineData;
}
finally
{
lock.unlock();
}
}
/**
* This is required because we implement Comparable.
*/
public int compareTo(Object o)
{
if (!o.getClass().equals(ClassData.class))
return Integer.MAX_VALUE;
return this.name.compareTo(((ClassData)o).name);
}
public boolean containsInstrumentationInfo()
{
lock.lock();
try
{
return this.containsInstrumentationInfo;
}
finally
{
lock.unlock();
}
}
/**
* Returns true if the given object is an instance of the
* ClassData class, and it contains the same data as this
* class.
*/
public boolean equals(Object obj)
{
if (this == obj)
return true;
if ((obj == null) || !(obj.getClass().equals(this.getClass())))
return false;
ClassData classData = (ClassData)obj;
getBothLocks(classData);
try
{
return super.equals(obj)
&& this.branches.equals(classData.branches)
&& this.methodNamesAndDescriptors
.equals(classData.methodNamesAndDescriptors)
&& this.name.equals(classData.name)
&& this.sourceFileName.equals(classData.sourceFileName);
}
finally
{
lock.unlock();
classData.lock.unlock();
}
}
public String getBaseName()
{
int lastDot = this.name.lastIndexOf('.');
if (lastDot == -1)
{
return this.name;
}
return this.name.substring(lastDot + 1);
}
/**
* @return The branch coverage rate for a particular method.
*/
public double getBranchCoverageRate(String methodNameAndDescriptor)
{
int total = 0;
int covered = 0;
lock.lock();
try
{
for (Iterator iter = branches.values().iterator(); iter.hasNext();) {
LineData next = (LineData) iter.next();
if (methodNameAndDescriptor.equals(next.getMethodName() + next.getMethodDescriptor()))
{
total += next.getNumberOfValidBranches();
covered += next.getNumberOfCoveredBranches();
}
}
if (total == 0) return 1.0;
return (double) covered / total;
}
finally
{
lock.unlock();
}
}
public Collection getBranches()
{
lock.lock();
try
{
return Collections.unmodifiableCollection(branches.keySet());
}
finally
{
lock.unlock();
}
}
/**
* @param lineNumber The source code line number.
* @return The coverage of the line
*/
public LineData getLineCoverage(int lineNumber)
{
Integer lineObject = new Integer(lineNumber);
lock.lock();
try
{
if (!children.containsKey(lineObject))
{
return null;
}
return (LineData) children.get(lineObject);
}
finally
{
lock.unlock();
}
}
/**
* @return The line coverage rate for particular method
*/
public double getLineCoverageRate(String methodNameAndDescriptor)
{
int total = 0;
int hits = 0;
lock.lock();
try
{
Iterator iter = children.values().iterator();
while (iter.hasNext())
{
LineData next = (LineData) iter.next();
if (methodNameAndDescriptor.equals(next.getMethodName() + next.getMethodDescriptor()))
{
total++;
if (next.getHits() > 0) {
hits++;
}
}
}
if (total == 0) return 1d;
return (double) hits / total;
}
finally
{
lock.unlock();
}
}
private LineData getLineData(int lineNumber)
{
lock.lock();
try
{
return (LineData)children.get(Integer.valueOf(lineNumber));
}
finally
{
lock.unlock();
}
}
public SortedSet getLines()
{
lock.lock();
try
{
return new TreeSet(this.children.values());
}
finally
{
lock.unlock();
}
}
public Collection getLines(String methodNameAndDescriptor)
{
Collection lines = new HashSet();
lock.lock();
try
{
Iterator iter = children.values().iterator();
while (iter.hasNext())
{
LineData next = (LineData)iter.next();
if (methodNameAndDescriptor.equals(next.getMethodName()
+ next.getMethodDescriptor()))
{
lines.add(next);
}
}
return lines;
}
finally
{
lock.unlock();
}
}
/**
* @return The method name and descriptor of each method found in the
* class represented by this instrumentation.
*/
public Set getMethodNamesAndDescriptors()
{
lock.lock();
try
{
return methodNamesAndDescriptors;
}
finally
{
lock.unlock();
}
}
public String getName()
{
return name;
}
/**
* @return The number of branches in this class.
*/
public int getNumberOfValidBranches()
{
int number = 0;
lock.lock();
try
{
for (Iterator i = branches.values().iterator();
i.hasNext();
number += ((LineData) i.next()).getNumberOfValidBranches())
;
return number;
}
finally
{
lock.unlock();
}
}
/**
* @see net.sourceforge.cobertura.coveragedata.CoverageData#getNumberOfCoveredBranches()
*/
public int getNumberOfCoveredBranches()
{
int number = 0;
lock.lock();
try
{
for (Iterator i = branches.values().iterator();
i.hasNext();
number += ((LineData) i.next()).getNumberOfCoveredBranches())
;
return number;
}
finally
{
lock.unlock();
}
}
public String getPackageName()
{
int lastDot = this.name.lastIndexOf('.');
if (lastDot == -1)
{
return "";
}
return this.name.substring(0, lastDot);
}
/**
* Return the name of the file containing this class. If this
* class' sourceFileName has not been set (for whatever reason)
* then this method will attempt to infer the name of the source
* file using the class name.
*
* @return The name of the source file, for example
* net/sourceforge/cobertura/coveragedata/ClassData.java
*/
public String getSourceFileName()
{
String baseName;
lock.lock();
try
{
if (sourceFileName != null)
baseName = sourceFileName;
else
{
baseName = getBaseName();
int firstDollarSign = baseName.indexOf('$');
if (firstDollarSign == -1 || firstDollarSign == 0)
baseName += ".java";
else
baseName = baseName.substring(0, firstDollarSign)
+ ".java";
}
String packageName = getPackageName();
if (packageName.equals(""))
return baseName;
return packageName.replace('.', '/') + '/' + baseName;
}
finally
{
lock.unlock();
}
}
public int hashCode()
{
return this.name.hashCode();
}
/**
* @return True if the line contains at least one condition jump (branch)
*/
public boolean hasBranch(int lineNumber)
{
lock.lock();
try
{
return branches.containsKey(Integer.valueOf(lineNumber));
}
finally
{
lock.unlock();
}
}
/**
* Determine if a given line number is a valid line of code.
*
* @return True if the line contains executable code. False
* if the line is empty, or a comment, etc.
*/
public boolean isValidSourceLineNumber(int lineNumber)
{
lock.lock();
try
{
return children.containsKey(Integer.valueOf(lineNumber));
}
finally
{
lock.unlock();
}
}
public void addLineJump(int lineNumber, int branchNumber)
{
lock.lock();
try
{
LineData lineData = getLineData(lineNumber);
if (lineData != null)
{
lineData.addJump(branchNumber);
this.branches.put(Integer.valueOf(lineNumber), lineData);
}
}
finally
{
lock.unlock();
}
}
public void addLineSwitch(int lineNumber, int switchNumber, int[] keys)
{
lock.lock();
try
{
LineData lineData = getLineData(lineNumber);
if (lineData != null)
{
lineData.addSwitch(switchNumber, keys);
this.branches.put(Integer.valueOf(lineNumber), lineData);
}
}
finally
{
lock.unlock();
}
}
public void addLineSwitch(int lineNumber, int switchNumber, int min, int max)
{
lock.lock();
try
{
LineData lineData = getLineData(lineNumber);
if (lineData != null)
{
lineData.addSwitch(switchNumber, min, max);
this.branches.put(Integer.valueOf(lineNumber), lineData);
}
}
finally
{
lock.unlock();
}
}
/**
* Merge some existing instrumentation with this instrumentation.
*
* @param coverageData Some existing coverage data.
*/
public void merge(CoverageData coverageData)
{
ClassData classData = (ClassData)coverageData;
// If objects contain data for different classes then don't merge
if (!this.getName().equals(classData.getName()))
return;
getBothLocks(classData);
try
{
super.merge(coverageData);
// We can't just call this.branches.putAll(classData.branches);
// Why not? If we did a putAll, then the LineData objects from
// the coverageData class would overwrite the LineData objects
// that are already in "this.branches" And we don't need to
// update the LineData objects that are already in this.branches
// because they are shared between this.branches and this.children,
// so the object hit counts will be moved when we called
// super.merge() above.
for (Iterator iter = classData.branches.keySet().iterator(); iter.hasNext();)
{
Object key = iter.next();
if (!this.branches.containsKey(key))
{
this.branches.put(key, classData.branches.get(key));
}
}
this.containsInstrumentationInfo |= classData.containsInstrumentationInfo;
this.methodNamesAndDescriptors.addAll(classData
.getMethodNamesAndDescriptors());
if (classData.sourceFileName != null)
this.sourceFileName = classData.sourceFileName;
}
finally
{
lock.unlock();
classData.lock.unlock();
}
}
public void removeLine(int lineNumber)
{
Integer lineObject = Integer.valueOf(lineNumber);
lock.lock();
try
{
children.remove(lineObject);
branches.remove(lineObject);
}
finally
{
lock.unlock();
}
}
public void setContainsInstrumentationInfo()
{
lock.lock();
try
{
this.containsInstrumentationInfo = true;
}
finally
{
lock.unlock();
}
}
public void setSourceFileName(String sourceFileName)
{
lock.lock();
try
{
this.sourceFileName = sourceFileName;
}
finally
{
lock.unlock();
}
}
/**
* Increment the number of hits for a particular line of code.
*
* @param lineNumber the line of code to increment the number of hits.
*/
public void touch(int lineNumber)
{
lock.lock();
try
{
LineData lineData = getLineData(lineNumber);
if (lineData == null)
lineData = addLine(lineNumber, null, null);
lineData.touch();
}
finally
{
lock.unlock();
}
}
/**
* Increments the number of hits for particular hit counter of particular branch on particular line number.
*
* @param lineNumber The line of code where the branch is
* @param branchNumber The branch on the line to change the hit counter
* @param branch The hit counter (true or false)
*/
public void touchJump(int lineNumber, int branchNumber, boolean branch) {
lock.lock();
try
{
LineData lineData = getLineData(lineNumber);
if (lineData == null)
lineData = addLine(lineNumber, null, null);
lineData.touchJump(branchNumber, branch);
}
finally
{
lock.unlock();
}
}
/**
* Increments the number of hits for particular hit counter of particular switch branch on particular line number.
*
* @param lineNumber The line of code where the branch is
* @param switchNumber The switch on the line to change the hit counter
* @param branch The hit counter
*/
public void touchSwitch(int lineNumber, int switchNumber, int branch) {
lock.lock();
try
{
LineData lineData = getLineData(lineNumber);
if (lineData == null)
lineData = addLine(lineNumber, null, null);
lineData.touchSwitch(switchNumber, branch);
}
finally
{
lock.unlock();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy