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