alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Cobertura example source code file (ComplexityCalculator.java)

This example Cobertura source code file (ComplexityCalculator.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Cobertura tags/keywords

complexity, complexity, complexitycalculator, filefinder, hashmap, io, ioexception, iterator, iterator, javancss, map, source, source, util, zero_complexity, zero_complexity

The Cobertura ComplexityCalculator.java source code

/*
 * 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.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

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.FunctionMetric;
import net.sourceforge.cobertura.javancss.Javancss;
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.
 * 
 * <p>One instance of this class should be used for the same set of source files - an 
 * object of this class can cache computed results.</p>
 * 
 * @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;
		}
		if (!sourceFileName.endsWith(".java"))
		{
			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());
		}

		List methodMetrics = javancss.getFunctionMetrics();
		int classCcn = 0;
        for( Iterator method = methodMetrics.iterator(); method.hasNext();)
        {
        	FunctionMetric singleMethodMetrics = (FunctionMetric)method.next();
        	classCcn += singleMethodMetrics.ccn;
        }
		
		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 <code>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 <code>sourceFile is null
	 * @return CCN for the specified source file, 0 if cannot map <code>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 <code>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;
		}
	}
}

Other Cobertura examples (source code examples)

Here is a short list of links related to this Cobertura ComplexityCalculator.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.