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