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 public List<Metric> dependsUponMetrics() { 075 return fallbackMetric != null ? newArrayList(mainMetric, fallbackMetric, byMetric) : newArrayList(mainMetric, byMetric); 076 } 077 078 /** 079 * {@inheritDoc} 080 */ 081 public Measure calculate(FormulaData data, FormulaContext context) { 082 if (!shouldDecorateResource(data, context)) { 083 return null; 084 } 085 086 Measure result; 087 if (ResourceUtils.isFile(context.getResource())) { 088 result = calculateForFile(data, context); 089 } else { 090 result = calculateOnChildren(data, context); 091 } 092 return result; 093 } 094 095 private Measure calculateOnChildren(FormulaData data, FormulaContext context) { 096 Measure result = null; 097 098 double totalByMeasure = 0; 099 double totalMainMeasure = 0; 100 boolean hasApplicableChildren = false; 101 102 for (FormulaData childrenData : data.getChildren()) { 103 Double fallbackMeasure = fallbackMetric != null ? MeasureUtils.getValue(childrenData.getMeasure(fallbackMetric), null) : null; 104 Double childrenByMeasure = MeasureUtils.getValue(childrenData.getMeasure(byMetric), null); 105 Double childrenMainMeasure = MeasureUtils.getValue(childrenData.getMeasure(mainMetric), fallbackMeasure); 106 if (childrenMainMeasure != null && childrenByMeasure != null && childrenByMeasure > 0.0) { 107 totalByMeasure += childrenByMeasure; 108 totalMainMeasure += childrenMainMeasure; 109 hasApplicableChildren = true; 110 } 111 } 112 if (hasApplicableChildren) { 113 result = new Measure(context.getTargetMetric(), totalMainMeasure / totalByMeasure); 114 } 115 return result; 116 } 117 118 private Measure calculateForFile(FormulaData data, FormulaContext context) { 119 Measure result = null; 120 121 Double fallbackMeasure = fallbackMetric != null ? MeasureUtils.getValue(data.getMeasure(fallbackMetric), null) : null; 122 Double byMeasure = MeasureUtils.getValue(data.getMeasure(byMetric), null); 123 Double mainMeasure = MeasureUtils.getValue(data.getMeasure(mainMetric), fallbackMeasure); 124 if (mainMeasure != null && byMeasure != null && byMeasure > 0.0) { 125 result = new Measure(context.getTargetMetric(), mainMeasure / byMeasure); 126 } 127 128 return result; 129 } 130 131 private boolean shouldDecorateResource(FormulaData data, FormulaContext context) { 132 return !MeasureUtils.hasValue(data.getMeasure(context.getTargetMetric())); 133 } 134 }