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

tec.units.ri.AbstractUnit Maven / Gradle / Ivy

There is a newer version: 1.0.3
Show newest version
/*
 * Units of Measurement Reference Implementation
 * Copyright (c) 2005-2016, Jean-Marie Dautelle, Werner Keil, V2COM.
 *
 * 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.units.ri;

import java.util.Map;

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

import tec.units.ri.format.SimpleUnitFormat;
import tec.units.ri.function.AddConverter;
import tec.units.ri.function.MultiplyConverter;
import tec.units.ri.function.RationalConverter;
import tec.units.ri.quantity.QuantityDimension;
import tec.units.ri.spi.DimensionalModel;
import tec.units.ri.unit.AlternateUnit;
import tec.units.ri.unit.AnnotatedUnit;
import tec.units.ri.unit.ProductUnit;
import tec.units.ri.unit.TransformedUnit;
import tec.units.ri.unit.Units;

/**
 * 

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

* *

* For all physics units, units 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"). *

* * @author Jean-Marie Dautelle * @author Werner Keil * @version 1.0, August 9, 2016 */ public abstract class AbstractUnit> implements Unit, Comparable> { /** * */ // private static final long serialVersionUID = -4344589505537030204L; /** * Holds the dimensionless unit ONE. * * @see Wikipedia: Natural Units - Choosing constants to * normalize */ public static final Unit ONE = new ProductUnit(); /** * Holds the name. */ protected String name; /** * Holds the symbol. */ private String symbol; /** * Default constructor. */ protected AbstractUnit() { } /** * 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; } } /** * 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() { AbstractUnit si = this.toSystemUnit(); return (this == si) || this.equals(si); } /** * Returns the unscaled standard (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 AbstractUnit toSystemUnit(); /** * Returns the converter from this unit to its unscaled {@link #toSystemUnit standard} unit. * * @return getConverterTo(this.toSystemUnit()) * @see #toSystemUnit */ 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: Unit PERCENT_VOL = * Units.PERCENT.annotate("vol"); // "%{vol}" AbstractUnit KG_TOTAL = * Units.KILOGRAM.annotate("total"); // "kg{total}" AbstractUnit * RED_BLOOD_CELLS = ONE.annotate("RBC"); // "{RBC}" * * Note: Annotations 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 physics unit represented by the specified characters. * * Locale-sensitive unit parsing may be handled using the OSGi {@link javax.measure.spi.UnitFormatService} or for non-OSGi applications instances of * {@link SimpleUnitFormat}. * *

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

* * @param charSequence * the character sequence to parse. * @return SimpleUnitFormat.getInstance().parse(csq) * @throws ParserException * if the specified character sequence cannot be parsed correctly. */ public static Unit parse(CharSequence charSequence) { return SimpleUnitFormat.getInstance().parse(charSequence); } /** * Returns the standard {@link String} representation of this 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 Map, Hashtable, etc. * * Locale-sensitive unit parsing should be handled using the OSGi {@link tec.units.ri.SimpleUnitFormat.service.UnitFormat} service (or * {@link SimpleUnitFormat} for non-OSGi applications). * * @return SimpleUnitFormat.getInstance().format(this) */ @Override public String toString() { return SimpleUnitFormat.getInstance().format(this); /* * final Appendable tmp = new StringBuilder(); try { return * SimpleUnitFormat.getInstance().format(this, tmp).toString(); } catch * (IOException ioException) { throw new Error(ioException); // Should * never happen. } finally { // if (tmp!=null) tmp.clear(); } */ } // /////////////////////////////////////////////////////// // Implements org.unitsofmeasurement.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:[code] static boolean isAngularVelocity(AbstractUnit unit) { return unit.getSystemUnit().equals(RADIAN.divide(SECOND)); } * assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. [/code] * * @return the unscaled metric unit from which this unit is derived. */ public final AbstractUnit 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. */ 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)); } /** * 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") public final > Unit asType(Class type) { Dimension typeDimension = QuantityDimension.getInstance(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; } public abstract Map, Integer> getBaseUnits(); 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; } 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") 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 abstract // units. DimensionalModel model = DimensionalModel.current(); AbstractUnit thisSystemUnit = this.getSystemUnit(); UnitConverter thisToDimension = model.getDimensionalTransform(thisSystemUnit.getDimension()).concatenate(this.getSystemConverter()); AbstractUnit thatSystemUnit = thatAbstr.getSystemUnit(); UnitConverter thatToDimension = model.getDimensionalTransform(thatSystemUnit.getDimension()).concatenate(thatAbstr.getSystemConverter()); return thatToDimension.inverse().concatenate(thisToDimension); } @Override public final Unit alternate(String symbol) { return new AlternateUnit(this, symbol); } @Override public final AbstractUnit transform(UnitConverter operation) { AbstractUnit systemUnit = this.getSystemUnit(); UnitConverter cvtr = this.getSystemConverter().concatenate(operation); if (cvtr.equals(AbstractConverter.IDENTITY)) return systemUnit; return new TransformedUnit(systemUnit, cvtr); } @Override public final Unit shift(double offset) { if (offset == 0) return this; return transform(new AddConverter(offset)); } @Override public final Unit multiply(double factor) { if (factor == 1) return this; if (isLongValue(factor)) return transform(new RationalConverter((long) factor, 1)); 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 */ public final Unit multiply(Unit that) { if (that instanceof AbstractUnit) return multiply((AbstractUnit) that); return that.multiply(this); // Commutatif. } /** * Returns the product of this physical unit with the one specified. * * @param that * the physical unit multiplicand. * @return this * that */ public final Unit 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 */ public final Unit 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. */ public final AbstractUnit divide(double divisor) { if (divisor == 1) return this; if (isLongValue(divisor)) return transform(new RationalConverter(1, (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()) */ 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. */ public final Unit 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. */ public final 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)); } // ////////////////////////////////////////////////////////////// // 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