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