001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2020, Jean-Marie Dautelle, Werner Keil, Otavio Santana.
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-385, Indriya nor the names of their 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 tech.units.indriya;
031
032import java.math.BigDecimal;
033import java.util.Objects;
034
035import javax.measure.Quantity;
036import javax.measure.Unit;
037import javax.measure.format.QuantityFormat;
038import javax.measure.quantity.Dimensionless;
039
040import static javax.measure.Quantity.Scale.ABSOLUTE;
041
042import tech.units.indriya.format.SimpleQuantityFormat;
043import tech.units.indriya.format.SimpleUnitFormat;
044import tech.units.indriya.function.Calculus;
045import tech.units.indriya.internal.function.ScaleHelper;
046import tech.units.indriya.quantity.Quantities;
047import tech.units.indriya.spi.NumberSystem;
048import tech.uom.lib.common.function.UnitSupplier;
049import tech.uom.lib.common.function.ValueSupplier;
050
051/**
052 * <p>
053 * This class represents the immutable result of a scalar quantity stated in a known unit.
054 * </p>
055 *
056 * <p>
057 * To avoid any loss of precision, known exact quantities (e.g. physical constants) should not be created from <code>double</code> constants but from
058 * their decimal representation.<br>
059 * <code>
060 *         public static final Quantity&lt;Velocity&gt; C = NumberQuantity.parse("299792458 m/s").asType(Velocity.class);
061 *         // Speed of Light (exact).
062 *    </code>
063 * </p>
064 * 
065 * <p>
066 * Quantities can be converted to different units.<br>
067 * <code>
068 *         Quantity&lt;Velocity&gt; milesPerHour = C.to(MILES_PER_HOUR); // Use double implementation (fast).
069 *         System.out.println(milesPerHour);
070 * 
071 *         &gt; 670616629.3843951 m/h
072 *     </code>
073 * </p>
074 * 
075 * <p>
076 * Applications may sub-class {@link AbstractQuantity} for particular quantity types.<br>
077 * <code>
078 *         // Quantity of type Mass based on double primitive types.<br>
079 * public class MassAmount extends AbstractQuantity&lt;Mass&gt; {<br>
080 * private final double kilograms; // Internal SI representation.<br>
081 * private Mass(double kg) { kilograms = kg; }<br>
082 * public static Mass of(double value, Unit&lt;Mass&gt; unit) {<br>
083 * return new Mass(unit.getConverterTo(SI.KILOGRAM).convert(value));<br>
084 * }<br>
085 * public Unit&lt;Mass&gt; getUnit() { return SI.KILOGRAM; }<br>
086 * public Double getValue() { return kilograms; }<br>
087 * ...<br>
088 * }<br>
089 * <br>
090 * // Complex number quantities.<br>
091 * public class ComplexQuantity
092 * &lt;Q extends Quantity&gt;extends AbstractQuantity
093 * &lt;Q&gt;{<br>
094 * public Complex getValue() { ... } // Assuming Complex is a Number.<br>
095 * ...<br>
096 * }<br>
097 * <br>
098 * // Specializations of complex numbers quantities.<br>
099 * public final class Current extends ComplexQuantity&lt;ElectricCurrent&gt; {...}<br>
100 * public final class Tension extends ComplexQuantity&lt;ElectricPotential&gt; {...} <br>
101 * </code>
102 * </p>
103 * 
104 * <p>
105 * All instances of this class shall be immutable.
106 * </p>
107 *
108 * @author <a href="mailto:[email protected]">Werner Keil</a>
109 * @author Andi Huber
110 * @version 2.0, Nov 21, 2020
111 * @since 1.0
112 */
113@SuppressWarnings("unchecked")
114public abstract class AbstractQuantity<Q extends Quantity<Q>> implements ComparableQuantity<Q>, UnitSupplier<Q>, ValueSupplier<Number> {
115
116   /**
117    * 
118    */
119    private static final long serialVersionUID = 293852425369811882L;
120
121    private final Unit<Q> unit;
122
123    private final Scale scale;
124    
125    /**
126     * Holds a dimensionless quantity of none (exact).
127     */
128    public static final Quantity<Dimensionless> NONE = Quantities.getQuantity(0, AbstractUnit.ONE);
129
130    /**
131     * Holds a dimensionless quantity of one (exact).
132     */
133    public static final Quantity<Dimensionless> ONE = Quantities.getQuantity(1, AbstractUnit.ONE);
134
135    /**
136     * Constructor.
137     * @param unit a unit
138     * @param sca the scale, absolute or relative
139     */
140    protected AbstractQuantity(Unit<Q> unit, Scale sca) {
141        this.unit = unit;
142        this.scale = sca;
143    }
144
145    /**
146     * Constructor. Applies {@code ABSOLUTE} {@code Scale} if none was given.
147     * @param unit a unit
148     */
149    protected AbstractQuantity(Unit<Q> unit) {
150        this(unit, ABSOLUTE);
151    }
152
153    /**
154     * Returns the numeric value of the quantity.
155     *
156     * @return the quantity value.
157     */
158    @Override
159    public abstract Number getValue();
160
161    /**
162     * Returns the measurement unit.
163     *
164     * @return the measurement unit.
165     */
166    @Override
167    public Unit<Q> getUnit() {
168        return unit;
169    }
170
171    /**
172     * Returns the absolute or relative scale.
173     *
174     * @return the scale.
175     */
176    @Override
177    public Scale getScale() {
178        return scale;
179    }
180
181    /**
182     * Returns this quantity after conversion to specified unit. The default implementation returns
183     * <code>NumberQuantity.of(doubleValue(unit), unit)</code> . If this quantity is already stated in the specified unit, then this quantity is
184     * returned and no conversion is performed.
185     *
186     * @param anotherUnit
187     *            the unit in which the returned quantity is stated.
188     * @return this quantity or a new quantity equivalent to this quantity stated in the specified unit.
189     * @throws ArithmeticException
190     *             if the result is inexact and the quotient has a non-terminating decimal expansion.
191     */
192    @Override
193    public ComparableQuantity<Q> to(Unit<Q> anotherUnit) {
194        if (anotherUnit.equals(this.getUnit())) {
195            return this;
196        }
197        return ScaleHelper.convertTo(this, anotherUnit);
198    }
199
200    @Override
201    public boolean isGreaterThan(Quantity<Q> that) {
202        return this.compareTo(that) > 0;
203    }
204
205    @Override
206    public boolean isGreaterThanOrEqualTo(Quantity<Q> that) {
207        return this.compareTo(that) >= 0;
208    }
209
210    @Override
211    public boolean isLessThan(Quantity<Q> that) {
212        return this.compareTo(that) < 0;
213    }
214
215    @Override
216    public boolean isLessThanOrEqualTo(Quantity<Q> that) {
217        return this.compareTo(that) <= 0;
218    }
219
220    @Override
221    public boolean isEquivalentTo(Quantity<Q> that) {
222        return this.compareTo(that) == 0;
223    }
224
225    /**
226     * 
227     * FIXME[220] update java-doc
228     * Compares this quantity to the specified quantity. The default implementation compares the {@link AbstractQuantity#doubleValue(Unit)}
229     * of both this quantity and the specified quantity stated in the same unit (this quantity's {@link #getUnit() unit}).
230     *
231     * @return a negative integer, zero, or a positive integer as this quantity is less than, equal to, or greater than the specified Measurement
232     *         quantity.
233     * @see {@link tech.uom.lib.common.util.NaturalQuantityComparator NaturalQuantityComparator}
234     */
235    @Override
236    public int compareTo(Quantity<Q> that) {
237        if (this.getUnit().equals(that.getUnit())) {
238            return numberSystem().compare(this.getValue(), that.getValue());
239        }
240        return numberSystem().compare(this.getValue(), that.to(this.getUnit()).getValue());
241    }
242
243    /**
244     * Compares this quantity against the specified object for <b>strict</b> equality (same unit and same amount).
245     *
246     * <p>
247     * Similarly to the {@link BigDecimal#equals} method which consider 2.0 and 2.00 as different objects because of different internal scales,
248     * quantities such as <code>Quantities.getQuantity(3.0, KILOGRAM)</code> <code>Quantities.getQuantity(3, KILOGRAM)</code> and
249     * <code>Quantities.getQuantity("3 kg")</code> might not be considered equals because of possible differences in their implementations.
250     * </p>
251     *
252     * <p>
253     * To compare quantities stated using different units or using different amount implementations the {@link #compareTo compareTo} or
254     * {@link #equals(javax.measure.Quantity, double, javax.measure.unit.Unit) equals(Quantity, epsilon, epsilonUnit)} methods should be used.
255     * </p>
256     *
257     * @param obj
258     *            the object to compare with.
259     * @return <code>this.getUnit.equals(obj.getUnit())
260     *         && this.getLevel().equals(obj.getLevel()
261     *         && this.getValue().equals(obj.getValue())</code>
262     */
263    @Override
264    public boolean equals(Object obj) {
265        if (this == obj) {
266            return true;
267        }
268        if (obj instanceof Quantity<?>) {
269            Quantity<?> that = (Quantity<?>) obj;
270            return Objects.equals(getUnit(), that.getUnit()) && 
271                    Objects.equals(getScale(), that.getScale()) && 
272                    Objects.equals(getValue(), that.getValue());
273        }
274        return false;
275    }
276
277    /**
278     * Returns the hash code for this quantity.
279     *
280     * @return the hash code value.
281     */
282    @Override
283    public int hashCode() {
284        return Objects.hash(getUnit(), getScale(), getValue());
285    }
286
287    /**
288     * Returns the <code>String</code> representation of this quantity. The string produced for a given quantity is always the same; it is not
289     * affected by locale. This means that it can be used as a canonical string representation for exchanging quantity, or as a key for a Hashtable,
290     * etc. Locale-sensitive quantity formatting and parsing is handled by the {@link QuantityFormat} implementations and its subclasses.
291     *
292     * @return <code>SimpleQuantityFormat.getInstance().format(this)</code>
293     */
294    @Override
295    public String toString() {
296        return SimpleQuantityFormat.getInstance().format(this);
297    }
298
299    @Override
300    public <T extends Quantity<T>, E extends Quantity<E>> ComparableQuantity<E> 
301    divide(Quantity<T> that, Class<E> asTypeQuantity) {
302        return divide(Objects.requireNonNull(that))
303                .asType(Objects.requireNonNull(asTypeQuantity));
304    }
305
306    @Override
307    public <T extends Quantity<T>, E extends Quantity<E>> ComparableQuantity<E> 
308    multiply(Quantity<T> that, Class<E> asTypeQuantity) {
309        return multiply(Objects.requireNonNull(that))
310                .asType(Objects.requireNonNull(asTypeQuantity));
311    }
312
313    @Override
314    public <T extends Quantity<T>> ComparableQuantity<T> inverse(Class<T> quantityClass) {
315        return inverse().asType(quantityClass);
316    }
317
318    /**
319     * Casts this quantity to a parameterized quantity of specified nature or throw a <code>ClassCastException</code> if the dimension of the
320     * specified quantity and its unit's dimension do not match. For example:<br>
321     * <code>
322     *     Quantity<Length> length = AbstractQuantity.parse("2 km").asType(Length.class);
323     * </code>
324     *
325     * @param type
326     *            the quantity class identifying the nature of the quantity.
327     * @return this quantity parameterized with the specified type.
328     * @throws ClassCastException
329     *             if the dimension of this unit is different from the specified quantity dimension.
330     * @throws UnsupportedOperationException
331     *             if the specified quantity class does not have a public static field named "UNIT" holding the SI unit for the quantity.
332     * @see Unit#asType(Class)
333     */
334    public final <T extends Quantity<T>> ComparableQuantity<T> 
335    asType(Class<T> type) throws ClassCastException {
336        this.getUnit().asType(type); // ClassCastException if dimension mismatches.
337        return (ComparableQuantity<T>) this;
338    }
339
340    /**
341     * Returns the quantity of unknown type corresponding to the specified representation. This method can be used to parse dimensionless
342     * quantities.<br>
343     * <code>
344     *     Quantity<Dimensionless> proportion = AbstractQuantity.parse("0.234").asType(Dimensionless.class);
345     * </code>
346     *
347     * <p>
348     * Note: This method handles only {@link SimpleUnitFormat#getStandard standard} unit format. Locale-sensitive quantity parsing is currently not
349     * supported.
350     * </p>
351     *
352     * @param csq
353     *            the decimal value and its unit (if any) separated by space(s).
354     * @return <code>QuantityFormat.getInstance().parse(csq)</code>
355     */
356    public static Quantity<?> parse(CharSequence csq) {
357        return SimpleQuantityFormat.getInstance().parse(csq);
358    }
359
360    protected boolean hasFraction(double value) {
361        return Math.round(value) != value;
362    }
363
364    protected boolean hasFraction(BigDecimal value) {
365        return value.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) != 0;
366    }
367
368    protected NumberSystem numberSystem() {
369        return Calculus.currentNumberSystem();
370    } 
371}