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.Optional;
26 import javax.annotation.CheckForNull;
27 import javax.annotation.Nullable;
28 import org.sonar.ce.task.projectanalysis.util.cache.DoubleCache;
30 import static com.google.common.base.Preconditions.checkArgument;
31 import static com.google.common.base.Preconditions.checkState;
32 import static java.util.Objects.requireNonNull;
34 public interface Measure {
37 NO_VALUE, BOOLEAN, INT, LONG, DOUBLE, STRING, LEVEL
45 * @deprecated since 7.6, warning quality gates doesn't exist anymore on new analysis
50 private final String colorName;
52 Level(String colorName) {
53 this.colorName = colorName;
56 public String getColorName() {
60 public static Optional<Level> toLevel(@Nullable String level) {
62 return Optional.empty();
66 return Optional.of(Level.valueOf(level));
67 } catch (IllegalArgumentException e) {
68 return Optional.empty();
74 * The type of value stored in the measure.
76 ValueType getValueType();
79 * The value of this measure as a boolean if the type is {@link Measure.ValueType#BOOLEAN}.
81 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#BOOLEAN}
83 boolean getBooleanValue();
86 * The value of this measure as a int if the type is {@link Measure.ValueType#INT}.
88 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#INT}
93 * The value of this measure as a long if the type is {@link Measure.ValueType#LONG}.
95 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LONG}
100 * The value of this measure as a double if the type is {@link Measure.ValueType#DOUBLE}.
102 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#DOUBLE}
104 double getDoubleValue();
107 * The value of this measure as a String if the type is {@link Measure.ValueType#STRING}.
109 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#STRING}
111 String getStringValue();
114 * The value of this measure as a Level if the type is {@link Measure.ValueType#LEVEL}.
116 * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LEVEL}
118 Level getLevelValue();
121 * The data of this measure if it exists.
123 * If the measure type is {@link Measure.ValueType#STRING}, the value returned by this function is the same as {@link #getStringValue()}.
129 * Any Measure, which ever is its value type, can have a QualityGate status.
131 boolean hasQualityGateStatus();
134 * The QualityGate status for this measure.
135 * <strong>Don't call this method unless you've checked the result of {@link #hasQualityGateStatus()} first</strong>
137 * @throws IllegalStateException if the measure has no QualityGate status
139 QualityGateStatus getQualityGateStatus();
142 * Any Measure, which ever is its value type, can have a variation.
144 boolean hasVariation();
147 * The variation of this measure.
149 * @throws IllegalStateException if the measure has no variation
151 double getVariation();
153 static NewMeasureBuilder newMeasureBuilder() {
154 return new NewMeasureBuilder();
157 static UpdateMeasureBuilder updatedMeasureBuilder(Measure measure) {
158 return new UpdateMeasureBuilder(measure);
161 class MeasureImpl implements Measure {
162 private final ValueType valueType;
164 private final Double value;
166 private final String data;
168 private final Level dataLevel;
170 private final QualityGateStatus qualityGateStatus;
172 private final Double variation;
174 private MeasureImpl(ValueType valueType, @Nullable Double value, @Nullable String data, @Nullable Level dataLevel,
175 @Nullable QualityGateStatus qualityGateStatus, @Nullable Double variation) {
176 this.valueType = valueType;
177 this.value = DoubleCache.intern(value);
179 this.dataLevel = dataLevel;
180 this.qualityGateStatus = qualityGateStatus;
181 this.variation = variation;
185 public ValueType getValueType() {
190 public boolean getBooleanValue() {
191 checkValueType(ValueType.BOOLEAN, valueType);
192 return value != null && value.intValue() == 1;
196 public int getIntValue() {
197 checkValueType(ValueType.INT, valueType);
198 return value.intValue();
202 public long getLongValue() {
203 checkValueType(ValueType.LONG, valueType);
204 return value.longValue();
208 public double getDoubleValue() {
209 checkValueType(ValueType.DOUBLE, valueType);
214 public String getStringValue() {
215 checkValueType(ValueType.STRING, valueType);
220 public Level getLevelValue() {
221 checkValueType(ValueType.LEVEL, valueType);
226 public String getData() {
231 public boolean hasQualityGateStatus() {
232 return this.qualityGateStatus != null;
236 public QualityGateStatus getQualityGateStatus() {
237 checkState(qualityGateStatus != null, "Measure does not have an QualityGate status");
238 return this.qualityGateStatus;
242 public boolean hasVariation() {
243 return variation != null;
247 public double getVariation() {
248 checkState(variation != null, "Measure does not have variation");
252 private static void checkValueType(ValueType expected, ValueType valueType) {
253 if (valueType != expected) {
254 throw new IllegalStateException(
256 "value can not be converted to %s because current value type is a %s",
257 expected.toString().toLowerCase(Locale.US),
263 public String toString() {
264 return com.google.common.base.MoreObjects.toStringHelper(this)
265 .add("valueType", valueType)
268 .add("dataLevel", dataLevel)
269 .add("qualityGateStatus", qualityGateStatus)
270 .add("variations", variation)
275 class ValueMeasureImpl implements Measure {
276 private final ValueType valueType;
278 private final Double value;
280 private ValueMeasureImpl(ValueType valueType, @Nullable Double value) {
281 this.valueType = valueType;
282 this.value = DoubleCache.intern(value);
286 public ValueType getValueType() {
291 public boolean getBooleanValue() {
292 MeasureImpl.checkValueType(ValueType.BOOLEAN, valueType);
293 return value != null && value.intValue() == 1;
297 public int getIntValue() {
298 MeasureImpl.checkValueType(ValueType.INT, valueType);
299 return value.intValue();
303 public long getLongValue() {
304 MeasureImpl.checkValueType(ValueType.LONG, valueType);
305 return value.longValue();
309 public double getDoubleValue() {
310 MeasureImpl.checkValueType(ValueType.DOUBLE, valueType);
315 public String getStringValue() {
316 throw new IllegalStateException();
320 public Level getLevelValue() {
321 throw new IllegalStateException();
325 public String getData() {
330 public boolean hasQualityGateStatus() {
335 public QualityGateStatus getQualityGateStatus() {
336 throw new IllegalStateException("Measure does not have an QualityGate status");
340 public boolean hasVariation() {
345 public double getVariation() {
346 throw new IllegalStateException("Measure does not have variation");
351 public String toString() {
352 return com.google.common.base.MoreObjects.toStringHelper(this)
353 .add("valueType", valueType)
359 class NoValueVariationMeasureImpl implements Measure {
361 private Double variation;
362 private ValueType valueType = ValueType.NO_VALUE;
364 private NoValueVariationMeasureImpl(@Nullable Double variation) {
365 this.variation = variation;
369 public ValueType getValueType() {
370 return ValueType.NO_VALUE;
374 public boolean getBooleanValue() {
375 throw new IllegalStateException();
379 public int getIntValue() {
380 throw new IllegalStateException();
384 public long getLongValue() {
385 throw new IllegalStateException();
389 public double getDoubleValue() {
390 throw new IllegalStateException();
394 public String getStringValue() {
395 throw new IllegalStateException();
399 public Level getLevelValue() {
400 throw new IllegalStateException();
405 public String getData() {
410 public boolean hasQualityGateStatus() {
415 public QualityGateStatus getQualityGateStatus() {
416 throw new IllegalStateException("Measure does not have an QualityGate status");
420 public boolean hasVariation() {
421 return variation != null;
425 public double getVariation() {
426 checkState(variation != null, "Measure does not have variation");
431 public String toString() {
432 return com.google.common.base.MoreObjects.toStringHelper(this)
433 .add("valueType", valueType)
434 .add("variations", variation)
439 class NewMeasureBuilder {
440 private QualityGateStatus qualityGateStatus;
441 private Double variation;
443 public NewMeasureBuilder setQualityGateStatus(QualityGateStatus qualityGateStatus) {
444 this.qualityGateStatus = requireNonNull(qualityGateStatus, "QualityGateStatus can not be set to null");
448 public NewMeasureBuilder setVariation(double variation) {
449 this.variation = variation;
453 public Measure create(boolean value, @Nullable String data) {
454 return createInternal(ValueType.BOOLEAN, value ? 1.0D : 0.0D, data);
457 public Measure create(boolean value) {
458 return create(value, null);
461 public Measure create(int value, @Nullable String data) {
462 return createInternal(ValueType.INT, (double) value, data);
465 public Measure create(int value) {
466 return create(value, null);
469 public Measure create(long value, @Nullable String data) {
470 return createInternal(ValueType.LONG, (double) value, data);
473 public Measure create(long value) {
474 return create(value, null);
477 public Measure create(double value, int decimalScale, @Nullable String data) {
478 checkArgument(!Double.isNaN(value), "NaN is not allowed as a Measure value");
479 double scaledValue = scale(value, decimalScale);
480 return createInternal(ValueType.DOUBLE, scaledValue, data);
483 private Measure createInternal(ValueType type, double value, @Nullable String data) {
484 if (data == null && qualityGateStatus == null && variation == null) {
485 return new ValueMeasureImpl(type, value);
488 return new MeasureImpl(type, value, data, null, qualityGateStatus, variation);
491 public Measure create(double value, int decimalScale) {
492 return create(value, decimalScale, null);
495 public Measure create(String value) {
496 return new MeasureImpl(ValueType.STRING, null, requireNonNull(value), null, qualityGateStatus, variation);
499 public Measure create(Level level) {
500 return new MeasureImpl(ValueType.LEVEL, null, null, requireNonNull(level), qualityGateStatus, variation);
503 public Measure createNoValue() {
504 if (qualityGateStatus == null) {
505 return new NoValueVariationMeasureImpl(variation);
507 return new MeasureImpl(ValueType.NO_VALUE, null, null, null, qualityGateStatus, variation);
510 private static double scale(double value, int decimalScale) {
511 BigDecimal bd = BigDecimal.valueOf(value);
512 return bd.setScale(decimalScale, RoundingMode.HALF_UP).doubleValue();
516 final class UpdateMeasureBuilder {
517 private final Measure source;
518 private QualityGateStatus qualityGateStatus;
519 private Double variation;
521 public UpdateMeasureBuilder(Measure source) {
522 this.source = requireNonNull(source, "Can not create a measure from null");
526 * Sets the QualityGateStatus of the updated Measure to create.
528 * @throws NullPointerException if the specified {@link QualityGateStatus} is {@code null}
529 * @throws UnsupportedOperationException if the source measure already has a {@link QualityGateStatus}
531 public UpdateMeasureBuilder setQualityGateStatus(QualityGateStatus qualityGateStatus) {
532 if (source.hasQualityGateStatus()) {
533 throw new UnsupportedOperationException("QualityGate status can not be changed if already set on source Measure");
535 this.qualityGateStatus = requireNonNull(qualityGateStatus, "QualityGateStatus can not be set to null");
540 * Sets the variation of the updated Measure to create.
542 * @throws UnsupportedOperationException if the source measure already has a variation
544 public UpdateMeasureBuilder setVariation(double variation) {
545 if (source.hasVariation()) {
546 throw new UnsupportedOperationException("Variation can not be changed if already set on source Measure");
548 this.variation = variation;
552 public Measure create() {
554 switch (source.getValueType()) {
556 value = source.getDoubleValue();
559 value = (double) source.getIntValue();
562 value = (double) source.getLongValue();
565 value = source.getBooleanValue() ? 1.0 : 0.0;
572 Level level = source.getValueType() == ValueType.LEVEL ? source.getLevelValue() : null;
573 QualityGateStatus status = source.hasQualityGateStatus() ? source.getQualityGateStatus() : qualityGateStatus;
575 if (source.hasVariation()) {
576 var = source.getVariation();
580 return new MeasureImpl(source.getValueType(), value, source.getData(), level, status, var);