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
025package net.sourceforge.cobertura.coveragedata;
026
027import java.io.IOException;
028import java.io.ObjectInputStream;
029import java.io.Serializable;
030import java.util.HashMap;
031import java.util.Iterator;
032import java.util.Map;
033import java.util.Collections;
034import java.util.concurrent.locks.Lock;
035import 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 */
049public 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}