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