/* 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 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 // ----------------------------------------------------------------------------