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,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    }