]> source.dussan.org Git - sonarqube.git/blob
2cf7fc130c6f8f11ea4e80209c9f0c7b122ce2ec
[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.collect.FluentIterable;
24 import com.google.common.collect.SetMultimap;
25 import java.math.BigDecimal;
26 import java.math.RoundingMode;
27 import java.util.Map;
28 import java.util.Objects;
29 import javax.annotation.Nonnull;
30 import javax.annotation.Nullable;
31 import org.sonar.ce.task.projectanalysis.component.Component;
32
33 /**
34  * This class represents a metric key and an associated measure.
35  * It can be used to easily compare the content of the SetMultimap returned by {@link MeasureRepository#getRawMeasures(Component)}
36  * or {@link MeasureRepositoryRule#getAddedRawMeasures(int)}.
37  * <p>
38  * This class is also highly useful to accurately make sure of the SetMultimap content since this
39  * object implements a deep equals of Measure objects (see {@link #deepEquals(Measure, Measure)}), when
40  * {@link Measure#equals(Object)} only care about the ruleId and characteristicId.
41  * </p>
42  * <p>
43  * In order to explore the content of the SetMultimap, use {@link #toEntries(SetMultimap)} to convert it
44  * to an Iterable of {@link MeasureRepoEntry} and then take benefit of AssertJ API, eg.:
45  * <pre>
46  * assertThat(MeasureRepoEntry.toEntries(measureRepository.getAddedRawMeasures(componentRef))).containsOnly(
47  *   MeasureRepoEntry.entryOf(DEVELOPMENT_COST_KEY, newMeasureBuilder().create(Long.toString(expectedDevCost))),
48  *   MeasureRepoEntry.entryOf(SQALE_DEBT_RATIO_KEY, newMeasureBuilder().create(expectedDebtRatio))
49  * );
50  * </pre>
51  * </p>
52  */
53 public final class MeasureRepoEntry {
54   private final String metricKey;
55   private final Measure measure;
56
57   public MeasureRepoEntry(String metricKey, Measure measure) {
58     this.metricKey = metricKey;
59     this.measure = measure;
60   }
61
62   public static Function<Map.Entry<String, Measure>, MeasureRepoEntry> toMeasureRepoEntry() {
63     return EntryToMeasureRepoEntry.INSTANCE;
64   }
65
66   public static Iterable<MeasureRepoEntry> toEntries(SetMultimap<String, Measure> data) {
67     return FluentIterable.from(data.entries()).transform(toMeasureRepoEntry()).toList();
68   }
69
70   public static MeasureRepoEntry entryOf(String metricKey, Measure measure) {
71     return new MeasureRepoEntry(metricKey, measure);
72   }
73
74   public static boolean deepEquals(Measure measure, Measure measure1) {
75     return measure.getValueType() == measure1.getValueType()
76       && equalsByValue(measure, measure1)
77       && equalsByVariation(measure, measure1)
78       && equalsByQualityGateStatus(measure, measure1)
79       && Objects.equals(measure.getData(), measure1.getData());
80   }
81
82   private static boolean equalsByValue(Measure measure, Measure measure1) {
83     switch (measure.getValueType()) {
84       case BOOLEAN:
85         return measure.getBooleanValue() == measure1.getBooleanValue();
86       case INT:
87         return measure.getIntValue() == measure1.getIntValue();
88       case LONG:
89         return measure.getLongValue() == measure1.getLongValue();
90       case DOUBLE:
91         return Double.compare(measure.getDoubleValue(), measure1.getDoubleValue()) == 0;
92       case STRING:
93         return measure.getStringValue().equals(measure1.getStringValue());
94       case LEVEL:
95         return measure.getLevelValue() == measure1.getLevelValue();
96       case NO_VALUE:
97         return true;
98       default:
99         throw new IllegalArgumentException("Unsupported ValueType " + measure.getValueType());
100     }
101   }
102
103   private static boolean equalsByVariation(Measure measure, Measure measure1) {
104     return measure.hasVariation() == measure1.hasVariation() && (!measure.hasVariation()
105       || Double.compare(scale(measure.getVariation()), scale(measure1.getVariation())) == 0);
106   }
107
108   private static final int DOUBLE_PRECISION = 1;
109
110   private static double scale(double value) {
111     BigDecimal bd = BigDecimal.valueOf(value);
112     return bd.setScale(DOUBLE_PRECISION, RoundingMode.HALF_UP).doubleValue();
113   }
114
115   private static boolean equalsByQualityGateStatus(Measure measure, Measure measure1) {
116     if (measure.hasQualityGateStatus() != measure1.hasQualityGateStatus()) {
117       return false;
118     }
119     if (!measure.hasQualityGateStatus()) {
120       return true;
121     }
122     return Objects.equals(measure.getQualityGateStatus(), measure1.getQualityGateStatus());
123   }
124
125   @Override
126   public boolean equals(@Nullable Object o) {
127     if (this == o) {
128       return true;
129     }
130     if (o == null || getClass() != o.getClass()) {
131       return false;
132     }
133     MeasureRepoEntry that = (MeasureRepoEntry) o;
134     return Objects.equals(metricKey, that.metricKey) &&
135       deepEquals(measure, that.measure);
136   }
137
138   @Override
139   public int hashCode() {
140     return Objects.hash(metricKey, measure);
141   }
142
143   @Override
144   public String toString() {
145     return "<" + metricKey + ", " + measure + '>';
146   }
147
148   private enum EntryToMeasureRepoEntry implements Function<Map.Entry<String, Measure>, MeasureRepoEntry> {
149     INSTANCE;
150
151     @Nullable
152     @Override
153     public MeasureRepoEntry apply(@Nonnull Map.Entry<String, Measure> input) {
154       return new MeasureRepoEntry(input.getKey(), input.getValue());
155     }
156   }
157 }