| /* 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: MetaData.java,v 1.1.1.1.2.2 2004/07/16 23:32:29 vlad_r Exp $ |
| */ |
| package com.vladium.emma.data; |
| |
| import java.io.DataInput; |
| import java.io.DataOutput; |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import com.vladium.logging.Logger; |
| import com.vladium.util.asserts.$assert; |
| |
| // ---------------------------------------------------------------------------- |
| /* |
| * Average mem size/class entry: 6166 bytes [1.4.1, rt.jar], 5764 bytes [1.3.1, rt.jar] |
| */ |
| /** |
| * @author Vlad Roubtsov, (C) 2003 |
| */ |
| final class MetaData implements IMetaData, Cloneable |
| { |
| // public: ................................................................ |
| |
| // TODO: MT-safety model |
| |
| // TODO: no duplicate detection is done here at the moment |
| // [may require supporting fast lookup for already added descriptors] |
| |
| public IMetaData shallowCopy () |
| { |
| final MetaData _clone; |
| try |
| { |
| _clone = (MetaData) super.clone (); |
| } |
| catch (CloneNotSupportedException cnse) |
| { |
| throw new Error (cnse.toString ()); |
| } |
| |
| final HashMap _classMap; |
| |
| synchronized (lock ()) |
| { |
| _classMap = (HashMap) m_classMap.clone (); |
| } |
| |
| // [m_packagesWarned is not cloned by design] |
| |
| _clone.m_classMap = _classMap; |
| |
| return _clone; |
| } |
| |
| public CoverageOptions getOptions () |
| { |
| return m_options; |
| } |
| |
| public int size () |
| { |
| return m_classMap.size (); |
| } |
| |
| public boolean hasSrcFileData () |
| { |
| return m_hasSrcFileInfo; |
| } |
| |
| public boolean hasLineNumberData () |
| { |
| return m_hasLineNumberInfo; |
| } |
| |
| public Iterator iterator () |
| { |
| return m_classMap.values ().iterator (); |
| } |
| |
| // public boolean hasDescriptor (final ClassDescriptor cls) |
| // { |
| // if ($assert.ENABLED) $assert.ASSERT (cls != null, "cls is null"); |
| // |
| // return m_classes.contains (cls); |
| // } |
| |
| public boolean hasDescriptor (final String classVMName) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (classVMName != null, "className is null"); |
| |
| return m_classMap.containsKey (classVMName); |
| } |
| |
| public Object lock () |
| { |
| return m_classMap; |
| } |
| |
| public boolean add (final ClassDescriptor cls, final boolean overwrite) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (cls != null, "cls is null"); |
| |
| final String classVMName = cls.getClassVMName (); |
| |
| if (overwrite || ! m_classMap.containsKey (classVMName)) |
| { |
| m_classMap.put (classVMName, cls); |
| |
| boolean incompleteDebugInfo = false; |
| |
| if (! cls.hasSrcFileInfo ()) |
| { |
| m_hasSrcFileInfo = false; |
| incompleteDebugInfo = true; |
| } |
| |
| if (! cls.hasCompleteLineNumberInfo ()) |
| { |
| m_hasLineNumberInfo = false; |
| incompleteDebugInfo = true; |
| } |
| |
| // SF FR 971176: provide user with sample classes that may later |
| // caused warnings about line coverage not available |
| |
| if (incompleteDebugInfo) |
| { |
| final Logger log = Logger.getLogger (); |
| |
| if (log.atINFO ()) |
| { |
| final String packageVMName = cls.getPackageVMName (); |
| |
| if (m_packagesWarned.add (packageVMName)) |
| { |
| log.info ("package [" + packageVMName + "] contains classes [" + cls.getName () + "] without full debug info"); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // IMergeable: |
| |
| public boolean isEmpty () |
| { |
| return m_classMap.isEmpty (); |
| } |
| |
| /* |
| * note: rhs entries must override current entries |
| */ |
| public IMergeable merge (final IMergeable rhs) |
| { |
| if ((rhs == null) || rhs.isEmpty () || (rhs == this)) |
| return this; |
| else |
| { |
| final MetaData rhsmdata = (MetaData) rhs; // TODO: redesign to avoid this cast? |
| final Map rhsclasses = rhsmdata.m_classMap; |
| |
| // rhs entries always override existing content: |
| |
| for (Iterator entries = rhsclasses.entrySet ().iterator (); entries.hasNext (); ) |
| { |
| final Map.Entry entry = (Map.Entry) entries.next (); |
| |
| final String classVMName = (String) entry.getKey (); |
| final Object rhsdescriptor = entry.getValue (); |
| |
| m_classMap.put (classVMName, rhsdescriptor); |
| } |
| |
| // update debug info flags if necessary: |
| |
| if (! rhsmdata.hasSrcFileData ()) m_hasSrcFileInfo = false; |
| if (! rhsmdata.hasLineNumberData ()) m_hasLineNumberInfo = false; |
| |
| return this; |
| } |
| } |
| |
| // protected: ............................................................. |
| |
| // package: ............................................................... |
| |
| |
| MetaData (final CoverageOptions options) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (options != null, "options is null"); |
| m_options = options; |
| |
| m_hasSrcFileInfo = true; |
| m_hasLineNumberInfo = true; |
| |
| m_classMap = new HashMap (); |
| m_packagesWarned = new HashSet (); |
| } |
| |
| |
| static MetaData readExternal (final DataInput in) |
| throws IOException |
| { |
| final CoverageOptions options = CoverageOptions.readExternal (in); |
| |
| final boolean hasSrcFileInfo = in.readBoolean (); |
| final boolean hasLineNumberInfo = in.readBoolean (); |
| |
| final int size = in.readInt (); |
| final HashMap classMap = new HashMap (size); |
| |
| for (int i = 0; i < size; ++ i) |
| { |
| final String classVMName = in.readUTF (); |
| final ClassDescriptor cls = ClassDescriptor.readExternal (in); |
| |
| classMap.put (classVMName, cls); |
| } |
| |
| // [m_packagesWarned is not part of persisted state] |
| |
| return new MetaData (options, classMap, hasSrcFileInfo, hasLineNumberInfo); |
| } |
| |
| static void writeExternal (final MetaData mdata, final DataOutput out) |
| throws IOException |
| { |
| CoverageOptions.writeExternal (mdata.m_options, out); |
| |
| out.writeBoolean (mdata.m_hasSrcFileInfo); |
| out.writeBoolean (mdata.m_hasLineNumberInfo); |
| |
| final Map classMap = mdata.m_classMap; |
| |
| final int size = classMap.size (); |
| out.writeInt (size); // too bad the capacity is not visible |
| |
| final Iterator entries = classMap.entrySet ().iterator (); |
| for (int i = 0; i < size; ++ i) |
| { |
| final Map.Entry entry = (Map.Entry) entries.next (); |
| |
| final String classVMName = (String) entry.getKey (); |
| final ClassDescriptor cls = (ClassDescriptor) entry.getValue (); |
| |
| out.writeUTF (classVMName); |
| ClassDescriptor.writeExternal (cls, out); |
| } |
| |
| // [m_packagesWarned is not part of persisted state] |
| } |
| |
| // private: ............................................................... |
| |
| |
| private MetaData (final CoverageOptions options, final HashMap classMap, |
| final boolean hasSrcFileInfo, final boolean hasLineNumberInfo) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (options != null, "options is null"); |
| m_options = options; |
| |
| m_hasSrcFileInfo = hasSrcFileInfo; |
| m_hasLineNumberInfo = hasLineNumberInfo; |
| |
| m_classMap = classMap; |
| } |
| |
| |
| private final CoverageOptions m_options; // [never null] |
| private boolean m_hasSrcFileInfo, m_hasLineNumberInfo; |
| private /*final*/ HashMap /* classVMName:String->ClassDescriptor */ m_classMap; // [never null] |
| |
| private /*final*/ transient HashSet /* packageVMName:String */ m_packagesWarned; // [never null] |
| |
| } // end of class |
| // ---------------------------------------------------------------------------- |