3 * Copyright (C) 2009-2022 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 label;
56 public String getLabel() {
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 default boolean isEmpty() {
154 return getValueType() == ValueType.NO_VALUE && !hasVariation() && getData() == null;
157 static NewMeasureBuilder newMeasureBuilder() {
158 return new NewMeasureBuilder();
161 static UpdateMeasureBuilder updatedMeasureBuilder(Measure measure) {
162 return new UpdateMeasureBuilder(measure);
165 class MeasureImpl implements Measure {
166 private final ValueType valueType;
168 private final Double value;
170 private final String data;
172 private final Level dataLevel;
174 private final QualityGateStatus qualityGateStatus;
176 private final Double variation;
178 private MeasureImpl(ValueType valueType, @Nullable Double value, @Nullable String data, @Nullable Level dataLevel,
179 @Nullable QualityGateStatus qualityGateStatus, @Nullable Double variation) {
180 this.valueType = valueType;
181 this.value = DoubleCache.intern(value);
183 this.dataLevel = dataLevel;
184 this.qualityGateStatus = qualityGateStatus;
185 this.variation = variation;
189 public ValueType getValueType() {
194 public boolean getBooleanValue() {
195 checkValueType(ValueType.BOOLEAN, valueType);
196 return value != null && value.intValue() == 1;
200 public int getIntValue() {
201 checkValueType(ValueType.INT, valueType);
202 return value.intValue();
206 public long getLongValue() {
207 checkValueType(ValueType.LONG, valueType);
208 return value.longValue();
212 public double getDoubleValue() {
213 checkValueType(ValueType.DOUBLE, valueType);
218 public String getStringValue() {
219 checkValueType(ValueType.STRING, valueType);
224 public Level getLevelValue() {
225 checkValueType(ValueType.LEVEL, valueType);
230 public String getData() {
235 public boolean hasQualityGateStatus() {
236 return this.qualityGateStatus != null;
240 public QualityGateStatus getQualityGateStatus() {
241 checkState(qualityGateStatus != null, "Measure does not have an QualityGate status");
242 return this.qualityGateStatus;
246 public boolean hasVariation() {
247 return variation != null;
251 public double getVariation() {
252 checkState(variation != null, "Measure does not have variation");
256 private static void checkValueType(ValueType expected, ValueType valueType) {
257 if (valueType != expected) {
258 throw new IllegalStateException(
260 "value can not be converted to %s because current value type is a %s",
261 expected.toString().toLowerCase(Locale.US),
267 public String toString() {
268 return com.google.common.base.MoreObjects.toStringHelper(this)
269 .add("valueType", valueType)
272 .add("dataLevel", dataLevel)
273 .add("qualityGateStatus", qualityGateStatus)
274 .add("variations", variation)
279 class ValueMeasureImpl implements Measure {
280 private final ValueType valueType;
282 private final Double value;
284 private ValueMeasureImpl(ValueType valueType, @Nullable Double value) {
285 this.valueType = valueType;
286 this.value = DoubleCache.intern(value);
290 public ValueType getValueType() {
295 public boolean getBooleanValue() {
296 MeasureImpl.checkValueType(ValueType.BOOLEAN, valueType);
297 return value != null && value.intValue() == 1;
301 public int getIntValue() {
302 MeasureImpl.checkValueType(ValueType.INT, valueType);
303 return value.intValue();
307 public long getLongValue() {
308 MeasureImpl.checkValueType(ValueType.LONG, valueType);
309 return value.longValue();
313 public double getDoubleValue() {
314 MeasureImpl.checkValueType(ValueType.DOUBLE, valueType);
319 public String getStringValue() {
320 throw new IllegalStateException();
324 public Level getLevelValue() {
325 throw new IllegalStateException();
329 public String getData() {
334 public boolean hasQualityGateStatus() {
339 public QualityGateStatus getQualityGateStatus() {
340 throw new IllegalStateException("Measure does not have an QualityGate status");
344 public boolean hasVariation() {
349 public double getVariation() {
350 throw new IllegalStateException("Measure does not have variation");
355 public String toString() {
356 return com.google.common.base.MoreObjects.toStringHelper(this)
357 .add("valueType", valueType)
363 class NoValueVariationMeasureImpl implements Measure {
365 private Double variation;
367 private NoValueVariationMeasureImpl(@Nullable Double variation) {
368 this.variation = variation;
372 public ValueType getValueType() {
373 return ValueType.NO_VALUE;
377 public boolean getBooleanValue() {
378 throw new IllegalStateException();
382 public int getIntValue() {
383 throw new IllegalStateException();
387 public long getLongValue() {
388 throw new IllegalStateException();
392 public double getDoubleValue() {
393 throw new IllegalStateException();
397 public String getStringValue() {
398 throw new IllegalStateException();
402 public Level getLevelValue() {
403 throw new IllegalStateException();
408 public String getData() {
413 public boolean hasQualityGateStatus() {
418 public QualityGateStatus getQualityGateStatus() {
419 throw new IllegalStateException("Measure does not have an QualityGate status");
423 public boolean hasVariation() {
424 return variation != null;
428 public double getVariation() {
429 checkState(variation != null, "Measure does not have variation");
434 public String toString() {
435 return com.google.common.base.MoreObjects.toStringHelper(this)
436 .add("valueType", ValueType.NO_VALUE)
437 .add("variations", variation)
442 class NewMeasureBuilder {
443 private QualityGateStatus qualityGateStatus;
444 private Double variation;
446 public NewMeasureBuilder setQualityGateStatus(QualityGateStatus qualityGateStatus) {
447 this.qualityGateStatus = requireNonNull(qualityGateStatus, "QualityGateStatus can not be set to null");
451 public NewMeasureBuilder setVariation(double variation) {
452 this.variation = variation;
456 public Measure create(boolean value, @Nullable String data) {
457 return createInternal(ValueType.BOOLEAN, value ? 1.0D : 0.0D, data);
460 public Measure create(boolean value) {
461 return create(value, null);
464 public Measure create(int value, @Nullable String data) {
465 return createInternal(ValueType.INT, value, data);
468 public Measure create(int value) {
469 return create(value, null);
472 public Measure create(long value, @Nullable String data) {
473 return createInternal(ValueType.LONG, value, data);
476 public Measure create(long value) {
477 return create(value, null);
480 public Measure create(double value, int decimalScale, @Nullable String data) {
481 checkArgument(!Double.isNaN(value), "NaN is not allowed as a Measure value");
482 double scaledValue = scale(value, decimalScale);
483 return createInternal(ValueType.DOUBLE, scaledValue, data);
486 private Measure createInternal(ValueType type, double value, @Nullable String data) {
487 if (data == null && qualityGateStatus == null && variation == null) {
488 return new ValueMeasureImpl(type, value);
491 return new MeasureImpl(type, value, data, null, qualityGateStatus, variation);
494 public Measure create(double value, int decimalScale) {
495 return create(value, decimalScale, null);
498 public Measure create(String value) {
499 return new MeasureImpl(ValueType.STRING, null, requireNonNull(value), null, qualityGateStatus, variation);
502 public Measure create(Level level) {
503 return new MeasureImpl(ValueType.LEVEL, null, null, requireNonNull(level), qualityGateStatus, variation);
506 public Measure createNoValue() {
507 if (qualityGateStatus == null) {
508 return new NoValueVariationMeasureImpl(variation);
510 return new MeasureImpl(ValueType.NO_VALUE, null, null, null, qualityGateStatus, variation);
513 private static double scale(double value, int decimalScale) {
514 BigDecimal bd = BigDecimal.valueOf(value);
515 return bd.setScale(decimalScale, RoundingMode.HALF_UP).doubleValue();
519 final class UpdateMeasureBuilder {
520 private final Measure source;
521 private QualityGateStatus qualityGateStatus;
522 private Double variation;
524 public UpdateMeasureBuilder(Measure source) {
525 this.source = requireNonNull(source, "Can not create a measure from null");
529 * Sets the QualityGateStatus of the updated Measure to create.
531 * @throws NullPointerException if the specified {@link QualityGateStatus} is {@code null}
532 * @throws UnsupportedOperationException if the source measure already has a {@link QualityGateStatus}
534 public UpdateMeasureBuilder setQualityGateStatus(QualityGateStatus qualityGateStatus) {
535 if (source.hasQualityGateStatus()) {
536 throw new UnsupportedOperationException("QualityGate status can not be changed if already set on source Measure");
538 this.qualityGateStatus = requireNonNull(qualityGateStatus, "QualityGateStatus can not be set to null");
543 * Sets the variation of the updated Measure to create.
545 * @throws UnsupportedOperationException if the source measure already has a variation
547 public UpdateMeasureBuilder setVariation(double variation) {
548 if (source.hasVariation()) {
549 throw new UnsupportedOperationException("Variation can not be changed if already set on source Measure");
551 this.variation = variation;
555 public Measure create() {
557 switch (source.getValueType()) {
559 value = source.getDoubleValue();
562 value = (double) source.getIntValue();
565 value = (double) source.getLongValue();
568 value = source.getBooleanValue() ? 1.0 : 0.0;
575 Level level = source.getValueType() == ValueType.LEVEL ? source.getLevelValue() : null;
576 QualityGateStatus status = source.hasQualityGateStatus() ? source.getQualityGateStatus() : qualityGateStatus;
578 if (source.hasVariation()) {
579 var = source.getVariation();
583 return new MeasureImpl(source.getValueType(), value, source.getData(), level, status, var);