blob: 4f94d75cf143f323d3d656890ce631e55b744bc7 [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: ReportDataModel.java,v 1.1.1.1 2004/05/09 16:57:38 vlad_r Exp $
*/
package com.vladium.emma.report;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.vladium.util.Descriptors;
import com.vladium.util.asserts.$assert;
import com.vladium.emma.IAppErrorCodes;
import com.vladium.emma.EMMARuntimeException;
import com.vladium.emma.data.ClassDescriptor;
import com.vladium.emma.data.IMetaData;
import com.vladium.emma.data.IMetadataConstants;
import com.vladium.emma.data.ICoverageData;
import com.vladium.emma.data.MethodDescriptor;
// ----------------------------------------------------------------------------
/**
* @author Vlad Roubtsov, (C) 2003
*/
final class ReportDataModel implements IReportDataModel
{
// public: ................................................................
public synchronized IReportDataView getView (final int viewType)
{
// TODO: merge the two branches together
if (viewType >= m_views.length) throw new IllegalArgumentException ("invalid viewType: " + viewType);
IReportDataView view = m_views [viewType];
if (view != null)
return view;
else
{
final boolean srcView = viewType == IReportDataView.HIER_SRC_VIEW;
if (srcView && ! m_mdata.hasSrcFileData ())
throw new IllegalStateException ("source file data view requested for metadata with incomplete SourceFile debug info");
final AllItem root = new AllItem ();
final Map /* String(pkg name) -> PackageItem */ packageMap = new HashMap ();
final Map /* String(pkg-prefixed src file name) -> ClassItem */ srcfileMap = new HashMap ();
for (Iterator /* ClassDescriptor */ descriptors = m_mdata.iterator (); descriptors.hasNext (); )
{
final ClassDescriptor cls = (ClassDescriptor) descriptors.next ();
String packageVMName = cls.getPackageVMName ();
PackageItem packageItem = (PackageItem) packageMap.get (packageVMName);
if (packageItem == null)
{
final String packageName = packageVMName.length () == 0 ? "default package" : Descriptors.vmNameToJavaName (packageVMName);
packageItem = new PackageItem (root, packageName, packageVMName);
packageMap.put (packageVMName, packageItem);
root.addChild (packageItem);
}
SrcFileItem srcfileItem = null;
if (srcView)
{
final String srcFileName = cls.getSrcFileName ();
if ($assert.ENABLED) $assert.ASSERT (srcFileName != null, "src file name = null");
final String fullSrcFileName = Descriptors.combineVMName (packageVMName, srcFileName);
srcfileItem = (SrcFileItem) srcfileMap.get (fullSrcFileName);
if (srcfileItem == null)
{
srcfileItem = new SrcFileItem (packageItem, srcFileName, fullSrcFileName);
srcfileMap.put (fullSrcFileName, srcfileItem);
packageItem.addChild (srcfileItem);
}
}
final ICoverageData.DataHolder data = m_cdata.getCoverage (cls);
// check metadata and coverage data consistency:
if (data != null)
{
if (data.m_stamp != cls.getStamp ())
throw new EMMARuntimeException (IAppErrorCodes.CLASS_STAMP_MISMATCH,
new Object [] { Descriptors.vmNameToJavaName (cls.getClassVMName ()) });
}
final boolean [][] coverage = data != null ? data.m_coverage : null;
if ($assert.ENABLED) $assert.ASSERT (! srcView || srcfileItem != null, "null srcfileItem");
final ClassItem classItem = srcView ? new ClassItem (srcfileItem, cls, coverage) : new ClassItem (packageItem, cls, coverage);
final MethodDescriptor [] methods = cls.getMethods ();
// TODO: handle edge case when all methods of a class have METHOD_NO_BLOCK_DATA set
for (int m = 0; m < methods.length; ++ m)
{
final MethodDescriptor method = methods [m];
if ((method.getStatus () & IMetadataConstants.METHOD_NO_BLOCK_DATA) != 0) continue;
// TODO: wouldn't it be more consistent to simply pass the entire descriptor into MethodItems? (eval mem savings)
final MethodItem methodItem = new MethodItem (classItem, m, method.getName (), method.getDescriptor (), method.getFirstLine ());
// TODO: need to fold class's name into a method name prefix for collapsing case [only when it is not the same as the file name]
classItem.addChild (methodItem);
}
if (srcView)
srcfileItem.addChild (classItem);
else
packageItem.addChild (classItem);
}
view = new ReportDataView (root);
m_views [viewType] = view;
return view;
}
}
// protected: .............................................................
// package: ...............................................................
ReportDataModel (final IMetaData mdata, final ICoverageData cdata)
{
if (mdata == null) throw new IllegalArgumentException ("null input: mdata");
if (cdata == null) throw new IllegalArgumentException ("null input: cdata");
m_views = new IReportDataView [2];
// TODO: report generators work off data model views only; I should deref
// mdata and cdata as soon as all possible views have been constructed and cached
m_mdata = mdata;
m_cdata = cdata;
}
// private: ...............................................................
private static final class ReportDataView implements IReportDataView
{
public IItem getRoot()
{
return m_root;
}
ReportDataView (final IItem root)
{
m_root = root;
}
private final IItem m_root;
} // end of nested class
private final IMetaData m_mdata;
private final ICoverageData m_cdata;
private final IReportDataView [] m_views;
} // end of class
// ----------------------------------------------------------------------------