]> source.dussan.org Git - sonarqube.git/blob
a34da8128dae4b7ff0f0d5e766391ea72c069e56
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2016 SonarSource SA
4  * mailto:contact AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.computation.task.projectanalysis.step;
21
22 import com.google.common.base.Function;
23 import com.google.common.base.Optional;
24 import com.google.common.base.Predicate;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import javax.annotation.CheckForNull;
31 import javax.annotation.Nonnull;
32 import javax.annotation.Nullable;
33 import org.sonar.db.DbClient;
34 import org.sonar.db.DbSession;
35 import org.sonar.db.measure.PastMeasureDto;
36 import org.sonar.server.computation.task.projectanalysis.component.Component;
37 import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit;
38 import org.sonar.server.computation.task.projectanalysis.component.DepthTraversalTypeAwareCrawler;
39 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
40 import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter;
41 import org.sonar.server.computation.task.projectanalysis.measure.Measure;
42 import org.sonar.server.computation.task.projectanalysis.measure.MeasureKey;
43 import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository;
44 import org.sonar.server.computation.task.projectanalysis.measure.MeasureVariations;
45 import org.sonar.server.computation.task.projectanalysis.metric.Metric;
46 import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository;
47 import org.sonar.server.computation.task.projectanalysis.period.Period;
48 import org.sonar.server.computation.task.projectanalysis.period.PeriodsHolder;
49 import org.sonar.server.computation.task.step.ComputationStep;
50
51 import static com.google.common.base.Preconditions.checkArgument;
52 import static com.google.common.collect.FluentIterable.from;
53 import static java.lang.String.format;
54 import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.DIRECTORY;
55 import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.SUBVIEW;
56 import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
57
58 /**
59  * Set variations on all numeric measures found in the repository.
60  * This step MUST be executed after all steps that create some measures
61  * <p/>
62  * Note that measures on developer are not handle yet.
63  */
64 public class ComputeMeasureVariationsStep implements ComputationStep {
65
66   private final DbClient dbClient;
67   private final TreeRootHolder treeRootHolder;
68   private final PeriodsHolder periodsHolder;
69   private final MetricRepository metricRepository;
70   private final MeasureRepository measureRepository;
71
72   private final Function<PastMeasureDto, MeasureKey> pastMeasureToMeasureKey = new Function<PastMeasureDto, MeasureKey>() {
73     @Nullable
74     @Override
75     public MeasureKey apply(@Nonnull PastMeasureDto input) {
76       Metric metric = metricRepository.getById((long)input.getMetricId());
77       return new MeasureKey(metric.getKey(), null);
78     }
79   };
80
81   public ComputeMeasureVariationsStep(DbClient dbClient, TreeRootHolder treeRootHolder, PeriodsHolder periodsHolder, MetricRepository metricRepository,
82     MeasureRepository measureRepository) {
83     this.dbClient = dbClient;
84     this.treeRootHolder = treeRootHolder;
85     this.periodsHolder = periodsHolder;
86     this.metricRepository = metricRepository;
87     this.measureRepository = measureRepository;
88   }
89
90   @Override
91   public void execute() {
92     DbSession dbSession = dbClient.openSession(false);
93     try {
94       List<Metric> metrics = from(metricRepository.getAll()).filter(NumericMetric.INSTANCE).toList();
95       new DepthTraversalTypeAwareCrawler(new VariationMeasuresVisitor(dbSession, metrics))
96         .visit(treeRootHolder.getRoot());
97     } finally {
98       dbClient.closeSession(dbSession);
99     }
100   }
101
102   private class VariationMeasuresVisitor extends TypeAwareVisitorAdapter {
103
104     private final DbSession session;
105     private final Set<Integer> metricIds;
106     private final List<Metric> metrics;
107
108     VariationMeasuresVisitor(DbSession session, List<Metric> metrics) {
109       // measures on files are currently purged, so past measures are not available on files
110       super(CrawlerDepthLimit.reportMaxDepth(DIRECTORY).withViewsMaxDepth(SUBVIEW), PRE_ORDER);
111       this.session = session;
112       this.metricIds = from(metrics).transform(MetricDtoToMetricId.INSTANCE).toSet();
113       this.metrics = metrics;
114     }
115
116     @Override
117     public void visitAny(Component component) {
118       MeasuresWithVariationRepository measuresWithVariationRepository = computeMeasuresWithVariations(component);
119       processMeasuresWithVariation(component, measuresWithVariationRepository);
120     }
121
122     private MeasuresWithVariationRepository computeMeasuresWithVariations(Component component) {
123       MeasuresWithVariationRepository measuresWithVariationRepository = new MeasuresWithVariationRepository();
124       for (Period period : periodsHolder.getPeriods()) {
125         List<PastMeasureDto> pastMeasures = dbClient.measureDao()
126           .selectPastMeasures(session, component.getUuid(), period.getAnalysisUuid(), metricIds);
127         setVariationMeasures(component, pastMeasures, period.getIndex(), measuresWithVariationRepository);
128       }
129       return measuresWithVariationRepository;
130     }
131
132     private void setVariationMeasures(Component component, List<PastMeasureDto> pastMeasures, int period, MeasuresWithVariationRepository measuresWithVariationRepository) {
133       Map<MeasureKey, PastMeasureDto> pastMeasuresByMeasureKey = from(pastMeasures).uniqueIndex(pastMeasureToMeasureKey);
134       for (Metric metric : metrics) {
135         Optional<Measure> measure = measureRepository.getRawMeasure(component, metric);
136         if (measure.isPresent() && !measure.get().hasVariations()) {
137           PastMeasureDto pastMeasure = pastMeasuresByMeasureKey.get(new MeasureKey(metric.getKey(), null));
138           double pastValue = (pastMeasure != null && pastMeasure.hasValue()) ? pastMeasure.getValue() : 0d;
139           measuresWithVariationRepository.add(metric, measure.get(), period, computeVariation(measure.get(), pastValue));
140         }
141       }
142     }
143
144     private double computeVariation(Measure measure, double pastValue) {
145       switch (measure.getValueType()) {
146         case INT:
147           return measure.getIntValue() - pastValue;
148         case LONG:
149           return measure.getLongValue() - pastValue;
150         case DOUBLE:
151           return measure.getDoubleValue() - pastValue;
152         case BOOLEAN:
153           return (measure.getBooleanValue() ? 1d : 0d) - pastValue;
154         default:
155           throw new IllegalArgumentException(format("Unsupported Measure.ValueType on measure '%s'", measure));
156       }
157     }
158
159     private void processMeasuresWithVariation(Component component, MeasuresWithVariationRepository measuresWithVariationRepository) {
160       for (MeasureWithVariations measureWithVariations : measuresWithVariationRepository.measures()) {
161         Metric metric = measureWithVariations.getMetric();
162         Measure measure = Measure.updatedMeasureBuilder(measureWithVariations.getMeasure())
163           .setVariations(new MeasureVariations(
164             measureWithVariations.getVariation(1),
165             measureWithVariations.getVariation(2),
166             measureWithVariations.getVariation(3),
167             measureWithVariations.getVariation(4),
168             measureWithVariations.getVariation(5)))
169           .create();
170         measureRepository.update(component, metric, measure);
171       }
172     }
173   }
174
175   private static final class MeasuresWithVariationRepository {
176
177     private final Map<MeasureKey, MeasureWithVariations> measuresWithVariations = new HashMap<>();
178
179     public void add(Metric metric, final Measure measure, int variationIndex, double variationValue) {
180       checkArgument(measure.getDeveloper() == null, "%s does not support computing variations of Measures for Developer", getClass().getSimpleName());
181       MeasureKey measureKey = new MeasureKey(metric.getKey(), null);
182       MeasureWithVariations measureWithVariations = measuresWithVariations.get(measureKey);
183       if (measureWithVariations == null) {
184         measureWithVariations = new MeasureWithVariations(metric, measure);
185         measuresWithVariations.put(measureKey, measureWithVariations);
186       }
187       measureWithVariations.setVariation(variationIndex, variationValue);
188     }
189
190     public Collection<MeasureWithVariations> measures() {
191       return measuresWithVariations.values();
192     }
193   }
194
195   private static final class MeasureWithVariations {
196     private final Metric metric;
197     private final Measure measure;
198     private final Double[] variations = new Double[5];
199
200     MeasureWithVariations(Metric metric, Measure measure) {
201       this.metric = metric;
202       this.measure = measure;
203     }
204
205     public Measure getMeasure() {
206       return measure;
207     }
208
209     public Metric getMetric() {
210       return metric;
211     }
212
213     public void setVariation(int index, @Nullable Double value) {
214       variations[index - 1] = value;
215     }
216
217     @CheckForNull
218     public Double getVariation(int index) {
219       return variations[index - 1];
220     }
221   }
222
223   private enum MetricDtoToMetricId implements Function<Metric, Integer> {
224     INSTANCE;
225
226     @Nullable
227     @Override
228     public Integer apply(@Nonnull Metric metric) {
229       return metric.getId();
230     }
231   }
232
233   private enum NumericMetric implements Predicate<Metric> {
234     INSTANCE;
235
236     @Override
237     public boolean apply(@Nonnull Metric metric) {
238       Measure.ValueType valueType = metric.getType().getValueType();
239       return Measure.ValueType.INT.equals(valueType)
240         || Measure.ValueType.LONG.equals(valueType)
241         || Measure.ValueType.DOUBLE.equals(valueType)
242         || Measure.ValueType.BOOLEAN.equals(valueType);
243     }
244   }
245
246   @Override
247   public String getDescription() {
248     return "Compute measure variations";
249   }
250 }