001/*
002 * Cobertura - http://cobertura.sourceforge.net/
003 *
004 * Copyright (C) 2006 Jiri Mares
005 *
006 * Cobertura is free software; you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published
008 * by the Free Software Foundation; either version 2 of the License,
009 * or (at your option) any later version.
010 *
011 * Cobertura is distributed in the hope that it will be useful, but
012 * WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with Cobertura; if not, write to the Free Software
018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019 * USA
020 */
021
022package net.sourceforge.cobertura.coveragedata;
023
024import java.io.IOException;
025import java.io.ObjectInputStream;
026import java.io.Serializable;
027import java.util.concurrent.locks.Lock;
028import java.util.concurrent.locks.ReentrantLock;
029
030/**
031 * <p>
032 * This class implements HasBeenInstrumented so that when cobertura instruments
033 * itself, it will omit this class. It does this to avoid an infinite recursion
034 * problem because instrumented classes make use of this class.
035 * </p>
036 */
037public class JumpData implements BranchCoverageData, Comparable, Serializable,
038                HasBeenInstrumented
039{
040        private static final long serialVersionUID = 8;
041
042        protected transient Lock lock;
043
044        private int conditionNumber;
045
046        private long trueHits;
047
048        private long falseHits;
049
050        JumpData(int conditionNumber)
051        {
052                super();
053                this.conditionNumber = conditionNumber;
054                this.trueHits = 0L;
055                this.falseHits = 0L;
056                initLock();
057        }
058        
059        private void initLock()
060        {
061                lock = new ReentrantLock();
062        }
063
064        public int compareTo(Object o)
065        {
066                if (!o.getClass().equals(JumpData.class))
067                        return Integer.MAX_VALUE;
068                return this.conditionNumber - ((JumpData) o).conditionNumber;
069        }
070
071        void touchBranch(boolean branch,int new_hits)
072        {
073                lock.lock();
074                try
075                {
076                        if (branch)
077                        {
078                                this.trueHits+=new_hits;
079                        }
080                        else
081                        {
082                                this.falseHits+=new_hits;
083                        }
084                }
085                finally
086                {
087                        lock.unlock();
088                }
089        }
090
091        public int getConditionNumber()
092        {
093                return this.conditionNumber;
094        }
095
096        public long getTrueHits()
097        {
098                lock.lock();
099                try
100                {
101                        return this.trueHits;
102                }
103                finally
104                {
105                        lock.unlock();
106                }
107        }
108
109        public long getFalseHits()
110        {
111                lock.lock();
112                try
113                {
114                        return this.falseHits;
115                }
116                finally
117                {
118                        lock.unlock();
119                }
120        }
121
122        public double getBranchCoverageRate()
123        {
124                lock.lock();
125                try
126                {
127                        return ((double) getNumberOfCoveredBranches()) / getNumberOfValidBranches();
128                }
129                finally
130                {
131                        lock.unlock();
132                }
133        }
134
135        public boolean equals(Object obj)
136        {
137                if (this == obj)
138                        return true;
139                if ((obj == null) || !(obj.getClass().equals(this.getClass())))
140                        return false;
141
142                JumpData branchData = (JumpData) obj;
143                getBothLocks(branchData);
144                try
145                {
146                        return (this.trueHits == branchData.trueHits)
147                                        && (this.falseHits == branchData.falseHits)
148                                        && (this.conditionNumber == branchData.conditionNumber);
149                }
150                finally
151                {
152                        lock.unlock();
153                        branchData.lock.unlock();
154                }
155        }
156
157        public int hashCode()
158        {
159                return this.conditionNumber;
160        }
161
162        public int getNumberOfCoveredBranches()
163        {
164                lock.lock();
165                try
166                {
167                        return ((trueHits > 0) ? 1 : 0) + ((falseHits > 0) ? 1: 0);
168                }
169                finally
170                {
171                        lock.unlock();
172                }
173        }
174
175        public int getNumberOfValidBranches()
176        {
177                return 2;
178        }
179
180        public void merge(BranchCoverageData coverageData)
181        {
182                JumpData jumpData = (JumpData) coverageData;
183                getBothLocks(jumpData);
184                try
185                {
186                        this.trueHits += jumpData.trueHits;
187                        this.falseHits += jumpData.falseHits;
188                }
189                finally
190                {
191                        lock.unlock();
192                        jumpData.lock.unlock();
193                }
194        }
195
196        private void getBothLocks(JumpData other) {
197                /*
198                 * To prevent deadlock, we need to get both locks or none at all.
199                 * 
200                 * When this method returns, the thread will have both locks.
201                 * Make sure you unlock them!
202                 */
203                boolean myLock = false;
204                boolean otherLock = false;
205                while ((!myLock) || (!otherLock))
206                {
207                        try
208                        {
209                                myLock = lock.tryLock();
210                                otherLock = other.lock.tryLock();
211                        }
212                        finally
213                        {
214                                if ((!myLock) || (!otherLock))
215                                {
216                                        //could not obtain both locks - so unlock the one we got.
217                                        if (myLock)
218                                        {
219                                                lock.unlock();
220                                        }
221                                        if (otherLock)
222                                        {
223                                                other.lock.unlock();
224                                        }
225                                        //do a yield so the other threads will get to work.
226                                        Thread.yield();
227                                }
228                        }
229                }
230        }
231        
232        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
233        {
234                in.defaultReadObject();
235                initLock();
236        }
237
238}