Coverage Report - net.sourceforge.cobertura.coveragedata.ClassData
 
Classes in this File Line Coverage Branch Coverage Complexity
ClassData
N/A
N/A
2.375
 
 1  
 /*
 2  
  * Cobertura - http://cobertura.sourceforge.net/
 3  
  *
 4  
  * Copyright (C) 2003 jcoverage ltd.
 5  
  * Copyright (C) 2005 Mark Doliner
 6  
  * Copyright (C) 2006 Jiri Mares
 7  
  *
 8  
  * Cobertura is free software; you can redistribute it and/or modify
 9  
  * it under the terms of the GNU General Public License as published
 10  
  * by the Free Software Foundation; either version 2 of the License,
 11  
  * or (at your option) any later version.
 12  
  *
 13  
  * Cobertura is distributed in the hope that it will be useful, but
 14  
  * WITHOUT ANY WARRANTY; without even the implied warranty of
 15  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 16  
  * General Public License for more details.
 17  
  *
 18  
  * You should have received a copy of the GNU General Public License
 19  
  * along with Cobertura; if not, write to the Free Software
 20  
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 21  
  * USA
 22  
  */
 23  
 
 24  
 package net.sourceforge.cobertura.coveragedata;
 25  
 
 26  
 import java.util.Collection;
 27  
 import java.util.Collections;
 28  
 import java.util.HashMap;
 29  
 import java.util.HashSet;
 30  
 import java.util.Iterator;
 31  
 import java.util.Map;
 32  
 import java.util.Set;
 33  
 import java.util.SortedSet;
 34  
 import java.util.TreeSet;
 35  
 
 36  
 /**
 37  
  * <p>
 38  
  * ProjectData information is typically serialized to a file. An
 39  
  * instance of this class records coverage information for a single
 40  
  * class that has been instrumented.
 41  
  * </p>
 42  
  *
 43  
  * <p>
 44  
  * This class implements HasBeenInstrumented so that when cobertura
 45  
  * instruments itself, it will omit this class.  It does this to
 46  
  * avoid an infinite recursion problem because instrumented classes
 47  
  * make use of this class.
 48  
  * </p>
 49  
  */
 50  
 
 51  
 public class ClassData extends CoverageDataContainer
 52  
         implements Comparable, HasBeenInstrumented 
 53  
 {
 54  
 
 55  
         private static final long serialVersionUID = 5;
 56  
 
 57  
         /**
 58  
          * Each key is a line number in this class, stored as an Integer object.
 59  
          * Each value is information about the line, stored as a LineData object.
 60  
          */
 61  
         private Map branches = new HashMap();
 62  
 
 63  
         private boolean containsInstrumentationInfo = false;
 64  
 
 65  
         private Set methodNamesAndDescriptors = new HashSet();
 66  
 
 67  
         private String name = null;
 68  
 
 69  
         private String sourceFileName = null;
 70  
 
 71  
         /**
 72  
          * @param name In the format "net.sourceforge.cobertura.coveragedata.ClassData"
 73  
          */
 74  
         public ClassData(String name)
 75  
         {
 76  
                 if (name == null)
 77  
                         throw new IllegalArgumentException(
 78  
                                 "Class name must be specified.");
 79  
                 this.name = name;
 80  
         }
 81  
 
 82  
         public LineData addLine(int lineNumber, String methodName,
 83  
                         String methodDescriptor)
 84  
         {
 85  
                 LineData lineData = getLineData(lineNumber);
 86  
                 if (lineData == null)
 87  
                 {
 88  
                         lineData = new LineData(lineNumber);
 89  
                         // Each key is a line number in this class, stored as an Integer object.
 90  
                         // Each value is information about the line, stored as a LineData object.
 91  
                         children.put(new Integer(lineNumber), lineData);
 92  
                 }
 93  
                 lineData.setMethodNameAndDescriptor(methodName, methodDescriptor);
 94  
       
 95  
                 // methodName and methodDescriptor can be null when cobertura.ser with 
 96  
                 // no line information was loaded (or was not loaded at all).
 97  
                 if( methodName!=null && methodDescriptor!=null)
 98  
                         methodNamesAndDescriptors.add(methodName + methodDescriptor);
 99  
                 return lineData;
 100  
         }
 101  
 
 102  
         /**
 103  
          * This is required because we implement Comparable.
 104  
          */
 105  
         public int compareTo(Object o)
 106  
         {
 107  
                 if (!o.getClass().equals(ClassData.class))
 108  
                         return Integer.MAX_VALUE;
 109  
                 return this.name.compareTo(((ClassData)o).name);
 110  
         }
 111  
 
 112  
         public boolean containsInstrumentationInfo()
 113  
         {
 114  
                 return this.containsInstrumentationInfo;
 115  
         }
 116  
 
 117  
         /**
 118  
          * Returns true if the given object is an instance of the
 119  
          * ClassData class, and it contains the same data as this
 120  
          * class.
 121  
          */
 122  
         public boolean equals(Object obj)
 123  
         {
 124  
                 if (this == obj)
 125  
                         return true;
 126  
                 if ((obj == null) || !(obj.getClass().equals(this.getClass())))
 127  
                         return false;
 128  
 
 129  
                 ClassData classData = (ClassData)obj;
 130  
                 return super.equals(obj)
 131  
                         && this.branches.equals(classData.branches)
 132  
                         && this.methodNamesAndDescriptors
 133  
                                 .equals(classData.methodNamesAndDescriptors)
 134  
                         && this.name.equals(classData.name)
 135  
                         && this.sourceFileName.equals(classData.sourceFileName);
 136  
         }
 137  
 
 138  
         public String getBaseName()
 139  
         {
 140  
                 int lastDot = this.name.lastIndexOf('.');
 141  
                 if (lastDot == -1)
 142  
                 {
 143  
                         return this.name;
 144  
                 }
 145  
                 return this.name.substring(lastDot + 1);
 146  
         }
 147  
 
 148  
         /**
 149  
          * @return The branch coverage rate for a particular method.
 150  
          */
 151  
         public double getBranchCoverageRate(String methodNameAndDescriptor)
 152  
         {
 153  
                 int total = 0;
 154  
                 int covered = 0;
 155  
 
 156  
                 for (Iterator iter = branches.values().iterator(); iter.hasNext();) {
 157  
                         LineData next = (LineData) iter.next();
 158  
                         if (methodNameAndDescriptor.equals(next.getMethodName() + next.getMethodDescriptor()))
 159  
                         {
 160  
                                 total += next.getNumberOfValidBranches();
 161  
                                 covered += next.getNumberOfCoveredBranches();
 162  
                         }
 163  
                 }
 164  
                 if (total == 0) return 1.0;
 165  
                 return (double) covered / total;
 166  
         }
 167  
 
 168  
         public Collection getBranches() 
 169  
         {
 170  
                 return Collections.unmodifiableCollection(branches.keySet());
 171  
         }
 172  
 
 173  
         /**
 174  
          * @param lineNumber The source code line number.
 175  
          * @return The coverage of the line
 176  
          */
 177  
         public LineData getLineCoverage(int lineNumber) 
 178  
         {
 179  
                 Integer lineObject = new Integer(lineNumber);
 180  
                 if (!children.containsKey(lineObject)) 
 181  
                 {
 182  
                         return null;
 183  
                 }
 184  
 
 185  
                 return (LineData) children.get(lineObject);
 186  
         }
 187  
 
 188  
         /**
 189  
          * @return The line coverage rate for particular method
 190  
          */
 191  
         public double getLineCoverageRate(String methodNameAndDescriptor) 
 192  
         {
 193  
                 int total = 0;
 194  
                 int hits = 0;
 195  
 
 196  
                 Iterator iter = children.values().iterator();
 197  
                 while (iter.hasNext()) 
 198  
                 {
 199  
                         LineData next = (LineData) iter.next();
 200  
                         if (methodNameAndDescriptor.equals(next.getMethodName() + next.getMethodDescriptor())) 
 201  
                         {
 202  
                                 total++;
 203  
                                 if (next.getHits() > 0) {
 204  
                                         hits++;
 205  
                                 }
 206  
                         }
 207  
                 }
 208  
                 if (total == 0) return 1d;
 209  
                 return (double) hits / total;
 210  
         }
 211  
 
 212  
         private LineData getLineData(int lineNumber)
 213  
         {
 214  
                 return (LineData)children.get(new Integer(lineNumber));
 215  
         }
 216  
 
 217  
         public SortedSet getLines()
 218  
         {
 219  
                 return new TreeSet(this.children.values());
 220  
         }
 221  
 
 222  
         public Collection getLines(String methodNameAndDescriptor)
 223  
         {
 224  
                 Collection lines = new HashSet();
 225  
                 Iterator iter = children.values().iterator();
 226  
                 while (iter.hasNext())
 227  
                 {
 228  
                         LineData next = (LineData)iter.next();
 229  
                         if (methodNameAndDescriptor.equals(next.getMethodName()
 230  
                                         + next.getMethodDescriptor()))
 231  
                         {
 232  
                                 lines.add(next);
 233  
                         }
 234  
                 }
 235  
                 return lines;
 236  
         }
 237  
 
 238  
         /**
 239  
          * @return The method name and descriptor of each method found in the
 240  
          *         class represented by this instrumentation.
 241  
          */
 242  
         public Set getMethodNamesAndDescriptors() 
 243  
         {
 244  
                 return methodNamesAndDescriptors;
 245  
         }
 246  
 
 247  
         public String getName() 
 248  
         {
 249  
                 return name;
 250  
         }
 251  
 
 252  
         /**
 253  
          * @return The number of branches in this class.
 254  
          */
 255  
         public int getNumberOfValidBranches() 
 256  
         {
 257  
                 int number = 0;
 258  
                 for (Iterator i = branches.values().iterator(); 
 259  
                         i.hasNext(); 
 260  
                         number += ((LineData) i.next()).getNumberOfValidBranches())
 261  
                         ;
 262  
                 return number;
 263  
         }
 264  
 
 265  
         /**
 266  
          * @see net.sourceforge.cobertura.coveragedata.CoverageData#getNumberOfCoveredBranches()
 267  
          */
 268  
         public int getNumberOfCoveredBranches() 
 269  
         {
 270  
                 int number = 0;
 271  
                 for (Iterator i = branches.values().iterator(); 
 272  
                         i.hasNext(); 
 273  
                         number += ((LineData) i.next()).getNumberOfCoveredBranches())
 274  
                         ;
 275  
                 return number;
 276  
         }
 277  
 
 278  
         public String getPackageName()
 279  
         {
 280  
                 int lastDot = this.name.lastIndexOf('.');
 281  
                 if (lastDot == -1)
 282  
                 {
 283  
                         return "";
 284  
                 }
 285  
                 return this.name.substring(0, lastDot);
 286  
         }
 287  
 
 288  
          /**
 289  
          * Return the name of the file containing this class.  If this
 290  
          * class' sourceFileName has not been set (for whatever reason)
 291  
          * then this method will attempt to infer the name of the source
 292  
          * file using the class name.
 293  
          *
 294  
          * @return The name of the source file, for example
 295  
          *         net/sourceforge/cobertura/coveragedata/ClassData.java
 296  
          */
 297  
         public String getSourceFileName()
 298  
         {
 299  
                 String baseName;
 300  
                 if (sourceFileName != null)
 301  
                         baseName = sourceFileName;
 302  
                 else
 303  
                 {
 304  
                         baseName = getBaseName();
 305  
                         int firstDollarSign = baseName.indexOf('$');
 306  
                         if (firstDollarSign == -1 || firstDollarSign == 0)
 307  
                                 baseName += ".java";
 308  
                         else
 309  
                                 baseName = baseName.substring(0, firstDollarSign)
 310  
                                         + ".java";
 311  
                 }
 312  
 
 313  
                 String packageName = getPackageName();
 314  
                 if (packageName.equals(""))
 315  
                         return baseName;
 316  
                 return packageName.replace('.', '/') + '/' + baseName;
 317  
         }
 318  
 
 319  
         public int hashCode()
 320  
         {
 321  
                 return this.name.hashCode();
 322  
         }
 323  
 
 324  
         /**
 325  
          * @return True if the line contains at least one condition jump (branch)
 326  
          */
 327  
         public boolean hasBranch(int lineNumber) 
 328  
         {
 329  
                 return branches.containsKey(new Integer(lineNumber));
 330  
         }
 331  
 
 332  
         /**
 333  
          * Determine if a given line number is a valid line of code.
 334  
          *
 335  
          * @return True if the line contains executable code.  False
 336  
          *         if the line is empty, or a comment, etc.
 337  
          */
 338  
         public boolean isValidSourceLineNumber(int lineNumber) 
 339  
         {
 340  
                 return children.containsKey(new Integer(lineNumber));
 341  
         }
 342  
 
 343  
         public void addLineJump(int lineNumber, int branchNumber) 
 344  
         {
 345  
                 LineData lineData = getLineData(lineNumber);
 346  
                 if (lineData != null) 
 347  
                 {
 348  
                         lineData.addJump(branchNumber);
 349  
                         this.branches.put(new Integer(lineNumber), lineData);
 350  
                 }
 351  
         }
 352  
 
 353  
         public void addLineSwitch(int lineNumber, int switchNumber, int[] keys) 
 354  
         {
 355  
                 LineData lineData = getLineData(lineNumber);
 356  
                 if (lineData != null) 
 357  
                 {
 358  
                         lineData.addSwitch(switchNumber, keys);
 359  
                         this.branches.put(new Integer(lineNumber), lineData);
 360  
                 }
 361  
         }
 362  
 
 363  
         public void addLineSwitch(int lineNumber, int switchNumber, int min, int max) 
 364  
         {
 365  
                 LineData lineData = getLineData(lineNumber);
 366  
                 if (lineData != null) 
 367  
                 {
 368  
                         lineData.addSwitch(switchNumber, min, max);
 369  
                         this.branches.put(new Integer(lineNumber), lineData);
 370  
                 }
 371  
         }
 372  
 
 373  
         /**
 374  
          * Merge some existing instrumentation with this instrumentation.
 375  
          *
 376  
          * @param coverageData Some existing coverage data.
 377  
          */
 378  
         public void merge(CoverageData coverageData)
 379  
         {
 380  
                 ClassData classData = (ClassData)coverageData;
 381  
 
 382  
                 // If objects contain data for different classes then don't merge
 383  
                 if (!this.getName().equals(classData.getName()))
 384  
                         return;
 385  
 
 386  
                 super.merge(coverageData);
 387  
 
 388  
                 // We can't just call this.branches.putAll(classData.branches);
 389  
                 // Why not?  If we did a putAll, then the LineData objects from
 390  
                 // the coverageData class would overwrite the LineData objects
 391  
                 // that are already in "this.branches"  And we don't need to
 392  
                 // update the LineData objects that are already in this.branches
 393  
                 // because they are shared between this.branches and this.children,
 394  
                 // so the object hit counts will be moved when we called
 395  
                 // super.merge() above.
 396  
                 for (Iterator iter = classData.branches.keySet().iterator(); iter.hasNext();)
 397  
                 {
 398  
                         Object key = iter.next();
 399  
                         if (!this.branches.containsKey(key))
 400  
                         {
 401  
                                 this.branches.put(key, classData.branches.get(key));
 402  
                         }
 403  
                 }
 404  
 
 405  
                 this.containsInstrumentationInfo |= classData.containsInstrumentationInfo;
 406  
                 this.methodNamesAndDescriptors.addAll(classData
 407  
                                 .getMethodNamesAndDescriptors());
 408  
                 if (classData.sourceFileName != null)
 409  
                         this.sourceFileName = classData.sourceFileName;
 410  
                 }
 411  
 
 412  
         public void removeLine(int lineNumber)
 413  
         {
 414  
                 Integer lineObject = new Integer(lineNumber);
 415  
                 children.remove(lineObject);
 416  
                 branches.remove(lineObject);
 417  
         }
 418  
 
 419  
         public void setContainsInstrumentationInfo()
 420  
         {
 421  
                 this.containsInstrumentationInfo = true;
 422  
         }
 423  
 
 424  
         public void setSourceFileName(String sourceFileName)
 425  
         {
 426  
                 this.sourceFileName = sourceFileName;
 427  
         }
 428  
 
 429  
         /**
 430  
          * Increment the number of hits for a particular line of code.
 431  
          *
 432  
          * @param lineNumber the line of code to increment the number of hits.
 433  
          */
 434  
         public void touch(int lineNumber)
 435  
         {
 436  
                 LineData lineData = getLineData(lineNumber);
 437  
                 if (lineData == null)
 438  
                         lineData = addLine(lineNumber, null, null);
 439  
                 lineData.touch();
 440  
         }
 441  
 
 442  
         /**
 443  
          * Increments the number of hits for particular hit counter of particular branch on particular line number.
 444  
          * 
 445  
          * @param lineNumber The line of code where the branch is
 446  
          * @param branchNumber  The branch on the line to change the hit counter
 447  
          * @param branch The hit counter (true or false)
 448  
          */
 449  
         public void touchJump(int lineNumber, int branchNumber, boolean branch) {
 450  
                 LineData lineData = getLineData(lineNumber);
 451  
                 if (lineData == null)
 452  
                         lineData = addLine(lineNumber, null, null);
 453  
                 lineData.touchJump(branchNumber, branch);
 454  
         }
 455  
 
 456  
         /**
 457  
          * Increments the number of hits for particular hit counter of particular switch branch on particular line number.
 458  
          * 
 459  
          * @param lineNumber The line of code where the branch is
 460  
          * @param switchNumber  The switch on the line to change the hit counter
 461  
          * @param branch The hit counter 
 462  
          */
 463  
         public void touchSwitch(int lineNumber, int switchNumber, int branch) {
 464  
                 LineData lineData = getLineData(lineNumber);
 465  
                 if (lineData == null)
 466  
                         lineData = addLine(lineNumber, null, null);
 467  
                 lineData.touchSwitch(switchNumber, branch);
 468  
         }
 469  
 
 470  
 }