001/*
002 * Units of Measurement Implementation for Java SE
003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil, V2COM.
004 *
005 * All rights reserved.
006 *
007 * Redistribution and use in source and binary forms, with or without modification,
008 * are permitted provided that the following conditions are met:
009 *
010 * 1. Redistributions of source code must retain the above copyright notice,
011 *    this list of conditions and the following disclaimer.
012 *
013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
014 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
015 *
016 * 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products
017 *    derived from this software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package tec.uom.se;
031
032import tec.uom.se.function.AddConverter;
033import tec.uom.se.function.MultiplyConverter;
034import tec.uom.se.function.RationalConverter;
035import tec.uom.se.quantity.QuantityDimension;
036import tec.uom.se.spi.DimensionalModel;
037import tec.uom.se.unit.AlternateUnit;
038import tec.uom.se.unit.AnnotatedUnit;
039import tec.uom.se.unit.ProductUnit;
040import tec.uom.se.unit.TransformedUnit;
041import tec.uom.se.unit.Units;
042
043import javax.measure.*;
044import javax.measure.quantity.Dimensionless;
045import javax.measure.spi.ServiceProvider;
046
047import java.io.Serializable;
048import java.math.BigInteger;
049import java.util.HashMap;
050import java.util.Map;
051import java.lang.reflect.ParameterizedType;
052import java.lang.reflect.Type;
053
054/**
055 * <p>
056 * The class represents units founded on the seven <b>SI</b> base units for seven base quantities assumed to be mutually independent.
057 * </p>
058 *
059 * <p>
060 * For all physics units, unit conversions are symmetrical: <code>u1.getConverterTo(u2).equals(u2.getConverterTo(u1).inverse())</code>. Non-physical
061 * units (e.g. currency units) for which conversion is not symmetrical should have their own separate class hierarchy and are considered distinct
062 * (e.g. financial units), although they can always be combined with physics units (e.g. "€/Kg", "$/h").
063 * </p>
064 *
065 * @see <a href= "http://en.wikipedia.org/wiki/International_System_of_Units">Wikipedia: International System of Units</a>
066 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
067 * @author <a href="mailto:units@catmedia.us">Werner Keil</a>
068 * @version 1.0.3, February 24, 2017
069 * @since 1.0
070 */
071public abstract class AbstractUnit<Q extends Quantity<Q>> implements Unit<Q>, Comparable<Unit<Q>>, Serializable {
072
073  /**
074     * 
075     */
076  private static final long serialVersionUID = -4344589505537030204L;
077
078  /**
079   * Holds the dimensionless unit <code>ONE</code>.
080   */
081  public static final Unit<Dimensionless> ONE = new ProductUnit<>();
082
083  /**
084   * Holds the name.
085   */
086  protected String name;
087
088  /**
089   * Holds the symbol.
090   */
091  private String symbol;
092
093  /**
094   * Holds the unique symbols collection (base units or alternate units).
095   */
096  protected static final Map<String, Unit<?>> SYMBOL_TO_UNIT = new HashMap<>();
097
098  /**
099   * DefaultQuantityFactory constructor.
100   */
101  protected AbstractUnit() {
102  }
103
104  protected Type getActualType() {
105    ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
106    return parameterizedType.getActualTypeArguments()[0].getClass().getGenericInterfaces()[0];
107  }
108
109  /**
110   * Indicates if this unit belongs to the set of coherent SI units (unscaled SI units).
111   * 
112   * 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
113   * following sense: when coherent units are used, equations between the numerical values of quantities take exactly the same form as the equations
114   * between the quantities themselves. Thus if only units from a coherent set are used, conversion factors between units are never required.
115   * 
116   * @return <code>equals(toSystemUnit())</code>
117   */
118  public boolean isSystemUnit() {
119    AbstractUnit<Q> si = this.toSystemUnit();
120    return (this == si) || this.equals(si);
121  }
122
123  /**
124   * Returns the unscaled {@link SI} unit from which this unit is derived.
125   * 
126   * They SI unit can be be used to identify a quantity given the unit. For example:[code] static boolean isAngularVelocity(AbstractUnit<?> unit) {
127   * return unit.toSI().equals(RADIAN.divide(SECOND)); } assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. [/code]
128   *
129   * @return the unscaled metric unit from which this unit is derived.
130   */
131  protected abstract AbstractUnit<Q> toSystemUnit();
132
133  /**
134   * Returns the converter from this unit to its unscaled {@link #toSysemUnit System Unit} unit.
135   *
136   * @return <code>getConverterTo(this.toSystemUnit())</code>
137   * @see #toSI
138   */
139  public abstract UnitConverter getSystemConverter();
140
141  /**
142   * Annotates the specified unit. Annotation does not change the unit semantic. Annotations are often written between curly braces behind units. For
143   * example: [code] AbstractUnit<Volume> PERCENT_VOL = Units.PERCENT.annotate("vol"); // "%{vol}" AbstractUnit<Mass> KG_TOTAL =
144   * Units.KILOGRAM.annotate("total"); // "kg{total}" AbstractUnit<Dimensionless> RED_BLOOD_CELLS = Units.ONE.annotate("RBC"); // "{RBC}" [/code]
145   *
146   * Note: Annotation of system units are not considered themselves as system units.
147   *
148   * @param annotation
149   *          the unit annotation.
150   * @return the annotated unit.
151   */
152  public AnnotatedUnit<Q> annotate(String annotation) {
153    return new AnnotatedUnit<>(this, annotation);
154  }
155
156  /**
157   * Returns the abstract unit represented by the specified characters as per standard <a href="http://www.unitsofmeasure.org/">UCUM</a> format.
158   *
159   * Locale-sensitive unit parsing should be handled using the OSGi {@link org.unitsofmeasurement.service.UnitFormatService} or for non-OSGi
160   * applications the {@link LocalUnitFormat} utility class.
161   *
162   * <p>
163   * Note: The standard format supports dimensionless units.[code] AbstractUnit<Dimensionless> PERCENT =
164   * AbstractUnit.parse("100").inverse().asType(Dimensionless.class); [/code]
165   * </p>
166   *
167   * @param charSequence
168   *          the character sequence to parse.
169   * @return <code>UCUMFormat.getCaseSensitiveInstance().parse(csq, new ParsePosition(0))</code>
170   * @throws ParserException
171   *           if the specified character sequence cannot be correctly parsed (e.g. not UCUM compliant).
172   */
173  public static Unit<?> parse(CharSequence charSequence) {
174    return ServiceProvider.current().getUnitFormatService().getUnitFormat().parse(charSequence);
175  }
176
177  /**
178   * 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
179   * locale. It can be used as a canonical string representation for exchanging units, or as a key for a Hashtable, etc.
180   *
181   * Locale-sensitive unit parsing should be handled using the {@link org.unitsofmeasurement.service.UnitFormat} service.
182   *
183   * @return <code>ServiceProvider.current().getUnitFormatService().getUnitFormat().format(this)</code>
184   */
185  @Override
186  public String toString() {
187    return ServiceProvider.current().getUnitFormatService().getUnitFormat().format(this);
188  }
189
190  // ///////////////////////////////////////////////////////
191  // Implements org.unitsofmeasurement.Unit<Q> interface //
192  // ///////////////////////////////////////////////////////
193
194  /**
195   * 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
196   * example:[code] static boolean isAngularVelocity(AbstractUnit<?> unit) { return unit.getSystemUnit().equals(RADIAN.divide(SECOND)); }
197   * assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. [/code]
198   *
199   * @return the unscaled metric unit from which this unit is derived.
200   */
201  @Override
202  public final AbstractUnit<Q> getSystemUnit() {
203    return toSystemUnit();
204  }
205
206  /**
207   * Indicates if this unit is compatible with the unit specified. To be compatible both units must be physics units having the same fundamental
208   * dimension.
209   *
210   * @param that
211   *          the other unit.
212   * @return <code>true</code> if this unit and that unit have equals fundamental dimension according to the current physics model; <code>false</code>
213   *         otherwise.
214   */
215  @Override
216  public final boolean isCompatible(Unit<?> that) {
217    if ((this == that) || this.equals(that))
218      return true;
219    if (!(that instanceof AbstractUnit))
220      return false;
221    Dimension thisDimension = this.getDimension();
222    Dimension thatDimension = that.getDimension();
223    if (thisDimension.equals(thatDimension))
224      return true;
225    DimensionalModel model = DimensionalModel.current(); // Use
226    // dimensional
227    // analysis
228    // model.
229    return model.getFundamentalDimension(thisDimension).equals(model.getFundamentalDimension(thatDimension));
230  }
231
232  /**
233   * Casts this unit to a parameterized unit of specified nature or throw a ClassCastException if the dimension of the specified quantity and this
234   * unit's dimension do not match (regardless whether or not the dimensions are independent or not).
235   *
236   * @param type
237   *          the quantity class identifying the nature of the unit.
238   * @throws ClassCastException
239   *           if the dimension of this unit is different from the SI dimension of the specified type.
240   * @see Units#getUnit(Class)
241   */
242  @SuppressWarnings("unchecked")
243  @Override
244  public final <T extends Quantity<T>> AbstractUnit<T> asType(Class<T> type) {
245    Dimension typeDimension = QuantityDimension.of(type);
246    if ((typeDimension != null) && (!typeDimension.equals(this.getDimension())))
247      throw new ClassCastException("The unit: " + this + " is not compatible with quantities of type " + type);
248    return (AbstractUnit<T>) this;
249  }
250
251  @Override
252  public abstract Map<? extends Unit<?>, Integer> getBaseUnits();
253
254  @Override
255  public abstract Dimension getDimension();
256
257  protected void setName(String name) {
258    this.name = name;
259  }
260
261  public String getName() {
262    return name;
263  }
264
265  public String getSymbol() {
266    return symbol;
267  }
268
269  protected void setSymbol(String s) {
270    this.symbol = s;
271  }
272
273  @Override
274  public final UnitConverter getConverterTo(Unit<Q> that) throws UnconvertibleException {
275    if ((this == that) || this.equals(that))
276      return AbstractConverter.IDENTITY; // Shortcut.
277    Unit<Q> thisSystemUnit = this.getSystemUnit();
278    Unit<Q> thatSystemUnit = that.getSystemUnit();
279    if (!thisSystemUnit.equals(thatSystemUnit))
280      try {
281        return getConverterToAny(that);
282      } catch (IncommensurableException e) {
283        throw new UnconvertibleException(e);
284      }
285    UnitConverter thisToSI = this.getSystemConverter();
286    UnitConverter thatToSI = that.getConverterTo(thatSystemUnit);
287    return thatToSI.inverse().concatenate(thisToSI);
288  }
289
290  @SuppressWarnings("rawtypes")
291  @Override
292  public final UnitConverter getConverterToAny(Unit<?> that) throws IncommensurableException, UnconvertibleException {
293    if (!isCompatible(that))
294      throw new IncommensurableException(this + " is not compatible with " + that);
295    AbstractUnit thatAbstr = (AbstractUnit) that; // Since both units are
296    // compatible they must
297    // be both physics
298    // units.
299    DimensionalModel model = DimensionalModel.current();
300    AbstractUnit thisSystemUnit = this.getSystemUnit();
301    UnitConverter thisToDimension = model.getDimensionalTransform(thisSystemUnit.getDimension()).concatenate(this.getSystemConverter());
302    AbstractUnit thatSystemUnit = thatAbstr.getSystemUnit();
303    UnitConverter thatToDimension = model.getDimensionalTransform(thatSystemUnit.getDimension()).concatenate(thatAbstr.getSystemConverter());
304    return thatToDimension.inverse().concatenate(thisToDimension);
305  }
306
307  @SuppressWarnings({ "rawtypes", "unchecked" })
308  @Override
309  public final Unit<Q> alternate(String symbol) {
310    return new AlternateUnit(this, symbol);
311  }
312
313  @Override
314  public final AbstractUnit<Q> transform(UnitConverter operation) {
315    AbstractUnit<Q> systemUnit = this.getSystemUnit();
316    UnitConverter cvtr = this.getSystemConverter().concatenate(operation);
317    if (cvtr.equals(AbstractConverter.IDENTITY))
318      return systemUnit;
319    return new TransformedUnit<>(systemUnit, cvtr);
320  }
321
322  @Override
323  public final AbstractUnit<Q> shift(double offset) {
324    if (offset == 0)
325      return this;
326    return transform(new AddConverter(offset));
327  }
328
329  @Override
330  public final AbstractUnit<Q> multiply(double factor) {
331    if (factor == 1)
332      return this;
333    if (isLongValue(factor))
334      return transform(new RationalConverter(BigInteger.valueOf((long) factor), BigInteger.ONE));
335    return transform(new MultiplyConverter(factor));
336  }
337
338  private static boolean isLongValue(double value) {
339    return !((value < Long.MIN_VALUE) || (value > Long.MAX_VALUE)) && Math.floor(value) == value;
340  }
341
342  /**
343   * Returns the product of this unit with the one specified.
344   *
345   * <p>
346   * Note: If the specified unit (that) is not a physical unit, then <code>that.multiply(this)</code> is returned.
347   * </p>
348   *
349   * @param that
350   *          the unit multiplicand.
351   * @return <code>this * that</code>
352   */
353  @Override
354  public final Unit<?> multiply(Unit<?> that) {
355    if (that instanceof AbstractUnit)
356      return multiply((AbstractUnit<?>) that);
357    // return that.multiply(this); // Commutatif.
358    return ProductUnit.getProductInstance(this, that);
359  }
360
361  /**
362   * Returns the product of this physical unit with the one specified.
363   *
364   * @param that
365   *          the physical unit multiplicand.
366   * @return <code>this * that</code>
367   */
368  public final Unit<?> multiply(AbstractUnit<?> that) {
369    if (this.equals(ONE))
370      return that;
371    if (that.equals(ONE))
372      return this;
373    return ProductUnit.getProductInstance(this, that);
374  }
375
376  /**
377   * Returns the inverse of this physical unit.
378   *
379   * @return <code>1 / this</code>
380   */
381  @Override
382  public final Unit<?> inverse() {
383    if (this.equals(ONE))
384      return this;
385    return ProductUnit.getQuotientInstance(ONE, this);
386  }
387
388  /**
389   * Returns the result of dividing this unit by the specifified divisor. If the factor is an integer value, the division is exact. For example:
390   * 
391   * <pre>
392   * <code>
393   *    QUART = GALLON_LIQUID_US.divide(4); // Exact definition.
394   * </code>
395   * </pre>
396   * 
397   * @param divisor
398   *          the divisor value.
399   * @return this unit divided by the specified divisor.
400   */
401  @Override
402  public final AbstractUnit<Q> divide(double divisor) {
403    if (divisor == 1)
404      return this;
405    if (isLongValue(divisor))
406      return transform(new RationalConverter(BigInteger.ONE, BigInteger.valueOf((long) divisor)));
407    return transform(new MultiplyConverter(1.0 / divisor));
408  }
409
410  /**
411   * Returns the quotient of this unit with the one specified.
412   *
413   * @param that
414   *          the unit divisor.
415   * @return <code>this.multiply(that.inverse())</code>
416   */
417  @Override
418  public final Unit<?> divide(Unit<?> that) {
419    return this.multiply(that.inverse());
420  }
421
422  /**
423   * Returns the quotient of this physical unit with the one specified.
424   *
425   * @param that
426   *          the physical unit divisor.
427   * @return <code>this.multiply(that.inverse())</code>
428   */
429  public final Unit<?> divide(AbstractUnit<?> that) {
430    return this.multiply(that.inverse());
431  }
432
433  /**
434   * Returns a unit equals to the given root of this unit.
435   *
436   * @param n
437   *          the root's order.
438   * @return the result of taking the given root of this unit.
439   * @throws ArithmeticException
440   *           if <code>n == 0</code> or if this operation would result in an unit with a fractional exponent.
441   */
442  @Override
443  public final Unit<?> root(int n) {
444    if (n > 0)
445      return ProductUnit.getRootInstance(this, n);
446    else if (n == 0)
447      throw new ArithmeticException("Root's order of zero");
448    else
449      // n < 0
450      return ONE.divide(this.root(-n));
451  }
452
453  /**
454   * Returns a unit equals to this unit raised to an exponent.
455   *
456   * @param n
457   *          the exponent.
458   * @return the result of raising this unit to the exponent.
459   */
460  @Override
461  public final Unit<?> pow(int n) {
462    if (n > 0)
463      return this.multiply(this.pow(n - 1));
464    else if (n == 0)
465      return ONE;
466    else
467      // n < 0
468      return ONE.divide(this.pow(-n));
469  }
470
471  /**
472   * Compares this unit to the specified unit. The default implementation compares the name and symbol of both this unit and the specified unit.
473   *
474   * @return a negative integer, zero, or a positive integer as this unit is less than, equal to, or greater than the specified unit.
475   */
476  public int compareTo(Unit<Q> that) {
477    if (name != null && getSymbol() != null) {
478      return name.compareTo(that.getName()) + getSymbol().compareTo(that.getSymbol());
479    } else if (name == null) {
480      if (getSymbol() != null && that.getSymbol() != null) {
481        return getSymbol().compareTo(that.getSymbol());
482      } else {
483        return -1;
484      }
485    } else if (getSymbol() == null) {
486      if (name != null) {
487        return name.compareTo(that.getName());
488      } else {
489        return -1;
490      }
491    } else {
492      return -1;
493    }
494  }
495
496  // //////////////////////////////////////////////////////////////
497  // Ensures that sub-classes implements hashCode/equals method.
498  // //////////////////////////////////////////////////////////////
499
500  @Override
501  public abstract int hashCode();
502
503  @Override
504  public abstract boolean equals(Object that);
505}