001 /*
002 * Cobertura - http://cobertura.sourceforge.net/
003 *
004 * Copyright (C) 2003 jcoverage ltd.
005 * Copyright (C) 2005 Mark Doliner
006 * Copyright (C) 2005 Jeremy Thomerson
007 * Copyright (C) 2005 Mark Sinke
008 *
009 * Cobertura is free software; you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as published
011 * by the Free Software Foundation; either version 2 of the License,
012 * or (at your option) any later version.
013 *
014 * Cobertura is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017 * General Public License for more details.
018 *
019 * You should have received a copy of the GNU General Public License
020 * along with Cobertura; if not, write to the Free Software
021 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
022 * USA
023 */
024
025 package net.sourceforge.cobertura.coveragedata;
026
027 import java.io.IOException;
028 import java.io.ObjectInputStream;
029 import java.io.Serializable;
030 import java.util.HashMap;
031 import java.util.Iterator;
032 import java.util.Map;
033 import java.util.Collections;
034 import java.util.concurrent.locks.Lock;
035 import java.util.concurrent.locks.ReentrantLock;
036
037 /**
038 * <p>
039 * Coverage data information is typically serialized to a file.
040 * </p>
041 *
042 * <p>
043 * This class implements HasBeenInstrumented so that when cobertura
044 * instruments itself, it will omit this class. It does this to
045 * avoid an infinite recursion problem because instrumented classes
046 * make use of this class.
047 * </p>
048 */
049 public abstract class CoverageDataContainer
050 implements CoverageData, HasBeenInstrumented, Serializable
051 {
052
053 private static final long serialVersionUID = 2;
054
055 protected transient Lock lock;
056
057 /**
058 * Each key is the name of a child, usually stored as a String or
059 * an Integer object. Each value is information about the child,
060 * stored as an object that implements the CoverageData interface.
061 */
062 Map children = new HashMap();
063
064 public CoverageDataContainer()
065 {
066 initLock();
067 }
068
069 private void initLock()
070 {
071 lock = new ReentrantLock();
072 }
073
074 /**
075 * Determine if this CoverageDataContainer is equal to
076 * another one. Subclasses should override this and
077 * make sure they implement the hashCode method.
078 *
079 * @param obj An object to test for equality.
080 * @return True if the objects are equal.
081 */
082 public boolean equals(Object obj)
083 {
084 if (this == obj)
085 return true;
086 if ((obj == null) || !(obj.getClass().equals(this.getClass())))
087 return false;
088
089 CoverageDataContainer coverageDataContainer = (CoverageDataContainer)obj;
090 lock.lock();
091 try
092 {
093 return this.children.equals(coverageDataContainer.children);
094 }
095 finally
096 {
097 lock.unlock();
098 }
099 }
100
101 /**
102 * @return The average branch coverage rate for all children
103 * in this container.
104 */
105 public double getBranchCoverageRate()
106 {
107 int number = 0;
108 int numberCovered = 0;
109 lock.lock();
110 try
111 {
112 Iterator iter = this.children.values().iterator();
113 while (iter.hasNext())
114 {
115 CoverageData coverageContainer = (CoverageData)iter.next();
116 number += coverageContainer.getNumberOfValidBranches();
117 numberCovered += coverageContainer.getNumberOfCoveredBranches();
118 }
119 }
120 finally
121 {
122 lock.unlock();
123 }
124 if (number == 0)
125 {
126 // no branches, therefore 100% branch coverage.
127 return 1d;
128 }
129 return (double)numberCovered / number;
130 }
131
132 /**
133 * Get a child from this container with the specified
134 * key.
135 * @param name The key used to lookup the child in the
136 * map.
137 * @return The child object, if found, or null if not found.
138 */
139 public CoverageData getChild(String name)
140 {
141 lock.lock();
142 try
143 {
144 return (CoverageData)this.children.get(name);
145 }
146 finally
147 {
148 lock.unlock();
149 }
150 }
151
152 /**
153 * @return The average line coverage rate for all children
154 * in this container. This number will be a decimal
155 * between 0 and 1, inclusive.
156 */
157 public double getLineCoverageRate()
158 {
159 int number = 0;
160 int numberCovered = 0;
161 lock.lock();
162 try
163 {
164 Iterator iter = this.children.values().iterator();
165 while (iter.hasNext())
166 {
167 CoverageData coverageContainer = (CoverageData)iter.next();
168 number += coverageContainer.getNumberOfValidLines();
169 numberCovered += coverageContainer.getNumberOfCoveredLines();
170 }
171 }
172 finally
173 {
174 lock.unlock();
175 }
176 if (number == 0)
177 {
178 // no lines, therefore 100% line coverage.
179 return 1d;
180 }
181 return (double)numberCovered / number;
182 }
183
184 /**
185 * @return The number of children in this container.
186 */
187 public int getNumberOfChildren()
188 {
189 lock.lock();
190 try
191 {
192 return this.children.size();
193 }
194 finally
195 {
196 lock.unlock();
197 }
198 }
199
200 public int getNumberOfCoveredBranches()
201 {
202 int number = 0;
203 lock.lock();
204 try
205 {
206 Iterator iter = this.children.values().iterator();
207 while (iter.hasNext())
208 {
209 CoverageData coverageContainer = (CoverageData)iter.next();
210 number += coverageContainer.getNumberOfCoveredBranches();
211 }
212 }
213 finally
214 {
215 lock.unlock();
216 }
217 return number;
218 }
219
220 public int getNumberOfCoveredLines()
221 {
222 int number = 0;
223 lock.lock();
224 try
225 {
226 Iterator iter = this.children.values().iterator();
227 while (iter.hasNext())
228 {
229 CoverageData coverageContainer = (CoverageData)iter.next();
230 number += coverageContainer.getNumberOfCoveredLines();
231 }
232 }
233 finally
234 {
235 lock.unlock();
236 }
237 return number;
238 }
239
240 public int getNumberOfValidBranches()
241 {
242 int number = 0;
243 lock.lock();
244 try
245 {
246 Iterator iter = this.children.values().iterator();
247 while (iter.hasNext())
248 {
249 CoverageData coverageContainer = (CoverageData)iter.next();
250 number += coverageContainer.getNumberOfValidBranches();
251 }
252 }
253 finally
254 {
255 lock.unlock();
256 }
257 return number;
258 }
259
260 public int getNumberOfValidLines()
261 {
262 int number = 0;
263 lock.lock();
264 try
265 {
266 Iterator iter = this.children.values().iterator();
267 while (iter.hasNext())
268 {
269 CoverageData coverageContainer = (CoverageData)iter.next();
270 number += coverageContainer.getNumberOfValidLines();
271 }
272 }
273 finally
274 {
275 lock.unlock();
276 }
277 return number;
278 }
279
280 /**
281 * It is highly recommended that classes extending this
282 * class override this hashCode method and generate a more
283 * effective hash code.
284 */
285 public int hashCode()
286 {
287 lock.lock();
288 try
289 {
290 return this.children.size();
291 }
292 finally
293 {
294 lock.unlock();
295 }
296 }
297
298 /**
299 * Merge two <code>CoverageDataContainer</code>s.
300 *
301 * @param coverageData The container to merge into this one.
302 */
303 public void merge(CoverageData coverageData)
304 {
305 CoverageDataContainer container = (CoverageDataContainer)coverageData;
306 getBothLocks(container);
307 try
308 {
309 Iterator iter = container.children.keySet().iterator();
310 while (iter.hasNext())
311 {
312 Object key = iter.next();
313 CoverageData newChild = (CoverageData)container.children.get(key);
314 CoverageData existingChild = (CoverageData)this.children.get(key);
315 if (existingChild != null)
316 {
317 existingChild.merge(newChild);
318 }
319 else
320 {
321 // TODO: Shouldn't we be cloning newChild here? I think so that
322 // would be better... but we would need to override the
323 // clone() method all over the place?
324 this.children.put(key, newChild);
325 }
326 }
327 }
328 finally
329 {
330 lock.unlock();
331 container.lock.unlock();
332 }
333 }
334
335 protected void getBothLocks(CoverageDataContainer other) {
336 /*
337 * To prevent deadlock, we need to get both locks or none at all.
338 *
339 * When this method returns, the thread will have both locks.
340 * Make sure you unlock them!
341 */
342 boolean myLock = false;
343 boolean otherLock = false;
344 while ((!myLock) || (!otherLock))
345 {
346 try
347 {
348 myLock = lock.tryLock();
349 otherLock = other.lock.tryLock();
350 }
351 finally
352 {
353 if ((!myLock) || (!otherLock))
354 {
355 //could not obtain both locks - so unlock the one we got.
356 if (myLock)
357 {
358 lock.unlock();
359 }
360 if (otherLock)
361 {
362 other.lock.unlock();
363 }
364 //do a yield so the other threads will get to work.
365 Thread.yield();
366 }
367 }
368 }
369 }
370
371 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
372 {
373 in.defaultReadObject();
374 initLock();
375 }
376 }