All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tec.uom.se.AbstractQuantity Maven / Gradle / Ivy

/*
 * Units of Measurement Implementation for Java SE
 * Copyright (c) 2005-2018, Jean-Marie Dautelle, Werner Keil, Otavio Santana.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package tec.uom.se;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.Comparator;
import java.util.Objects;

import javax.measure.Quantity;
import javax.measure.Unit;
import javax.measure.UnitConverter;
import javax.measure.quantity.Dimensionless;

import tec.uom.lib.common.function.UnitSupplier;
import tec.uom.lib.common.function.ValueSupplier;
import tec.uom.se.format.QuantityFormat;
import tec.uom.se.function.NaturalOrder;
import tec.uom.se.quantity.Quantities;

/**
 * 

* This class represents the immutable result of a scalar measurement stated in a known unit. *

* *

* To avoid any loss of precision, known exact quantities (e.g. physical constants) should not be created from double constants but from * their decimal representation.
* * public static final Quantity<Velocity> C = NumberQuantity.parse("299792458 m/s").asType(Velocity.class); * // Speed of Light (exact). * *

* *

* Quantities can be converted to different units.
* * Quantity<Velocity> milesPerHour = C.to(MILES_PER_HOUR); // Use double implementation (fast). * System.out.println(milesPerHour); * * > 670616629.3843951 m/h * *

* *

* Applications may sub-class {@link AbstractQuantity} for particular quantity types.
* * // Quantity of type Mass based on double primitive types.
* public class MassAmount extends AbstractQuantity<Mass> {
* private final double kilograms; // Internal SI representation.
* private Mass(double kg) { kilograms = kg; }
* public static Mass of(double value, Unit<Mass> unit) {
* return new Mass(unit.getConverterTo(SI.KILOGRAM).convert(value));
* }
* public Unit<Mass> getUnit() { return SI.KILOGRAM; }
* public Double getValue() { return kilograms; }
* ...
* }
*

* // Complex numbers measurements.
* public class ComplexQuantity * <Q extends Quantity>extends AbstractQuantity * <Q>{
* public Complex getValue() { ... } // Assuming Complex is a Number.
* ...
* }
*
* // Specializations of complex numbers measurements.
* public final class Current extends ComplexQuantity<ElectricCurrent> {...}
* public final class Tension extends ComplexQuantity<ElectricPotential> {...}
*
*

* *

* All instances of this class shall be immutable. *

* * @author Werner Keil * @version 1.0.6, August 7, 2017 * @since 1.0 */ @SuppressWarnings("unchecked") public abstract class AbstractQuantity> implements ComparableQuantity, UnitSupplier, ValueSupplier { /** * */ private static final long serialVersionUID = 293852425369811882L; private final Unit unit; /** * Holds a dimensionless quantity of none (exact). */ public static final Quantity NONE = Quantities.getQuantity(0, AbstractUnit.ONE); /** * Holds a dimensionless quantity of one (exact). */ public static final Quantity ONE = Quantities.getQuantity(1, AbstractUnit.ONE); /** * constructor. */ protected AbstractQuantity(Unit unit) { this.unit = unit; } /** * Returns the numeric value of the quantity. * * @return the quantity value. */ @Override public abstract Number getValue(); /** * Returns the measurement unit. * * @return the measurement unit. */ @Override public Unit getUnit() { return unit; } /** * Convenient method equivalent to {@link #to(javax.measure.unit.Unit) to(this.getUnit().toSI())}. * * @return this measure or a new measure equivalent to this measure but stated in SI units. * @throws ArithmeticException * if the result is inexact and the quotient has a non-terminating decimal expansion. */ public Quantity toSI() { return to(this.getUnit().getSystemUnit()); } /** * Returns this measure after conversion to specified unit. The default implementation returns Measure.valueOf(doubleValue(unit), unit) * . If this measure is already stated in the specified unit, then this measure is returned and no conversion is performed. * * @param unit * the unit in which the returned measure is stated. * @return this measure or a new measure equivalent to this measure but stated in the specified unit. * @throws ArithmeticException * if the result is inexact and the quotient has a non-terminating decimal expansion. */ @Override public ComparableQuantity to(Unit unit) { if (unit.equals(this.getUnit())) { return this; } UnitConverter t = getUnit().getConverterTo(unit); Number convertedValue = t.convert(getValue()); return Quantities.getQuantity(convertedValue, unit); } /** * Returns this measure after conversion to specified unit. The default implementation returns * Measure.valueOf(decimalValue(unit, ctx), unit). If this measure is already stated in the specified unit, then this measure is * returned and no conversion is performed. * * @param unit * the unit in which the returned measure is stated. * @param ctx * the math context to use for conversion. * @return this measure or a new measure equivalent to this measure but stated in the specified unit. * @throws ArithmeticException * if the result is inexact but the rounding mode is UNNECESSARY or mathContext.precision == 0 and the quotient * has a non-terminating decimal expansion. */ public Quantity to(Unit unit, MathContext ctx) { if (unit.equals(this.getUnit())) { return this; } return Quantities.getQuantity(decimalValue(unit, ctx), unit); } @Override public boolean isGreaterThan(Quantity that) { return this.compareTo(that) > 0; } @Override public boolean isGreaterThanOrEqualTo(Quantity that) { return this.compareTo(that) >= 0; } @Override public boolean isLessThan(Quantity that) { return this.compareTo(that) < 0; } @Override public boolean isLessThanOrEqualTo(Quantity that) { return this.compareTo(that) <= 0; } @Override public boolean isEquivalentOf(Quantity that) { return this.compareTo(that) == 0; } @Override public boolean isEquivalentTo(Quantity that) { return isEquivalentOf(that); } /** * Compares this measure to the specified Measurement quantity. The default implementation compares the {@link AbstractQuantity#doubleValue(Unit)} * of both this measure and the specified Measurement stated in the same unit (this measure's {@link #getUnit() unit}). * * @return a negative integer, zero, or a positive integer as this measure is less than, equal to, or greater than the specified Measurement * quantity. * @see {@link NaturalOrder} */ @Override public int compareTo(Quantity that) { final Comparator> comparator = new NaturalOrder<>(); return comparator.compare(this, that); } /** * Compares this quantity against the specified object for strict equality (same unit and same amount). * *

* Similarly to the {@link BigDecimal#equals} method which consider 2.0 and 2.00 as different objects because of different internal scales, * quantities such as Quantities.getQuantity(3.0, KILOGRAM) Quantities.getQuantity(3, KILOGRAM) and * Quantities.getQuantity("3 kg") might not be considered equals because of possible differences in their implementations. *

* *

* To compare quantities stated using different units or using different amount implementations the {@link #compareTo compareTo} or * {@link #equals(javax.measure.Quantity, double, javax.measure.unit.Unit) equals(Quantity, epsilon, epsilonUnit)} methods should be used. *

* * @param obj * the object to compare with. * @return this.getUnit.equals(obj.getUnit()) * && this.getValue().equals(obj.getValue()) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof AbstractQuantity) { AbstractQuantity that = (AbstractQuantity) obj; return Objects.equals(getUnit(), that.getUnit()) && Objects.equals(getValue(), that.getValue()); } return false; } /** * Compares this quantity and the specified quantity to the given accuracy. Quantities are considered approximately equals if their absolute * differences when stated in the same specified unit is less than the specified epsilon. * * @param that * the quantity to compare with. * @param epsilon * the absolute error stated in epsilonUnit. * @param epsilonUnit * the epsilon unit. * @return abs(this.doubleValue(epsilonUnit) - that.doubleValue(epsilonUnit)) <= epsilon */ public boolean equals(AbstractQuantity that, double epsilon, Unit epsilonUnit) { return Math.abs(this.doubleValue(epsilonUnit) - that.doubleValue(epsilonUnit)) <= epsilon; } /** * Returns the hash code for this quantity. * * @return the hash code value. */ @Override public int hashCode() { return Objects.hash(getUnit(), getValue()); } public abstract boolean isBig(); /** * Returns the String representation of this quantity. The string produced for a given quantity is always the same; it is not affected * by locale. This means that it can be used as a canonical string representation for exchanging quantity, or as a key for a Hashtable, etc. * Locale-sensitive quantity formatting and parsing is handled by the {@link MeasurementFormat} class and its subclasses. * * @return UnitFormat.getInternational().format(this) */ @Override public String toString() { return String.valueOf(getValue()) + " " + String.valueOf(getUnit()); } public abstract BigDecimal decimalValue(Unit unit, MathContext ctx) throws ArithmeticException; public abstract double doubleValue(Unit unit) throws ArithmeticException; public final int intValue(Unit unit) throws ArithmeticException { long longValue = longValue(unit); if ((longValue < Integer.MIN_VALUE) || (longValue > Integer.MAX_VALUE)) { throw new ArithmeticException("Cannot convert " + longValue + " to int (overflow)"); } return (int) longValue; } protected long longValue(Unit unit) throws ArithmeticException { double result = doubleValue(unit); if ((result < Long.MIN_VALUE) || (result > Long.MAX_VALUE)) { throw new ArithmeticException("Overflow (" + result + ")"); } return (long) result; } protected final float floatValue(Unit unit) { return (float) doubleValue(unit); } @Override public , E extends Quantity> ComparableQuantity divide(Quantity that, Class asTypeQuantity) { return divide(Objects.requireNonNull(that)).asType(Objects.requireNonNull(asTypeQuantity)); } @Override public , E extends Quantity> ComparableQuantity multiply(Quantity that, Class asTypeQuantity) { return multiply(Objects.requireNonNull(that)).asType(Objects.requireNonNull(asTypeQuantity)); } @Override public > ComparableQuantity inverse(Class quantityClass) { return inverse().asType(quantityClass); } /** * Casts this quantity to a parameterized quantity of specified nature or throw a ClassCastException if the dimension of the specified * quantity and its unit's dimension do not match. For example:
* * Quantity length = AbstractQuantity.parse("2 km").asType(Length.class); * * * @param type * the quantity class identifying the nature of the quantity. * @return this quantity parameterized with the specified type. * @throws ClassCastException * if the dimension of this unit is different from the specified quantity dimension. * @throws UnsupportedOperationException * if the specified quantity class does not have a public static field named "UNIT" holding the SI unit for the quantity. * @see Unit#asType(Class) */ public final > ComparableQuantity asType(Class type) throws ClassCastException { this.getUnit().asType(type); // Raises ClassCastException if dimension // mismatches. return (ComparableQuantity) this; } /** * Returns the quantity of unknown type corresponding to the specified representation. This method can be used to parse dimensionless quantities.
* * Quatity proportion = AbstractQuantity.parse("0.234").asType(Dimensionless.class); * * *

* Note: This method handles only {@link SimpleUnitFormat#getStandard standard} unit format. Locale-sensitive quantity parsing is currently not * supported. *

* * @param csq * the decimal value and its unit (if any) separated by space(s). * @return QuantityFormat.getInstance().parse(csq) */ public static Quantity parse(CharSequence csq) { return QuantityFormat.getInstance().parse(csq); } /** * Utility class for number comparison and equality */ protected static final class Equalizer { /** * Converts a number to {@link BigDecimal} * * @param value * the value to be converted * @return the value converted */ public static BigDecimal toBigDecimal(Number value) { if (BigDecimal.class.isInstance(value)) { return BigDecimal.class.cast(value); } else if (BigInteger.class.isInstance(value)) { return new BigDecimal(BigInteger.class.cast(value)); } return BigDecimal.valueOf(value.doubleValue()); } /** * Check if the both value has equality number, in other words, 1 is equals to 1.0000 and 1.0. * * If the first value is a Number of either Double, Float, Integer, Long, * Short or Byte it is compared using the respective *value() method of Number. Otherwise it * is checked, if {@link BigDecimal#compareTo(Object)} is equal to zero. * * @param valueA * the value a * @param valueB * the value B * @return {@link BigDecimal#compareTo(Object)} == zero */ public static boolean hasEquality(Number valueA, Number valueB) { Objects.requireNonNull(valueA); Objects.requireNonNull(valueB); if (valueA instanceof Double && valueB instanceof Double) { return valueA.doubleValue() == valueB.doubleValue(); } else if (valueA instanceof Float && valueB instanceof Float) { return valueA.floatValue() == valueB.floatValue(); } else if (valueA instanceof Integer && valueB instanceof Integer) { return valueA.intValue() == valueB.intValue(); } else if (valueA instanceof Long && valueB instanceof Long) { return valueA.longValue() == valueB.longValue(); } else if (valueA instanceof Short && valueB instanceof Short) { return valueA.shortValue() == valueB.shortValue(); } else if (valueA instanceof Byte && valueB instanceof Byte) { return valueA.byteValue() == valueB.byteValue(); } return toBigDecimal(valueA).compareTo(toBigDecimal(valueB)) == 0; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy