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    }