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: CoverageData.java,v 1.1.1.1 2004/05/09 16:57:31 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.Iterator;
16import java.util.Map;
17
18import com.vladium.util.asserts.$assert;
19
20// ----------------------------------------------------------------------------
21/**
22 * @author Vlad Roubtsov, (C) 2003
23 */
24final class CoverageData implements ICoverageData, Cloneable
25{
26    // public: ................................................................
27
28    // TODO: duplicate issue
29
30    public Object lock ()
31    {
32        return m_coverageMap;
33    }
34
35    public ICoverageData shallowCopy ()
36    {
37        final CoverageData _clone;
38        try
39        {
40            _clone = (CoverageData) super.clone ();
41        }
42        catch (CloneNotSupportedException cnse)
43        {
44            throw new Error (cnse.toString ());
45        }
46
47        final HashMap _coverageMap;
48
49        synchronized (lock ())
50        {
51            _coverageMap = (HashMap) m_coverageMap.clone ();
52        }
53
54        _clone.m_coverageMap = _coverageMap;
55
56        return _clone;
57    }
58
59    public int size ()
60    {
61        return m_coverageMap.size ();
62    }
63
64    public DataHolder getCoverage (final ClassDescriptor cls)
65    {
66        if (cls == null) throw new IllegalArgumentException ("null input: cls");
67
68        return (DataHolder) m_coverageMap.get (cls.getClassVMName ());
69    }
70
71    public void addClass (final boolean [][] coverage, final String classVMName, final long stamp)
72    {
73        m_coverageMap.put (classVMName, new DataHolder (coverage, stamp));
74    }
75
76    // IMergeable:
77
78    public boolean isEmpty ()
79    {
80        return m_coverageMap.isEmpty ();
81    }
82
83    /*
84     * This method is not MT-safe wrt addClass() etc.
85     *
86     * note: rhs entries override current entries if they have different stamps;
87     * otherwise, the data is merged
88     */
89    public IMergeable merge (final IMergeable rhs)
90    {
91        if ((rhs == null) || rhs.isEmpty () || (rhs == this))
92            return this;
93        else
94        {
95            final CoverageData rhscdata = (CoverageData) rhs; // TODO: redesign so that the cast is not necessary
96            final Map rhscoverageData = rhscdata.m_coverageMap;
97
98            for (Iterator entries = rhscoverageData.entrySet ().iterator (); entries.hasNext (); )
99            {
100                final Map.Entry entry = (Map.Entry) entries.next ();
101                final String classVMName = (String) entry.getKey ();
102
103                final DataHolder rhsdata = (DataHolder) entry.getValue ();
104                // [assertion: rhsdata != null]
105
106                final DataHolder data = (DataHolder) m_coverageMap.get (classVMName);
107
108                if (data == null)
109                    m_coverageMap.put (classVMName, rhsdata);
110                else
111                {
112                    if (rhsdata.m_stamp != data.m_stamp)
113                        m_coverageMap.put (classVMName, rhsdata);
114                    else // merge two runtime profiles
115                    {
116                        final boolean [][] rhscoverage = rhsdata.m_coverage;
117                        final boolean [][] coverage = data.m_coverage;
118
119                        // [assertion: both coverage and rhscoverage aren't null]
120
121                        if ($assert.ENABLED) $assert.ASSERT (coverage.length == rhscoverage.length, "coverage.length [" + coverage.length + "] != rhscoverage.length [" + rhscoverage.length + "]");
122                        for (int m = 0, mLimit = coverage.length; m < mLimit; ++ m)
123                        {
124                            final boolean [] rhsmcoverage = rhscoverage [m];
125                            final boolean [] mcoverage = coverage [m];
126
127                            if (mcoverage == null)
128                            {
129                                if ($assert.ENABLED) $assert.ASSERT (rhsmcoverage == null, "mcoverage == null but rhsmcoverage != null");
130
131                                // [nothing to merge]
132                            }
133                            else
134                            {
135                                if ($assert.ENABLED) $assert.ASSERT (rhsmcoverage != null, "mcoverage != null but rhsmcoverage == null");
136                                if ($assert.ENABLED) $assert.ASSERT (mcoverage.length == rhsmcoverage.length, "mcoverage.length [" + mcoverage.length + "] != rhsmcoverage.length [" + rhsmcoverage.length + "]");
137
138                                for (int b = 0, bLimit = mcoverage.length; b < bLimit; ++ b)
139                                {
140                                    if (rhsmcoverage [b]) mcoverage [b] = true;
141                                }
142                            }
143                        }
144                    }
145                }
146            }
147
148            return this;
149        }
150    }
151
152    // protected: .............................................................
153
154    // package: ...............................................................
155
156
157    CoverageData ()
158    {
159        m_coverageMap = new HashMap ();
160    }
161
162
163    static CoverageData readExternal (final DataInput in)
164        throws IOException
165    {
166        final int size = in.readInt ();
167        final HashMap coverageMap = new HashMap (size);
168
169        for (int i = 0; i < size; ++ i)
170        {
171            final String classVMName = in.readUTF ();
172            final long stamp = in.readLong ();
173
174            final int length = in.readInt ();
175            final boolean [][] coverage = new boolean [length][];
176            for (int c = 0; c < length; ++ c)
177            {
178                coverage [c] = DataFactory.readBooleanArray (in);
179            }
180
181            coverageMap.put (classVMName, new DataHolder (coverage, stamp));
182        }
183
184        return new CoverageData (coverageMap);
185    }
186
187    static void writeExternal (final CoverageData cdata, final DataOutput out)
188        throws IOException
189    {
190        final Map coverageMap = cdata.m_coverageMap;
191
192        final int size = coverageMap.size ();
193        out.writeInt (size);
194
195        final Iterator entries = coverageMap.entrySet ().iterator ();
196        for (int i = 0; i < size; ++ i)
197        {
198            final Map.Entry entry = (Map.Entry) entries.next ();
199
200            final String classVMName = (String) entry.getKey ();
201            final DataHolder data = (DataHolder) entry.getValue ();
202
203            final boolean [][] coverage = data.m_coverage;
204
205            out.writeUTF (classVMName);
206            out.writeLong (data.m_stamp);
207
208            final int length = coverage.length;
209            out.writeInt (length);
210            for (int c = 0; c < length; ++ c)
211            {
212                DataFactory.writeBooleanArray (coverage [c], out);
213            }
214        }
215    }
216
217    // private: ...............................................................
218
219
220    private CoverageData (final HashMap coverageMap)
221    {
222        if ($assert.ENABLED) $assert.ASSERT (coverageMap != null, "coverageMap is null");
223        m_coverageMap = coverageMap;
224    }
225
226
227    private /*final*/ HashMap /* String(classVMName) -> DataHolder */ m_coverageMap; // never null
228
229} // end of class
230// ----------------------------------------------------------------------------