KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cobertura > reporting > ComplexityCalculator


1 /*
2  * Cobertura - http://cobertura.sourceforge.net/
3  *
4  * Copyright (C) 2005 Mark Doliner
5  * Copyright (C) 2005 Jeremy Thomerson
6  * Copyright (C) 2005 Grzegorz Lukasik
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 package net.sourceforge.cobertura.reporting;
24
25 import java.io.File JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31
32 import net.sourceforge.cobertura.coveragedata.ClassData;
33 import net.sourceforge.cobertura.coveragedata.PackageData;
34 import net.sourceforge.cobertura.coveragedata.ProjectData;
35 import net.sourceforge.cobertura.coveragedata.SourceFileData;
36 import net.sourceforge.cobertura.javancss.Javancss;
37 import net.sourceforge.cobertura.util.FileFinder;
38
39 import org.apache.log4j.Logger;
40
41
42 /**
43  * Allows complexity computing for source files, packages and a whole project. Average
44  * McCabe's number for methods contained in the specified entity is returned. This class
45  * depends on FileFinder which is used to map source file names to existing files.
46  *
47  * <p>One instance of this class should be used for the same set of source files - an
48  * object of this class can cache computed results.</p>
49  *
50  * @author Grzegorz Lukasik
51  */

52 public class ComplexityCalculator {
53     private static final Logger logger = Logger.getLogger(ComplexityCalculator.class);
54
55     public static final Complexity ZERO_COMPLEXITY = new Complexity(0,0);
56     
57     // Finder used to map source file names to existing files
58
private final FileFinder finder;
59     
60     // Contains pairs (String sourceFileName, Complexity complexity)
61
private Map JavaDoc sourceFileCNNCache = new HashMap JavaDoc();
62
63     // Contains pairs (String packageName, Complexity complexity)
64
private Map JavaDoc packageCNNCache = new HashMap JavaDoc();
65
66     /**
67      * Creates new calculator. Passed {@link FileFinder} will be used to
68      * map source file names to existing files when needed.
69      *
70      * @param finder {@link FileFinder} that allows to find source files
71      * @throws NullPointerException if finder is null
72      */

73     public ComplexityCalculator( FileFinder finder) {
74         if( finder==null)
75             throw new NullPointerException JavaDoc();
76         this.finder = finder;
77     }
78     
79     /**
80      * Calculates the code complexity number for single source file.
81      * "CCN" stands for "code complexity number." This is
82      * sometimes referred to as McCabe's number. This method
83      * calculates the average cyclomatic code complexity of all
84      * methods of all classes in a given directory.
85      *
86      * @param file The source file for which you want to calculate
87      * the complexity
88      * @return average complexity for the specified source file
89      */

90     private Complexity getAccumlatedCCNForSingleFile(File JavaDoc file) {
91         Javancss javancss = new Javancss(file.getAbsolutePath());
92
93         List JavaDoc methodComplexities = javancss.getMethodComplexities();
94         if (methodComplexities.size() <= 0)
95             return ZERO_COMPLEXITY;
96
97         int ccnAccumulator = 0;
98         Iterator JavaDoc iter = methodComplexities.iterator();
99         while (iter.hasNext())
100         {
101             ccnAccumulator += ((Integer JavaDoc)iter.next()).intValue();
102         }
103         
104         return new Complexity( ccnAccumulator, methodComplexities.size());
105     }
106
107
108     /**
109      * Computes CCN for all sources contained in the project.
110      * CCN for whole project is an average CCN for source files.
111      * All source files for which CCN cannot be computed are ignored.
112      *
113      * @param projectData project to compute CCN for
114      * @throws NullPointerException if projectData is null
115      * @return CCN for project or 0 if no source files were found
116      */

117     public double getCCNForProject( ProjectData projectData) {
118         // Sum complexity for all packages
119
Complexity act = new Complexity();
120         for( Iterator JavaDoc it = projectData.getPackages().iterator(); it.hasNext();) {
121             PackageData packageData = (PackageData)it.next();
122             act.add( getCCNForPackageInternal( packageData));
123         }
124
125         // Return average CCN for source files
126
return act.averageCCN();
127     }
128     
129     /**
130      * Computes CCN for all sources contained in the specified package.
131      * All source files that cannot be mapped to existing files are ignored.
132      *
133      * @param packageData package to compute CCN for
134      * @throws NullPointerException if <code>packageData</code> is <code>null</code>
135      * @return CCN for the specified package or 0 if no source files were found
136      */

137     public double getCCNForPackage(PackageData packageData) {
138         return getCCNForPackageInternal(packageData).averageCCN();
139     }
140
141     private Complexity getCCNForPackageInternal(PackageData packageData) {
142         // Return CCN if computed earlier
143
Complexity cachedCCN = (Complexity) packageCNNCache.get( packageData.getName());
144         if( cachedCCN!=null) {
145             return cachedCCN;
146         }
147         
148         // Compute CCN for all source files inside package
149
Complexity act = new Complexity();
150         for( Iterator JavaDoc it = packageData.getSourceFiles().iterator(); it.hasNext();) {
151             SourceFileData sourceData = (SourceFileData)it.next();
152             act.add( getCCNForSourceFileNameInternal( sourceData.getName()));
153         }
154         
155         // Cache result and return it
156
packageCNNCache.put( packageData.getName(), act);
157         return act;
158     }
159
160     
161     /**
162      * Computes CCN for single source file.
163      *
164      * @param sourceFile source file to compute CCN for
165      * @throws NullPointerException if <code>sourceFile</code> is <code>null</code>
166      * @return CCN for the specified source file, 0 if cannot map <code>sourceFile</code> to existing file
167      */

168     public double getCCNForSourceFile(SourceFileData sourceFile) {
169         return getCCNForSourceFileNameInternal( sourceFile.getName()).averageCCN();
170     }
171
172     private Complexity getCCNForSourceFileNameInternal(String JavaDoc sourceFileName) {
173         // Return CCN if computed earlier
174
Complexity cachedCCN = (Complexity) sourceFileCNNCache.get( sourceFileName);
175         if( cachedCCN!=null) {
176             return cachedCCN;
177         }
178
179         // Compute CCN and cache it for further use
180
Complexity result = ZERO_COMPLEXITY;
181         try {
182             result = getAccumlatedCCNForSingleFile( finder.getFileForSource(sourceFileName));
183         } catch( IOException JavaDoc ex) {
184             logger.info( "Cannot find source file during CCN computation, source=["+sourceFileName+"]");
185         }
186         sourceFileCNNCache.put( sourceFileName, result);
187         return result;
188     }
189
190     /**
191      * Computes CCN for source file the specified class belongs to.
192      *
193      * @param classData package to compute CCN for
194      * @return CCN for source file the specified class belongs to
195      * @throws NullPointerException if <code>classData</code> is <code>null</code>
196      */

197     public double getCCNForClass(ClassData classData) {
198         return getCCNForSourceFileNameInternal( classData.getSourceFileName()).averageCCN();
199     }
200
201
202     /**
203      * Represents complexity of source file, package or project. Stores the number of
204      * methods inside entity and accumlated complexity for these methods.
205      */

206     private static class Complexity {
207         private double accumlatedCCN;
208         private int methodsNum;
209         public Complexity(double accumlatedCCN, int methodsNum) {
210             this.accumlatedCCN = accumlatedCCN;
211             this.methodsNum = methodsNum;
212         }
213         public Complexity() {
214             this(0,0);
215         }
216         public double averageCCN() {
217             if( methodsNum==0) {
218                 return 0;
219             }
220             return accumlatedCCN/methodsNum;
221         }
222         public void add( Complexity second) {
223             accumlatedCCN += second.accumlatedCCN;
224             methodsNum += second.methodsNum;
225         }
226     }
227 }
228
Popular Tags