001/**
002 * www.jcoverage.com
003 * Copyright (C)2003 jcoverage ltd.
004 *
005 * This file is part of jcoverage.
006 *
007 * jcoverage is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published
009 * by the Free Software Foundation; either version 2 of the License,
010 * or (at your option) any later version.
011 *
012 * jcoverage is distributed in the hope that it will be useful, but
013 * WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with jcoverage; if not, write to the Free Software
019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
020 * USA
021 *
022 */
023package com.jcoverage.coverage;
024
025import com.jcoverage.util.SetHelper;
026
027import java.util.Collections;
028import java.util.Iterator;
029import java.util.Map;
030import java.util.Set;
031import java.util.TreeMap;
032import java.util.TreeSet;
033
034import org.apache.log4j.Logger;
035
036
037class InstrumentationImpl implements InstrumentationInternal,HasBeenInstrumented {
038  static final long serialVersionUID=247748305779236308L;
039
040  static final transient Logger logger=Logger.getLogger(InstrumentationImpl.class);
041  static final transient Long ZERO=new Long(0);
042
043  final Map lineCounts=new TreeMap();
044  final Set sourceLineNumbers=new TreeSet();
045  final Map sourceLineNumbersByMethod=new TreeMap();
046  final Map conditionalsByMethod=new TreeMap();
047  final Set methodNamesAndSignatures=new TreeSet();
048
049  int linesOfCode=0;
050  String sourceFileName;
051
052
053  InstrumentationImpl() {
054  }
055
056  public Map getCoverage() {
057    return Collections.unmodifiableMap(lineCounts);
058  }
059
060  public long getCoverage(int lineNumber) {
061    return getLineCount(lineNumber).longValue();
062  }
063
064  public void touch(int lineNumber) {
065    Integer key=new Integer(lineNumber);
066    setLineCount(key,increment(getLineCount(key)));
067  }
068
069  public void merge(Instrumentation instrumentation) {
070    sourceLineNumbers.addAll(instrumentation.getSourceLineNumbers());
071    methodNamesAndSignatures.addAll(instrumentation.getMethodNamesAndSignatures());
072    sourceLineNumbersByMethod.putAll(((InstrumentationImpl)instrumentation).getSourceLineNumbersByMethod());
073    conditionalsByMethod.putAll(((InstrumentationImpl)instrumentation).getConditionalsByMethod());
074
075    Iterator i=instrumentation.getCoverage().entrySet().iterator();
076    while(i.hasNext()) {
077      Map.Entry entry=(Map.Entry)i.next();
078
079      if(lineCounts.containsKey(entry.getKey())) {
080        long total=((Long)entry.getValue()).longValue()+getLineCount((Integer)entry.getKey()).longValue();
081        setLineCount((Integer)entry.getKey(),new Long(total));
082      } else {
083        setLineCount((Integer)entry.getKey(),(Long)entry.getValue());
084      }
085    }
086
087    if(getSourceFileName()==null) {
088      setSourceFileName(instrumentation.getSourceFileName());
089    }
090  }
091
092  public Set getSourceLineNumbers() {
093    return Collections.unmodifiableSet(sourceLineNumbers);
094  }
095
096  public void setSourceLineNumbers(Set sourceLineNumbers) {
097    this.sourceLineNumbers.addAll(sourceLineNumbers);
098  }
099
100  public double getLineCoverageRate() {
101    if(sourceLineNumbers.size()==0) {
102      return 1d;
103    }
104    return ((double)lineCounts.keySet().size())/((double)sourceLineNumbers.size());
105  }
106
107  public double getBranchCoverageRate() {
108    double sum=0d;
109    Iterator i=sourceLineNumbersByMethod.keySet().iterator();
110    while(i.hasNext()) {
111      sum+=getBranchCoverageRate((String)i.next());
112    }
113    return sum/(double)sourceLineNumbersByMethod.keySet().size();
114  }
115
116
117  public double getLineCoverageRate(String methodNameAndSignature) {
118    if(!sourceLineNumbersByMethod.containsKey(methodNameAndSignature)) {
119      if(logger.isDebugEnabled()) {
120        logger.debug("sourceLineNumbersByMethod: "+sourceLineNumbersByMethod.keySet());
121      }
122
123      throw new IllegalArgumentException(methodNameAndSignature);
124    }
125
126    Set lineNumbers=(Set)sourceLineNumbersByMethod.get(methodNameAndSignature);
127    if(lineNumbers.size()==0) {
128      return 1d;
129    }
130
131    int count=0;
132    Iterator i=lineNumbers.iterator();
133    while(i.hasNext()) {
134      if(getLineCount((Integer)i.next()).longValue()>0) {
135        count++;
136      }
137    }
138
139    return ((double)count)/((double)lineNumbers.size());
140  }
141
142  Set getTouchedLines(String methodNameAndSignature) {
143    Set results=new TreeSet();
144
145    Iterator i=((Set)sourceLineNumbersByMethod.get(methodNameAndSignature)).iterator();
146    while(i.hasNext()) {
147      Integer lineNumber=(Integer)i.next();
148      if(getLineCount(lineNumber).longValue()>0) {
149        results.add(lineNumber);
150      }
151    }
152    return results;
153  }
154
155  public double getBranchCoverageRate(String methodNameAndSignature) {
156    if(!sourceLineNumbersByMethod.containsKey(methodNameAndSignature)) {
157      if(logger.isDebugEnabled()) {
158        logger.debug("sourceLineNumbersByMethod: "+sourceLineNumbersByMethod.keySet());
159      }
160
161      throw new IllegalArgumentException(methodNameAndSignature);
162    }
163
164    Set conditionals=(Set)conditionalsByMethod.get(methodNameAndSignature);
165    if(conditionals.size()==0) {
166      // no conditional branches, therefore 100% branch coverage.
167      return 1d;
168    }
169
170    Set requiredHits=new TreeSet();
171    Iterator i=conditionals.iterator();
172    while(i.hasNext()) {
173      Conditional conditional=(Conditional)i.next();
174      requiredHits.add(findNextSourceLineAfter(methodNameAndSignature,conditional.getLineNumber()));
175      requiredHits.add(new Integer(conditional.getTargetLineNumber()));
176    }
177
178    return ((double)SetHelper.intersection(requiredHits,getTouchedLines(methodNameAndSignature)).size())/((double)requiredHits.size());
179  }
180
181  Integer findNextSourceLineAfter(String methodNameAndSignature,int thisOne) {
182    Iterator i=((Set)sourceLineNumbersByMethod.get(methodNameAndSignature)).iterator();
183    Integer lineNumber=new Integer(0);
184
185    while(i.hasNext()&&(lineNumber.intValue()<thisOne)) {
186      lineNumber=(Integer)i.next();
187    }
188    
189    return lineNumber;
190  }
191
192
193  public void setSourceFileName(String sourceFileName) {
194    this.sourceFileName=sourceFileName;
195  }
196  
197  public String getSourceFileName() {
198    return sourceFileName;
199  }
200
201  private Long increment(Long count) {
202    return new Long(count.longValue()+1);
203  }
204
205  private Long getLineCount(int lineNumber) {
206    return getLineCount(new Integer(lineNumber));
207  }
208
209  private Long getLineCount(Integer lineNumber) {
210    if(!lineCounts.containsKey(lineNumber)) {
211      lineCounts.put(lineNumber,ZERO);
212    }
213
214    return (Long)lineCounts.get(lineNumber);
215  }
216
217  private void setLineCount(Integer lineNumber,Long lineCount) {
218    lineCounts.put(lineNumber,lineCount);
219  }
220
221  public Map getSourceLineNumbersByMethod() {
222    return sourceLineNumbersByMethod;
223  }
224
225  public void setSourceLineNumbersByMethod(Map sourceLineNumbersByMethod) {
226    this.sourceLineNumbersByMethod.putAll(sourceLineNumbersByMethod);
227  }
228
229  public Map getConditionalsByMethod() {
230    return conditionalsByMethod;
231  }
232
233  public void setConditionalsByMethod(Map conditionalsByMethod) {
234    this.conditionalsByMethod.putAll(conditionalsByMethod);
235  }
236
237  public Set getMethodNamesAndSignatures() {
238    return methodNamesAndSignatures;
239  }
240
241  public void setMethodNamesAndSignatures(Set x) {
242    if(logger.isDebugEnabled()) {
243      logger.debug("x: "+x);
244    }
245
246    this.methodNamesAndSignatures.addAll(x);
247
248    if(logger.isDebugEnabled()) {
249      logger.debug("methodNamesAndSignatures: "+methodNamesAndSignatures);
250    }
251  }
252
253}