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    }