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.Arrays;
028 import java.util.concurrent.locks.Lock;
029 import java.util.concurrent.locks.ReentrantLock;
030
031 /**
032 * <p>
033 * This class implements HasBeenInstrumented so that when cobertura instruments
034 * itself, it will omit this class. It does this to avoid an infinite recursion
035 * problem because instrumented classes make use of this class.
036 * </p>
037 */
038 public class SwitchData implements BranchCoverageData, Comparable, Serializable,
039 HasBeenInstrumented
040 {
041 private static final long serialVersionUID = 9;
042
043 private transient Lock lock;
044
045 private int switchNumber;
046
047 private long defaultHits;
048
049 private long[] hits;
050
051 private int[] keys;
052
053 public SwitchData(int switchNumber, int[] keys)
054 {
055 super();
056 this.switchNumber = switchNumber;
057 defaultHits = 0;
058 hits = new long[keys.length];
059 Arrays.fill(hits, 0);
060 this.keys = new int[keys.length];
061 System.arraycopy(keys, 0, this.keys, 0, keys.length);
062 initLock();
063 }
064
065 public SwitchData(int switchNumber, int min, int max)
066 {
067 super();
068 this.switchNumber = switchNumber;
069 defaultHits = 0;
070 hits = new long[max - min + 1];
071 Arrays.fill(hits, 0);
072 this.keys = new int[max - min + 1];
073 for (int i = 0; min <= max; keys[i++] = min++);
074 initLock();
075 }
076
077 public SwitchData(int switchNumber)
078 {
079 this(switchNumber, new int[0]);
080 }
081
082 private void initLock()
083 {
084 lock = new ReentrantLock();
085 }
086
087 public int compareTo(Object o)
088 {
089 if (!o.getClass().equals(SwitchData.class))
090 return Integer.MAX_VALUE;
091 return this.switchNumber - ((SwitchData) o).switchNumber;
092 }
093
094 void touchBranch(int branch)
095 {
096 lock.lock();
097 try
098 {
099 if (branch == -1)
100 defaultHits++;
101 else
102 {
103 if (hits.length <= branch)
104 {
105 long[] old = hits;
106 hits = new long[branch + 1];
107 System.arraycopy(old, 0, hits, 0, old.length);
108 Arrays.fill(hits, old.length, hits.length - 1, 0);
109 }
110 hits[branch]++;
111 }
112 }
113 finally
114 {
115 lock.unlock();
116 }
117 }
118
119 public int getSwitchNumber()
120 {
121 return this.switchNumber;
122 }
123
124 public long getHits(int branch)
125 {
126 lock.lock();
127 try
128 {
129 if (hits.length > branch)
130 return hits[branch];
131 return -1;
132 }
133 finally
134 {
135 lock.unlock();
136 }
137 }
138
139 public long getDefaultHits()
140 {
141 lock.lock();
142 try
143 {
144 return defaultHits;
145 }
146 finally
147 {
148 lock.unlock();
149 }
150 }
151
152 public double getBranchCoverageRate()
153 {
154 lock.lock();
155 try
156 {
157 int branches = hits.length + 1;
158 int hit = (defaultHits > 0) ? 1 : 0;
159 for (int i = hits.length - 1; i >= 0; hit += ((hits[i--] > 0) ? 1 : 0));
160 return ((double) hit) / branches;
161 }
162 finally
163 {
164 lock.unlock();
165 }
166 }
167
168 public boolean equals(Object obj)
169 {
170 if (this == obj)
171 return true;
172 if ((obj == null) || !(obj.getClass().equals(this.getClass())))
173 return false;
174
175 SwitchData switchData = (SwitchData) obj;
176 getBothLocks(switchData);
177 try
178 {
179 return (this.defaultHits == switchData.defaultHits)
180 && (Arrays.equals(this.hits, switchData.hits))
181 && (this.switchNumber == switchData.switchNumber);
182 }
183 finally
184 {
185 lock.unlock();
186 switchData.lock.unlock();
187 }
188 }
189
190 public int hashCode()
191 {
192 return this.switchNumber;
193 }
194
195 public int getNumberOfCoveredBranches()
196 {
197 lock.lock();
198 try
199 {
200 int ret = (defaultHits > 0) ? 1 : 0;
201 for (int i = hits.length -1; i >= 0;i--)
202 {
203 if (hits[i] > 0) ret++;
204 }
205 return ret;
206 }
207 finally
208 {
209 lock.unlock();
210 }
211 }
212
213 public int getNumberOfValidBranches()
214 {
215 lock.lock();
216 try
217 {
218 return hits.length + 1;
219 }
220 finally
221 {
222 lock.unlock();
223 }
224 }
225
226 public void merge(BranchCoverageData coverageData)
227 {
228 SwitchData switchData = (SwitchData) coverageData;
229 getBothLocks(switchData);
230 try
231 {
232 defaultHits += switchData.defaultHits;
233 for (int i = Math.min(hits.length, switchData.hits.length) - 1; i >= 0; i--)
234 hits[i] += switchData.hits[i];
235 if (switchData.hits.length > hits.length)
236 {
237 long[] old = hits;
238 hits = new long[switchData.hits.length];
239 System.arraycopy(old, 0, hits, 0, old.length);
240 System.arraycopy(switchData.hits, old.length, hits, old.length, hits.length - old.length);
241 }
242 if ((this.keys.length == 0) && (switchData.keys.length > 0))
243 this.keys = switchData.keys;
244 }
245 finally
246 {
247 lock.unlock();
248 switchData.lock.unlock();
249 }
250
251 }
252
253 private void getBothLocks(SwitchData other) {
254 /*
255 * To prevent deadlock, we need to get both locks or none at all.
256 *
257 * When this method returns, the thread will have both locks.
258 * Make sure you unlock them!
259 */
260 boolean myLock = false;
261 boolean otherLock = false;
262 while ((!myLock) || (!otherLock))
263 {
264 try
265 {
266 myLock = lock.tryLock();
267 otherLock = other.lock.tryLock();
268 }
269 finally
270 {
271 if ((!myLock) || (!otherLock))
272 {
273 //could not obtain both locks - so unlock the one we got.
274 if (myLock)
275 {
276 lock.unlock();
277 }
278 if (otherLock)
279 {
280 other.lock.unlock();
281 }
282 //do a yield so the other threads will get to work.
283 Thread.yield();
284 }
285 }
286 }
287 }
288
289 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
290 {
291 in.defaultReadObject();
292 initLock();
293 }
294 }