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