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 java.math.BigDecimal;
23 import java.math.RoundingMode;
24 import java.util.Locale;
25 import java.util.Objects;
26 import java.util.Optional;
27 import javax.annotation.CheckForNull;
28 import javax.annotation.Nullable;
29 import org.sonar.ce.task.projectanalysis.component.Developer;
31 import static com.google.common.base.Preconditions.checkArgument;
32 import static com.google.common.base.Preconditions.checkState;
33 import static java.util.Objects.requireNonNull;
35 public final class Measure {
37 public enum ValueType {
38 NO_VALUE, BOOLEAN, INT, LONG, DOUBLE, STRING, LEVEL
46 private final String colorName;
48 Level(String colorName) {
49 this.colorName = colorName;
52 public String getColorName() {
56 public static Optional<Level> toLevel(@Nullable String level) {
58 return Optional.empty();
62 return Optional.of(Level.valueOf(level));
63 } catch (IllegalArgumentException e) {
64 return Optional.empty();
69 private final ValueType valueType;
71 private final Developer developer;
73 private final Double value;
75 private final String data;
77 private final Level dataLevel;
79 private final QualityGateStatus qualityGateStatus;
81 private final Double variation;
83 private Measure(ValueType valueType, @Nullable Developer developer,
84 @Nullable Double value, @Nullable String data, @Nullable Level dataLevel,
85 @Nullable QualityGateStatus qualityGateStatus, @Nullable Double variation) {
86 this.valueType = valueType;
87 this.developer = developer;
90 this.dataLevel = dataLevel;
91 this.qualityGateStatus = qualityGateStatus;
92 this.variation = variation;
95 public static NewMeasureBuilder newMeasureBuilder() {
96 return new NewMeasureBuilder();
99 public static UpdateMeasureBuilder updatedMeasureBuilder(Measure measure) {
100 return new UpdateMeasureBuilder(measure);
103 public static final class NewMeasureBuilder {
104 private Developer developer;
105 private QualityGateStatus qualityGateStatus;
106 private Double variation;
109 * Sets the developer this measure is associated to.
112 public NewMeasureBuilder forDeveloper(Developer developer) {
113 this.developer = developer;
117 public NewMeasureBuilder setQualityGateStatus(QualityGateStatus qualityGateStatus) {
118 this.qualityGateStatus = requireNonNull(qualityGateStatus, "QualityGateStatus can not be set to null");
122 public NewMeasureBuilder setVariation(double variation) {
123 this.variation = variation;
127 public Measure create(boolean value, @Nullable String data) {
128 return new Measure(ValueType.BOOLEAN, developer, value ? 1.0d : 0.0d, data, null, qualityGateStatus, variation);
131 public Measure create(boolean value) {
132 return create(value, null);
135 public Measure create(int value, @Nullable String data) {
136 return new Measure(ValueType.INT, developer, (double) value, data, null, qualityGateStatus, variation);
139 public Measure create(int value) {
140 return create(value, null);
143 public Measure create(long value, @Nullable String data) {
144 return new Measure(ValueType.LONG, developer, (double) value, data, null, qualityGateStatus, variation);
147 public Measure create(long value) {
148 return create(value, null);
151 public Measure create(double value, int decimalScale, @Nullable String data) {
152 checkArgument(!Double.isNaN(value), "NaN is not allowed as a Measure value");
153 double scaledValue = scale(value, decimalScale);
154 return new Measure(ValueType.DOUBLE, developer, scaledValue, data, null, qualityGateStatus, variation);
157 public Measure create(double value, int decimalScale) {
158 return create(value, decimalScale, null);
161 public Measure create(String value) {
162 return new Measure(ValueType.STRING, developer, null, requireNonNull(value), null, qualityGateStatus, variation);
165 public Measure create(Level level) {
166 return new Measure(ValueType.LEVEL, developer, null, null, requireNonNull(level), qualityGateStatus, variation);
169 public Measure createNoValue() {
170 return new Measure(ValueType.NO_VALUE, developer, null, null, null, qualityGateStatus, variation);
173 private static double scale(double value, int decimalScale) {
174 BigDecimal bd = BigDecimal.valueOf(value);
175 return bd.setScale(decimalScale, RoundingMode.HALF_UP).doubleValue();
179 public static final class UpdateMeasureBuilder {
180 private final Measure source;
181 private QualityGateStatus qualityGateStatus;
182 private Double variation;
184 public UpdateMeasureBuilder(Measure source) {
185 this.source = requireNonNull(source, "Can not create a measure from null");
189 * Sets the QualityGateStatus of the updated Measure to create.
191 * @throws NullPointerException if the specified {@link QualityGateStatus} is {@code null}
192 * @throws UnsupportedOperationException if the source measure already has a {@link QualityGateStatus}
194 public UpdateMeasureBuilder setQualityGateStatus(QualityGateStatus qualityGateStatus) {
195 if (source.qualityGateStatus != null) {
196 throw new UnsupportedOperationException("QualityGate status can not be changed if already set on source Measure");
198 this.qualityGateStatus = requireNonNull(qualityGateStatus, "QualityGateStatus can not be set to null");
203 * Sets the variation of the updated Measure to create.
205 * @throws UnsupportedOperationException if the source measure already has a variation
207 public UpdateMeasureBuilder setVariation(double variation) {
208 if (source.variation != null) {
209 throw new UnsupportedOperationException("Variation can not be changed if already set on source Measure");
211 this.variation = variation;
215 public Measure create() {
216 return new Measure(source.valueType, source.developer,
217 source.value, source.data, source.dataLevel,
218 source.qualityGateStatus == null ? qualityGateStatus : source.qualityGateStatus,
219 source.variation == null ? variation : source.variation);
224 public Developer getDeveloper() {
229 * The type of value stored in the measure.
231 public ValueType getValueType() {
236 * The value of this measure as a boolean if the type is {@link Measure.ValueType#BOOLEAN}.
238 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#BOOLEAN}
240 public boolean getBooleanValue() {
241 checkValueType(ValueType.BOOLEAN);
242 return value == 1.0d;
246 * The value of this measure as a int if the type is {@link Measure.ValueType#INT}.
248 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#INT}
250 public int getIntValue() {
251 checkValueType(ValueType.INT);
252 return value.intValue();
256 * The value of this measure as a long if the type is {@link Measure.ValueType#LONG}.
258 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LONG}
260 public long getLongValue() {
261 checkValueType(ValueType.LONG);
262 return value.longValue();
266 * The value of this measure as a double if the type is {@link Measure.ValueType#DOUBLE}.
268 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#DOUBLE}
270 public double getDoubleValue() {
271 checkValueType(ValueType.DOUBLE);
276 * The value of this measure as a String if the type is {@link Measure.ValueType#STRING}.
278 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#STRING}
280 public String getStringValue() {
281 checkValueType(ValueType.STRING);
286 * The value of this measure as a Level if the type is {@link Measure.ValueType#LEVEL}.
288 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LEVEL}
290 public Level getLevelValue() {
291 checkValueType(ValueType.LEVEL);
296 * The data of this measure if it exists.
298 * If the measure type is {@link Measure.ValueType#STRING}, the value returned by this function is the same as {@link #getStringValue()}.
301 public String getData() {
305 private void checkValueType(ValueType expected) {
306 if (valueType != expected) {
307 throw new IllegalStateException(
309 "value can not be converted to %s because current value type is a %s",
310 expected.toString().toLowerCase(Locale.US),
316 * Any Measure, which ever is its value type, can have a QualityGate status.
318 public boolean hasQualityGateStatus() {
319 return this.qualityGateStatus != null;
323 * The QualityGate status for this measure.
324 * <strong>Don't call this method unless you've checked the result of {@link #hasQualityGateStatus()} first</strong>
326 * @throws IllegalStateException if the measure has no QualityGate status
328 public QualityGateStatus getQualityGateStatus() {
329 checkState(qualityGateStatus != null, "Measure does not have an QualityGate status");
330 return this.qualityGateStatus;
334 * Any Measure, which ever is its value type, can have a variation.
336 public boolean hasVariation() {
337 return variation != null;
341 * The variation of this measure.
343 * @throws IllegalStateException if the measure has no variation
345 public double getVariation() {
346 checkState(variation != null, "Measure does not have variation");
351 * a Metric is equal to another Metric if it has the same ruleId/characteristicId paar (both being potentially
352 * {@code null} but only one of them can be non {@code null}).
355 public boolean equals(@Nullable Object o) {
359 if (o == null || getClass() != o.getClass()) {
362 Measure measure = (Measure) o;
363 return Objects.equals(developer, measure.developer);
367 public int hashCode() {
368 return Objects.hash(developer);
372 public String toString() {
373 return com.google.common.base.MoreObjects.toStringHelper(this)
374 .add("valueType", valueType)
375 .add("developer", developer)
378 .add("dataLevel", dataLevel)
379 .add("qualityGateStatus", qualityGateStatus)
380 .add("variations", variation)