001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2012 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * Sonar is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019     */
020    
021    package org.sonar.api.measures;
022    
023    import org.sonar.api.resources.ResourceUtils;
024    
025    import java.util.Arrays;
026    import java.util.List;
027    
028    /**
029     * Formula used to compute an average for a given metric A, which is the result of the sum of measures of this metric (A) divided by another metric (B).
030     * <p/>
031     * For example: to compute the metric "complexity by file", the main metric (A) is "complexity" and the other metric (B) is "file".
032     *
033     * @since 3.0
034     */
035    public class AverageFormula implements Formula {
036    
037      private Metric mainMetric;
038      private Metric byMetric;
039    
040      /**
041       * This method should be private but it kep package-protected because of AverageComplexityFormula.
042       */
043      AverageFormula(Metric mainMetric, Metric byMetric) {
044        this.mainMetric = mainMetric;
045        this.byMetric = byMetric;
046      }
047    
048      /**
049       * Creates a new {@link AverageFormula} class.
050       *
051       * @param main The metric on which average should be calculated (ex.: "complexity")
052       * @param by   The metric used to divide the main metric to compute average (ex.: "file" for "complexity by file")
053       */
054      public static AverageFormula create(Metric main, Metric by) {
055        return new AverageFormula(main, by);
056      }
057    
058      /**
059       * {@inheritDoc}
060       */
061      public List<Metric> dependsUponMetrics() {
062        return Arrays.asList(mainMetric, byMetric);
063      }
064    
065      /**
066       * {@inheritDoc}
067       */
068      public Measure calculate(FormulaData data, FormulaContext context) {
069        if (!shouldDecorateResource(data, context)) {
070          return null;
071        }
072    
073        Measure result;
074        if (ResourceUtils.isFile(context.getResource())) {
075          result = calculateForFile(data, context);
076        } else {
077          result = calculateOnChildren(data, context);
078        }
079        return result;
080      }
081    
082      private Measure calculateOnChildren(FormulaData data, FormulaContext context) {
083        Measure result = null;
084    
085        double totalByMeasure = 0;
086        double totalMainMeasure = 0;
087        boolean hasApplicableChildren = false;
088    
089        for (FormulaData childrenData : data.getChildren()) {
090          Double childrenByMeasure = MeasureUtils.getValue(childrenData.getMeasure(byMetric), null);
091          Double childrenMainMeasure = MeasureUtils.getValue(childrenData.getMeasure(mainMetric), null);
092          if (childrenMainMeasure != null && childrenByMeasure != null && childrenByMeasure > 0.0) {
093            totalByMeasure += childrenByMeasure;
094            totalMainMeasure += childrenMainMeasure;
095            hasApplicableChildren = true;
096          }
097        }
098        if (hasApplicableChildren) {
099          result = new Measure(context.getTargetMetric(), (totalMainMeasure / totalByMeasure));
100        }
101        return result;
102      }
103    
104      private Measure calculateForFile(FormulaData data, FormulaContext context) {
105        Measure result = null;
106    
107        Double byMeasure = MeasureUtils.getValue(data.getMeasure(byMetric), null);
108        Double mainMeasure = MeasureUtils.getValue(data.getMeasure(mainMetric), null);
109        if (mainMeasure != null && byMeasure != null && byMeasure > 0.0) {
110          result = new Measure(context.getTargetMetric(), (mainMeasure / byMeasure));
111        }
112    
113        return result;
114      }
115    
116      private boolean shouldDecorateResource(FormulaData data, FormulaContext context) {
117        return !MeasureUtils.hasValue(data.getMeasure(context.getTargetMetric()));
118      }
119    }