blob: 4b06847836a913168fc7d748b541f0f39c27f5b0 [file] [log] [blame]
/* 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: SrcFileItem.java,v 1.1.1.1.2.1 2004/06/20 20:07:22 vlad_r Exp $
*/
package com.vladium.emma.report;
import java.util.Iterator;
import com.vladium.util.IntObjectMap;
import com.vladium.util.asserts.$assert;
import com.vladium.emma.data.ClassDescriptor;
import com.vladium.emma.data.MethodDescriptor;
// ----------------------------------------------------------------------------
/**
* @author Vlad Roubtsov, (C) 2003
*/
public
final class SrcFileItem extends Item
{
// public: ................................................................
public final class LineCoverageData
{
public static final int LINE_COVERAGE_ZERO = 0;
public static final int LINE_COVERAGE_PARTIAL = 1;
public static final int LINE_COVERAGE_COMPLETE = 2;
public final int m_coverageStatus;
public final int [/* units mode */][/* total, coverage */] m_coverageRatio; // not null in LINE_COVERAGE_PARTIAL status only]
LineCoverageData (final int coverageStatus, final int [][] coverageRatio)
{
m_coverageStatus = coverageStatus;
m_coverageRatio = coverageRatio;
}
} // end of nested class
public SrcFileItem (final IItem parent, final String name, final String fullVMName)
{
super (parent);
m_name = name;
m_fullVMName = fullVMName;
}
public String getName ()
{
return m_name;
}
public String getFullVMName ()
{
return m_fullVMName;
}
public int getFirstLine ()
{
// TODO: state validation
if (m_firstLine == 0)
{
getAggregate (TOTAL_LINE_COUNT); // fault line coverage calculation
}
return m_firstLine;
}
public IntObjectMap /* line_no:int -> LineCoverageData */ getLineCoverage ()
{
if (m_lineCoverage == null)
{
getAggregate (TOTAL_LINE_COUNT); // fault line coverage calculation
}
return m_lineCoverage;
}
public int getAggregate (final int type)
{
final int [] aggregates = m_aggregates;
int value = aggregates [type];
if (value < 0)
{
switch (type)
{
case COVERAGE_CLASS_COUNT:
case TOTAL_CLASS_COUNT:
{
aggregates [TOTAL_CLASS_COUNT] = getChildCount ();
value = 0;
for (Iterator children = getChildren (); children.hasNext (); )
{
// SF BUG 972725: this was incorrectly using 'type' instead
// of the COVERAGE_CLASS_COUNT aggregate type, making class
// coverage computation dependent on the order of how item
// nodes were traversed in report generators
value += ((IItem) children.next ()).getAggregate (COVERAGE_CLASS_COUNT);
}
aggregates [COVERAGE_CLASS_COUNT] = value;
return aggregates [type];
}
//break;
case TOTAL_SRCFILE_COUNT:
{
return aggregates [TOTAL_SRCFILE_COUNT] = 1;
}
//break;
case COVERAGE_LINE_COUNT:
case TOTAL_LINE_COUNT:
case COVERAGE_LINE_INSTR:
{
// line aggregate types are special when used on srcfile items:
// unlike all others, they do not simply add up when the line
// info is available; instead, lines from all classes belonging
// to the same srcfile parent are set-merged
final IntObjectMap /* line -> int[2] */ fldata = new IntObjectMap ();
for (Iterator classes = getChildren (); classes.hasNext (); )
{
final ClassItem cls = (ClassItem) classes.next ();
final boolean [][] ccoverage = cls.getCoverage (); // this can be null
final ClassDescriptor clsdesc = cls.getClassDescriptor ();
final MethodDescriptor [] methoddescs = clsdesc.getMethods ();
for (Iterator methods = cls.getChildren (); methods.hasNext (); )
{
final MethodItem method = (MethodItem) methods.next ();
final int methodID = method.getID ();
final boolean [] mcoverage = ccoverage == null ? null : ccoverage [methodID];
final MethodDescriptor methoddesc = methoddescs [methodID];
final int [] mbsizes = methoddesc.getBlockSizes ();
final IntObjectMap mlineMap = methoddesc.getLineMap ();
if ($assert.ENABLED) $assert.ASSERT (mlineMap != null);
final int [] mlines = mlineMap.keys ();
for (int ml = 0, mlLimit = mlines.length; ml < mlLimit; ++ ml)
{
final int mline = mlines [ml];
int [] data = (int []) fldata.get (mline);
if (data == null)
{
data = new int [4]; // { totalcount, totalinstr, coveragecount, coverageinstr }
fldata.put (mline, data);
}
final int [] lblocks = (int []) mlineMap.get (mline);
final int bCount = lblocks.length;
data [0] += bCount;
for (int bID = 0; bID < bCount; ++ bID)
{
final int block = lblocks [bID];
final boolean bcovered = mcoverage != null && mcoverage [block];
final int instr = mbsizes [block];
data [1] += instr;
if (bcovered)
{
++ data [2];
data [3] += instr;
}
}
}
}
}
final int lineCount = fldata.size ();
aggregates [TOTAL_LINE_COUNT] = lineCount;
int coverageLineCount = 0;
int coverageLineInstr = 0;
final IntObjectMap /* line_no:int -> LineCoverageData */ lineCoverage = new IntObjectMap (lineCount);
int firstLine = Integer.MAX_VALUE;
final int [] clines = fldata.keys ();
for (int cl = 0; cl < lineCount; ++ cl)
{
final int cline = clines [cl];
final int [] data = (int []) fldata.get (cline);
final int ltotalCount = data [0];
final int ltotalInstr = data [1];
final int lcoverageCount = data [2];
final int lcoverageInstr = data [3];
if (lcoverageInstr > 0)
{
coverageLineCount += (PRECISION * lcoverageCount) / ltotalCount;
coverageLineInstr += (PRECISION * lcoverageInstr) / ltotalInstr;
}
// side effect: populate line coverage data map [used by getLineCoverage()]
final int lcoverageStatus;
int [][] lcoverageRatio = null;
if (lcoverageInstr == 0)
lcoverageStatus = LineCoverageData.LINE_COVERAGE_ZERO;
else if (lcoverageInstr == ltotalInstr)
lcoverageStatus = LineCoverageData.LINE_COVERAGE_COMPLETE;
else
{
lcoverageStatus = LineCoverageData.LINE_COVERAGE_PARTIAL;
lcoverageRatio = new int [][] {{ltotalCount, lcoverageCount}, {ltotalInstr, lcoverageInstr}}; // note: ordering depends on IItemAttribute.UNITS_xxx
}
lineCoverage.put (cline, new LineCoverageData (lcoverageStatus, lcoverageRatio));
// side effect: compute m_firstLine
if (cline < firstLine) firstLine = cline;
}
m_lineCoverage = lineCoverage; // side effect
m_firstLine = firstLine; // side effect
aggregates [COVERAGE_LINE_COUNT] = coverageLineCount;
aggregates [COVERAGE_LINE_INSTR] = coverageLineInstr;
return aggregates [type];
}
//break;
default: return super.getAggregate (type);
}
}
return value;
}
public void accept (final IItemVisitor visitor, final Object ctx)
{
visitor.visit (this, ctx);
}
public final IItemMetadata getMetadata ()
{
return METADATA;
}
public static IItemMetadata getTypeMetadata ()
{
return METADATA;
}
// protected: .............................................................
// package: ...............................................................
// private: ...............................................................
private final String m_name, m_fullVMName;
private IntObjectMap /* line_no:int -> LineCoverageData */ m_lineCoverage;
private int m_firstLine;
private static final Item.ItemMetadata METADATA; // set in <clinit>
static
{
METADATA = new Item.ItemMetadata (IItemMetadata.TYPE_ID_SRCFILE, "srcfile",
1 << IItemAttribute.ATTRIBUTE_NAME_ID |
1 << IItemAttribute.ATTRIBUTE_CLASS_COVERAGE_ID |
1 << IItemAttribute.ATTRIBUTE_METHOD_COVERAGE_ID |
1 << IItemAttribute.ATTRIBUTE_BLOCK_COVERAGE_ID |
1 << IItemAttribute.ATTRIBUTE_LINE_COVERAGE_ID);
}
} // end of class
// ----------------------------------------------------------------------------