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