All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sourceforge.cobertura.reporting.ComplexityCalculator Maven / Gradle / Ivy

/*
 * Cobertura - http://cobertura.sourceforge.net/
 *
 * Copyright (C) 2005 Mark Doliner
 * Copyright (C) 2005 Jeremy Thomerson
 * Copyright (C) 2005 Grzegorz Lukasik
 * Copyright (C) 2008 Tri Bao Ho
 * Copyright (C) 2009 John Lewis
 *
 * Cobertura is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 *
 * Cobertura is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Cobertura; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */
package net.sourceforge.cobertura.reporting;

import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import net.sourceforge.cobertura.coveragedata.ClassData;
import net.sourceforge.cobertura.coveragedata.PackageData;
import net.sourceforge.cobertura.coveragedata.ProjectData;
import net.sourceforge.cobertura.coveragedata.SourceFileData;
import net.sourceforge.cobertura.javancss.Javancss;
import net.sourceforge.cobertura.javancss.JavancssConstants;
import net.sourceforge.cobertura.util.FileFinder;
import net.sourceforge.cobertura.util.Source;

import org.apache.log4j.Logger;


/**
 * Allows complexity computing for source files, packages and a whole project. Average
 * McCabe's number for methods contained in the specified entity is returned. This class
 * depends on FileFinder which is used to map source file names to existing files.
 * 
 * 

One instance of this class should be used for the same set of source files - an * object of this class can cache computed results.

* * @author Grzegorz Lukasik */ public class ComplexityCalculator { private static final Logger logger = Logger.getLogger(ComplexityCalculator.class); public static final Complexity ZERO_COMPLEXITY = new Complexity(); // Finder used to map source file names to existing files private final FileFinder finder; // Contains pairs (String sourceFileName, Complexity complexity) private Map sourceFileCNNCache = new HashMap(); // Contains pairs (String packageName, Complexity complexity) private Map packageCNNCache = new HashMap(); /** * Creates new calculator. Passed {@link FileFinder} will be used to * map source file names to existing files when needed. * * @param finder {@link FileFinder} that allows to find source files * @throws NullPointerException if finder is null */ public ComplexityCalculator( FileFinder finder) { if( finder==null) throw new NullPointerException(); this.finder = finder; } /** * Calculates the code complexity number for an input stream. * "CCN" stands for "code complexity number." This is * sometimes referred to as McCabe's number. This method * calculates the average cyclomatic code complexity of all * methods of all classes in a given directory. * * @param file The input stream for which you want to calculate * the complexity * @return average complexity for the specified input stream */ private Complexity getAccumlatedCCNForSource(String sourceFileName, Source source) { if (source == null) { return ZERO_COMPLEXITY; } Javancss javancss = new Javancss(source.getInputStream()); if (javancss.getLastErrorMessage() != null) { //there is an error while parsing the java file. log it logger.warn("JavaNCSS got an error while parsing the java " + source.getOriginDesc() + "\n" + javancss.getLastErrorMessage()); } Vector methodMetrics = javancss.getFunctionMetrics(); int classCcn = 0; for( Enumeration method = methodMetrics.elements(); method.hasMoreElements();) { Vector singleMethodMetrics = (Vector)method.nextElement(); classCcn += ((Integer)singleMethodMetrics.elementAt(JavancssConstants.FCT_CCN)).intValue(); } return new Complexity( classCcn, methodMetrics.size()); } /** * Calculates the code complexity number for single source file. * "CCN" stands for "code complexity number." This is * sometimes referred to as McCabe's number. This method * calculates the average cyclomatic code complexity of all * methods of all classes in a given directory. * @param sourceFileName * * @param file The source file for which you want to calculate * the complexity * @return average complexity for the specified source file * @throws IOException */ private Complexity getAccumlatedCCNForSingleFile(String sourceFileName) throws IOException { Source source = finder.getSource(sourceFileName); try { return getAccumlatedCCNForSource(sourceFileName, source); } finally { if (source != null) { source.close(); } } } /** * Computes CCN for all sources contained in the project. * CCN for whole project is an average CCN for source files. * All source files for which CCN cannot be computed are ignored. * * @param projectData project to compute CCN for * @throws NullPointerException if projectData is null * @return CCN for project or 0 if no source files were found */ public double getCCNForProject( ProjectData projectData) { // Sum complexity for all packages Complexity act = new Complexity(); for( Iterator it = projectData.getPackages().iterator(); it.hasNext();) { PackageData packageData = (PackageData)it.next(); act.add( getCCNForPackageInternal( packageData)); } // Return average CCN for source files return act.averageCCN(); } /** * Computes CCN for all sources contained in the specified package. * All source files that cannot be mapped to existing files are ignored. * * @param packageData package to compute CCN for * @throws NullPointerException if packageData is null * @return CCN for the specified package or 0 if no source files were found */ public double getCCNForPackage(PackageData packageData) { return getCCNForPackageInternal(packageData).averageCCN(); } private Complexity getCCNForPackageInternal(PackageData packageData) { // Return CCN if computed earlier Complexity cachedCCN = (Complexity) packageCNNCache.get( packageData.getName()); if( cachedCCN!=null) { return cachedCCN; } // Compute CCN for all source files inside package Complexity act = new Complexity(); for( Iterator it = packageData.getSourceFiles().iterator(); it.hasNext();) { SourceFileData sourceData = (SourceFileData)it.next(); act.add( getCCNForSourceFileNameInternal( sourceData.getName())); } // Cache result and return it packageCNNCache.put( packageData.getName(), act); return act; } /** * Computes CCN for single source file. * * @param sourceFile source file to compute CCN for * @throws NullPointerException if sourceFile is null * @return CCN for the specified source file, 0 if cannot map sourceFile to existing file */ public double getCCNForSourceFile(SourceFileData sourceFile) { return getCCNForSourceFileNameInternal( sourceFile.getName()).averageCCN(); } private Complexity getCCNForSourceFileNameInternal(String sourceFileName) { // Return CCN if computed earlier Complexity cachedCCN = (Complexity) sourceFileCNNCache.get( sourceFileName); if( cachedCCN!=null) { return cachedCCN; } // Compute CCN and cache it for further use Complexity result = ZERO_COMPLEXITY; try { result = getAccumlatedCCNForSingleFile( sourceFileName ); } catch( IOException ex) { logger.info( "Cannot find source file during CCN computation, source=["+sourceFileName+"]"); } sourceFileCNNCache.put( sourceFileName, result); return result; } /** * Computes CCN for source file the specified class belongs to. * * @param classData package to compute CCN for * @return CCN for source file the specified class belongs to * @throws NullPointerException if classData is null */ public double getCCNForClass(ClassData classData) { return getCCNForSourceFileNameInternal( classData.getSourceFileName()).averageCCN(); } /** * Represents complexity of source file, package or project. Stores the number of * methods inside entity and accumlated complexity for these methods. */ private static class Complexity { private double accumlatedCCN; private int methodsNum; public Complexity(double accumlatedCCN, int methodsNum) { this.accumlatedCCN = accumlatedCCN; this.methodsNum = methodsNum; } public Complexity() { this(0,0); } public double averageCCN() { if( methodsNum==0) { return 0; } return accumlatedCCN/methodsNum; } public void add( Complexity second) { accumlatedCCN += second.accumlatedCCN; methodsNum += second.methodsNum; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy