001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2025, 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;
051import javax.measure.spi.SystemOfUnits;
052
053import org.apiguardian.api.API;
054
055import tech.units.indriya.format.LocalUnitFormat;
056import tech.units.indriya.format.SimpleUnitFormat;
057import tech.units.indriya.function.AbstractConverter;
058import tech.units.indriya.function.AddConverter;
059import tech.units.indriya.function.Calculus;
060import tech.units.indriya.function.MultiplyConverter;
061import tech.units.indriya.function.RationalNumber;
062import tech.units.indriya.internal.function.Calculator;
063import tech.units.indriya.spi.DimensionalModel;
064import tech.units.indriya.unit.AlternateUnit;
065import tech.units.indriya.unit.AnnotatedUnit;
066import tech.units.indriya.unit.ProductUnit;
067import tech.units.indriya.unit.TransformedUnit;
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.2, February 19, 2025
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#getInstance()
291         */
292        @Override
293        public final <T extends Quantity<T>> Unit<T> asType(Class<T> type) {
294                return asType(type, Units.getInstance());               
295                /*
296                 * Dimension typeDimension = UnitDimension.of(type); if (typeDimension != null
297                 * && !typeDimension.equals(this.getDimension())) throw new
298                 * ClassCastException("The unit: " + this +
299                 * " is not compatible with quantities of type " + type); return (Unit<T>) this;
300                 */     
301        }
302
303        @Override
304        public abstract Map<? extends Unit<?>, Integer> getBaseUnits();
305
306        @Override
307        public abstract Dimension getDimension();
308
309        protected void setName(String name) {
310                this.name = name;
311        }
312
313        public String getName() {
314                return name;
315        }
316
317        public String getSymbol() {
318                return symbol;
319        }
320
321        protected void setSymbol(String s) {
322                this.symbol = s;
323        }
324
325        @Override
326        public final UnitConverter getConverterTo(Unit<Q> that) throws UnconvertibleException {
327                return internalGetConverterTo(that, true);
328        }
329
330        @Override
331        public final UnitConverter getConverterToAny(Unit<?> that) throws IncommensurableException, UnconvertibleException {
332                return getConverterToAny(that, ABSOLUTE);
333        }
334        
335        /**
336         * Casts this unit to a parameterized unit of specified nature or throw a
337         * ClassCastException if the dimension of the specified quantity and this unit's
338         * dimension do not match (regardless whether or not the dimensions are
339         * independent or not).
340         *
341         * @param type the quantity class identifying the nature of the unit.
342         * @param typeSystem the system of units using the quantity class.
343         * @throws ClassCastException if the dimension of this unit is different from
344         *                            the SI dimension of the specified type.
345         * @see javax.measure.spi.SystemOfUnits#getUnit(Class)
346         */      
347        @SuppressWarnings("unchecked")
348        public final <T extends Quantity<T>> Unit<T> asType(Class<T> type, SystemOfUnits typeSystem) {
349                Unit<T> typedUnit = typeSystem.getUnit(type); 
350                final Dimension typeDimension = (typedUnit != null ? typedUnit.getDimension() : null);
351                if (typeDimension != null && !typeDimension.equals(this.getDimension()))
352                        throw new ClassCastException("The unit: " + this + " is not compatible with quantities of type " + type);
353                return (Unit<T>) this;
354        }
355        
356   /**
357     * Returns a converter of numeric values from this unit to another unit of same type. This method performs the same work as
358     * {@link #getConverterToAny(Unit)} without raising checked exception.
359     *
360     * @param that
361     *          the unit of same type to which to convert the numeric values.
362     * @param scale the measurement scale.          
363     * @return the converter from this unit to {@code that} unit in the given {@code scale}.
364     * @throws UnconvertibleException
365     *           if a converter cannot be constructed.
366     *
367     * @see #getConverterToAny(Unit)
368     */
369        @API(status=EXPERIMENTAL)
370        public final UnitConverter getConverterTo(Unit<Q> that, Scale scale) throws UnconvertibleException {
371                this.scale = scale;
372                return getConverterTo(that);
373        }
374        
375        /**
376     * 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
377     * unknown at compile-time or when dimensional analysis allows for conversion between units of different type.
378     *
379     * <p>
380     * To convert to a unit having the same parameterized type, {@link #getConverterTo(Unit, Scale)} is preferred (no checked exception raised).
381     * </p>
382     *
383     * @param that
384     *          the unit to which to convert the numeric values.
385     * @param scale the measurement scale.
386     * @return the converter from {@code this} unit to {@code that} unit using the given {@code scale}.
387     * @throws IncommensurableException
388     *           if this unit is not {@linkplain #isCompatible(Unit) compatible} with {@code that}.
389     * @throws UnconvertibleException
390     *           if a converter cannot be constructed.
391     *
392     * @see #getConverterTo(Unit)
393     * @see #isCompatible(Unit)
394     */
395        @API(status=EXPERIMENTAL)
396        @SuppressWarnings("rawtypes")
397        public final UnitConverter getConverterToAny(Unit<?> that, Scale scale) throws IncommensurableException, UnconvertibleException {
398                if (!isCompatible(that))
399                        throw new IncommensurableException(this + " is not compatible with " + that);
400                this.scale = scale;
401                final AbstractUnit thatAbstr = (AbstractUnit) that; // Since both units are
402                // compatible they must both be abstract units.
403                final DimensionalModel model = DimensionalModel.current();
404                Unit thisSystemUnit = this.getSystemUnit();
405                UnitConverter thisToDimension = model.getDimensionalTransform(thisSystemUnit.getDimension())
406                                .concatenate(this.getSystemConverter());
407                Unit thatSystemUnit = thatAbstr.getSystemUnit();
408                UnitConverter thatToDimension = model.getDimensionalTransform(thatSystemUnit.getDimension())
409                                .concatenate(thatAbstr.getSystemConverter());
410                return thatToDimension.inverse().concatenate(thisToDimension);
411        }
412
413        @Override
414        public final Unit<Q> alternate(String newSymbol) {
415                return new AlternateUnit<>(this, newSymbol);
416        }
417
418        @Override
419        public final Unit<Q> transform(UnitConverter operation) {
420                Unit<Q> systemUnit = this.getSystemUnit();
421                UnitConverter cvtr;
422                if (this.isSystemUnit()) {
423                        cvtr = this.getSystemConverter().concatenate(operation);
424                } else {
425                        cvtr = operation;
426                }
427                return cvtr.isIdentity() ? systemUnit : new TransformedUnit<>(null, this, systemUnit, cvtr);
428        }
429
430        @Override
431        public final Unit<Q> shift(Number offset) {
432                if (Calculus.currentNumberSystem().isZero(offset))
433                        return this;
434                return transform(new AddConverter(offset));
435        }
436
437        @Override
438        public final Unit<Q> multiply(Number factor) {
439                if (Calculus.currentNumberSystem().isOne(factor))
440                        return this;
441                return transform(MultiplyConverter.of(factor));
442        }
443
444        @Override
445        public Unit<Q> shift(double offset) {
446                return shift(RationalNumber.of(offset));
447        }
448
449        @Override
450        public Unit<Q> multiply(double multiplier) {
451                return multiply(RationalNumber.of(multiplier));
452        }
453
454        @Override
455        public Unit<Q> divide(double divisor) {
456                return divide(RationalNumber.of(divisor));
457        }
458        
459        /**
460         * Internal helper for isCompatible
461         */
462        private final boolean internalIsCompatible(Unit<?> that, boolean checkEquals) {
463                if (checkEquals) {
464                        if (this == that || this.equals(that))
465                                return true;
466                } else {
467                        if (this == that)
468                                return true;
469                }
470                if (!(that instanceof Unit))
471                        return false;
472                Dimension thisDimension = this.getDimension();
473                Dimension thatDimension = that.getDimension();
474                if (thisDimension.equals(thatDimension))
475                        return true;
476                DimensionalModel model = DimensionalModel.current(); // Use
477                // dimensional
478                // analysis
479                // model.
480                return model.getFundamentalDimension(thisDimension).equals(model.getFundamentalDimension(thatDimension));
481        }
482
483        protected final UnitConverter internalGetConverterTo(Unit<Q> that, boolean useEquals)
484                        throws UnconvertibleException {
485                if (useEquals) {
486                        if (this == that || this.equals(that))
487                                return AbstractConverter.IDENTITY;
488                } else {
489                        if (this == that)
490                                return AbstractConverter.IDENTITY;
491                }
492                Unit<Q> thisSystemUnit = this.getSystemUnit();
493                Unit<Q> thatSystemUnit = that.getSystemUnit();
494                if (!thisSystemUnit.equals(thatSystemUnit))
495                        try {
496                                return getConverterToAny(that);
497                        } catch (IncommensurableException e) {
498                                throw new UnconvertibleException(e);
499                        }
500                UnitConverter thisToSI = this.getSystemConverter();
501                UnitConverter thatToSI = that.getConverterTo(thatSystemUnit);
502                return thatToSI.inverse().concatenate(thisToSI);
503        }
504
505        /**
506         * Returns the product of this physical unit with the one specified.
507         *
508         * @param that the physical unit multiplicand.
509         * @return <code>this * that</code>
510         */
511        public final Unit<?> multiply(Unit<?> that) {
512                if (this.equals(ONE))
513                        return that;
514                if (that.equals(ONE))
515                        return this;
516                return ProductUnit.ofProduct(this, that);
517        }
518
519        /**
520         * Returns the inverse of this physical unit.
521         *
522         * @return <code>1 / this</code>
523         */
524        @Override
525        public final Unit<?> inverse() {
526                if (this.equals(ONE))
527                        return this;
528                return ProductUnit.ofQuotient(ONE, this);
529        }
530
531        /**
532         * Returns the result of dividing this unit by the specified divisor. If the
533         * factor is an integer value, the division is exact. For example:
534         * 
535         * <pre>
536         * <code>
537         *    QUART = GALLON_LIQUID_US.divide(4); // Exact definition.
538         * </code>
539         * </pre>
540         * 
541         * @param divisor the divisor value.
542         * @return this unit divided by the specified divisor.
543         */
544        @Override
545        public final Unit<Q> divide(Number divisor) {
546            if (Calculus.currentNumberSystem().isOne(divisor))
547                        return this;
548            Number factor = Calculator.of(divisor).reciprocal().peek(); 
549                return transform(MultiplyConverter.of(factor));
550        }
551
552        /**
553         * Returns the quotient of this unit with the one specified.
554         *
555         * @param that the unit divisor.
556         * @return <code>this.multiply(that.inverse())</code>
557         */
558        @Override
559        public final Unit<?> divide(Unit<?> that) {
560                return this.multiply(that.inverse());
561        }
562
563        /**
564         * Returns a unit equals to the given root of this unit.
565         *
566         * @param n the root's order.
567         * @return the result of taking the given root of this unit.
568         * @throws ArithmeticException if <code>n == 0</code> or if this operation would
569         *                             result in an unit with a fractional exponent.
570         */
571        @Override
572        public final Unit<?> root(int n) {
573                if (n > 0)
574                        return ProductUnit.ofRoot(this, n);
575                else if (n == 0)
576                        throw new ArithmeticException("Root's order of zero");
577                else
578                        // n < 0
579                        return ONE.divide(this.root(-n));
580        }
581
582        /**
583         * Returns a unit equals to this unit raised to an exponent.
584         *
585         * @param n the exponent.
586         * @return the result of raising this unit to the exponent.
587         */
588        @Override
589        public Unit<?> pow(int n) {
590                if (n > 0)
591                        return this.multiply(this.pow(n - 1));
592                else if (n == 0)
593                        return ONE;
594                else
595                        // n < 0
596                        return ONE.divide(this.pow(-n));
597        }
598
599        @Override
600        public Unit<Q> prefix(Prefix prefix) {
601                return this.transform(MultiplyConverter.ofPrefix(prefix));
602        }
603        
604        /**
605         * Compares this unit to the specified unit. The default implementation compares
606         * the name and symbol of both this unit and the specified unit, giving
607         * precedence to the symbol.
608         *
609         * @return a negative integer, zero, or a positive integer as this unit is less
610         *         than, equal to, or greater than the specified unit.
611         */
612        @Override
613        public int compareTo(Unit<Q> that) {
614                int symbolComparison = compareToWithPossibleNullValues(getSymbol(), that.getSymbol());
615                if (symbolComparison == 0) {
616                        return compareToWithPossibleNullValues(name, that.getName());
617                } else {
618                        return symbolComparison;
619                }
620        }
621
622        private int compareToWithPossibleNullValues(String a, String b) {
623                if (a == null) {
624                        return (b == null) ? 0 : -1;
625                } else {
626                        return (b == null) ? 1 : a.compareTo(b);
627                }
628        }
629
630        @Override
631        public boolean isEquivalentTo(Unit<Q> that) {
632                return this.getConverterTo(that).isIdentity();
633        }
634
635        // //////////////////////////////////////////////////////////////
636        // Ensures that sub-classes implement the hashCode method.
637        // //////////////////////////////////////////////////////////////
638
639        @Override
640        public abstract boolean equals(Object obj);
641
642        @Override
643        public abstract int hashCode();
644
645        /**
646         * Utility class for number comparison and equality
647         */
648        protected static final class Equalizer {
649                /**
650                 * Indicates if this unit is considered equals to the specified object. order).
651                 *
652                 * @param obj the object to compare for equality.
653                 * @return <code>true</code> if <code>this</code> and <code>obj</code> are
654                 *         considered equal; <code>false</code>otherwise.
655                 */
656                public static boolean areEqual(@SuppressWarnings("rawtypes") Unit u1,
657                                @SuppressWarnings("rawtypes") Unit u2) {
658                        /*
659                         * if (u1 != null && u2 != null) { if (u1.getName() != null && u1.getSymbol() !=
660                         * null) { return u1.getName().equals(u2.getName()) &&
661                         * u1.getSymbol().equals(u2.getSymbol()) && u1.internalIsCompatible(u2, false);
662                         * } else if (u1.getSymbol() != null) { return
663                         * u1.getSymbol().equals(u2.getSymbol()) && u1.internalIsCompatible(u2, false);
664                         * } else { return u1.toString().equals(u2.toString()) &&
665                         * u1.internalIsCompatible(u2, false); } } else {
666                         */
667                        if (u1 != null && u1.equals(u2))
668                                return true;
669                        return false;
670                }
671        }
672}