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