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