/* * Sonar, open source software quality management tool. * Copyright (C) 2009 SonarSource * mailto:contact AT sonarsource DOT com * * Sonar is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * Sonar 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with Sonar; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ package org.sonar.plugins.qi; import org.apache.commons.configuration.Configuration; import org.sonar.api.batch.DecoratorContext; import org.sonar.api.batch.DependedUpon; import org.sonar.api.batch.DependsUpon; import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric; import org.sonar.api.resources.Resource; import org.sonar.api.utils.KeyValueFormat; import java.util.Arrays; import java.util.List; import java.util.Map; /** * An implementation of AbstractDecorator to measure the QI complexity axis */ public class ComplexityDecorator extends AbstractDecorator { /** * Creates a ComplexityDecorator * * @param configuration the config */ public ComplexityDecorator(Configuration configuration) { super(configuration, QIMetrics.QI_COMPLEXITY, QIPlugin.QI_COMPLEXITY_AXIS_WEIGHT, QIPlugin.QI_COMPLEXITY_AXIS_WEIGHT_DEFAULT); } /** * The decorator will generate 3 metrics * * @return the 3 metrics on complexity */ @DependedUpon @Override public List<Metric> dependedUpon() { return Arrays.asList(QIMetrics.QI_COMPLEXITY_FACTOR, QIMetrics.QI_COMPLEXITY_FACTOR_METHODS, QIMetrics.QI_COMPLEXITY); } /** * The Complexity distribution must be computed before we can start decorating... * * @return the list of dependencies */ @DependsUpon @Override public List<Metric> dependsUpon() { return Arrays.asList(QIMetrics.QI_COMPLEX_DISTRIBUTION); } /** * Decorates the resource with the 3 indicators, i.e. save the measures * * @param resource the resource * @param context the context */ public void decorate(Resource resource, DecoratorContext context) { // Do not want to decorate anything on unit tests files if (Utils.shouldSaveMeasure(resource)) { saveSpecificMeasure(context, computeComplexityFactor(context), QIMetrics.QI_COMPLEXITY_FACTOR); saveSpecificMeasure(context, computeComplexMethodsCount(context), QIMetrics.QI_COMPLEXITY_FACTOR_METHODS); saveMeasure(context, computeComplexity(context)); } } /** * Compute the complexity axis for the QI * * @param context the context * @return the complexity value */ protected double computeComplexity(DecoratorContext context) { double complexity = getMethodCount(context, true); return complexity / getValidLines(context); } /** * Computes the complexity factor, i.e. a density of complex methods * * @param context the context * @return the factor */ protected double computeComplexityFactor(DecoratorContext context) { double methodWithComplexityCount = getMethodCount(context, false); if (methodWithComplexityCount == 0) { return 0; } double complexityFactor = 5 * 100 * getComplexMethods(context) / methodWithComplexityCount; return Math.min(complexityFactor, 100); } /** * @param context the context * @return the number of complex methods */ protected double computeComplexMethodsCount(DecoratorContext context) { return getComplexMethods(context); } /** * Counts the number of methods that are taken into account for complexity indicator calculation * * @param context the context * @param weighted whether to weight the methods depending on the complexity * @return the weighted or not sum */ private double getMethodCount(DecoratorContext context, boolean weighted) { Measure measure = context.getMeasure(QIMetrics.QI_COMPLEX_DISTRIBUTION); if (measure == null) { return 0; } Map<Integer, Integer> distribution = KeyValueFormat.parse(measure.getData(), new KeyValueFormat.IntegerNumbersPairTransformer()); double methodWithComplexityCount; if (weighted) { methodWithComplexityCount = distribution.get(2) + 3 * distribution.get(10) + 5 * distribution.get(20) + 10 * distribution.get(30); } else { methodWithComplexityCount = distribution.get(2) + distribution.get(10) + distribution.get(20) + distribution.get(30); } return methodWithComplexityCount; } /** * @param context the context * @return the number of methods with a complexity higher than 30 */ private double getComplexMethods(DecoratorContext context) { Measure measure = context.getMeasure(QIMetrics.QI_COMPLEX_DISTRIBUTION); if (measure == null) { return 0; } Map<Integer, Integer> distribution = KeyValueFormat.parse(measure.getData(), new KeyValueFormat.IntegerNumbersPairTransformer()); return distribution.get(30); } /** * Method used to save extra measures : complexity factor and number of complex methods * * @param context the context * @param value the value * @param metric the metric */ private void saveSpecificMeasure(DecoratorContext context, double value, Metric metric) { context.saveMeasure(metric, value); } }