]> source.dussan.org Git - sonarqube.git/blob
1bcc58d75292644d5538cd3ccab311fd9d75798e
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2019 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("Green"),
42     ERROR("Red"),
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 colorName;
51
52     Level(String colorName) {
53       this.colorName = colorName;
54     }
55
56     public String getColorName() {
57       return colorName;
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   static NewMeasureBuilder newMeasureBuilder() {
154     return new NewMeasureBuilder();
155   }
156
157   static UpdateMeasureBuilder updatedMeasureBuilder(Measure measure) {
158     return new UpdateMeasureBuilder(measure);
159   }
160
161   class MeasureImpl implements Measure {
162     private final ValueType valueType;
163     @CheckForNull
164     private final Double value;
165     @CheckForNull
166     private final String data;
167     @CheckForNull
168     private final Level dataLevel;
169     @CheckForNull
170     private final QualityGateStatus qualityGateStatus;
171     @CheckForNull
172     private final Double variation;
173
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);
178       this.data = data;
179       this.dataLevel = dataLevel;
180       this.qualityGateStatus = qualityGateStatus;
181       this.variation = variation;
182     }
183
184     @Override
185     public ValueType getValueType() {
186       return valueType;
187     }
188
189     @Override
190     public boolean getBooleanValue() {
191       checkValueType(ValueType.BOOLEAN, valueType);
192       return value != null && value.intValue() == 1;
193     }
194
195     @Override
196     public int getIntValue() {
197       checkValueType(ValueType.INT, valueType);
198       return value.intValue();
199     }
200
201     @Override
202     public long getLongValue() {
203       checkValueType(ValueType.LONG, valueType);
204       return value.longValue();
205     }
206
207     @Override
208     public double getDoubleValue() {
209       checkValueType(ValueType.DOUBLE, valueType);
210       return value;
211     }
212
213     @Override
214     public String getStringValue() {
215       checkValueType(ValueType.STRING, valueType);
216       return data;
217     }
218
219     @Override
220     public Level getLevelValue() {
221       checkValueType(ValueType.LEVEL, valueType);
222       return dataLevel;
223     }
224
225     @Override
226     public String getData() {
227       return data;
228     }
229
230     @Override
231     public boolean hasQualityGateStatus() {
232       return this.qualityGateStatus != null;
233     }
234
235     @Override
236     public QualityGateStatus getQualityGateStatus() {
237       checkState(qualityGateStatus != null, "Measure does not have an QualityGate status");
238       return this.qualityGateStatus;
239     }
240
241     @Override
242     public boolean hasVariation() {
243       return variation != null;
244     }
245
246     @Override
247     public double getVariation() {
248       checkState(variation != null, "Measure does not have variation");
249       return variation;
250     }
251
252     private static void checkValueType(ValueType expected, ValueType valueType) {
253       if (valueType != expected) {
254         throw new IllegalStateException(
255           String.format(
256             "value can not be converted to %s because current value type is a %s",
257             expected.toString().toLowerCase(Locale.US),
258             valueType));
259       }
260     }
261
262     @Override
263     public String toString() {
264       return com.google.common.base.MoreObjects.toStringHelper(this)
265         .add("valueType", valueType)
266         .add("value", value)
267         .add("data", data)
268         .add("dataLevel", dataLevel)
269         .add("qualityGateStatus", qualityGateStatus)
270         .add("variations", variation)
271         .toString();
272     }
273   }
274
275   class ValueMeasureImpl implements Measure {
276     private final ValueType valueType;
277     @CheckForNull
278     private final Double value;
279
280     private ValueMeasureImpl(ValueType valueType, @Nullable Double value) {
281       this.valueType = valueType;
282       this.value = DoubleCache.intern(value);
283     }
284
285     @Override
286     public ValueType getValueType() {
287       return valueType;
288     }
289
290     @Override
291     public boolean getBooleanValue() {
292       MeasureImpl.checkValueType(ValueType.BOOLEAN, valueType);
293       return value != null && value.intValue() == 1;
294     }
295
296     @Override
297     public int getIntValue() {
298       MeasureImpl.checkValueType(ValueType.INT, valueType);
299       return value.intValue();
300     }
301
302     @Override
303     public long getLongValue() {
304       MeasureImpl.checkValueType(ValueType.LONG, valueType);
305       return value.longValue();
306     }
307
308     @Override
309     public double getDoubleValue() {
310       MeasureImpl.checkValueType(ValueType.DOUBLE, valueType);
311       return value;
312     }
313
314     @Override
315     public String getStringValue() {
316       throw new IllegalStateException();
317     }
318
319     @Override
320     public Level getLevelValue() {
321       throw new IllegalStateException();
322     }
323
324     @Override
325     public String getData() {
326       return null;
327     }
328
329     @Override
330     public boolean hasQualityGateStatus() {
331       return false;
332     }
333
334     @Override
335     public QualityGateStatus getQualityGateStatus() {
336       throw new IllegalStateException("Measure does not have an QualityGate status");
337     }
338
339     @Override
340     public boolean hasVariation() {
341       return false;
342     }
343
344     @Override
345     public double getVariation() {
346       throw new IllegalStateException("Measure does not have variation");
347
348     }
349
350     @Override
351     public String toString() {
352       return com.google.common.base.MoreObjects.toStringHelper(this)
353         .add("valueType", valueType)
354         .add("value", value)
355         .toString();
356     }
357   }
358
359   class NoValueVariationMeasureImpl implements Measure {
360     @Nullable
361     private Double variation;
362     private ValueType valueType = ValueType.NO_VALUE;
363
364     private NoValueVariationMeasureImpl(@Nullable Double variation) {
365       this.variation = variation;
366     }
367
368     @Override
369     public ValueType getValueType() {
370       return ValueType.NO_VALUE;
371     }
372
373     @Override
374     public boolean getBooleanValue() {
375       throw new IllegalStateException();
376     }
377
378     @Override
379     public int getIntValue() {
380       throw new IllegalStateException();
381     }
382
383     @Override
384     public long getLongValue() {
385       throw new IllegalStateException();
386     }
387
388     @Override
389     public double getDoubleValue() {
390       throw new IllegalStateException();
391     }
392
393     @Override
394     public String getStringValue() {
395       throw new IllegalStateException();
396     }
397
398     @Override
399     public Level getLevelValue() {
400       throw new IllegalStateException();
401
402     }
403
404     @Override
405     public String getData() {
406       return null;
407     }
408
409     @Override
410     public boolean hasQualityGateStatus() {
411       return false;
412     }
413
414     @Override
415     public QualityGateStatus getQualityGateStatus() {
416       throw new IllegalStateException("Measure does not have an QualityGate status");
417     }
418
419     @Override
420     public boolean hasVariation() {
421       return variation != null;
422     }
423
424     @Override
425     public double getVariation() {
426       checkState(variation != null, "Measure does not have variation");
427       return variation;
428     }
429
430     @Override
431     public String toString() {
432       return com.google.common.base.MoreObjects.toStringHelper(this)
433         .add("valueType", valueType)
434         .add("variations", variation)
435         .toString();
436     }
437   }
438
439   class NewMeasureBuilder {
440     private QualityGateStatus qualityGateStatus;
441     private Double variation;
442
443     public NewMeasureBuilder setQualityGateStatus(QualityGateStatus qualityGateStatus) {
444       this.qualityGateStatus = requireNonNull(qualityGateStatus, "QualityGateStatus can not be set to null");
445       return this;
446     }
447
448     public NewMeasureBuilder setVariation(double variation) {
449       this.variation = variation;
450       return this;
451     }
452
453     public Measure create(boolean value, @Nullable String data) {
454       return createInternal(ValueType.BOOLEAN, value ? 1.0D : 0.0D, data);
455     }
456
457     public Measure create(boolean value) {
458       return create(value, null);
459     }
460
461     public Measure create(int value, @Nullable String data) {
462       return createInternal(ValueType.INT, (double) value, data);
463     }
464
465     public Measure create(int value) {
466       return create(value, null);
467     }
468
469     public Measure create(long value, @Nullable String data) {
470       return createInternal(ValueType.LONG, (double) value, data);
471     }
472
473     public Measure create(long value) {
474       return create(value, null);
475     }
476
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);
481     }
482
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);
486
487       }
488       return new MeasureImpl(type, value, data, null, qualityGateStatus, variation);
489     }
490
491     public Measure create(double value, int decimalScale) {
492       return create(value, decimalScale, null);
493     }
494
495     public Measure create(String value) {
496       return new MeasureImpl(ValueType.STRING, null, requireNonNull(value), null, qualityGateStatus, variation);
497     }
498
499     public Measure create(Level level) {
500       return new MeasureImpl(ValueType.LEVEL, null, null, requireNonNull(level), qualityGateStatus, variation);
501     }
502
503     public Measure createNoValue() {
504       if (qualityGateStatus == null) {
505         return new NoValueVariationMeasureImpl(variation);
506       }
507       return new MeasureImpl(ValueType.NO_VALUE, null, null, null, qualityGateStatus, variation);
508     }
509
510     private static double scale(double value, int decimalScale) {
511       BigDecimal bd = BigDecimal.valueOf(value);
512       return bd.setScale(decimalScale, RoundingMode.HALF_UP).doubleValue();
513     }
514   }
515
516   final class UpdateMeasureBuilder {
517     private final Measure source;
518     private QualityGateStatus qualityGateStatus;
519     private Double variation;
520
521     public UpdateMeasureBuilder(Measure source) {
522       this.source = requireNonNull(source, "Can not create a measure from null");
523     }
524
525     /**
526      * Sets the QualityGateStatus of the updated Measure to create.
527      *
528      * @throws NullPointerException          if the specified {@link QualityGateStatus} is {@code null}
529      * @throws UnsupportedOperationException if the source measure already has a {@link QualityGateStatus}
530      */
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");
534       }
535       this.qualityGateStatus = requireNonNull(qualityGateStatus, "QualityGateStatus can not be set to null");
536       return this;
537     }
538
539     /**
540      * Sets the variation of the updated Measure to create.
541      *
542      * @throws UnsupportedOperationException if the source measure already has a variation
543      */
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");
547       }
548       this.variation = variation;
549       return this;
550     }
551
552     public Measure create() {
553       Double value;
554       switch (source.getValueType()) {
555         case DOUBLE:
556           value = source.getDoubleValue();
557           break;
558         case INT:
559           value = (double) source.getIntValue();
560           break;
561         case LONG:
562           value = (double) source.getLongValue();
563           break;
564         case BOOLEAN:
565           value = source.getBooleanValue() ? 1.0 : 0.0;
566           break;
567         case NO_VALUE:
568         default:
569           value = null;
570           break;
571       }
572       Level level = source.getValueType() == ValueType.LEVEL ? source.getLevelValue() : null;
573       QualityGateStatus status = source.hasQualityGateStatus() ? source.getQualityGateStatus() : qualityGateStatus;
574       Double var;
575       if (source.hasVariation()) {
576         var = source.getVariation();
577       } else {
578         var = variation;
579       }
580       return new MeasureImpl(source.getValueType(), value, source.getData(), level, status, var);
581     }
582   }
583 }