blob: 5416f8a8f386515f61967e69d84555b76ad6b492 [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: 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
// ----------------------------------------------------------------------------