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