001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 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 package org.sonar.plugins.core.timemachine; 021 022 import com.google.common.collect.Maps; 023 import org.apache.commons.lang.StringUtils; 024 import org.sonar.api.CoreProperties; 025 import org.sonar.api.batch.*; 026 import org.sonar.api.measures.*; 027 import org.sonar.api.resources.Project; 028 import org.sonar.api.resources.Qualifiers; 029 import org.sonar.api.resources.Resource; 030 import org.sonar.api.resources.Scopes; 031 import org.sonar.batch.components.PastMeasuresLoader; 032 import org.sonar.batch.components.PastSnapshot; 033 import org.sonar.batch.components.TimeMachineConfiguration; 034 035 import java.util.Collection; 036 import java.util.List; 037 import java.util.Map; 038 039 @DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE) 040 public class VariationDecorator implements Decorator { 041 042 private List<PastSnapshot> projectPastSnapshots; 043 private MetricFinder metricFinder; 044 private PastMeasuresLoader pastMeasuresLoader; 045 private final boolean enabledFileVariation; 046 047 048 public VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, TimeMachineConfiguration configuration) { 049 this(pastMeasuresLoader, metricFinder, configuration.getProjectPastSnapshots(), configuration.isFileVariationEnabled()); 050 } 051 052 VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, List<PastSnapshot> projectPastSnapshots, boolean enabledFileVariation) { 053 this.pastMeasuresLoader = pastMeasuresLoader; 054 this.projectPastSnapshots = projectPastSnapshots; 055 this.metricFinder = metricFinder; 056 this.enabledFileVariation = enabledFileVariation; 057 } 058 059 public boolean shouldExecuteOnProject(Project project) { 060 return project.isLatestAnalysis(); 061 } 062 063 @DependsUpon 064 public Collection<Metric> dependsUponMetrics() { 065 return pastMeasuresLoader.getMetrics(); 066 } 067 068 public void decorate(Resource resource, DecoratorContext context) { 069 for (PastSnapshot projectPastSnapshot : projectPastSnapshots) { 070 if (shouldComputeVariation(projectPastSnapshot.getMode(), resource)) { 071 computeVariation(resource, context, projectPastSnapshot); 072 } 073 } 074 } 075 076 boolean shouldComputeVariation(String variationMode, Resource resource) { 077 if (Scopes.FILE.equals(resource.getScope()) && !Qualifiers.UNIT_TEST_FILE.equals(resource.getQualifier())) { 078 return enabledFileVariation && StringUtils.equals(variationMode, CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS); 079 } 080 081 // measures on files are currently purged, so past measures are not available on files 082 return StringUtils.equals(Scopes.PROJECT, resource.getScope()) || StringUtils.equals(Scopes.DIRECTORY, resource.getScope()); 083 } 084 085 private void computeVariation(Resource resource, DecoratorContext context, PastSnapshot pastSnapshot) { 086 List<Object[]> pastMeasures = pastMeasuresLoader.getPastMeasures(resource, pastSnapshot); 087 compareWithPastMeasures(context, pastSnapshot.getIndex(), pastMeasures); 088 } 089 090 void compareWithPastMeasures(DecoratorContext context, int index, List<Object[]> pastMeasures) { 091 Map<MeasureKey, Object[]> pastMeasuresByKey = Maps.newHashMap(); 092 for (Object[] pastMeasure : pastMeasures) { 093 pastMeasuresByKey.put(new MeasureKey(pastMeasure), pastMeasure); 094 } 095 096 // for each measure, search equivalent past measure 097 for (Measure measure : context.getMeasures(MeasuresFilters.all())) { 098 // compare with past measure 099 Integer metricId = (measure.getMetric().getId() != null ? measure.getMetric().getId() : metricFinder.findByKey(measure.getMetric().getKey()).getId()); 100 Integer characteristicId = (measure.getCharacteristic() != null ? measure.getCharacteristic().getId() : null); 101 Integer ruleId = (measure instanceof RuleMeasure ? ((RuleMeasure)measure).getRule().getId() : null); 102 103 Object[] pastMeasure = pastMeasuresByKey.get(new MeasureKey(metricId, characteristicId, ruleId)); 104 if (updateVariation(measure, pastMeasure, index)) { 105 context.saveMeasure(measure); 106 } 107 } 108 } 109 110 boolean updateVariation(Measure measure, Object[] pastMeasure, int index) { 111 if (pastMeasure != null && PastMeasuresLoader.hasValue(pastMeasure) && measure.getValue() != null) { 112 double variation = (measure.getValue().doubleValue() - PastMeasuresLoader.getValue(pastMeasure)); 113 measure.setVariation(index, variation); 114 return true; 115 } 116 return false; 117 } 118 119 @Override 120 public String toString() { 121 return getClass().getSimpleName(); 122 } 123 124 static final class MeasureKey { 125 int metricId; 126 Integer characteristicId; 127 Integer ruleId; 128 129 MeasureKey(Object[] pastFields) { 130 metricId = PastMeasuresLoader.getMetricId(pastFields); 131 characteristicId = PastMeasuresLoader.getCharacteristicId(pastFields); 132 ruleId = PastMeasuresLoader.getRuleId(pastFields); 133 } 134 135 MeasureKey(int metricId, Integer characteristicId, Integer ruleId) { 136 this.metricId = metricId; 137 this.characteristicId = characteristicId; 138 this.ruleId = ruleId; 139 } 140 141 @Override 142 public boolean equals(Object o) { 143 if (this == o) { 144 return true; 145 } 146 if (o == null || getClass() != o.getClass()) { 147 return false; 148 } 149 MeasureKey that = (MeasureKey) o; 150 if (metricId != that.metricId) { 151 return false; 152 } 153 if (characteristicId != null ? !characteristicId.equals(that.characteristicId) : that.characteristicId != null) { 154 return false; 155 } 156 if (ruleId != null ? !ruleId.equals(that.ruleId) : that.ruleId != null) { 157 return false; 158 } 159 return true; 160 } 161 162 @Override 163 public int hashCode() { 164 int result = metricId; 165 result = 31 * result + (characteristicId != null ? characteristicId.hashCode() : 0); 166 result = 31 * result + (ruleId != null ? ruleId.hashCode() : 0); 167 return result; 168 } 169 } 170 }