]> source.dussan.org Git - sonarqube.git/blob
4159f8c57bd0039bd8334481eadf76a351c72195
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 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.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;
29
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;
33
34 public interface Measure {
35
36   enum ValueType {
37     NO_VALUE, BOOLEAN, INT, LONG, DOUBLE, STRING, LEVEL
38   }
39
40   enum Level {
41     OK("Passed"),
42     ERROR("Failed"),
43
44     /**
45      * @deprecated since 7.6, warning quality gates doesn't exist anymore on new analysis
46      */
47     @Deprecated
48     WARN("Orange");
49
50     private final String label;
51
52     Level(String label) {
53       this.label = label;
54     }
55
56     public String getLabel() {
57       return label;
58     }
59
60     public static Optional<Level> toLevel(@Nullable String level) {
61       if (level == null) {
62         return Optional.empty();
63       }
64
65       try {
66         return Optional.of(Level.valueOf(level));
67       } catch (IllegalArgumentException e) {
68         return Optional.empty();
69       }
70     }
71   }
72
73   /**
74    * The type of value stored in the measure.
75    */
76   ValueType getValueType();
77
78   /**
79    * The value of this measure as a boolean if the type is {@link Measure.ValueType#BOOLEAN}.
80    *
81    * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#BOOLEAN}
82    */
83   boolean getBooleanValue();
84
85   /**
86    * The value of this measure as a int if the type is {@link Measure.ValueType#INT}.
87    *
88    * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#INT}
89    */
90   int getIntValue();
91
92   /**
93    * The value of this measure as a long if the type is {@link Measure.ValueType#LONG}.
94    *
95    * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LONG}
96    */
97   long getLongValue();
98
99   /**
100    * The value of this measure as a double if the type is {@link Measure.ValueType#DOUBLE}.
101    *
102    * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#DOUBLE}
103    */
104   double getDoubleValue();
105
106   /**
107    * The value of this measure as a String if the type is {@link Measure.ValueType#STRING}.
108    *
109    * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#STRING}
110    */
111   String getStringValue();
112
113   /**
114    * The value of this measure as a Level if the type is {@link Measure.ValueType#LEVEL}.
115    *
116    * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LEVEL}
117    */
118   Level getLevelValue();
119
120   /**
121    * The data of this measure if it exists.
122    * <p>
123    * If the measure type is {@link Measure.ValueType#STRING}, the value returned by this function is the same as {@link #getStringValue()}.
124    * </p>
125    */
126   String getData();
127
128   /**
129    * Any Measure, which ever is its value type, can have a QualityGate status.
130    */
131   boolean hasQualityGateStatus();
132
133   /**
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>
136    *
137    * @throws IllegalStateException if the measure has no QualityGate status
138    */
139   QualityGateStatus getQualityGateStatus();
140
141   /**
142    * Any Measure, which ever is its value type, can have a variation.
143    */
144   boolean hasVariation();
145
146   /**
147    * The variation of this measure.
148    *
149    * @throws IllegalStateException if the measure has no variation
150    */
151   double getVariation();
152
153   default boolean isEmpty() {
154     return getValueType() == ValueType.NO_VALUE && !hasVariation() && getData() == null;
155   }
156
157   static NewMeasureBuilder newMeasureBuilder() {
158     return new NewMeasureBuilder();
159   }
160
161   static UpdateMeasureBuilder updatedMeasureBuilder(Measure measure) {
162     return new UpdateMeasureBuilder(measure);
163   }
164
165   class MeasureImpl implements Measure {
166     private final ValueType valueType;
167     @CheckForNull
168     private final Double value;
169     @CheckForNull
170     private final String data;
171     @CheckForNull
172     private final Level dataLevel;
173     @CheckForNull
174     private final QualityGateStatus qualityGateStatus;
175     @CheckForNull
176     private final Double variation;
177
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);
182       this.data = data;
183       this.dataLevel = dataLevel;
184       this.qualityGateStatus = qualityGateStatus;
185       this.variation = variation;
186     }
187
188     @Override
189     public ValueType getValueType() {
190       return valueType;
191     }
192
193     @Override
194     public boolean getBooleanValue() {
195       checkValueType(ValueType.BOOLEAN, valueType);
196       return value != null && value.intValue() == 1;
197     }
198
199     @Override
200     public int getIntValue() {
201       checkValueType(ValueType.INT, valueType);
202       return value.intValue();
203     }
204
205     @Override
206     public long getLongValue() {
207       checkValueType(ValueType.LONG, valueType);
208       return value.longValue();
209     }
210
211     @Override
212     public double getDoubleValue() {
213       checkValueType(ValueType.DOUBLE, valueType);
214       return value;
215     }
216
217     @Override
218     public String getStringValue() {
219       checkValueType(ValueType.STRING, valueType);
220       return data;
221     }
222
223     @Override
224     public Level getLevelValue() {
225       checkValueType(ValueType.LEVEL, valueType);
226       return dataLevel;
227     }
228
229     @Override
230     public String getData() {
231       return data;
232     }
233
234     @Override
235     public boolean hasQualityGateStatus() {
236       return this.qualityGateStatus != null;
237     }
238
239     @Override
240     public QualityGateStatus getQualityGateStatus() {
241       checkState(qualityGateStatus != null, "Measure does not have an QualityGate status");
242       return this.qualityGateStatus;
243     }
244
245     @Override
246     public boolean hasVariation() {
247       return variation != null;
248     }
249
250     @Override
251     public double getVariation() {
252       checkState(variation != null, "Measure does not have variation");
253       return variation;
254     }
255
256     private static void checkValueType(ValueType expected, ValueType valueType) {
257       if (valueType != expected) {
258         throw new IllegalStateException(
259           String.format(
260             "value can not be converted to %s because current value type is a %s",
261             expected.toString().toLowerCase(Locale.US),
262             valueType));
263       }
264     }
265
266     @Override
267     public String toString() {
268       return com.google.common.base.MoreObjects.toStringHelper(this)
269         .add("valueType", valueType)
270         .add("value", value)
271         .add("data", data)
272         .add("dataLevel", dataLevel)
273         .add("qualityGateStatus", qualityGateStatus)
274         .add("variations", variation)
275         .toString();
276     }
277   }
278
279   class ValueMeasureImpl implements Measure {
280     private final ValueType valueType;
281     @CheckForNull
282     private final Double value;
283
284     private ValueMeasureImpl(ValueType valueType, @Nullable Double value) {
285       this.valueType = valueType;
286       this.value = DoubleCache.intern(value);
287     }
288
289     @Override
290     public ValueType getValueType() {
291       return valueType;
292     }
293
294     @Override
295     public boolean getBooleanValue() {
296       MeasureImpl.checkValueType(ValueType.BOOLEAN, valueType);
297       return value != null && value.intValue() == 1;
298     }
299
300     @Override
301     public int getIntValue() {
302       MeasureImpl.checkValueType(ValueType.INT, valueType);
303       return value.intValue();
304     }
305
306     @Override
307     public long getLongValue() {
308       MeasureImpl.checkValueType(ValueType.LONG, valueType);
309       return value.longValue();
310     }
311
312     @Override
313     public double getDoubleValue() {
314       MeasureImpl.checkValueType(ValueType.DOUBLE, valueType);
315       return value;
316     }
317
318     @Override
319     public String getStringValue() {
320       throw new IllegalStateException();
321     }
322
323     @Override
324     public Level getLevelValue() {
325       throw new IllegalStateException();
326     }
327
328     @Override
329     public String getData() {
330       return null;
331     }
332
333     @Override
334     public boolean hasQualityGateStatus() {
335       return false;
336     }
337
338     @Override
339     public QualityGateStatus getQualityGateStatus() {
340       throw new IllegalStateException("Measure does not have an QualityGate status");
341     }
342
343     @Override
344     public boolean hasVariation() {
345       return false;
346     }
347
348     @Override
349     public double getVariation() {
350       throw new IllegalStateException("Measure does not have variation");
351
352     }
353
354     @Override
355     public String toString() {
356       return com.google.common.base.MoreObjects.toStringHelper(this)
357         .add("valueType", valueType)
358         .add("value", value)
359         .toString();
360     }
361   }
362
363   class NoValueVariationMeasureImpl implements Measure {
364     @Nullable
365     private Double variation;
366
367     private NoValueVariationMeasureImpl(@Nullable Double variation) {
368       this.variation = variation;
369     }
370
371     @Override
372     public ValueType getValueType() {
373       return ValueType.NO_VALUE;
374     }
375
376     @Override
377     public boolean getBooleanValue() {
378       throw new IllegalStateException();
379     }
380
381     @Override
382     public int getIntValue() {
383       throw new IllegalStateException();
384     }
385
386     @Override
387     public long getLongValue() {
388       throw new IllegalStateException();
389     }
390
391     @Override
392     public double getDoubleValue() {
393       throw new IllegalStateException();
394     }
395
396     @Override
397     public String getStringValue() {
398       throw new IllegalStateException();
399     }
400
401     @Override
402     public Level getLevelValue() {
403       throw new IllegalStateException();
404
405     }
406
407     @Override
408     public String getData() {
409       return null;
410     }
411
412     @Override
413     public boolean hasQualityGateStatus() {
414       return false;
415     }
416
417     @Override
418     public QualityGateStatus getQualityGateStatus() {
419       throw new IllegalStateException("Measure does not have an QualityGate status");
420     }
421
422     @Override
423     public boolean hasVariation() {
424       return variation != null;
425     }
426
427     @Override
428     public double getVariation() {
429       checkState(variation != null, "Measure does not have variation");
430       return variation;
431     }
432
433     @Override
434     public String toString() {
435       return com.google.common.base.MoreObjects.toStringHelper(this)
436         .add("valueType", ValueType.NO_VALUE)
437         .add("variations", variation)
438         .toString();
439     }
440   }
441
442   class NewMeasureBuilder {
443     private QualityGateStatus qualityGateStatus;
444     private Double variation;
445
446     public NewMeasureBuilder setQualityGateStatus(QualityGateStatus qualityGateStatus) {
447       this.qualityGateStatus = requireNonNull(qualityGateStatus, "QualityGateStatus can not be set to null");
448       return this;
449     }
450
451     public NewMeasureBuilder setVariation(double variation) {
452       this.variation = variation;
453       return this;
454     }
455
456     public Measure create(boolean value, @Nullable String data) {
457       return createInternal(ValueType.BOOLEAN, value ? 1.0D : 0.0D, data);
458     }
459
460     public Measure create(boolean value) {
461       return create(value, null);
462     }
463
464     public Measure create(int value, @Nullable String data) {
465       return createInternal(ValueType.INT, value, data);
466     }
467
468     public Measure create(int value) {
469       return create(value, null);
470     }
471
472     public Measure create(long value, @Nullable String data) {
473       return createInternal(ValueType.LONG, value, data);
474     }
475
476     public Measure create(long value) {
477       return create(value, null);
478     }
479
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);
484     }
485
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);
489
490       }
491       return new MeasureImpl(type, value, data, null, qualityGateStatus, variation);
492     }
493
494     public Measure create(double value, int decimalScale) {
495       return create(value, decimalScale, null);
496     }
497
498     public Measure create(String value) {
499       return new MeasureImpl(ValueType.STRING, null, requireNonNull(value), null, qualityGateStatus, variation);
500     }
501
502     public Measure create(Level level) {
503       return new MeasureImpl(ValueType.LEVEL, null, null, requireNonNull(level), qualityGateStatus, variation);
504     }
505
506     public Measure createNoValue() {
507       if (qualityGateStatus == null) {
508         return new NoValueVariationMeasureImpl(variation);
509       }
510       return new MeasureImpl(ValueType.NO_VALUE, null, null, null, qualityGateStatus, variation);
511     }
512
513     private static double scale(double value, int decimalScale) {
514       BigDecimal bd = BigDecimal.valueOf(value);
515       return bd.setScale(decimalScale, RoundingMode.HALF_UP).doubleValue();
516     }
517   }
518
519   final class UpdateMeasureBuilder {
520     private final Measure source;
521     private QualityGateStatus qualityGateStatus;
522     private Double variation;
523
524     public UpdateMeasureBuilder(Measure source) {
525       this.source = requireNonNull(source, "Can not create a measure from null");
526     }
527
528     /**
529      * Sets the QualityGateStatus of the updated Measure to create.
530      *
531      * @throws NullPointerException          if the specified {@link QualityGateStatus} is {@code null}
532      * @throws UnsupportedOperationException if the source measure already has a {@link QualityGateStatus}
533      */
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");
537       }
538       this.qualityGateStatus = requireNonNull(qualityGateStatus, "QualityGateStatus can not be set to null");
539       return this;
540     }
541
542     /**
543      * Sets the variation of the updated Measure to create.
544      *
545      * @throws UnsupportedOperationException if the source measure already has a variation
546      */
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");
550       }
551       this.variation = variation;
552       return this;
553     }
554
555     public Measure create() {
556       Double value;
557       switch (source.getValueType()) {
558         case DOUBLE:
559           value = source.getDoubleValue();
560           break;
561         case INT:
562           value = (double) source.getIntValue();
563           break;
564         case LONG:
565           value = (double) source.getLongValue();
566           break;
567         case BOOLEAN:
568           value = source.getBooleanValue() ? 1.0 : 0.0;
569           break;
570         case NO_VALUE:
571         default:
572           value = null;
573           break;
574       }
575       Level level = source.getValueType() == ValueType.LEVEL ? source.getLevelValue() : null;
576       QualityGateStatus status = source.hasQualityGateStatus() ? source.getQualityGateStatus() : qualityGateStatus;
577       Double var;
578       if (source.hasVariation()) {
579         var = source.getVariation();
580       } else {
581         var = variation;
582       }
583       return new MeasureImpl(source.getValueType(), value, source.getData(), level, status, var);
584     }
585   }
586 }