]> source.dussan.org Git - sonarqube.git/blob
d83245d86acc825585b3bb0e6a7b47b2a80312cc
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2019 SonarSource SA
4  * mailto:info 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.ce.task.projectanalysis.measure;
21
22 import com.google.common.base.Function;
23 import com.google.common.base.Predicate;
24 import com.google.common.collect.ImmutableSetMultimap;
25 import com.google.common.collect.SetMultimap;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.Map;
29 import java.util.Objects;
30 import java.util.Optional;
31 import java.util.Set;
32 import javax.annotation.CheckForNull;
33 import javax.annotation.Nonnull;
34 import javax.annotation.Nullable;
35 import org.junit.rules.ExternalResource;
36 import org.sonar.ce.task.projectanalysis.component.Component;
37 import org.sonar.ce.task.projectanalysis.component.ComponentProvider;
38 import org.sonar.ce.task.projectanalysis.component.NoComponentProvider;
39 import org.sonar.ce.task.projectanalysis.component.TreeComponentProvider;
40 import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
41 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderComponentProvider;
42 import org.sonar.ce.task.projectanalysis.metric.Metric;
43 import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
44
45 import static com.google.common.base.Preconditions.checkArgument;
46 import static com.google.common.base.Preconditions.checkState;
47 import static com.google.common.collect.FluentIterable.from;
48 import static com.google.common.collect.Maps.filterKeys;
49 import static java.lang.String.format;
50 import static java.util.Objects.requireNonNull;
51
52 /**
53  * An implementation of MeasureRepository as a JUnit rule which provides add methods for raw measures and extra add
54  * methods that takes component ref and metric keys thanks to the integration with various Component and Metric
55  * providers.
56  */
57 public class MeasureRepositoryRule extends ExternalResource implements MeasureRepository {
58   private final ComponentProvider componentProvider;
59   @CheckForNull
60   private final MetricRepositoryRule metricRepositoryRule;
61   private final Map<InternalKey, Measure> baseMeasures = new HashMap<>();
62   private final Map<InternalKey, Measure> rawMeasures = new HashMap<>();
63   private final Map<InternalKey, Measure> initialRawMeasures = new HashMap<>();
64   private Collection<Component> loadedAsRawComponents;
65   private Collection<Metric> loadedAsRawMetrics;
66   private final Predicate<Map.Entry<InternalKey, Measure>> isAddedMeasure = new Predicate<Map.Entry<InternalKey, Measure>>() {
67     @Override
68     public boolean apply(@Nonnull Map.Entry<InternalKey, Measure> input) {
69       return !initialRawMeasures.containsKey(input.getKey())
70         || !MeasureRepoEntry.deepEquals(input.getValue(), initialRawMeasures.get(input.getKey()));
71     }
72   };
73
74   private MeasureRepositoryRule(ComponentProvider componentProvider, @Nullable MetricRepositoryRule metricRepositoryRule) {
75     this.componentProvider = componentProvider;
76     this.metricRepositoryRule = metricRepositoryRule;
77   }
78
79   @Override
80   protected void after() {
81     componentProvider.reset();
82     baseMeasures.clear();
83     rawMeasures.clear();
84   }
85
86   public static MeasureRepositoryRule create() {
87     return new MeasureRepositoryRule(NoComponentProvider.INSTANCE, null);
88   }
89
90   public static MeasureRepositoryRule create(TreeRootHolder treeRootHolder, MetricRepositoryRule metricRepositoryRule) {
91     return new MeasureRepositoryRule(new TreeRootHolderComponentProvider(treeRootHolder), requireNonNull(metricRepositoryRule));
92   }
93
94   public static MeasureRepositoryRule create(Component treeRoot, MetricRepositoryRule metricRepositoryRule) {
95     return new MeasureRepositoryRule(new TreeComponentProvider(treeRoot), requireNonNull(metricRepositoryRule));
96   }
97
98   public MeasureRepositoryRule addBaseMeasure(Component component, Metric metric, Measure measure) {
99     checkAndInitProvidersState();
100
101     InternalKey internalKey = new InternalKey(component, metric);
102     checkState(!baseMeasures.containsKey(internalKey),
103       format("Can not add a BaseMeasure twice for a Component (ref=%s) and Metric (key=%s)", getRef(component), metric.getKey()));
104
105     baseMeasures.put(internalKey, measure);
106
107     return this;
108   }
109
110   public MeasureRepositoryRule addBaseMeasure(int componentRef, String metricKey, Measure measure) {
111     checkAndInitProvidersState();
112
113     InternalKey internalKey = new InternalKey(componentProvider.getByRef(componentRef), metricRepositoryRule.getByKey(metricKey));
114     checkState(!baseMeasures.containsKey(internalKey), format("Can not add a BaseMeasure twice for a Component (ref=%s) and Metric (key=%s)", componentRef, metricKey));
115
116     baseMeasures.put(internalKey, measure);
117
118     return this;
119   }
120
121   public SetMultimap<String, Measure> getRawMeasures(int componentRef) {
122     return getRawMeasures(componentProvider.getByRef(componentRef));
123   }
124
125   /**
126    * Return measures that were added by the step (using {@link #add(Component, Metric, Measure)}).
127    * It does not contain the one added in the test by {@link #addRawMeasure(int, String, Measure)}
128    */
129   public SetMultimap<String, Measure> getAddedRawMeasures(int componentRef) {
130     checkAndInitProvidersState();
131
132     return getAddedRawMeasures(componentProvider.getByRef(componentRef));
133   }
134
135   /**
136    * Return a measure that were added by the step (using {@link #add(Component, Metric, Measure)}).
137    * It does not contain the one added in the test by {@link #addRawMeasure(int, String, Measure)}
138    */
139   public Optional<Measure> getAddedRawMeasure(Component component, String metricKey) {
140     return getAddedRawMeasure(component.getReportAttributes().getRef(), metricKey);
141   }
142
143   /**
144    * Return a measure that were added by the step (using {@link #add(Component, Metric, Measure)}).
145    * It does not contain the one added in the test by {@link #addRawMeasure(int, String, Measure)}
146    */
147   public Optional<Measure> getAddedRawMeasure(int componentRef, String metricKey) {
148     checkAndInitProvidersState();
149
150     Set<Measure> measures = getAddedRawMeasures(componentProvider.getByRef(componentRef)).get(metricKey);
151     if (measures.isEmpty()) {
152       return Optional.empty();
153     }
154     checkArgument(measures.size() == 1, String.format("There is more than one measure on metric '%s' for component '%s'", metricKey, componentRef));
155     return Optional.of(measures.iterator().next());
156   }
157
158   /**
159    * Return measures that were added by the step (using {@link #add(Component, Metric, Measure)}).
160    * It does not contain the one added in the test by {@link #addRawMeasure(int, String, Measure)}
161    */
162   public SetMultimap<String, Measure> getAddedRawMeasures(Component component) {
163     checkAndInitProvidersState();
164
165     ImmutableSetMultimap.Builder<String, Measure> builder = ImmutableSetMultimap.builder();
166     for (Map.Entry<InternalKey, Measure> entry : from(filterKeys(rawMeasures, hasComponentRef(component)).entrySet()).filter(isAddedMeasure)) {
167       builder.put(entry.getKey().getMetricKey(), entry.getValue());
168     }
169     return builder.build();
170   }
171
172   public MeasureRepositoryRule addRawMeasure(int componentRef, String metricKey, Measure measure) {
173     checkAndInitProvidersState();
174
175     InternalKey internalKey = new InternalKey(componentProvider.getByRef(componentRef), metricRepositoryRule.getByKey(metricKey));
176     checkState(!rawMeasures.containsKey(internalKey), format(
177       "A measure can only be set once for Component (ref=%s), Metric (key=%s)",
178       componentRef, metricKey));
179
180     rawMeasures.put(internalKey, measure);
181     initialRawMeasures.put(internalKey, measure);
182
183     return this;
184   }
185
186   @Override
187   public Optional<Measure> getBaseMeasure(Component component, Metric metric) {
188     return Optional.ofNullable(baseMeasures.get(new InternalKey(component, metric)));
189   }
190
191   public Collection<Component> getComponentsLoadedAsRaw() {
192     return loadedAsRawComponents;
193   }
194
195   public Collection<Metric> getMetricsLoadedAsRaw() {
196     return loadedAsRawMetrics;
197   }
198
199   @Override
200   public Optional<Measure> getRawMeasure(Component component, Metric metric) {
201     return Optional.ofNullable(rawMeasures.get(new InternalKey(component, metric)));
202   }
203
204   public Optional<Measure> getRawRuleMeasure(Component component, Metric metric, int ruleId) {
205     return Optional.ofNullable(rawMeasures.get(new InternalKey(component, metric)));
206   }
207
208   @Override
209   public Set<Measure> getRawMeasures(Component component, Metric metric) {
210     return from(filterKeys(rawMeasures, hasComponentRef(component)).entrySet()).filter(new MatchMetric(metric)).transform(ToMeasure.INSTANCE).toSet();
211   }
212
213   @Override
214   public SetMultimap<String, Measure> getRawMeasures(Component component) {
215     ImmutableSetMultimap.Builder<String, Measure> builder = ImmutableSetMultimap.builder();
216     for (Map.Entry<InternalKey, Measure> entry : filterKeys(rawMeasures, hasComponentRef(component)).entrySet()) {
217       builder.put(entry.getKey().getMetricKey(), entry.getValue());
218     }
219     return builder.build();
220   }
221
222   private HasComponentRefPredicate hasComponentRef(Component component) {
223     return new HasComponentRefPredicate(component);
224   }
225
226   @Override
227   public void add(Component component, Metric metric, Measure measure) {
228     String ref = getRef(component);
229     InternalKey internalKey = new InternalKey(ref, metric.getKey());
230     if (rawMeasures.containsKey(internalKey)) {
231       throw new UnsupportedOperationException(format(
232         "A measure can only be set once for Component (ref=%s), Metric (key=%s)",
233         ref, metric.getKey()));
234     }
235     rawMeasures.put(internalKey, measure);
236   }
237
238   @Override
239   public void update(Component component, Metric metric, Measure measure) {
240     String componentRef = getRef(component);
241     InternalKey internalKey = new InternalKey(componentRef, metric.getKey());
242     if (!rawMeasures.containsKey(internalKey)) {
243       throw new UnsupportedOperationException(format(
244         "A measure can only be updated if it has been added first for Component (ref=%s), Metric (key=%s)",
245         componentRef, metric.getKey()));
246     }
247     rawMeasures.put(internalKey, measure);
248   }
249
250   private void checkAndInitProvidersState() {
251     checkState(metricRepositoryRule != null, "Can not add a measure by metric key if MeasureRepositoryRule has not been created for a MetricRepository");
252     componentProvider.ensureInitialized();
253   }
254
255   public boolean isEmpty() {
256     return rawMeasures.isEmpty();
257   }
258
259   private static final class InternalKey {
260     private final String componentRef;
261     private final String metricKey;
262
263     public InternalKey(Component component, Metric metric) {
264       this(getRef(component), metric.getKey());
265     }
266
267     private InternalKey(String componentRef, String metricKey) {
268       this.componentRef = componentRef;
269       this.metricKey = metricKey;
270     }
271
272     public String getComponentRef() {
273       return componentRef;
274     }
275
276     public String getMetricKey() {
277       return metricKey;
278     }
279
280     @Override
281     public boolean equals(@Nullable Object o) {
282       if (this == o) {
283         return true;
284       }
285       if (o == null || getClass() != o.getClass()) {
286         return false;
287       }
288       InternalKey that = (InternalKey) o;
289       return Objects.equals(componentRef, that.componentRef) &&
290         Objects.equals(metricKey, that.metricKey);
291     }
292
293     @Override
294     public int hashCode() {
295       return Objects.hash(componentRef, metricKey);
296     }
297
298     @Override
299     public String toString() {
300       return "InternalKey{" +
301         "component=" + componentRef +
302         ", metric='" + metricKey + '\'' +
303         '}';
304     }
305   }
306
307   private static class HasComponentRefPredicate implements Predicate<InternalKey> {
308
309     private final String componentRef;
310
311     public HasComponentRefPredicate(Component component) {
312       this.componentRef = getRef(component);
313     }
314
315     @Override
316     public boolean apply(@Nonnull InternalKey input) {
317       return input.getComponentRef().equals(this.componentRef);
318     }
319   }
320
321   private static String getRef(Component component) {
322     return component.getType().isReportType() ? String.valueOf(component.getReportAttributes().getRef()) : component.getDbKey();
323   }
324
325   private static class MatchMetric implements Predicate<Map.Entry<InternalKey, Measure>> {
326     private final Metric metric;
327
328     public MatchMetric(Metric metric) {
329       this.metric = metric;
330     }
331
332     @Override
333     public boolean apply(@Nonnull Map.Entry<InternalKey, Measure> input) {
334       return input.getKey().getMetricKey().equals(metric.getKey());
335     }
336   }
337
338   private enum ToMeasure implements Function<Map.Entry<InternalKey, Measure>, Measure> {
339     INSTANCE;
340
341     @Nullable
342     @Override
343     public Measure apply(@Nonnull Map.Entry<InternalKey, Measure> input) {
344       return input.getValue();
345     }
346   }
347
348 }