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 static javax.measure.Quantity.Scale.ABSOLUTE;
033import static org.apiguardian.api.API.Status.EXPERIMENTAL;
034
035import java.io.Serializable;
036import java.lang.reflect.ParameterizedType;
037import java.lang.reflect.Type;
038import java.util.HashMap;
039import java.util.Map;
040
041import javax.measure.Dimension;
042import javax.measure.IncommensurableException;
043import javax.measure.Prefix;
044import javax.measure.Quantity;
045import javax.measure.UnconvertibleException;
046import javax.measure.Unit;
047import javax.measure.UnitConverter;
048import javax.measure.Quantity.Scale;
049import javax.measure.format.MeasurementParseException;
050import javax.measure.quantity.Dimensionless;
051
052import org.apiguardian.api.API;
053
054import tech.units.indriya.format.LocalUnitFormat;
055import tech.units.indriya.format.SimpleUnitFormat;
056import tech.units.indriya.function.AbstractConverter;
057import tech.units.indriya.function.AddConverter;
058import tech.units.indriya.function.Calculus;
059import tech.units.indriya.function.MultiplyConverter;
060import tech.units.indriya.function.RationalNumber;
061import tech.units.indriya.internal.function.Calculator;
062import tech.units.indriya.spi.DimensionalModel;
063import tech.units.indriya.unit.AlternateUnit;
064import tech.units.indriya.unit.AnnotatedUnit;
065import tech.units.indriya.unit.ProductUnit;
066import tech.units.indriya.unit.TransformedUnit;
067import tech.units.indriya.unit.UnitDimension;
068import tech.units.indriya.unit.Units;
069import tech.uom.lib.common.function.Nameable;
070import tech.uom.lib.common.function.PrefixOperator;
071import tech.uom.lib.common.function.SymbolSupplier;
072
073/**
074 * <p>
075 * The class represents units founded on the seven <b>SI</b> base units for
076 * seven base quantities assumed to be mutually independent.
077 * </p>
078 *
079 * <p>
080 * For all physics units, unit conversions are symmetrical:
081 * <code>u1.getConverterTo(u2).equals(u2.getConverterTo(u1).inverse())</code>.
082 * Non-physical units (e.g. currency units) for which conversion is not
083 * symmetrical should have their own separate class hierarchy and are considered
084 * distinct (e.g. financial units), although they can always be combined with
085 * physics units (e.g. "€/Kg", "$/h").
086 * </p>
087 *
088 * @see <a href=
089 *      "http://en.wikipedia.org/wiki/International_System_of_Units">Wikipedia:
090 *      International System of Units</a>
091 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
092 * @author <a href="mailto:werner@units.tech">Werner Keil</a>
093 * @version 4.1, October 4, 2024
094 * @since 1.0
095 */
096public abstract class AbstractUnit<Q extends Quantity<Q>>
097                implements Unit<Q>, Comparable<Unit<Q>>, PrefixOperator<Q>, Nameable, Serializable, SymbolSupplier {
098
099        /**
100         * 
101         */
102        private static final long serialVersionUID = -4344589505537030204L;
103
104        /**
105         * Holds the dimensionless unit <code>ONE</code>.
106         * 
107         * @see <a href=
108         *      "https://en.wikipedia.org/wiki/Natural_units#Choosing_constants_to_normalize">
109         *      Wikipedia: Natural Units - Choosing constants to normalize</a>
110         * @see <a href= "http://www.av8n.com/physics/dimensionless-units.htm">Units of
111         *      Dimension One</a>
112         */
113        public static final Unit<Dimensionless> ONE = new ProductUnit<>();
114
115        /**
116         * Holds the name.
117         */
118        private String name;
119
120        /**
121         * Holds the symbol.
122         */
123        private String symbol;
124        
125    /**
126     * Holds the measurement scale
127     */
128        protected Scale scale = ABSOLUTE;
129
130        /**
131         * Holds the unique symbols collection (base units or alternate units).
132         */
133        protected static final transient Map<String, Unit<?>> SYMBOL_TO_UNIT = new HashMap<>();
134
135        /**
136         * Default constructor.
137         */
138        protected AbstractUnit() {
139        }
140
141        /**
142         * Constructor setting a symbol.
143         * 
144         * @param symbol the unit symbol.
145         */
146        protected AbstractUnit(String symbol) {
147                this.symbol = symbol;
148        }
149
150        protected Type getActualType() {
151                ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
152                return parameterizedType.getActualTypeArguments()[0].getClass().getGenericInterfaces()[0];
153        }
154
155        /**
156         * Indicates if this unit belongs to the set of coherent SI units (unscaled SI
157         * units).
158         * 
159         * The base and coherent derived units of the SI form a coherent set, designated
160         * the set of coherent SI units. The word coherent is used here in the following
161         * sense: when coherent units are used, equations between the numerical values
162         * of quantities take exactly the same form as the equations between the
163         * quantities themselves. Thus if only units from a coherent set are used,
164         * conversion factors between units are never required.
165         * 
166         * @return <code>equals(toSystemUnit())</code>
167         */
168        public boolean isSystemUnit() {
169                Unit<Q> sys = this.toSystemUnit();
170                return this == sys || this.equals(sys);
171        }
172        
173        /**
174         * Returns the converter from this unit to its unscaled {@link #toSystemUnit
175         * System Unit} unit.
176         *
177         * @return <code>getConverterTo(this.toSystemUnit())</code>
178         * @see #toSystemUnit
179         */
180        public abstract UnitConverter getSystemConverter();
181        
182        /**
183         * Returns the unscaled {@link SI} unit from which this unit is derived.
184         * 
185         * The SI unit can be be used to identify a quantity given the unit. For
186         * example:<code> static boolean isAngularVelocity(AbstractUnit<?> unit) {
187         * return unit.toSystemUnit().equals(RADIAN.divide(SECOND)); } assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. </code>
188         *
189         * @return the unscaled metric unit from which this unit is derived.
190         */
191        protected abstract Unit<Q> toSystemUnit();
192
193        /**
194         * Annotates the specified unit. Annotation does not change the unit semantic.
195         * Annotations are often written between curly braces behind units. For
196         * example:<br>
197         * <code> Unit<Volume> PERCENT_VOL = ((AbstractUnit)Units.PERCENT).annotate("vol"); // "%{vol}" Unit<Mass> KG_TOTAL =
198         * ((AbstractUnit)Units.KILOGRAM).annotate("total"); // "kg{total}" Unit<Dimensionless> RED_BLOOD_CELLS = ((AbstractUnit)Units.ONE).annotate("RBC"); // "{RBC}" </code>
199         *
200         * Note: Annotation of system units are not considered themselves as system
201         * units.
202         *
203         * @param annotation the unit annotation.
204         * @return the annotated unit.
205         */
206    public final Unit<Q> annotate(String annotation) {
207      return new AnnotatedUnit<>(this, annotation);
208    }
209
210        /**
211         * Returns the abstract unit represented by the specified characters as per
212         * default format.
213         *
214         * Locale-sensitive unit parsing could be handled using {@link LocalUnitFormat}
215         * in subclasses of AbstractUnit.
216         *
217         * <p>
218         * Note: The standard format supports dimensionless
219         * units.<code> AbstractUnit<Dimensionless> PERCENT =
220         * AbstractUnit.parse("100").inverse().asType(Dimensionless.class); </code>
221         * </p>
222         *
223         * @param charSequence the character sequence to parse.
224         * @return <code>SimpleUnitFormat.getInstance().parse(csq)</code>
225         * @throws MeasurementParseException if the specified character sequence cannot
226         *                                   be correctly parsed (e.g. not UCUM
227         *                                   compliant).
228         */
229        public static Unit<?> parse(CharSequence charSequence) {
230                return SimpleUnitFormat.getInstance().parse(charSequence);
231        }
232
233        /**
234         * Returns the standard representation of this physics unit. The string produced
235         * for a given unit is always the same; it is not affected by the locale. It can
236         * be used as a canonical string representation for exchanging units, or as a
237         * key for a Hashtable, etc.
238         *
239         * Locale-sensitive unit parsing could be handled using {@link LocalUnitFormat}
240         * in subclasses of AbstractUnit.
241         *
242         * @return <code>SimpleUnitFormat.getInstance().format(this)</code>
243         */
244        @Override
245        public String toString() {
246                return SimpleUnitFormat.getInstance().format(this);
247        }
248
249        // ///////////////////////////////////////////////////////
250        // Implements javax.measure.Unit<Q> interface //
251        // ///////////////////////////////////////////////////////
252
253        /**
254         * Returns the system unit (unscaled SI unit) from which this unit is derived.
255         * They can be be used to identify a quantity given the unit. For example:<br>
256         * <code> static boolean isAngularVelocity(AbstractUnit<?> unit) {<br>&nbsp;&nbsp;return unit.getSystemUnit().equals(RADIAN.divide(SECOND));<br>}
257         * <br>assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. </code>
258         *
259         * @return the unscaled metric unit from which this unit is derived.
260         */
261        @Override
262        public final Unit<Q> getSystemUnit() {
263                return toSystemUnit();
264        }
265
266        /**
267         * Indicates if this unit is compatible with the unit specified. To be
268         * compatible both units must be physics units having the same fundamental
269         * dimension.
270         *
271         * @param that the other unit.
272         * @return <code>true</code> if this unit and that unit have the same
273         *         fundamental dimension according to the current dimensional model;
274         *         <code>false</code> otherwise.
275         */
276        @Override
277        public final boolean isCompatible(Unit<?> that) {
278                return internalIsCompatible(that, true);
279        }
280
281        /**
282         * Casts this unit to a parameterized unit of specified nature or throw a
283         * ClassCastException if the dimension of the specified quantity and this unit's
284         * dimension do not match (regardless whether or not the dimensions are
285         * independent or not).
286         *
287         * @param type the quantity class identifying the nature of the unit.
288         * @throws ClassCastException if the dimension of this unit is different from
289         *                            the SI dimension of the specified type.
290         * @see Units#getUnit(Class)
291         */
292        @SuppressWarnings("unchecked")
293        @Override
294        public final <T extends Quantity<T>> Unit<T> asType(Class<T> type) {
295                Dimension typeDimension = UnitDimension.of(type);
296                if (typeDimension != null && !typeDimension.equals(this.getDimension()))
297                        throw new ClassCastException("The unit: " + this + " is not compatible with quantities of type " + type);
298                return (Unit<T>) this;
299        }
300
301        @Override
302        public abstract Map<? extends Unit<?>, Integer> getBaseUnits();
303
304        @Override
305        public abstract Dimension getDimension();
306
307        protected void setName(String name) {
308                this.name = name;
309        }
310
311        public String getName() {
312                return name;
313        }
314
315        public String getSymbol() {
316                return symbol;
317        }
318
319        protected void setSymbol(String s) {
320                this.symbol = s;
321        }
322
323        @Override
324        public final UnitConverter getConverterTo(Unit<Q> that) throws UnconvertibleException {
325                return internalGetConverterTo(that, true);
326        }
327
328        @Override
329        public final UnitConverter getConverterToAny(Unit<?> that) throws IncommensurableException, UnconvertibleException {
330                return getConverterToAny(that, ABSOLUTE);
331        }
332        
333   /**
334     * Returns a converter of numeric values from this unit to another unit of same type. This method performs the same work as
335     * {@link #getConverterToAny(Unit)} without raising checked exception.
336     *
337     * @param that
338     *          the unit of same type to which to convert the numeric values.
339     * @param scale the measurement scale.          
340     * @return the converter from this unit to {@code that} unit in the given {@code scale}.
341     * @throws UnconvertibleException
342     *           if a converter cannot be constructed.
343     *
344     * @see #getConverterToAny(Unit)
345     */
346        @API(status=EXPERIMENTAL)
347        public final UnitConverter getConverterTo(Unit<Q> that, Scale scale) throws UnconvertibleException {
348                this.scale = scale;
349                return getConverterTo(that);
350        }
351        
352        /**
353     * Returns a converter from this unit to the specified unit of type unknown in the given scale. This method can be used when the quantity type of the specified unit is
354     * unknown at compile-time or when dimensional analysis allows for conversion between units of different type.
355     *
356     * <p>
357     * To convert to a unit having the same parameterized type, {@link #getConverterTo(Unit, Scale)} is preferred (no checked exception raised).
358     * </p>
359     *
360     * @param that
361     *          the unit to which to convert the numeric values.
362     * @param scale the measurement scale.
363     * @return the converter from {@code this} unit to {@code that} unit using the given {@code scale}.
364     * @throws IncommensurableException
365     *           if this unit is not {@linkplain #isCompatible(Unit) compatible} with {@code that}.
366     * @throws UnconvertibleException
367     *           if a converter cannot be constructed.
368     *
369     * @see #getConverterTo(Unit)
370     * @see #isCompatible(Unit)
371     */
372        @API(status=EXPERIMENTAL)
373        @SuppressWarnings("rawtypes")
374        public final UnitConverter getConverterToAny(Unit<?> that, Scale scale) throws IncommensurableException, UnconvertibleException {
375                if (!isCompatible(that))
376                        throw new IncommensurableException(this + " is not compatible with " + that);
377                this.scale = scale;
378                final AbstractUnit thatAbstr = (AbstractUnit) that; // Since both units are
379                // compatible they must both be abstract units.
380                final DimensionalModel model = DimensionalModel.current();
381                Unit thisSystemUnit = this.getSystemUnit();
382                UnitConverter thisToDimension = model.getDimensionalTransform(thisSystemUnit.getDimension())
383                                .concatenate(this.getSystemConverter());
384                Unit thatSystemUnit = thatAbstr.getSystemUnit();
385                UnitConverter thatToDimension = model.getDimensionalTransform(thatSystemUnit.getDimension())
386                                .concatenate(thatAbstr.getSystemConverter());
387                return thatToDimension.inverse().concatenate(thisToDimension);
388        }
389
390        @Override
391        public final Unit<Q> alternate(String newSymbol) {
392                return new AlternateUnit<>(this, newSymbol);
393        }
394
395        @Override
396        public final Unit<Q> transform(UnitConverter operation) {
397                Unit<Q> systemUnit = this.getSystemUnit();
398                UnitConverter cvtr;
399                if (this.isSystemUnit()) {
400                        cvtr = this.getSystemConverter().concatenate(operation);
401                } else {
402                        cvtr = operation;
403                }
404                return cvtr.isIdentity() ? systemUnit : new TransformedUnit<>(null, this, systemUnit, cvtr);
405        }
406
407        @Override
408        public final Unit<Q> shift(Number offset) {
409                if (Calculus.currentNumberSystem().isZero(offset))
410                        return this;
411                return transform(new AddConverter(offset));
412        }
413
414        @Override
415        public final Unit<Q> multiply(Number factor) {
416                if (Calculus.currentNumberSystem().isOne(factor))
417                        return this;
418                return transform(MultiplyConverter.of(factor));
419        }
420
421        @Override
422        public Unit<Q> shift(double offset) {
423                return shift(RationalNumber.of(offset));
424        }
425
426        @Override
427        public Unit<Q> multiply(double multiplier) {
428                return multiply(RationalNumber.of(multiplier));
429        }
430
431        @Override
432        public Unit<Q> divide(double divisor) {
433                return divide(RationalNumber.of(divisor));
434        }
435        
436        /**
437         * Internal helper for isCompatible
438         */
439        private final boolean internalIsCompatible(Unit<?> that, boolean checkEquals) {
440                if (checkEquals) {
441                        if (this == that || this.equals(that))
442                                return true;
443                } else {
444                        if (this == that)
445                                return true;
446                }
447                if (!(that instanceof Unit))
448                        return false;
449                Dimension thisDimension = this.getDimension();
450                Dimension thatDimension = that.getDimension();
451                if (thisDimension.equals(thatDimension))
452                        return true;
453                DimensionalModel model = DimensionalModel.current(); // Use
454                // dimensional
455                // analysis
456                // model.
457                return model.getFundamentalDimension(thisDimension).equals(model.getFundamentalDimension(thatDimension));
458        }
459
460        protected final UnitConverter internalGetConverterTo(Unit<Q> that, boolean useEquals)
461                        throws UnconvertibleException {
462                if (useEquals) {
463                        if (this == that || this.equals(that))
464                                return AbstractConverter.IDENTITY;
465                } else {
466                        if (this == that)
467                                return AbstractConverter.IDENTITY;
468                }
469                Unit<Q> thisSystemUnit = this.getSystemUnit();
470                Unit<Q> thatSystemUnit = that.getSystemUnit();
471                if (!thisSystemUnit.equals(thatSystemUnit))
472                        try {
473                                return getConverterToAny(that);
474                        } catch (IncommensurableException e) {
475                                throw new UnconvertibleException(e);
476                        }
477                UnitConverter thisToSI = this.getSystemConverter();
478                UnitConverter thatToSI = that.getConverterTo(thatSystemUnit);
479                return thatToSI.inverse().concatenate(thisToSI);
480        }
481
482        /**
483         * Returns the product of this physical unit with the one specified.
484         *
485         * @param that the physical unit multiplicand.
486         * @return <code>this * that</code>
487         */
488        public final Unit<?> multiply(Unit<?> that) {
489                if (this.equals(ONE))
490                        return that;
491                if (that.equals(ONE))
492                        return this;
493                return ProductUnit.ofProduct(this, that);
494        }
495
496        /**
497         * Returns the inverse of this physical unit.
498         *
499         * @return <code>1 / this</code>
500         */
501        @Override
502        public final Unit<?> inverse() {
503                if (this.equals(ONE))
504                        return this;
505                return ProductUnit.ofQuotient(ONE, this);
506        }
507
508        /**
509         * Returns the result of dividing this unit by the specified divisor. If the
510         * factor is an integer value, the division is exact. For example:
511         * 
512         * <pre>
513         * <code>
514         *    QUART = GALLON_LIQUID_US.divide(4); // Exact definition.
515         * </code>
516         * </pre>
517         * 
518         * @param divisor the divisor value.
519         * @return this unit divided by the specified divisor.
520         */
521        @Override
522        public final Unit<Q> divide(Number divisor) {
523            if (Calculus.currentNumberSystem().isOne(divisor))
524                        return this;
525            Number factor = Calculator.of(divisor).reciprocal().peek(); 
526                return transform(MultiplyConverter.of(factor));
527        }
528
529        /**
530         * Returns the quotient of this unit with the one specified.
531         *
532         * @param that the unit divisor.
533         * @return <code>this.multiply(that.inverse())</code>
534         */
535        @Override
536        public final Unit<?> divide(Unit<?> that) {
537                return this.multiply(that.inverse());
538        }
539
540        /**
541         * Returns a unit equals to the given root of this unit.
542         *
543         * @param n the root's order.
544         * @return the result of taking the given root of this unit.
545         * @throws ArithmeticException if <code>n == 0</code> or if this operation would
546         *                             result in an unit with a fractional exponent.
547         */
548        @Override
549        public final Unit<?> root(int n) {
550                if (n > 0)
551                        return ProductUnit.ofRoot(this, n);
552                else if (n == 0)
553                        throw new ArithmeticException("Root's order of zero");
554                else
555                        // n < 0
556                        return ONE.divide(this.root(-n));
557        }
558
559        /**
560         * Returns a unit equals to this unit raised to an exponent.
561         *
562         * @param n the exponent.
563         * @return the result of raising this unit to the exponent.
564         */
565        @Override
566        public Unit<?> pow(int n) {
567                if (n > 0)
568                        return this.multiply(this.pow(n - 1));
569                else if (n == 0)
570                        return ONE;
571                else
572                        // n < 0
573                        return ONE.divide(this.pow(-n));
574        }
575
576        @Override
577        public Unit<Q> prefix(Prefix prefix) {
578                return this.transform(MultiplyConverter.ofPrefix(prefix));
579        }
580        
581        /**
582         * Compares this unit to the specified unit. The default implementation compares
583         * the name and symbol of both this unit and the specified unit, giving
584         * precedence to the symbol.
585         *
586         * @return a negative integer, zero, or a positive integer as this unit is less
587         *         than, equal to, or greater than the specified unit.
588         */
589        @Override
590        public int compareTo(Unit<Q> that) {
591                int symbolComparison = compareToWithPossibleNullValues(getSymbol(), that.getSymbol());
592                if (symbolComparison == 0) {
593                        return compareToWithPossibleNullValues(name, that.getName());
594                } else {
595                        return symbolComparison;
596                }
597        }
598
599        private int compareToWithPossibleNullValues(String a, String b) {
600                if (a == null) {
601                        return (b == null) ? 0 : -1;
602                } else {
603                        return (b == null) ? 1 : a.compareTo(b);
604                }
605        }
606
607        @Override
608        public boolean isEquivalentTo(Unit<Q> that) {
609                return this.getConverterTo(that).isIdentity();
610        }
611
612        // //////////////////////////////////////////////////////////////
613        // Ensures that sub-classes implement the hashCode method.
614        // //////////////////////////////////////////////////////////////
615
616        @Override
617        public abstract boolean equals(Object obj);
618
619        @Override
620        public abstract int hashCode();
621
622        /**
623         * Utility class for number comparison and equality
624         */
625        protected static final class Equalizer {
626                /**
627                 * Indicates if this unit is considered equals to the specified object. order).
628                 *
629                 * @param obj the object to compare for equality.
630                 * @return <code>true</code> if <code>this</code> and <code>obj</code> are
631                 *         considered equal; <code>false</code>otherwise.
632                 */
633                public static boolean areEqual(@SuppressWarnings("rawtypes") Unit u1,
634                                @SuppressWarnings("rawtypes") Unit u2) {
635                        /*
636                         * if (u1 != null && u2 != null) { if (u1.getName() != null && u1.getSymbol() !=
637                         * null) { return u1.getName().equals(u2.getName()) &&
638                         * u1.getSymbol().equals(u2.getSymbol()) && u1.internalIsCompatible(u2, false);
639                         * } else if (u1.getSymbol() != null) { return
640                         * u1.getSymbol().equals(u2.getSymbol()) && u1.internalIsCompatible(u2, false);
641                         * } else { return u1.toString().equals(u2.toString()) &&
642                         * u1.internalIsCompatible(u2, false); } } else {
643                         */
644                        if (u1 != null && u1.equals(u2))
645                                return true;
646                        return false;
647                }
648        }
649}