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.Serializable; 028 import java.util.HashMap; 029 import java.util.Iterator; 030 import java.util.Map; 031 import java.util.Collections; 032 033 /** 034 * <p> 035 * Coverage data information is typically serialized to a file. 036 * </p> 037 * 038 * <p> 039 * This class implements HasBeenInstrumented so that when cobertura 040 * instruments itself, it will omit this class. It does this to 041 * avoid an infinite recursion problem because instrumented classes 042 * make use of this class. 043 * </p> 044 */ 045 public abstract class CoverageDataContainer 046 implements CoverageData, HasBeenInstrumented, Serializable 047 { 048 049 private static final long serialVersionUID = 2; 050 051 /** 052 * Each key is the name of a child, usually stored as a String or 053 * an Integer object. Each value is information about the child, 054 * stored as an object that implements the CoverageData interface. 055 */ 056 Map children = Collections.synchronizedMap(new HashMap()); 057 058 /** 059 * Determine if this CoverageDataContainer is equal to 060 * another one. Subclasses should override this and 061 * make sure they implement the hashCode method. 062 * 063 * @param obj An object to test for equality. 064 * @return True if the objects are equal. 065 */ 066 public boolean equals(Object obj) 067 { 068 if (this == obj) 069 return true; 070 if ((obj == null) || !(obj.getClass().equals(this.getClass()))) 071 return false; 072 073 CoverageDataContainer coverageDataContainer = (CoverageDataContainer)obj; 074 return this.children.equals(coverageDataContainer.children); 075 } 076 077 /** 078 * @return The average branch coverage rate for all children 079 * in this container. 080 */ 081 public double getBranchCoverageRate() 082 { 083 int number = 0; 084 int numberCovered = 0; 085 Iterator iter = this.children.values().iterator(); 086 while (iter.hasNext()) 087 { 088 CoverageData coverageContainer = (CoverageData)iter.next(); 089 number += coverageContainer.getNumberOfValidBranches(); 090 numberCovered += coverageContainer.getNumberOfCoveredBranches(); 091 } 092 if (number == 0) 093 { 094 // no branches, therefore 100% branch coverage. 095 return 1d; 096 } 097 return (double)numberCovered / number; 098 } 099 100 /** 101 * Get a child from this container with the specified 102 * key. 103 * @param name The key used to lookup the child in the 104 * map. 105 * @return The child object, if found, or null if not found. 106 */ 107 public CoverageData getChild(String name) 108 { 109 return (CoverageData)this.children.get(name); 110 } 111 112 /** 113 * @return The average line coverage rate for all children 114 * in this container. This number will be a decimal 115 * between 0 and 1, inclusive. 116 */ 117 public double getLineCoverageRate() 118 { 119 int number = 0; 120 int numberCovered = 0; 121 Iterator iter = this.children.values().iterator(); 122 while (iter.hasNext()) 123 { 124 CoverageData coverageContainer = (CoverageData)iter.next(); 125 number += coverageContainer.getNumberOfValidLines(); 126 numberCovered += coverageContainer.getNumberOfCoveredLines(); 127 } 128 if (number == 0) 129 { 130 // no lines, therefore 100% line coverage. 131 return 1d; 132 } 133 return (double)numberCovered / number; 134 } 135 136 /** 137 * @return The number of children in this container. 138 */ 139 public int getNumberOfChildren() 140 { 141 return this.children.size(); 142 } 143 144 public int getNumberOfCoveredBranches() 145 { 146 int number = 0; 147 Iterator iter = this.children.values().iterator(); 148 while (iter.hasNext()) 149 { 150 CoverageData coverageContainer = (CoverageData)iter.next(); 151 number += coverageContainer.getNumberOfCoveredBranches(); 152 } 153 return number; 154 } 155 156 public int getNumberOfCoveredLines() 157 { 158 int number = 0; 159 Iterator iter = this.children.values().iterator(); 160 while (iter.hasNext()) 161 { 162 CoverageData coverageContainer = (CoverageData)iter.next(); 163 number += coverageContainer.getNumberOfCoveredLines(); 164 } 165 return number; 166 } 167 168 public int getNumberOfValidBranches() 169 { 170 int number = 0; 171 Iterator iter = this.children.values().iterator(); 172 while (iter.hasNext()) 173 { 174 CoverageData coverageContainer = (CoverageData)iter.next(); 175 number += coverageContainer.getNumberOfValidBranches(); 176 } 177 return number; 178 } 179 180 public int getNumberOfValidLines() 181 { 182 int number = 0; 183 Iterator iter = this.children.values().iterator(); 184 while (iter.hasNext()) 185 { 186 CoverageData coverageContainer = (CoverageData)iter.next(); 187 number += coverageContainer.getNumberOfValidLines(); 188 } 189 return number; 190 } 191 192 /** 193 * It is highly recommended that classes extending this 194 * class override this hashCode method and generate a more 195 * effective hash code. 196 */ 197 public int hashCode() 198 { 199 return this.children.size(); 200 } 201 202 /** 203 * Merge two <code>CoverageDataContainer</code>s. 204 * 205 * @param coverageData The container to merge into this one. 206 */ 207 public void merge(CoverageData coverageData) 208 { 209 CoverageDataContainer container = (CoverageDataContainer)coverageData; 210 Iterator iter = container.children.keySet().iterator(); 211 while (iter.hasNext()) 212 { 213 Object key = iter.next(); 214 CoverageData newChild = (CoverageData)container.children.get(key); 215 CoverageData existingChild = (CoverageData)this.children.get(key); 216 if (existingChild != null) 217 { 218 existingChild.merge(newChild); 219 } 220 else 221 { 222 // TODO: Shouldn't we be cloning newChild here? I think so that 223 // would be better... but we would need to override the 224 // clone() method all over the place? 225 this.children.put(key, newChild); 226 } 227 } 228 } 229 230 }