/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.performance.measure; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collections; import java.util.List; public abstract class Units<Q> implements Comparable<Units<Q>> { private final Class<Q> type; private final String displaySingular; private final String displayPlural; protected Units(Class<Q> type, String displaySingular, String displayPlural) { this.type = type; this.displaySingular = displaySingular; this.displayPlural = displayPlural; } /** * Creates the base units for a given quantity. */ public static <Q> Units<Q> base(Class<Q> type, String displaySingular) { return new BaseUnits<Q>(type, displaySingular, displaySingular); } /** * Creates the base units for a given quantity. */ public static <Q> Units<Q> base(Class<Q> type, String displaySingular, String displayPlural) { return new BaseUnits<Q>(type, displaySingular, displayPlural); } protected Class<Q> getType() { return type; } @Override public String toString() { return displayPlural; } /** * Creates units that are some multiple of this units. */ public abstract Units<Q> times(long value, String displaySingular, String displayPlural); /** * Creates units that are some multiple of this units. */ public Units<Q> times(long value, String displaySingular) { return times(value, displaySingular, displaySingular); } protected abstract Units<Q> getBaseUnits(); protected abstract List<Units<Q>> getUnitsForQuantity(); protected abstract BigDecimal getFactor(); protected abstract BigDecimal scaleTo(BigDecimal value, Units<Q> units); protected String format(BigDecimal value) { return value.compareTo(BigDecimal.ONE) == 0 ? displaySingular : displayPlural; } private static class BaseUnits<Q> extends Units<Q> { private final List<Units<Q>> units = new ArrayList<Units<Q>>(); protected BaseUnits(Class<Q> type, String displaySingular, String displayPlural) { super(type, displaySingular, displayPlural); units.add(this); } @Override public BigDecimal scaleTo(BigDecimal value, Units<Q> units) { if (units == this) { return value; } ScaledUnits<Q> scaledUnits = (ScaledUnits<Q>) units; return value.divide(scaledUnits.factor, 6, RoundingMode.HALF_UP).stripTrailingZeros(); } @Override protected List<Units<Q>> getUnitsForQuantity() { return units; } @Override public Units<Q> times(long value, String displaySingular, String displayPlural) { return new ScaledUnits<Q>(this, displaySingular, displayPlural, BigDecimal.valueOf(value)); } @Override protected Units<Q> getBaseUnits() { return this; } @Override protected BigDecimal getFactor() { return BigDecimal.ONE; } public int compareTo(Units<Q> o) { if (o == this) { return 0; } if (o.getType() != getType()) { throw new IllegalArgumentException(String.format("Cannot compare units of %s with units of %s.", getType(), o.getType())); } return -1; } public void add(ScaledUnits<Q> units) { this.units.add(units); Collections.sort(this.units); } } private static class ScaledUnits<Q> extends Units<Q> { private final BaseUnits<Q> baseUnits; private final BigDecimal factor; public ScaledUnits(BaseUnits<Q> baseUnits, String displaySingular, String displayPlural, BigDecimal factor) { super(baseUnits.getType(), displaySingular, displayPlural); assert factor.compareTo(BigDecimal.ONE) > 0; this.baseUnits = baseUnits; this.factor = factor; baseUnits.add(this); } @Override public Units<Q> times(long value, String displaySingular, String displayPlural) { return new ScaledUnits<Q>(baseUnits, displaySingular, displayPlural, factor.multiply(BigDecimal.valueOf(value))); } @Override public BigDecimal scaleTo(BigDecimal value, Units<Q> units) { if (units == this) { return value; } if (units.equals(baseUnits)) { return value.multiply(factor); } ScaledUnits<Q> other = (ScaledUnits<Q>) units; return value.multiply(factor).divide(other.factor, 6, RoundingMode.HALF_UP).stripTrailingZeros(); } @Override protected List<Units<Q>> getUnitsForQuantity() { return baseUnits.getUnitsForQuantity(); } @Override protected Units<Q> getBaseUnits() { return baseUnits; } @Override protected BigDecimal getFactor() { return factor; } public int compareTo(Units<Q> o) { if (o.getType() != getType()) { throw new IllegalArgumentException(String.format("Cannot compare units of %s with units of %s.", getType(), o.getType())); } if (o.equals(baseUnits)) { return 1; } ScaledUnits<Q> other = (ScaledUnits<Q>) o; if (!other.baseUnits.equals(baseUnits)) { throw new IllegalArgumentException("Cannot compare units with different base units."); } return factor.compareTo(other.factor); } } }