001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube 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 * SonarQube 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 License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020
021package org.sonar.api.measures;
022
023import java.util.List;
024import org.sonar.api.resources.ResourceUtils;
025
026import static com.google.common.collect.Lists.newArrayList;
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 * @deprecated since 5.2 decorators are no more executed on batch side
035 */
036@Deprecated
037public class AverageFormula implements Formula {
038
039  private Metric mainMetric;
040  private Metric byMetric;
041  private Metric fallbackMetric;
042
043  /**
044   * This method should be private but it kep package-protected because of AverageComplexityFormula.
045   */
046  AverageFormula(Metric mainMetric, Metric byMetric) {
047    this.mainMetric = mainMetric;
048    this.byMetric = byMetric;
049  }
050
051  /**
052   * Creates a new {@link AverageFormula} class.
053   *
054   * @param main The metric on which average should be calculated (ex.: "complexity")
055   * @param by   The metric used to divide the main metric to compute average (ex.: "file" for "complexity by file")
056   */
057  public static AverageFormula create(Metric main, Metric by) {
058    return new AverageFormula(main, by);
059  }
060
061  /**
062   * Set a fallback metric if no measures found for the main metric.
063   *
064   * @param fallbackMetric The fallback metric
065   * @since 3.6
066   */
067  public AverageFormula setFallbackForMainMetric(Metric fallbackMetric) {
068    this.fallbackMetric = fallbackMetric;
069    return this;
070  }
071
072  /**
073   * {@inheritDoc}
074   */
075  @Override
076  public List<Metric> dependsUponMetrics() {
077    return fallbackMetric != null ? newArrayList(mainMetric, fallbackMetric, byMetric) : newArrayList(mainMetric, byMetric);
078  }
079
080  /**
081   * {@inheritDoc}
082   */
083  @Override
084  public Measure calculate(FormulaData data, FormulaContext context) {
085    if (!shouldDecorateResource(data, context)) {
086      return null;
087    }
088
089    Measure result;
090    if (ResourceUtils.isFile(context.getResource())) {
091      result = calculateForFile(data, context);
092    } else {
093      result = calculateOnChildren(data, context);
094    }
095    return result;
096  }
097
098  private Measure calculateOnChildren(FormulaData data, FormulaContext context) {
099    Measure result = null;
100
101    double totalByMeasure = 0;
102    double totalMainMeasure = 0;
103    boolean hasApplicableChildren = false;
104
105    for (FormulaData childrenData : data.getChildren()) {
106      Double fallbackMeasure = fallbackMetric != null ? MeasureUtils.getValue(childrenData.getMeasure(fallbackMetric), null) : null;
107      Double childrenByMeasure = MeasureUtils.getValue(childrenData.getMeasure(byMetric), null);
108      Double childrenMainMeasure = MeasureUtils.getValue(childrenData.getMeasure(mainMetric), fallbackMeasure);
109      if (childrenMainMeasure != null && childrenByMeasure != null && childrenByMeasure > 0.0) {
110        totalByMeasure += childrenByMeasure;
111        totalMainMeasure += childrenMainMeasure;
112        hasApplicableChildren = true;
113      }
114    }
115    if (hasApplicableChildren) {
116      result = new Measure(context.getTargetMetric(), totalMainMeasure / totalByMeasure);
117    }
118    return result;
119  }
120
121  private Measure calculateForFile(FormulaData data, FormulaContext context) {
122    Measure result = null;
123
124    Double fallbackMeasure = fallbackMetric != null ? MeasureUtils.getValue(data.getMeasure(fallbackMetric), null) : null;
125    Double byMeasure = MeasureUtils.getValue(data.getMeasure(byMetric), null);
126    Double mainMeasure = MeasureUtils.getValue(data.getMeasure(mainMetric), fallbackMeasure);
127    if (mainMeasure != null && byMeasure != null && byMeasure > 0.0) {
128      result = new Measure(context.getTargetMetric(), mainMeasure / byMeasure);
129    }
130
131    return result;
132  }
133
134  private static boolean shouldDecorateResource(FormulaData data, FormulaContext context) {
135    return !MeasureUtils.hasValue(data.getMeasure(context.getTargetMetric()));
136  }
137}