001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2021, 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.unit;
031
032import javax.measure.Dimension;
033import javax.measure.Quantity;
034import javax.measure.Unit;
035import javax.measure.UnitConverter;
036
037import tech.units.indriya.AbstractUnit;
038
039import java.util.Map;
040import java.util.Objects;
041
042/**
043 * <p>
044 * This class represents units used in expressions to distinguish between
045 * quantities of a different nature but of the same dimensions.
046 * </p>
047 * 
048 * <p>
049 * Examples of alternate units:
050 * </p>
051 *
052 * <code>
053 *     {@literal Unit<Angle>} RADIAN = AlternateUnit.of(ONE, "rad").asType(Angle.class);<br>
054 *     {@literal Unit<Force>} NEWTON = AlternateUnit.of(METRE.multiply(KILOGRAM).divide(SECOND.pow(2)), "N").asType(Force.class);<br>
055 *     {@literal Unit<Pressure>} PASCAL = AlternateUnit.of(NEWTON.divide(METRE.pow(2), "Pa").asType(Pressure.class);<br>
056 * </code>
057 *
058 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
059 * @author <a href="mailto:werner@units.tech">Werner Keil</a>
060 * @version 2.0, February 08, 2020
061 * @since 1.0
062 */
063public final class AlternateUnit<Q extends Quantity<Q>> extends AbstractUnit<Q> {
064
065        /**
066         * 
067         */
068        private static final long serialVersionUID = 4696690756456282705L;
069
070        /**
071         * Holds the parent unit (a system unit).
072         */
073        private final Unit<?> parentUnit;
074
075        /**
076         * Creates an alternate unit for the specified system unit identified by the
077         * specified name and symbol.
078         *
079         * @param parentUnit the system unit from which this alternate unit is derived.
080         * @param symbol     the symbol for this alternate unit.
081         * @throws IllegalArgumentException if the specified parent unit is not an
082         *                                  {@link AbstractUnit#isSystemUnit() system
083         *                                  unit}
084         */
085        @SuppressWarnings("rawtypes")
086        public AlternateUnit(Unit<?> parentUnit, String symbol) {
087                super(symbol);
088                if (!(parentUnit instanceof AbstractUnit))
089                        throw new IllegalArgumentException("The parent unit: " + parentUnit + " is not an AbstractUnit");
090                if (!((AbstractUnit) parentUnit).isSystemUnit())
091                        throw new IllegalArgumentException("The parent unit: " + parentUnit + " is not an unscaled SI unit");
092                this.parentUnit = parentUnit instanceof AlternateUnit ? ((AlternateUnit) parentUnit).getParentUnit()
093                                : parentUnit;
094        }
095
096        /**
097         * Creates an alternate unit for the specified system unit identified by the
098         * specified name and symbol.
099         *
100         * @param parentUnit the system unit from which this alternate unit is derived.
101         * @param symbol     the symbol for this alternate unit.
102         * @param name       the name for this alternate unit.
103         * @throws IllegalArgumentException if the specified parent unit is not an
104         *                                  {@link AbstractUnit#isSystemUnit() system
105         *                                  unit}
106         * @since 2.0
107         */
108        AlternateUnit(Unit<?> parentUnit, String symbol, String name) {
109                this(parentUnit, symbol);
110                this.name = name;
111        }
112
113        /**
114         * Returns the parent unit of this alternate unit, always a system unit and
115         * never an alternate unit.
116         *
117         * @return the parent unit.
118         */
119        public Unit<?> getParentUnit() {
120                return parentUnit;
121        }
122
123        @Override
124        public Dimension getDimension() {
125                return parentUnit.getDimension();
126        }
127
128        @SuppressWarnings("rawtypes")
129        @Override
130        public UnitConverter getSystemConverter() {
131                return ((AbstractUnit) parentUnit).getSystemConverter();
132        }
133
134        @Override
135        public Unit<Q> toSystemUnit() {
136                return this; // Alternate units are SI units.
137        }
138
139        @Override
140        public Map<? extends Unit<?>, Integer> getBaseUnits() {
141                return parentUnit.getBaseUnits();
142        }
143
144        @Override
145        public int hashCode() {
146                return Objects.hash(parentUnit, getSymbol());
147        }
148
149        @SuppressWarnings("rawtypes")
150        @Override
151        public boolean equals(Object obj) {
152                if (this == obj) {
153                        return true;
154                }
155                if (obj instanceof AlternateUnit) {
156                        AlternateUnit that = (AlternateUnit) obj;
157                        return Objects.equals(parentUnit, that.parentUnit) && Objects.equals(getSymbol(), that.getSymbol());
158                }
159                return false;
160        }
161
162        /**
163     * Creates an alternate unit for the specified system unit identified by the specified name and symbol.
164     *
165     * @param parent
166     *            the system unit from which this alternate unit is derived.
167     * @param symbol
168     *            the symbol for this alternate unit.
169     * @throws IllegalArgumentException
170     *             if the specified parent unit is not an unscaled standard {@link AbstractUnit#isSystemUnit() system unit}.
171     * @throws MeasurementException
172     *           if the specified symbol is not valid or is already associated to a different unit.
173     */
174    public static <Q extends Quantity<Q>> AlternateUnit<Q> of(Unit<?> parent, String symbol) {
175        return new AlternateUnit<>(parent, symbol);
176    }
177
178        /**
179     * Creates an alternate unit for the specified system unit identified by the specified name and symbol.
180     *
181     * @param parent
182     *            the system unit from which this alternate unit is derived.
183     * @param symbol
184     *            the symbol for this alternate unit.
185     * @param name       the name for this alternate unit.
186     * @throws IllegalArgumentException
187     *             if the specified parent unit is not an unscaled standard {@link AbstractUnit#isSystemUnit() system unit}.
188     * @throws MeasurementException
189     *           if the specified symbol is not valid or is already associated to a different unit.
190     * @since 2.0
191     */
192    public static <Q extends Quantity<Q>> AlternateUnit<Q> of(Unit<?> parent, String symbol, String name) {
193        return new AlternateUnit<>(parent, symbol, name);
194    }
195}