3 * Copyright (C) 2009-2019 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.ce.task.projectanalysis.measure;
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;
28 import java.util.Objects;
29 import javax.annotation.Nonnull;
30 import javax.annotation.Nullable;
31 import org.sonar.ce.task.projectanalysis.component.Component;
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)}.
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.
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.:
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))
53 public final class MeasureRepoEntry {
54 private final String metricKey;
55 private final Measure measure;
57 public MeasureRepoEntry(String metricKey, Measure measure) {
58 this.metricKey = metricKey;
59 this.measure = measure;
62 public static Function<Map.Entry<String, Measure>, MeasureRepoEntry> toMeasureRepoEntry() {
63 return EntryToMeasureRepoEntry.INSTANCE;
66 public static Iterable<MeasureRepoEntry> toEntries(SetMultimap<String, Measure> data) {
67 return FluentIterable.from(data.entries()).transform(toMeasureRepoEntry()).toList();
70 public static MeasureRepoEntry entryOf(String metricKey, Measure measure) {
71 return new MeasureRepoEntry(metricKey, measure);
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());
82 private static boolean equalsByValue(Measure measure, Measure measure1) {
83 switch (measure.getValueType()) {
85 return measure.getBooleanValue() == measure1.getBooleanValue();
87 return measure.getIntValue() == measure1.getIntValue();
89 return measure.getLongValue() == measure1.getLongValue();
91 return Double.compare(measure.getDoubleValue(), measure1.getDoubleValue()) == 0;
93 return measure.getStringValue().equals(measure1.getStringValue());
95 return measure.getLevelValue() == measure1.getLevelValue();
99 throw new IllegalArgumentException("Unsupported ValueType " + measure.getValueType());
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);
108 private static final int DOUBLE_PRECISION = 1;
110 private static double scale(double value) {
111 BigDecimal bd = BigDecimal.valueOf(value);
112 return bd.setScale(DOUBLE_PRECISION, RoundingMode.HALF_UP).doubleValue();
115 private static boolean equalsByQualityGateStatus(Measure measure, Measure measure1) {
116 if (measure.hasQualityGateStatus() != measure1.hasQualityGateStatus()) {
119 if (!measure.hasQualityGateStatus()) {
122 return Objects.equals(measure.getQualityGateStatus(), measure1.getQualityGateStatus());
126 public boolean equals(@Nullable Object o) {
130 if (o == null || getClass() != o.getClass()) {
133 MeasureRepoEntry that = (MeasureRepoEntry) o;
134 return Objects.equals(metricKey, that.metricKey) &&
135 deepEquals(measure, that.measure);
139 public int hashCode() {
140 return Objects.hash(metricKey, measure);
144 public String toString() {
145 return "<" + metricKey + ", " + measure + '>';
148 private enum EntryToMeasureRepoEntry implements Function<Map.Entry<String, Measure>, MeasureRepoEntry> {
153 public MeasureRepoEntry apply(@Nonnull Map.Entry<String, Measure> input) {
154 return new MeasureRepoEntry(input.getKey(), input.getValue());