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 }