]> source.dussan.org Git - sonarqube.git/commitdiff
Create memory efficient implementations of Measures
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Tue, 30 Jul 2019 20:41:10 +0000 (15:41 -0500)
committerSonarTech <sonartech@sonarsource.com>
Wed, 4 Sep 2019 18:21:05 +0000 (20:21 +0200)
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/measure/Measure.java

index 5b4872f962d087ba3a097acd0bf1e1d625861368..8d73b7e9669adbbe67f1f670ae658acbb058b71f 100644 (file)
@@ -31,13 +31,13 @@ import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
 
-public final class Measure {
+public interface Measure {
 
-  public enum ValueType {
+  enum ValueType {
     NO_VALUE, BOOLEAN, INT, LONG, DOUBLE, STRING, LEVEL
   }
 
-  public enum Level {
+  enum Level {
     OK("Green"),
     ERROR("Red"),
 
@@ -70,37 +70,373 @@ public final class Measure {
     }
   }
 
-  private final ValueType valueType;
-  @CheckForNull
-  private final Double value;
-  @CheckForNull
-  private final String data;
-  @CheckForNull
-  private final Level dataLevel;
-  @CheckForNull
-  private final QualityGateStatus qualityGateStatus;
-  @CheckForNull
-  private final Double variation;
-
-  private Measure(ValueType valueType, @Nullable Double value, @Nullable String data, @Nullable Level dataLevel,
-    @Nullable QualityGateStatus qualityGateStatus, @Nullable Double variation) {
-    this.valueType = valueType;
-    this.value = DoubleCache.intern(value);
-    this.data = data;
-    this.dataLevel = dataLevel;
-    this.qualityGateStatus = qualityGateStatus;
-    this.variation = variation;
-  }
+  /**
+   * The type of value stored in the measure.
+   */
+  ValueType getValueType();
+
+  /**
+   * The value of this measure as a boolean if the type is {@link Measure.ValueType#BOOLEAN}.
+   *
+   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#BOOLEAN}
+   */
+  boolean getBooleanValue();
+
+  /**
+   * The value of this measure as a int if the type is {@link Measure.ValueType#INT}.
+   *
+   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#INT}
+   */
+  int getIntValue();
+
+  /**
+   * The value of this measure as a long if the type is {@link Measure.ValueType#LONG}.
+   *
+   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LONG}
+   */
+  long getLongValue();
+
+  /**
+   * The value of this measure as a double if the type is {@link Measure.ValueType#DOUBLE}.
+   *
+   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#DOUBLE}
+   */
+  double getDoubleValue();
+
+  /**
+   * The value of this measure as a String if the type is {@link Measure.ValueType#STRING}.
+   *
+   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#STRING}
+   */
+  String getStringValue();
+
+  /**
+   * The value of this measure as a Level if the type is {@link Measure.ValueType#LEVEL}.
+   *
+   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LEVEL}
+   */
+  Level getLevelValue();
+
+  /**
+   * The data of this measure if it exists.
+   * <p>
+   * If the measure type is {@link Measure.ValueType#STRING}, the value returned by this function is the same as {@link #getStringValue()}.
+   * </p>
+   */
+  String getData();
+
+  /**
+   * Any Measure, which ever is its value type, can have a QualityGate status.
+   */
+  boolean hasQualityGateStatus();
+
+  /**
+   * The QualityGate status for this measure.
+   * <strong>Don't call this method unless you've checked the result of {@link #hasQualityGateStatus()} first</strong>
+   *
+   * @throws IllegalStateException if the measure has no QualityGate status
+   */
+  QualityGateStatus getQualityGateStatus();
+
+  /**
+   * Any Measure, which ever is its value type, can have a variation.
+   */
+  boolean hasVariation();
+
+  /**
+   * The variation of this measure.
+   *
+   * @throws IllegalStateException if the measure has no variation
+   */
+  double getVariation();
 
-  public static NewMeasureBuilder newMeasureBuilder() {
+  static NewMeasureBuilder newMeasureBuilder() {
     return new NewMeasureBuilder();
   }
 
-  public static UpdateMeasureBuilder updatedMeasureBuilder(Measure measure) {
+  static UpdateMeasureBuilder updatedMeasureBuilder(Measure measure) {
     return new UpdateMeasureBuilder(measure);
   }
 
-  public static final class NewMeasureBuilder {
+  class MeasureImpl implements Measure {
+    private final ValueType valueType;
+    @CheckForNull
+    private final Double value;
+    @CheckForNull
+    private final String data;
+    @CheckForNull
+    private final Level dataLevel;
+    @CheckForNull
+    private final QualityGateStatus qualityGateStatus;
+    @CheckForNull
+    private final Double variation;
+
+    private MeasureImpl(ValueType valueType, @Nullable Double value, @Nullable String data, @Nullable Level dataLevel,
+      @Nullable QualityGateStatus qualityGateStatus, @Nullable Double variation) {
+      this.valueType = valueType;
+      this.value = DoubleCache.intern(value);
+      this.data = data;
+      this.dataLevel = dataLevel;
+      this.qualityGateStatus = qualityGateStatus;
+      this.variation = variation;
+    }
+
+    @Override
+    public ValueType getValueType() {
+      return valueType;
+    }
+
+    @Override
+    public boolean getBooleanValue() {
+      checkValueType(ValueType.BOOLEAN, valueType);
+      return value != null && value.intValue() == 1;
+    }
+
+    @Override
+    public int getIntValue() {
+      checkValueType(ValueType.INT, valueType);
+      return value.intValue();
+    }
+
+    @Override
+    public long getLongValue() {
+      checkValueType(ValueType.LONG, valueType);
+      return value.longValue();
+    }
+
+    @Override
+    public double getDoubleValue() {
+      checkValueType(ValueType.DOUBLE, valueType);
+      return value;
+    }
+
+    @Override
+    public String getStringValue() {
+      checkValueType(ValueType.STRING, valueType);
+      return data;
+    }
+
+    @Override
+    public Level getLevelValue() {
+      checkValueType(ValueType.LEVEL, valueType);
+      return dataLevel;
+    }
+
+    @Override
+    public String getData() {
+      return data;
+    }
+
+    @Override
+    public boolean hasQualityGateStatus() {
+      return this.qualityGateStatus != null;
+    }
+
+    @Override
+    public QualityGateStatus getQualityGateStatus() {
+      checkState(qualityGateStatus != null, "Measure does not have an QualityGate status");
+      return this.qualityGateStatus;
+    }
+
+    @Override
+    public boolean hasVariation() {
+      return variation != null;
+    }
+
+    @Override
+    public double getVariation() {
+      checkState(variation != null, "Measure does not have variation");
+      return variation;
+    }
+
+    private static void checkValueType(ValueType expected, ValueType valueType) {
+      if (valueType != expected) {
+        throw new IllegalStateException(
+          String.format(
+            "value can not be converted to %s because current value type is a %s",
+            expected.toString().toLowerCase(Locale.US),
+            valueType));
+      }
+    }
+
+    @Override
+    public String toString() {
+      return com.google.common.base.MoreObjects.toStringHelper(this)
+        .add("valueType", valueType)
+        .add("value", value)
+        .add("data", data)
+        .add("dataLevel", dataLevel)
+        .add("qualityGateStatus", qualityGateStatus)
+        .add("variations", variation)
+        .toString();
+    }
+  }
+
+  class ValueMeasureImpl implements Measure {
+    private final ValueType valueType;
+    @CheckForNull
+    private final Double value;
+
+    private ValueMeasureImpl(ValueType valueType, @Nullable Double value) {
+      this.valueType = valueType;
+      this.value = DoubleCache.intern(value);
+    }
+
+    @Override
+    public ValueType getValueType() {
+      return valueType;
+    }
+
+    @Override
+    public boolean getBooleanValue() {
+      MeasureImpl.checkValueType(ValueType.BOOLEAN, valueType);
+      return value != null && value.intValue() == 1;
+    }
+
+    @Override
+    public int getIntValue() {
+      MeasureImpl.checkValueType(ValueType.INT, valueType);
+      return value.intValue();
+    }
+
+    @Override
+    public long getLongValue() {
+      MeasureImpl.checkValueType(ValueType.LONG, valueType);
+      return value.longValue();
+    }
+
+    @Override
+    public double getDoubleValue() {
+      MeasureImpl.checkValueType(ValueType.DOUBLE, valueType);
+      return value;
+    }
+
+    @Override
+    public String getStringValue() {
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public Level getLevelValue() {
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public String getData() {
+      return null;
+    }
+
+    @Override
+    public boolean hasQualityGateStatus() {
+      return false;
+    }
+
+    @Override
+    public QualityGateStatus getQualityGateStatus() {
+      throw new IllegalStateException("Measure does not have an QualityGate status");
+    }
+
+    @Override
+    public boolean hasVariation() {
+      return false;
+    }
+
+    @Override
+    public double getVariation() {
+      throw new IllegalStateException("Measure does not have variation");
+
+    }
+
+    @Override
+    public String toString() {
+      return com.google.common.base.MoreObjects.toStringHelper(this)
+        .add("valueType", valueType)
+        .add("value", value)
+        .toString();
+    }
+  }
+
+  class NoValueVariationMeasureImpl implements Measure {
+    @Nullable
+    private Double variation;
+    private ValueType valueType = ValueType.NO_VALUE;
+
+    private NoValueVariationMeasureImpl(@Nullable Double variation) {
+      this.variation = variation;
+    }
+
+    @Override
+    public ValueType getValueType() {
+      return ValueType.NO_VALUE;
+    }
+
+    @Override
+    public boolean getBooleanValue() {
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public int getIntValue() {
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public long getLongValue() {
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public double getDoubleValue() {
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public String getStringValue() {
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public Level getLevelValue() {
+      throw new IllegalStateException();
+
+    }
+
+    @Override
+    public String getData() {
+      return null;
+    }
+
+    @Override
+    public boolean hasQualityGateStatus() {
+      return false;
+    }
+
+    @Override
+    public QualityGateStatus getQualityGateStatus() {
+      throw new IllegalStateException("Measure does not have an QualityGate status");
+    }
+
+    @Override
+    public boolean hasVariation() {
+      return variation != null;
+    }
+
+    @Override
+    public double getVariation() {
+      checkState(variation != null, "Measure does not have variation");
+      return variation;
+    }
+
+    @Override
+    public String toString() {
+      return com.google.common.base.MoreObjects.toStringHelper(this)
+        .add("valueType", valueType)
+        .add("variations", variation)
+        .toString();
+    }
+  }
+
+  class NewMeasureBuilder {
     private QualityGateStatus qualityGateStatus;
     private Double variation;
 
@@ -115,7 +451,7 @@ public final class Measure {
     }
 
     public Measure create(boolean value, @Nullable String data) {
-      return new Measure(ValueType.BOOLEAN, value ? 1.0D : 0.0D, data, null, qualityGateStatus, variation);
+      return createInternal(ValueType.BOOLEAN, value ? 1.0D : 0.0D, data);
     }
 
     public Measure create(boolean value) {
@@ -123,7 +459,7 @@ public final class Measure {
     }
 
     public Measure create(int value, @Nullable String data) {
-      return new Measure(ValueType.INT, (double) value, data, null, qualityGateStatus, variation);
+      return createInternal(ValueType.INT, (double) value, data);
     }
 
     public Measure create(int value) {
@@ -131,7 +467,7 @@ public final class Measure {
     }
 
     public Measure create(long value, @Nullable String data) {
-      return new Measure(ValueType.LONG, (double) value, data, null, qualityGateStatus, variation);
+      return createInternal(ValueType.LONG, (double) value, data);
     }
 
     public Measure create(long value) {
@@ -141,7 +477,15 @@ public final class Measure {
     public Measure create(double value, int decimalScale, @Nullable String data) {
       checkArgument(!Double.isNaN(value), "NaN is not allowed as a Measure value");
       double scaledValue = scale(value, decimalScale);
-      return new Measure(ValueType.DOUBLE, scaledValue, data, null, qualityGateStatus, variation);
+      return createInternal(ValueType.DOUBLE, scaledValue, data);
+    }
+
+    private Measure createInternal(ValueType type, double value, @Nullable String data) {
+      if (data == null && qualityGateStatus == null && variation == null) {
+        return new ValueMeasureImpl(type, value);
+
+      }
+      return new MeasureImpl(type, value, data, null, qualityGateStatus, variation);
     }
 
     public Measure create(double value, int decimalScale) {
@@ -149,15 +493,18 @@ public final class Measure {
     }
 
     public Measure create(String value) {
-      return new Measure(ValueType.STRING, null, requireNonNull(value), null, qualityGateStatus, variation);
+      return new MeasureImpl(ValueType.STRING, null, requireNonNull(value), null, qualityGateStatus, variation);
     }
 
     public Measure create(Level level) {
-      return new Measure(ValueType.LEVEL, null, null, requireNonNull(level), qualityGateStatus, variation);
+      return new MeasureImpl(ValueType.LEVEL, null, null, requireNonNull(level), qualityGateStatus, variation);
     }
 
     public Measure createNoValue() {
-      return new Measure(ValueType.NO_VALUE, null, null, null, qualityGateStatus, variation);
+      if (qualityGateStatus == null) {
+        return new NoValueVariationMeasureImpl(variation);
+      }
+      return new MeasureImpl(ValueType.NO_VALUE, null, null, null, qualityGateStatus, variation);
     }
 
     private static double scale(double value, int decimalScale) {
@@ -166,7 +513,7 @@ public final class Measure {
     }
   }
 
-  public static final class UpdateMeasureBuilder {
+  final class UpdateMeasureBuilder {
     private final Measure source;
     private QualityGateStatus qualityGateStatus;
     private Double variation;
@@ -182,7 +529,7 @@ public final class Measure {
      * @throws UnsupportedOperationException if the source measure already has a {@link QualityGateStatus}
      */
     public UpdateMeasureBuilder setQualityGateStatus(QualityGateStatus qualityGateStatus) {
-      if (source.qualityGateStatus != null) {
+      if (source.hasQualityGateStatus()) {
         throw new UnsupportedOperationException("QualityGate status can not be changed if already set on source Measure");
       }
       this.qualityGateStatus = requireNonNull(qualityGateStatus, "QualityGateStatus can not be set to null");
@@ -195,7 +542,7 @@ public final class Measure {
      * @throws UnsupportedOperationException if the source measure already has a variation
      */
     public UpdateMeasureBuilder setVariation(double variation) {
-      if (source.variation != null) {
+      if (source.hasVariation()) {
         throw new UnsupportedOperationException("Variation can not be changed if already set on source Measure");
       }
       this.variation = variation;
@@ -203,145 +550,31 @@ public final class Measure {
     }
 
     public Measure create() {
-      return new Measure(source.valueType,
-        source.value, source.data, source.dataLevel,
-        source.qualityGateStatus == null ? qualityGateStatus : source.qualityGateStatus,
-        source.variation == null ? variation : source.variation);
-    }
-  }
-
-  /**
-   * The type of value stored in the measure.
-   */
-  public ValueType getValueType() {
-    return valueType;
-  }
-
-  /**
-   * The value of this measure as a boolean if the type is {@link Measure.ValueType#BOOLEAN}.
-   *
-   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#BOOLEAN}
-   */
-  public boolean getBooleanValue() {
-    checkValueType(ValueType.BOOLEAN);
-    return value != null && value.intValue() == 1;
-  }
-
-  /**
-   * The value of this measure as a int if the type is {@link Measure.ValueType#INT}.
-   *
-   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#INT}
-   */
-  public int getIntValue() {
-    checkValueType(ValueType.INT);
-    return value.intValue();
-  }
-
-  /**
-   * The value of this measure as a long if the type is {@link Measure.ValueType#LONG}.
-   *
-   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LONG}
-   */
-  public long getLongValue() {
-    checkValueType(ValueType.LONG);
-    return value.longValue();
-  }
-
-  /**
-   * The value of this measure as a double if the type is {@link Measure.ValueType#DOUBLE}.
-   *
-   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#DOUBLE}
-   */
-  public double getDoubleValue() {
-    checkValueType(ValueType.DOUBLE);
-    return value;
-  }
-
-  /**
-   * The value of this measure as a String if the type is {@link Measure.ValueType#STRING}.
-   *
-   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#STRING}
-   */
-  public String getStringValue() {
-    checkValueType(ValueType.STRING);
-    return data;
-  }
-
-  /**
-   * The value of this measure as a Level if the type is {@link Measure.ValueType#LEVEL}.
-   *
-   * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LEVEL}
-   */
-  public Level getLevelValue() {
-    checkValueType(ValueType.LEVEL);
-    return dataLevel;
-  }
-
-  /**
-   * The data of this measure if it exists.
-   * <p>
-   * If the measure type is {@link Measure.ValueType#STRING}, the value returned by this function is the same as {@link #getStringValue()}.
-   * </p>
-   */
-  public String getData() {
-    return data;
-  }
+      Double value;
+      switch (source.getValueType()) {
+        case DOUBLE:
+          value = source.getDoubleValue();
+          break;
+        case INT:
+          value = (double) source.getIntValue();
+          break;
+        case LONG:
+          value = (double) source.getLongValue();
+          break;
+        case BOOLEAN:
+          value = source.getBooleanValue() ? 1.0 : 0.0;
+          break;
+        case NO_VALUE:
+        default:
+          value = null;
+          break;
+      }
+      Level level = source.getValueType() == ValueType.LEVEL ? source.getLevelValue() : null;
+      QualityGateStatus status = source.hasQualityGateStatus() ? source.getQualityGateStatus() : qualityGateStatus;
+      boolean hasVar = source.hasVariation();
 
-  private void checkValueType(ValueType expected) {
-    if (valueType != expected) {
-      throw new IllegalStateException(
-        String.format(
-          "value can not be converted to %s because current value type is a %s",
-          expected.toString().toLowerCase(Locale.US),
-          valueType));
+      Double var = hasVar ? source.getVariation() : variation;
+      return new MeasureImpl(source.getValueType(), value, source.getData(), level, status, var);
     }
   }
-
-  /**
-   * Any Measure, which ever is its value type, can have a QualityGate status.
-   */
-  public boolean hasQualityGateStatus() {
-    return this.qualityGateStatus != null;
-  }
-
-  /**
-   * The QualityGate status for this measure.
-   * <strong>Don't call this method unless you've checked the result of {@link #hasQualityGateStatus()} first</strong>
-   *
-   * @throws IllegalStateException if the measure has no QualityGate status
-   */
-  public QualityGateStatus getQualityGateStatus() {
-    checkState(qualityGateStatus != null, "Measure does not have an QualityGate status");
-    return this.qualityGateStatus;
-  }
-
-  /**
-   * Any Measure, which ever is its value type, can have a variation.
-   */
-  public boolean hasVariation() {
-    return variation != null;
-  }
-
-  /**
-   * The variation of this measure.
-   *
-   * @throws IllegalStateException if the measure has no variation
-   */
-  public double getVariation() {
-    checkState(variation != null, "Measure does not have variation");
-    return variation;
-  }
-
-  @Override
-  public String toString() {
-    return com.google.common.base.MoreObjects.toStringHelper(this)
-      .add("valueType", valueType)
-      .add("value", value)
-      .add("data", data)
-      .add("dataLevel", dataLevel)
-      .add("qualityGateStatus", qualityGateStatus)
-      .add("variations", variation)
-      .toString();
-  }
-
 }