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
030import net.sourceforge.cobertura.CoverageIgnore;
031
032/**
033 */
034@CoverageIgnore
035public class JumpData implements BranchCoverageData, Comparable<Object>, Serializable
036{
037        private static final long serialVersionUID = 8;
038
039        protected transient Lock lock;
040
041        private int conditionNumber;
042
043        private long trueHits;
044
045        private long falseHits;
046
047        JumpData(int conditionNumber)
048        {
049                super();
050                this.conditionNumber = conditionNumber;
051                this.trueHits = 0L;
052                this.falseHits = 0L;
053                initLock();
054        }
055        
056        private void initLock()
057        {
058                lock = new ReentrantLock();
059        }
060
061        public int compareTo(Object o)
062        {
063                if (!o.getClass().equals(JumpData.class))
064                        return Integer.MAX_VALUE;
065                return this.conditionNumber - ((JumpData) o).conditionNumber;
066        }
067
068        void touchBranch(boolean branch,int new_hits)
069        {
070                lock.lock();
071                try
072                {
073                        if (branch)
074                        {
075                                this.trueHits+=new_hits;
076                        }
077                        else
078                        {
079                                this.falseHits+=new_hits;
080                        }
081                }
082                finally
083                {
084                        lock.unlock();
085                }
086        }
087
088        public int getConditionNumber()
089        {
090                return this.conditionNumber;
091        }
092
093        public long getTrueHits()
094        {
095                lock.lock();
096                try
097                {
098                        return this.trueHits;
099                }
100                finally
101                {
102                        lock.unlock();
103                }
104        }
105
106        public long getFalseHits()
107        {
108                lock.lock();
109                try
110                {
111                        return this.falseHits;
112                }
113                finally
114                {
115                        lock.unlock();
116                }
117        }
118
119        public double getBranchCoverageRate()
120        {
121                lock.lock();
122                try
123                {
124                        return ((double) getNumberOfCoveredBranches()) / getNumberOfValidBranches();
125                }
126                finally
127                {
128                        lock.unlock();
129                }
130        }
131
132        public boolean equals(Object obj)
133        {
134                if (this == obj)
135                        return true;
136                if ((obj == null) || !(obj.getClass().equals(this.getClass())))
137                        return false;
138
139                JumpData branchData = (JumpData) obj;
140                getBothLocks(branchData);
141                try
142                {
143                        return (this.trueHits == branchData.trueHits)
144                                        && (this.falseHits == branchData.falseHits)
145                                        && (this.conditionNumber == branchData.conditionNumber);
146                }
147                finally
148                {
149                        lock.unlock();
150                        branchData.lock.unlock();
151                }
152        }
153
154        public int hashCode()
155        {
156                return this.conditionNumber;
157        }
158
159        public int getNumberOfCoveredBranches()
160        {
161                lock.lock();
162                try
163                {
164                        return ((trueHits > 0) ? 1 : 0) + ((falseHits > 0) ? 1: 0);
165                }
166                finally
167                {
168                        lock.unlock();
169                }
170        }
171
172        public int getNumberOfValidBranches()
173        {
174                return 2;
175        }
176
177        public void merge(BranchCoverageData coverageData)
178        {
179                JumpData jumpData = (JumpData) coverageData;
180                getBothLocks(jumpData);
181                try
182                {
183                        this.trueHits += jumpData.trueHits;
184                        this.falseHits += jumpData.falseHits;
185                }
186                finally
187                {
188                        lock.unlock();
189                        jumpData.lock.unlock();
190                }
191        }
192
193        private void getBothLocks(JumpData other) {
194                /*
195                 * To prevent deadlock, we need to get both locks or none at all.
196                 * 
197                 * When this method returns, the thread will have both locks.
198                 * Make sure you unlock them!
199                 */
200                boolean myLock = false;
201                boolean otherLock = false;
202                while ((!myLock) || (!otherLock))
203                {
204                        try
205                        {
206                                myLock = lock.tryLock();
207                                otherLock = other.lock.tryLock();
208                        }
209                        finally
210                        {
211                                if ((!myLock) || (!otherLock))
212                                {
213                                        //could not obtain both locks - so unlock the one we got.
214                                        if (myLock)
215                                        {
216                                                lock.unlock();
217                                        }
218                                        if (otherLock)
219                                        {
220                                                other.lock.unlock();
221                                        }
222                                        //do a yield so the other threads will get to work.
223                                        Thread.yield();
224                                }
225                        }
226                }
227        }
228        
229        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
230        {
231                in.defaultReadObject();
232                initLock();
233        }
234
235}