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

tec.uom.se.AbstractUnit 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 tec.uom.se.format.SimpleUnitFormat;
import tec.uom.se.function.AddConverter;
import tec.uom.se.function.MultiplyConverter;
import tec.uom.se.function.RationalConverter;
import tec.uom.se.quantity.QuantityDimension;
import tec.uom.se.spi.DimensionalModel;
import tec.uom.se.unit.AlternateUnit;
import tec.uom.se.unit.AnnotatedUnit;
import tec.uom.se.unit.ProductUnit;
import tec.uom.se.unit.TransformedUnit;

import javax.measure.*;
import javax.measure.quantity.Dimensionless;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 

* 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 1.0.10, March 4, 2018 * @since 1.0 */ public abstract class AbstractUnit> implements ComparableUnit { /** * */ 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 AbstractUnit 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 Map> SYMBOL_TO_UNIT = new HashMap<>(); /** * DefaultQuantityFactory constructor. */ protected AbstractUnit() { } 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 si = this.toSystemUnit(); return (this == si) || this.equals(si); } /** * 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(); /** * Returns the converter from this unit to its unscaled {@link #toSysemUnit System Unit} unit. * * @return getConverterTo(this.toSystemUnit()) * @see #toSI */ public abstract UnitConverter getSystemConverter(); /** * Annotates the specified unit. Annotation does not change the unit semantic. Annotations are often written between curly braces behind units. For * example: * AbstractUnit PERCENT_VOL = Units.PERCENT.annotate("vol"); // "%{vol}" AbstractUnit KG_TOTAL = * Units.KILOGRAM.annotate("total"); // "kg{total}" AbstractUnit RED_BLOOD_CELLS = 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 AnnotatedUnit 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, new ParsePosition(0)) * @throws ParserException * if the specified character sequence cannot be correctly parsed (e.g. not UCUM compliant). */ public static AbstractUnit 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 equals fundamental dimension according to the current physics model; false * otherwise. */ @Override public final boolean isCompatible(Unit that) { if ((this == that) || this.equals(that)) return true; if (!(that instanceof AbstractUnit)) 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)); } public boolean isEquivalentTo(Unit that) { if (this.compareTo(that) == 0) return true; return this.getConverterTo(that).equals(that.getConverterTo(this)); } public boolean isEquivalentOf(Unit that) { return isEquivalentTo(that); } /** * 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 > AbstractUnit asType(Class type) { Dimension typeDimension = QuantityDimension.of(type); if ((typeDimension != null) && (!typeDimension.equals(this.getDimension()))) throw new ClassCastException("The unit: " + this + " is not compatible with quantities of type " + type); return (AbstractUnit) 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 { if ((this == that) || this.equals(that)) return AbstractConverter.IDENTITY; // Shortcut. 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); } @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 // be both physics // units. 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); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public final AbstractUnit alternate(String symbol) { return new AlternateUnit(this, symbol); } @Override public final AbstractUnit transform(UnitConverter operation) { Unit systemUnit = this.getSystemUnit(); UnitConverter cvtr; if (this.isSystemUnit()) { cvtr = this.getSystemConverter().concatenate(operation); } else { cvtr = operation; } if (cvtr.equals(AbstractConverter.IDENTITY) && systemUnit instanceof AbstractUnit) { return (AbstractUnit) systemUnit; } else { return new TransformedUnit<>(null, this, systemUnit, cvtr); } } @Override public final AbstractUnit shift(double offset) { if (offset == 0) return this; return transform(new AddConverter(offset)); } @Override public final AbstractUnit multiply(double factor) { if (factor == 1) return this; if (isLongValue(factor)) return transform(new RationalConverter(BigInteger.valueOf((long) factor), BigInteger.ONE)); return transform(new MultiplyConverter(factor)); } private static boolean isLongValue(double value) { return !((value < Long.MIN_VALUE) || (value > Long.MAX_VALUE)) && Math.floor(value) == value; } /** * Returns the product of this unit with the one specified. * *

* Note: If the specified unit (that) is not a physical unit, then that.multiply(this) is returned. *

* * @param that * the unit multiplicand. * @return this * that */ @Override public final AbstractUnit multiply(Unit that) { if (that instanceof AbstractUnit) return multiply((AbstractUnit) that); // return that.multiply(this); // Commutatif. return ProductUnit.getProductInstance(this, that); } /** * Returns the product of this physical unit with the one specified. * * @param that * the physical unit multiplicand. * @return this * that */ protected final AbstractUnit multiply(AbstractUnit that) { if (this.equals(ONE)) return that; if (that.equals(ONE)) return this; return ProductUnit.getProductInstance(this, that); } /** * Returns the inverse of this physical unit. * * @return 1 / this */ @Override public final AbstractUnit inverse() { if (this.equals(ONE)) return this; return ProductUnit.getQuotientInstance(ONE, this); } /** * Returns the result of dividing this unit by the specifified 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 AbstractUnit divide(double divisor) { if (divisor == 1) return this; if (isLongValue(divisor)) return transform(new RationalConverter(BigInteger.ONE, BigInteger.valueOf((long) divisor))); return transform(new MultiplyConverter(1.0 / divisor)); } /** * Returns the quotient of this unit with the one specified. * * @param that * the unit divisor. * @return this.multiply(that.inverse()) */ @Override public final AbstractUnit divide(Unit that) { return this.multiply(that.inverse()); } /** * Returns the quotient of this physical unit with the one specified. * * @param that * the physical unit divisor. * @return this.multiply(that.inverse()) */ protected final AbstractUnit divide(AbstractUnit 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 AbstractUnit root(int n) { if (n > 0) return ProductUnit.getRootInstance(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 final AbstractUnit 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)); } /** * Compares this unit to the specified unit. The default implementation compares the name and symbol of both this unit and the specified unit. * * @return a negative integer, zero, or a positive integer as this unit is less than, equal to, or greater than the specified unit. */ public int compareTo(Unit that) { if (name != null && getSymbol() != null) { return name.compareTo(that.getName()) + getSymbol().compareTo(that.getSymbol()); } else if (name == null) { if (getSymbol() != null && that.getSymbol() != null) { return getSymbol().compareTo(that.getSymbol()); } else { return -1; } } else if (getSymbol() == null) { if (name != null) { return name.compareTo(that.getName()); } else { return -1; } } else { return -1; } } // ////////////////////////////////////////////////////////////// // Ensures that sub-classes implements hashCode/equals method. // ////////////////////////////////////////////////////////////// @Override public abstract int hashCode(); @Override public abstract boolean equals(Object that); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy