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
021 package org.sonar.api.measures;
022
023 import org.sonar.api.resources.ResourceUtils;
024
025 import java.util.List;
026
027 import static com.google.common.collect.Lists.newArrayList;
028
029 /**
030 * 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).
031 * <p/>
032 * For example: to compute the metric "complexity by file", the main metric (A) is "complexity" and the other metric (B) is "file".
033 *
034 * @since 3.0
035 */
036 public class AverageFormula implements Formula {
037
038 private Metric mainMetric;
039 private Metric byMetric;
040 private Metric fallbackMetric;
041
042 /**
043 * This method should be private but it kep package-protected because of AverageComplexityFormula.
044 */
045 AverageFormula(Metric mainMetric, Metric byMetric) {
046 this.mainMetric = mainMetric;
047 this.byMetric = byMetric;
048 }
049
050 /**
051 * Creates a new {@link AverageFormula} class.
052 *
053 * @param main The metric on which average should be calculated (ex.: "complexity")
054 * @param by The metric used to divide the main metric to compute average (ex.: "file" for "complexity by file")
055 */
056 public static AverageFormula create(Metric main, Metric by) {
057 return new AverageFormula(main, by);
058 }
059
060 /**
061 * Set a fallback metric if no measures found for the main metric.
062 *
063 * @param fallbackMetric The fallback metric
064 * @since 3.6
065 */
066 public AverageFormula setFallbackForMainMetric(Metric fallbackMetric) {
067 this.fallbackMetric = fallbackMetric;
068 return this;
069 }
070
071 /**
072 * {@inheritDoc}
073 */
074 @Override
075 public List<Metric> dependsUponMetrics() {
076 return fallbackMetric != null ? newArrayList(mainMetric, fallbackMetric, byMetric) : newArrayList(mainMetric, byMetric);
077 }
078
079 /**
080 * {@inheritDoc}
081 */
082 @Override
083 public Measure calculate(FormulaData data, FormulaContext context) {
084 if (!shouldDecorateResource(data, context)) {
085 return null;
086 }
087
088 Measure result;
089 if (ResourceUtils.isFile(context.getResource())) {
090 result = calculateForFile(data, context);
091 } else {
092 result = calculateOnChildren(data, context);
093 }
094 return result;
095 }
096
097 private Measure calculateOnChildren(FormulaData data, FormulaContext context) {
098 Measure result = null;
099
100 double totalByMeasure = 0;
101 double totalMainMeasure = 0;
102 boolean hasApplicableChildren = false;
103
104 for (FormulaData childrenData : data.getChildren()) {
105 Double fallbackMeasure = fallbackMetric != null ? MeasureUtils.getValue(childrenData.getMeasure(fallbackMetric), null) : null;
106 Double childrenByMeasure = MeasureUtils.getValue(childrenData.getMeasure(byMetric), null);
107 Double childrenMainMeasure = MeasureUtils.getValue(childrenData.getMeasure(mainMetric), fallbackMeasure);
108 if (childrenMainMeasure != null && childrenByMeasure != null && childrenByMeasure > 0.0) {
109 totalByMeasure += childrenByMeasure;
110 totalMainMeasure += childrenMainMeasure;
111 hasApplicableChildren = true;
112 }
113 }
114 if (hasApplicableChildren) {
115 result = new Measure(context.getTargetMetric(), totalMainMeasure / totalByMeasure);
116 }
117 return result;
118 }
119
120 private Measure calculateForFile(FormulaData data, FormulaContext context) {
121 Measure result = null;
122
123 Double fallbackMeasure = fallbackMetric != null ? MeasureUtils.getValue(data.getMeasure(fallbackMetric), null) : null;
124 Double byMeasure = MeasureUtils.getValue(data.getMeasure(byMetric), null);
125 Double mainMeasure = MeasureUtils.getValue(data.getMeasure(mainMetric), fallbackMeasure);
126 if (mainMeasure != null && byMeasure != null && byMeasure > 0.0) {
127 result = new Measure(context.getTargetMetric(), mainMeasure / byMeasure);
128 }
129
130 return result;
131 }
132
133 private boolean shouldDecorateResource(FormulaData data, FormulaContext context) {
134 return !MeasureUtils.hasValue(data.getMeasure(context.getTargetMetric()));
135 }
136 }