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 }