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