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