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