From f0f1986388884dbadab590bd03b33720a669f0d9 Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Tue, 30 Jul 2019 15:41:10 -0500 Subject: [PATCH] Create memory efficient implementations of Measures --- .../task/projectanalysis/measure/Measure.java | 583 ++++++++++++------ 1 file changed, 408 insertions(+), 175 deletions(-) diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/measure/Measure.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/measure/Measure.java index 5b4872f962d..8d73b7e9669 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/measure/Measure.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/measure/Measure.java @@ -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. + *

+ * If the measure type is {@link Measure.ValueType#STRING}, the value returned by this function is the same as {@link #getStringValue()}. + *

+ */ + String getData(); + + /** + * Any Measure, which ever is its value type, can have a QualityGate status. + */ + boolean hasQualityGateStatus(); + + /** + * The QualityGate status for this measure. + * Don't call this method unless you've checked the result of {@link #hasQualityGateStatus()} first + * + * @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. - *

- * If the measure type is {@link Measure.ValueType#STRING}, the value returned by this function is the same as {@link #getStringValue()}. - *

- */ - 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. - * Don't call this method unless you've checked the result of {@link #hasQualityGateStatus()} first - * - * @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(); - } - } -- 2.39.5