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