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
022 package net.sourceforge.cobertura.coveragedata;
023
024 import java.io.IOException;
025 import java.io.ObjectInputStream;
026 import java.io.Serializable;
027 import java.util.concurrent.locks.Lock;
028 import 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 */
037 public 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)
072 {
073 lock.lock();
074 try
075 {
076 if (branch)
077 {
078 this.trueHits++;
079 }
080 else
081 {
082 this.falseHits++;
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 }