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