1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: MetaData.java,v 1.1.1.1.2.2 2004/07/16 23:32:29 vlad_r Exp $ 8 */ 9package com.vladium.emma.data; 10 11import java.io.DataInput; 12import java.io.DataOutput; 13import java.io.IOException; 14import java.util.HashMap; 15import java.util.HashSet; 16import java.util.Iterator; 17import java.util.Map; 18 19import com.vladium.logging.Logger; 20import com.vladium.util.asserts.$assert; 21 22// ---------------------------------------------------------------------------- 23/* 24 * Average mem size/class entry: 6166 bytes [1.4.1, rt.jar], 5764 bytes [1.3.1, rt.jar] 25 */ 26/** 27 * @author Vlad Roubtsov, (C) 2003 28 */ 29final class MetaData implements IMetaData, Cloneable 30{ 31 // public: ................................................................ 32 33 // TODO: MT-safety model 34 35 // TODO: no duplicate detection is done here at the moment 36 // [may require supporting fast lookup for already added descriptors] 37 38 public IMetaData shallowCopy () 39 { 40 final MetaData _clone; 41 try 42 { 43 _clone = (MetaData) super.clone (); 44 } 45 catch (CloneNotSupportedException cnse) 46 { 47 throw new Error (cnse.toString ()); 48 } 49 50 final HashMap _classMap; 51 52 synchronized (lock ()) 53 { 54 _classMap = (HashMap) m_classMap.clone (); 55 } 56 57 // [m_packagesWarned is not cloned by design] 58 59 _clone.m_classMap = _classMap; 60 61 return _clone; 62 } 63 64 public CoverageOptions getOptions () 65 { 66 return m_options; 67 } 68 69 public int size () 70 { 71 return m_classMap.size (); 72 } 73 74 public boolean hasSrcFileData () 75 { 76 return m_hasSrcFileInfo; 77 } 78 79 public boolean hasLineNumberData () 80 { 81 return m_hasLineNumberInfo; 82 } 83 84 public Iterator iterator () 85 { 86 return m_classMap.values ().iterator (); 87 } 88 89// public boolean hasDescriptor (final ClassDescriptor cls) 90// { 91// if ($assert.ENABLED) $assert.ASSERT (cls != null, "cls is null"); 92// 93// return m_classes.contains (cls); 94// } 95 96 public boolean hasDescriptor (final String classVMName) 97 { 98 if ($assert.ENABLED) $assert.ASSERT (classVMName != null, "className is null"); 99 100 return m_classMap.containsKey (classVMName); 101 } 102 103 public Object lock () 104 { 105 return m_classMap; 106 } 107 108 public boolean add (final ClassDescriptor cls, final boolean overwrite) 109 { 110 if ($assert.ENABLED) $assert.ASSERT (cls != null, "cls is null"); 111 112 final String classVMName = cls.getClassVMName (); 113 114 if (overwrite || ! m_classMap.containsKey (classVMName)) 115 { 116 m_classMap.put (classVMName, cls); 117 118 boolean incompleteDebugInfo = false; 119 120 if (! cls.hasSrcFileInfo ()) 121 { 122 m_hasSrcFileInfo = false; 123 incompleteDebugInfo = true; 124 } 125 126 if (! cls.hasCompleteLineNumberInfo ()) 127 { 128 m_hasLineNumberInfo = false; 129 incompleteDebugInfo = true; 130 } 131 132 // SF FR 971176: provide user with sample classes that may later 133 // caused warnings about line coverage not available 134 135 if (incompleteDebugInfo) 136 { 137 final Logger log = Logger.getLogger (); 138 139 if (log.atINFO ()) 140 { 141 final String packageVMName = cls.getPackageVMName (); 142 143 if (m_packagesWarned.add (packageVMName)) 144 { 145 log.info ("package [" + packageVMName + "] contains classes [" + cls.getName () + "] without full debug info"); 146 } 147 } 148 } 149 150 return true; 151 } 152 153 return false; 154 } 155 156 // IMergeable: 157 158 public boolean isEmpty () 159 { 160 return m_classMap.isEmpty (); 161 } 162 163 /* 164 * note: rhs entries must override current entries 165 */ 166 public IMergeable merge (final IMergeable rhs) 167 { 168 if ((rhs == null) || rhs.isEmpty () || (rhs == this)) 169 return this; 170 else 171 { 172 final MetaData rhsmdata = (MetaData) rhs; // TODO: redesign to avoid this cast? 173 final Map rhsclasses = rhsmdata.m_classMap; 174 175 // rhs entries always override existing content: 176 177 for (Iterator entries = rhsclasses.entrySet ().iterator (); entries.hasNext (); ) 178 { 179 final Map.Entry entry = (Map.Entry) entries.next (); 180 181 final String classVMName = (String) entry.getKey (); 182 final Object rhsdescriptor = entry.getValue (); 183 184 m_classMap.put (classVMName, rhsdescriptor); 185 } 186 187 // update debug info flags if necessary: 188 189 if (! rhsmdata.hasSrcFileData ()) m_hasSrcFileInfo = false; 190 if (! rhsmdata.hasLineNumberData ()) m_hasLineNumberInfo = false; 191 192 return this; 193 } 194 } 195 196 // protected: ............................................................. 197 198 // package: ............................................................... 199 200 201 MetaData (final CoverageOptions options) 202 { 203 if ($assert.ENABLED) $assert.ASSERT (options != null, "options is null"); 204 m_options = options; 205 206 m_hasSrcFileInfo = true; 207 m_hasLineNumberInfo = true; 208 209 m_classMap = new HashMap (); 210 m_packagesWarned = new HashSet (); 211 } 212 213 214 static MetaData readExternal (final DataInput in) 215 throws IOException 216 { 217 final CoverageOptions options = CoverageOptions.readExternal (in); 218 219 final boolean hasSrcFileInfo = in.readBoolean (); 220 final boolean hasLineNumberInfo = in.readBoolean (); 221 222 final int size = in.readInt (); 223 final HashMap classMap = new HashMap (size); 224 225 for (int i = 0; i < size; ++ i) 226 { 227 final String classVMName = in.readUTF (); 228 final ClassDescriptor cls = ClassDescriptor.readExternal (in); 229 230 classMap.put (classVMName, cls); 231 } 232 233 // [m_packagesWarned is not part of persisted state] 234 235 return new MetaData (options, classMap, hasSrcFileInfo, hasLineNumberInfo); 236 } 237 238 static void writeExternal (final MetaData mdata, final DataOutput out) 239 throws IOException 240 { 241 CoverageOptions.writeExternal (mdata.m_options, out); 242 243 out.writeBoolean (mdata.m_hasSrcFileInfo); 244 out.writeBoolean (mdata.m_hasLineNumberInfo); 245 246 final Map classMap = mdata.m_classMap; 247 248 final int size = classMap.size (); 249 out.writeInt (size); // too bad the capacity is not visible 250 251 final Iterator entries = classMap.entrySet ().iterator (); 252 for (int i = 0; i < size; ++ i) 253 { 254 final Map.Entry entry = (Map.Entry) entries.next (); 255 256 final String classVMName = (String) entry.getKey (); 257 final ClassDescriptor cls = (ClassDescriptor) entry.getValue (); 258 259 out.writeUTF (classVMName); 260 ClassDescriptor.writeExternal (cls, out); 261 } 262 263 // [m_packagesWarned is not part of persisted state] 264 } 265 266 // private: ............................................................... 267 268 269 private MetaData (final CoverageOptions options, final HashMap classMap, 270 final boolean hasSrcFileInfo, final boolean hasLineNumberInfo) 271 { 272 if ($assert.ENABLED) $assert.ASSERT (options != null, "options is null"); 273 m_options = options; 274 275 m_hasSrcFileInfo = hasSrcFileInfo; 276 m_hasLineNumberInfo = hasLineNumberInfo; 277 278 m_classMap = classMap; 279 } 280 281 282 private final CoverageOptions m_options; // [never null] 283 private boolean m_hasSrcFileInfo, m_hasLineNumberInfo; 284 private /*final*/ HashMap /* classVMName:String->ClassDescriptor */ m_classMap; // [never null] 285 286 private /*final*/ transient HashSet /* packageVMName:String */ m_packagesWarned; // [never null] 287 288} // end of class 289// ----------------------------------------------------------------------------