001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2024, 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.6 Oct 6, 2024
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    /**
126     * Holds the measurement unit
127     */
128    private final Unit<Q> unit;
129
130    /**
131     * Holds the measurement scale
132     */
133    private final Scale scale;
134    
135    /**
136     * Holds a dimensionless quantity of none (exact).
137     */
138    public static final Quantity<Dimensionless> NONE = Quantities.getQuantity(0, AbstractUnit.ONE);
139
140    /**
141     * Holds a dimensionless quantity of one (exact).
142     */
143    public static final Quantity<Dimensionless> ONE = Quantities.getQuantity(1, AbstractUnit.ONE);
144
145    /**
146     * Constructor.
147     * @param unit a unit
148     * @param scale the scale, absolute or relative
149     */
150    protected AbstractQuantity(Unit<Q> unit, Scale scale) {
151        this.unit = unit;
152        this.scale = scale;
153    }
154
155    /**
156     * Constructor. Applies {@code ABSOLUTE} {@code Scale} if none was given.
157     * @param unit a unit
158     */
159    protected AbstractQuantity(Unit<Q> unit) {
160        this(unit, ABSOLUTE);
161    }
162
163    /**
164     * Returns the numeric value of the quantity.
165     *
166     * @return the quantity value.
167     */
168    @Override
169    public abstract Number getValue();
170
171    /**
172     * Returns the measurement unit.
173     *
174     * @return the measurement unit.
175     */
176    @Override
177    public Unit<Q> getUnit() {
178        return unit;
179    }
180
181    /**
182     * Returns the absolute or relative scale.
183     *
184     * @return the scale.
185     */
186    @Override
187    public Scale getScale() {
188        return scale;
189    }
190
191    /**
192     * Returns this quantity after conversion to specified unit. The default implementation returns
193     * <code>NumberQuantity.of(doubleValue(unit), unit)</code> . If this quantity is already stated in the specified unit, then this quantity is
194     * returned and no conversion is performed.
195     *
196     * @param anotherUnit
197     *            the unit in which the returned quantity is stated.
198     * @return this quantity or a new quantity equivalent to this quantity stated in the specified unit.
199     * @throws ArithmeticException
200     *             if the result is inexact and the quotient has a non-terminating decimal expansion.
201     */
202    @Override
203    public ComparableQuantity<Q> to(Unit<Q> anotherUnit) {
204        if (anotherUnit.equals(this.getUnit())) {
205            return this;
206        }
207        return ScaleHelper.convertTo(this, anotherUnit);
208    }
209
210    @Override
211    public boolean isGreaterThan(Quantity<Q> that) {
212        return this.compareTo(that) > 0;
213    }
214
215    @Override
216    public boolean isGreaterThanOrEqualTo(Quantity<Q> that) {
217        return this.compareTo(that) >= 0;
218    }
219
220    @Override
221    public boolean isLessThan(Quantity<Q> that) {
222        return this.compareTo(that) < 0;
223    }
224
225    @Override
226    public boolean isLessThanOrEqualTo(Quantity<Q> that) {
227        return this.compareTo(that) <= 0;
228    }
229
230    @Override
231    public boolean isEquivalentTo(Quantity<Q> that) {
232        return this.compareTo(that) == 0;
233    }
234
235    /** 
236     * Compares this quantity to the specified quantity. The default implementation compares the value of both this quantity and the specified quantity
237     *  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.
238     *
239     * @param  that {@code Quantity} to which this {@code AbstractQuantity} is to be compared.
240     *
241     * @implNote
242     * Note: this class uses implicit unit conversion that is inconsistent with {@code equals}.
243     * 
244     * @return a negative integer, zero, or a positive integer as this quantity is less than, equal/equivalent to, or greater than the specified Measurement
245     *         quantity.
246     * @see {@link tech.units.indriya.spi.NumberSystem#compare}
247     */
248    @Override
249    public int compareTo(Quantity<Q> that) {
250        if (this.getUnit().equals(that.getUnit())) {
251            return numberSystem().compare(this.getValue(), that.getValue());
252        }
253        return numberSystem().compare(this.getValue(), that.to(this.getUnit()).getValue());
254    }
255
256    /**
257     * Compares this quantity against the specified object for <b>strict</b> equality (same unit and same amount).
258     *
259     * <p>
260     * Similarly to the {@link BigDecimal#equals} method which consider 2.0 and 2.00 as different objects because of different internal scales,
261     * quantities such as <code>Quantities.getQuantity(3.0, KILOGRAM)</code> <code>Quantities.getQuantity(3, KILOGRAM)</code> and
262     * <code>Quantities.getQuantity("3 kg")</code> might not be considered equals because of possible differences in their implementations.
263     * </p>
264     *
265     * <p>
266     * To compare quantities stated using different units or using different amount implementations the {@link #compareTo compareTo} or
267     * {@link #isEquivalentTo} methods should be used.
268     * </p>
269     *
270     * @param obj
271     *            the object to compare with.
272     * @return <code>this.getUnit.equals(obj.getUnit())
273     *         && this.getScale().equals(obj.getScale()
274     *         && this.getValue().equals(obj.getValue())</code>
275     */
276    @Override
277    public boolean equals(Object obj) {
278        if (this == obj) {
279            return true;
280        }
281        if (obj instanceof Quantity<?>) {
282            Quantity<?> that = (Quantity<?>) obj;
283            return Objects.equals(getUnit(), that.getUnit()) && 
284                    Objects.equals(getScale(), that.getScale()) && 
285                    Objects.equals(getValue(), that.getValue());
286        }
287        return false;
288    }
289
290    /**
291     * Returns the hash code for this quantity.
292     *
293     * @return the hash code value.
294     */
295    @Override
296    public int hashCode() {
297        return Objects.hash(getUnit(), getScale(), getValue());
298    }
299
300    /**
301     * Returns the <code>String</code> representation of this quantity. The string produced for a given quantity is always the same; it is not
302     * 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,
303     * etc. Locale-sensitive quantity formatting and parsing is handled by the {@link QuantityFormat} implementations and its subclasses.
304     *
305     * @return <code>SimpleQuantityFormat.getInstance().format(this)</code>
306     */
307    @Override
308    public String toString() {
309        return SimpleQuantityFormat.getInstance().format(this);
310    }
311
312    @Override
313    public <T extends Quantity<T>, E extends Quantity<E>> ComparableQuantity<E> 
314    divide(Quantity<T> that, Class<E> asTypeQuantity) {
315        return divide(Objects.requireNonNull(that))
316                .asType(Objects.requireNonNull(asTypeQuantity));
317    }
318
319    @Override
320    public <T extends Quantity<T>, E extends Quantity<E>> ComparableQuantity<E> 
321    multiply(Quantity<T> that, Class<E> asTypeQuantity) {
322        return multiply(Objects.requireNonNull(that))
323                .asType(Objects.requireNonNull(asTypeQuantity));
324    }
325
326    @Override
327    public <T extends Quantity<T>> ComparableQuantity<T> inverse(Class<T> quantityClass) {
328        return inverse().asType(quantityClass);
329    }
330
331    /**
332     * Casts this quantity to a parameterized quantity of specified nature or throw a <code>ClassCastException</code> if the dimension of the
333     * specified quantity and its unit's dimension do not match. For example:<br>
334     * <code>
335     *     Quantity<Length> length = AbstractQuantity.parse("2 km").asType(Length.class);
336     * </code>
337     *
338     * @param type
339     *            the quantity class identifying the nature of the quantity.
340     * @return this quantity parameterized with the specified type.
341     * @throws ClassCastException
342     *             if the dimension of this unit is different from the specified quantity dimension.
343     * @throws UnsupportedOperationException
344     *             if the specified quantity class does not have a public static field named "UNIT" holding the SI unit for the quantity.
345     * @see Unit#asType(Class)
346     */
347    public final <T extends Quantity<T>> ComparableQuantity<T> 
348    asType(Class<T> type) throws ClassCastException {
349        this.getUnit().asType(type); // ClassCastException if dimension mismatches.
350        return (ComparableQuantity<T>) this;
351    }
352
353    /**
354     * Returns the quantity of unknown type corresponding to the specified representation. This method can be used to parse dimensionless
355     * quantities.<br>
356     * <code>
357     *     Quantity<Dimensionless> proportion = AbstractQuantity.parse("0.234").asType(Dimensionless.class);
358     * </code>
359     *
360     * <p>
361     * <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
362     * 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.
363     * </p>
364     *
365     * @param csq
366     *            the decimal value and its unit (if any) separated by space(s).
367     * @return <code>SimpleQuantityFormat.getInstance().parse(csq)</code>
368     */
369    public static Quantity<?> parse(CharSequence csq) {
370        return SimpleQuantityFormat.getInstance().parse(csq);
371    }
372
373    /** @deprecated seems unused */
374    protected boolean hasFraction(double value) {
375        return Math.round(value) != value;
376    }
377
378    /** @deprecated seems unused */
379    protected boolean hasFraction(BigDecimal value) {
380        return value.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) != 0;
381    }
382
383    private NumberSystem numberSystem() {
384        return Calculus.currentNumberSystem();
385    } 
386}