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.concurrent.locks.Lock;
034import 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 */
048public 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}