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 025package net.sourceforge.cobertura.coveragedata; 026 027import java.io.IOException; 028import java.io.ObjectInputStream; 029import java.io.Serializable; 030import java.util.HashMap; 031import java.util.Iterator; 032import java.util.Map; 033import java.util.concurrent.locks.Lock; 034import 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 */ 048public 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}