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

tech.units.indriya.AbstractUnit Maven / Gradle / Ivy

There is a newer version: 2.2
Show newest version
/*
 * Units of Measurement Reference Implementation
 * Copyright (c) 2005-2023, 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-385, Indriya nor the names of their 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 tech.units.indriya;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import javax.measure.Dimension;
import javax.measure.IncommensurableException;
import javax.measure.Prefix;
import javax.measure.Quantity;
import javax.measure.UnconvertibleException;
import javax.measure.Unit;
import javax.measure.UnitConverter;
import javax.measure.format.MeasurementParseException;
import javax.measure.quantity.Dimensionless;

import tech.units.indriya.format.LocalUnitFormat;
import tech.units.indriya.format.SimpleUnitFormat;
import tech.units.indriya.function.AbstractConverter;
import tech.units.indriya.function.AddConverter;
import tech.units.indriya.function.Calculus;
import tech.units.indriya.function.MultiplyConverter;
import tech.units.indriya.function.RationalNumber;
import tech.units.indriya.internal.function.Calculator;
import tech.units.indriya.spi.DimensionalModel;
import tech.units.indriya.unit.AlternateUnit;
import tech.units.indriya.unit.AnnotatedUnit;
import tech.units.indriya.unit.ProductUnit;
import tech.units.indriya.unit.TransformedUnit;
import tech.units.indriya.unit.UnitDimension;
import tech.units.indriya.unit.Units;
import tech.uom.lib.common.function.Nameable;
import tech.uom.lib.common.function.PrefixOperator;
import tech.uom.lib.common.function.SymbolSupplier;

/**
 * 

* The class represents units founded on the seven SI base units for * seven base quantities assumed to be mutually independent. *

* *

* For all physics units, unit conversions are symmetrical: * u1.getConverterTo(u2).equals(u2.getConverterTo(u1).inverse()). * Non-physical units (e.g. currency units) for which conversion is not * symmetrical should have their own separate class hierarchy and are considered * distinct (e.g. financial units), although they can always be combined with * physics units (e.g. "€/Kg", "$/h"). *

* * @see Wikipedia: * International System of Units * @author Jean-Marie Dautelle * @author Werner Keil * @version 3.2, April 16, 2021 * @since 1.0 */ public abstract class AbstractUnit> implements Unit, Comparable>, PrefixOperator, Nameable, Serializable, SymbolSupplier { /** * */ private static final long serialVersionUID = -4344589505537030204L; /** * Holds the dimensionless unit ONE. * * @see * Wikipedia: Natural Units - Choosing constants to normalize * @see Units of * Dimension One */ public static final Unit ONE = new ProductUnit<>(); /** * Holds the name. */ protected String name; /** * Holds the symbol. */ private String symbol; /** * Holds the unique symbols collection (base units or alternate units). */ protected static final transient Map> SYMBOL_TO_UNIT = new HashMap<>(); /** * Default constructor. */ protected AbstractUnit() { } /** * Constructor setting a symbol. * * @param symbol the unit symbol. */ protected AbstractUnit(String symbol) { this.symbol = symbol; } protected Type getActualType() { ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass(); return parameterizedType.getActualTypeArguments()[0].getClass().getGenericInterfaces()[0]; } /** * Indicates if this unit belongs to the set of coherent SI units (unscaled SI * units). * * The base and coherent derived units of the SI form a coherent set, designated * the set of coherent SI units. The word coherent is used here in the following * sense: when coherent units are used, equations between the numerical values * of quantities take exactly the same form as the equations between the * quantities themselves. Thus if only units from a coherent set are used, * conversion factors between units are never required. * * @return equals(toSystemUnit()) */ public boolean isSystemUnit() { Unit sys = this.toSystemUnit(); return this == sys || this.equals(sys); } /** * Returns the converter from this unit to its unscaled {@link #toSystemUnit * System Unit} unit. * * @return getConverterTo(this.toSystemUnit()) * @see #toSystemUnit */ public abstract UnitConverter getSystemConverter(); /** * Returns the unscaled {@link SI} unit from which this unit is derived. * * The SI unit can be be used to identify a quantity given the unit. For * example: static boolean isAngularVelocity(AbstractUnit unit) { * return unit.toSystemUnit().equals(RADIAN.divide(SECOND)); } assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. * * @return the unscaled metric unit from which this unit is derived. */ protected abstract Unit toSystemUnit(); /** * Annotates the specified unit. Annotation does not change the unit semantic. * Annotations are often written between curly braces behind units. For * example:
* Unit PERCENT_VOL = ((AbstractUnit)Units.PERCENT).annotate("vol"); // "%{vol}" Unit KG_TOTAL = * ((AbstractUnit)Units.KILOGRAM).annotate("total"); // "kg{total}" Unit RED_BLOOD_CELLS = ((AbstractUnit)Units.ONE).annotate("RBC"); // "{RBC}" * * Note: Annotation of system units are not considered themselves as system * units. * * @param annotation the unit annotation. * @return the annotated unit. */ public final Unit annotate(String annotation) { return new AnnotatedUnit<>(this, annotation); } /** * Returns the abstract unit represented by the specified characters as per * default format. * * Locale-sensitive unit parsing could be handled using {@link LocalUnitFormat} * in subclasses of AbstractUnit. * *

* Note: The standard format supports dimensionless * units. AbstractUnit PERCENT = * AbstractUnit.parse("100").inverse().asType(Dimensionless.class); *

* * @param charSequence the character sequence to parse. * @return SimpleUnitFormat.getInstance().parse(csq) * @throws MeasurementParseException if the specified character sequence cannot * be correctly parsed (e.g. not UCUM * compliant). */ public static Unit parse(CharSequence charSequence) { return SimpleUnitFormat.getInstance().parse(charSequence); } /** * Returns the standard representation of this physics unit. The string produced * for a given unit is always the same; it is not affected by the locale. It can * be used as a canonical string representation for exchanging units, or as a * key for a Hashtable, etc. * * Locale-sensitive unit parsing could be handled using {@link LocalUnitFormat} * in subclasses of AbstractUnit. * * @return SimpleUnitFormat.getInstance().format(this) */ @Override public String toString() { return SimpleUnitFormat.getInstance().format(this); } // /////////////////////////////////////////////////////// // Implements javax.measure.Unit interface // // /////////////////////////////////////////////////////// /** * Returns the system unit (unscaled SI unit) from which this unit is derived. * They can be be used to identify a quantity given the unit. For example:
* static boolean isAngularVelocity(AbstractUnit unit) {
  return unit.getSystemUnit().equals(RADIAN.divide(SECOND));
} *
assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true.
* * @return the unscaled metric unit from which this unit is derived. */ @Override public final Unit getSystemUnit() { return toSystemUnit(); } /** * Indicates if this unit is compatible with the unit specified. To be * compatible both units must be physics units having the same fundamental * dimension. * * @param that the other unit. * @return true if this unit and that unit have the same * fundamental dimension according to the current dimensional model; * false otherwise. */ @Override public final boolean isCompatible(Unit that) { return internalIsCompatible(that, true); } /** * Casts this unit to a parameterized unit of specified nature or throw a * ClassCastException if the dimension of the specified quantity and this unit's * dimension do not match (regardless whether or not the dimensions are * independent or not). * * @param type the quantity class identifying the nature of the unit. * @throws ClassCastException if the dimension of this unit is different from * the SI dimension of the specified type. * @see Units#getUnit(Class) */ @SuppressWarnings("unchecked") @Override public final > Unit asType(Class type) { Dimension typeDimension = UnitDimension.of(type); if (typeDimension != null && !typeDimension.equals(this.getDimension())) throw new ClassCastException("The unit: " + this + " is not compatible with quantities of type " + type); return (Unit) this; } @Override public abstract Map, Integer> getBaseUnits(); @Override public abstract Dimension getDimension(); protected void setName(String name) { this.name = name; } public String getName() { return name; } public String getSymbol() { return symbol; } protected void setSymbol(String s) { this.symbol = s; } @Override public final UnitConverter getConverterTo(Unit that) throws UnconvertibleException { return internalGetConverterTo(that, true); } @SuppressWarnings("rawtypes") @Override public final UnitConverter getConverterToAny(Unit that) throws IncommensurableException, UnconvertibleException { if (!isCompatible(that)) throw new IncommensurableException(this + " is not compatible with " + that); AbstractUnit thatAbstr = (AbstractUnit) that; // Since both units are // compatible they must both be abstract units. final DimensionalModel model = DimensionalModel.current(); Unit thisSystemUnit = this.getSystemUnit(); UnitConverter thisToDimension = model.getDimensionalTransform(thisSystemUnit.getDimension()) .concatenate(this.getSystemConverter()); Unit thatSystemUnit = thatAbstr.getSystemUnit(); UnitConverter thatToDimension = model.getDimensionalTransform(thatSystemUnit.getDimension()) .concatenate(thatAbstr.getSystemConverter()); return thatToDimension.inverse().concatenate(thisToDimension); } @Override public final Unit alternate(String newSymbol) { return new AlternateUnit<>(this, newSymbol); } @Override public final Unit transform(UnitConverter operation) { Unit systemUnit = this.getSystemUnit(); UnitConverter cvtr; if (this.isSystemUnit()) { cvtr = this.getSystemConverter().concatenate(operation); } else { cvtr = operation; } return cvtr.isIdentity() ? systemUnit : new TransformedUnit<>(null, this, systemUnit, cvtr); } @Override public final Unit shift(Number offset) { if (Calculus.currentNumberSystem().isZero(offset)) return this; return transform(new AddConverter(offset)); } @Override public final Unit multiply(Number factor) { if (Calculus.currentNumberSystem().isOne(factor)) return this; return transform(MultiplyConverter.of(factor)); } @Override public Unit shift(double offset) { return shift(RationalNumber.of(offset)); } @Override public Unit multiply(double multiplier) { return multiply(RationalNumber.of(multiplier)); } @Override public Unit divide(double divisor) { return divide(RationalNumber.of(divisor)); } /** * Internal helper for isCompatible */ private final boolean internalIsCompatible(Unit that, boolean checkEquals) { if (checkEquals) { if (this == that || this.equals(that)) return true; } else { if (this == that) return true; } if (!(that instanceof Unit)) return false; Dimension thisDimension = this.getDimension(); Dimension thatDimension = that.getDimension(); if (thisDimension.equals(thatDimension)) return true; DimensionalModel model = DimensionalModel.current(); // Use // dimensional // analysis // model. return model.getFundamentalDimension(thisDimension).equals(model.getFundamentalDimension(thatDimension)); } protected final UnitConverter internalGetConverterTo(Unit that, boolean useEquals) throws UnconvertibleException { if (useEquals) { if (this == that || this.equals(that)) return AbstractConverter.IDENTITY; } else { if (this == that) return AbstractConverter.IDENTITY; } Unit thisSystemUnit = this.getSystemUnit(); Unit thatSystemUnit = that.getSystemUnit(); if (!thisSystemUnit.equals(thatSystemUnit)) try { return getConverterToAny(that); } catch (IncommensurableException e) { throw new UnconvertibleException(e); } UnitConverter thisToSI = this.getSystemConverter(); UnitConverter thatToSI = that.getConverterTo(thatSystemUnit); return thatToSI.inverse().concatenate(thisToSI); } /** * Returns the product of this physical unit with the one specified. * * @param that the physical unit multiplicand. * @return this * that */ public final Unit multiply(Unit that) { if (this.equals(ONE)) return that; if (that.equals(ONE)) return this; return ProductUnit.ofProduct(this, that); } /** * Returns the inverse of this physical unit. * * @return 1 / this */ @Override public final Unit inverse() { if (this.equals(ONE)) return this; return ProductUnit.ofQuotient(ONE, this); } /** * Returns the result of dividing this unit by the specified divisor. If the * factor is an integer value, the division is exact. For example: * *
	 * 
	 *    QUART = GALLON_LIQUID_US.divide(4); // Exact definition.
	 * 
	 * 
* * @param divisor the divisor value. * @return this unit divided by the specified divisor. */ @Override public final Unit divide(Number divisor) { if (Calculus.currentNumberSystem().isOne(divisor)) return this; Number factor = Calculator.of(divisor).reciprocal().peek(); return transform(MultiplyConverter.of(factor)); } /** * Returns the quotient of this unit with the one specified. * * @param that the unit divisor. * @return this.multiply(that.inverse()) */ @Override public final Unit divide(Unit that) { return this.multiply(that.inverse()); } /** * Returns a unit equals to the given root of this unit. * * @param n the root's order. * @return the result of taking the given root of this unit. * @throws ArithmeticException if n == 0 or if this operation would * result in an unit with a fractional exponent. */ @Override public final Unit root(int n) { if (n > 0) return ProductUnit.ofRoot(this, n); else if (n == 0) throw new ArithmeticException("Root's order of zero"); else // n < 0 return ONE.divide(this.root(-n)); } /** * Returns a unit equals to this unit raised to an exponent. * * @param n the exponent. * @return the result of raising this unit to the exponent. */ @Override public Unit pow(int n) { if (n > 0) return this.multiply(this.pow(n - 1)); else if (n == 0) return ONE; else // n < 0 return ONE.divide(this.pow(-n)); } @Override public Unit prefix(Prefix prefix) { return this.transform(MultiplyConverter.ofPrefix(prefix)); } /** * Compares this unit to the specified unit. The default implementation compares * the name and symbol of both this unit and the specified unit, giving * precedence to the symbol. * * @return a negative integer, zero, or a positive integer as this unit is less * than, equal to, or greater than the specified unit. */ @Override public int compareTo(Unit that) { int symbolComparison = compareToWithPossibleNullValues(getSymbol(), that.getSymbol()); if (symbolComparison == 0) { return compareToWithPossibleNullValues(name, that.getName()); } else { return symbolComparison; } } private int compareToWithPossibleNullValues(String a, String b) { if (a == null) { return (b == null) ? 0 : -1; } else { return (b == null) ? 1 : a.compareTo(b); } } @Override public boolean isEquivalentTo(Unit that) { return this.getConverterTo(that).isIdentity(); } // ////////////////////////////////////////////////////////////// // Ensures that sub-classes implement the hashCode method. // ////////////////////////////////////////////////////////////// @Override public abstract boolean equals(Object obj); @Override public abstract int hashCode(); /** * Utility class for number comparison and equality */ protected static final class Equalizer { /** * Indicates if this unit is considered equals to the specified object. order). * * @param obj the object to compare for equality. * @return true if this and obj are * considered equal; falseotherwise. */ public static boolean areEqual(@SuppressWarnings("rawtypes") Unit u1, @SuppressWarnings("rawtypes") Unit u2) { /* * if (u1 != null && u2 != null) { if (u1.getName() != null && u1.getSymbol() != * null) { return u1.getName().equals(u2.getName()) && * u1.getSymbol().equals(u2.getSymbol()) && u1.internalIsCompatible(u2, false); * } else if (u1.getSymbol() != null) { return * u1.getSymbol().equals(u2.getSymbol()) && u1.internalIsCompatible(u2, false); * } else { return u1.toString().equals(u2.toString()) && * u1.internalIsCompatible(u2, false); } } else { */ if (u1 != null && u1.equals(u2)) return true; return false; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy