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;
035
036import tech.units.indriya.AbstractUnit;
037import java.io.Serializable;
038import java.util.HashMap;
039import java.util.Map;
040import java.util.Objects;
041import java.util.logging.Level;
042import java.util.logging.Logger;
043
044/**
045 * <p>
046 * This class represents a dimension of a unit of measurement.
047 * </p>
048 *
049 * <p>
050 * The dimension associated to any given quantity are given by the published
051 * {@link Dimension} instances. For convenience, a static method
052 * <code>UnitDimension.of(Class)</code> aggregating the results of all
053 * 
054 * {@link Dimension} instances is provided.<br>
055 * <br>
056 * <code>
057 *        Dimension speedDimension
058 *            = UnitDimension.of(Speed.class);
059 *     </code>
060 * </p>
061 *
062 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
063 * @author <a href="mailto:werner@units.tech">Werner Keil</a>
064 * @author  Martin Desruisseaux (Geomatys)
065 * @version 1.1, $Date: 2020-08-06 $
066 * @since 2.0
067 */
068public class UnitDimension implements Dimension, Serializable {
069    /**
070         * 
071         */
072        private static final long serialVersionUID = 7806787530512644696L;
073
074        private static final Logger LOGGER = Logger.getLogger(UnitDimension.class.getName());
075
076        /**
077         * Holds dimensionless.
078         * 
079         * @since 1.0
080         */
081        public static final Dimension NONE = new UnitDimension(AbstractUnit.ONE);
082
083        /**
084         * Holds length dimension (L).
085         * 
086         * @since 1.0
087         */
088        public static final Dimension LENGTH = new UnitDimension('L');
089
090        /**
091         * Holds mass dimension (M).
092         * 
093         * @since 1.0
094         */
095        public static final Dimension MASS = new UnitDimension('M');
096
097        /**
098         * Holds time dimension (T).
099         * 
100         * @since 1.0
101         */
102        public static final Dimension TIME = new UnitDimension('T');
103
104        /**
105         * Holds electric current dimension (I).
106         * 
107         * @since 1.0
108         */
109        public static final Dimension ELECTRIC_CURRENT = new UnitDimension('I');
110
111        /**
112         * Holds temperature dimension (Θ).
113         * 
114         * @since 1.0
115         */
116        public static final Dimension TEMPERATURE = new UnitDimension('\u0398');
117
118        /**
119         * Holds amount of substance dimension (N).
120         * 
121         * @since 1.0
122         */
123        public static final Dimension AMOUNT_OF_SUBSTANCE = new UnitDimension('N');
124
125        /**
126         * Holds luminous intensity dimension (J).
127         */
128        public static final Dimension LUMINOUS_INTENSITY = new UnitDimension('J');
129
130        /**
131         * Holds the pseudo unit associated to this dimension.
132         */
133        private final Unit<?> pseudoUnit;
134
135        /**
136         * Returns the dimension for the specified quantity type by aggregating the
137         * results of {@link DimensionService} or <code>null</code> if the specified
138         * quantity is unknown.
139         *
140         * @param quantityType the quantity type.
141         * @return the dimension for the quantity type or <code>null</code>.
142         * @since 1.1
143         */
144        public static <Q extends Quantity<Q>> Dimension of(Class<Q> quantityType) {
145                // TODO: Track services and aggregate results (register custom
146                // types)
147                Unit<Q> siUnit = Units.getInstance().getUnit(quantityType);
148                if (siUnit == null) {
149                        LOGGER.log(Level.FINER, "Quantity type: " + quantityType + " unknown");
150                        // we're logging but probably FINER is enough?
151                }
152                return (siUnit != null) ? siUnit.getDimension() : null;
153        }
154
155        /**
156         * Returns the dimension for the specified symbol.
157         *
158         * @param sambol the quantity symbol.
159         * @return the dimension for the given symbol.
160         * @since 1.0.1
161         */
162        public static Dimension parse(char symbol) {
163                return new UnitDimension(symbol);
164        }
165
166        /**
167         * Returns the unit dimension having the specified symbol.
168         *
169         * @param symbol the associated symbol.
170         */
171        @SuppressWarnings("rawtypes")
172        private UnitDimension(char symbol) {
173                pseudoUnit = new BaseUnit("[" + symbol + ']', NONE);
174        }
175
176        /**
177         * Constructor from pseudo-unit (not visible).
178         *
179         * @param pseudoUnit the pseudo-unit.
180         */
181        private UnitDimension(Unit<?> pseudoUnit) {
182                this.pseudoUnit = pseudoUnit;
183        }
184        
185        /**
186         * Default Constructor (not visible).
187         *
188         */
189        protected UnitDimension() {
190                this(AbstractUnit.ONE);
191        }
192        
193
194        /**
195         * Returns the product of this dimension with the one specified. If the
196         * specified dimension is not a physics dimension, then
197         * <code>that.multiply(this)</code> is returned.
198         *
199         * @param that the dimension multiplicand.
200         * @return <code>this * that</code>
201         * @since 1.0
202         */
203        public Dimension multiply(Dimension that) {
204                return that instanceof UnitDimension ? this.multiply((UnitDimension) that) : this.multiply(that);
205        }
206
207        /**
208         * Returns the product of this dimension with the one specified.
209         *
210         * @param that the dimension multiplicand.
211         * @return <code>this * that</code>
212         * @since 1.0
213         */
214        private UnitDimension multiply(UnitDimension that) {
215                return new UnitDimension(this.pseudoUnit.multiply(that.pseudoUnit));
216        }
217
218        /**
219         * Returns the quotient of this dimension with the one specified.
220         *
221         * @param that the dimension divisor.
222         * @return <code>this.multiply(that.pow(-1))</code>
223         * @since 1.0
224         */
225        public Dimension divide(Dimension that) {
226                return that instanceof UnitDimension ? this.divide((UnitDimension) that) : this.divide(that);
227        }
228
229        /**
230         * Returns the quotient of this dimension with the one specified.
231         *
232         * @param that the dimension divisor.
233         * @return <code>this.multiply(that.pow(-1))</code>
234         * @since 1.0
235         */
236        private UnitDimension divide(UnitDimension that) {
237                return new UnitDimension(ProductUnit.ofQuotient(pseudoUnit, that.pseudoUnit));
238        }
239
240        /**
241         * Returns this dimension raised to an exponent.
242         *
243         * @param n the exponent.
244         * @return the result of raising this dimension to the exponent.
245         * @since 1.0
246         */
247        public UnitDimension pow(int n) {
248                return new UnitDimension(this.pseudoUnit.pow(n));
249        }
250
251        /**
252         * Returns the given root of this dimension.
253         *
254         * @param n the root's order.
255         * @return the result of taking the given root of this dimension.
256         * @throws ArithmeticException if <code>n == 0</code>.
257         * @since 1.0
258         */
259        public UnitDimension root(int n) {
260                return new UnitDimension(this.pseudoUnit.root(n));
261        }
262
263        /**
264         * Returns the fundamental (base) dimensions and their exponent whose product is
265         * this dimension or <code>null</code> if this dimension is a fundamental
266         * dimension.
267         *
268         * @return the mapping between the base dimensions and their exponent.
269         * @since 1.0
270         */
271        @SuppressWarnings("rawtypes")
272        public Map<? extends Dimension, Integer> getBaseDimensions() {
273                Map<? extends Unit, Integer> pseudoUnits = pseudoUnit.getBaseUnits();
274                if (pseudoUnits == null) {
275                        return null;
276                }
277                final Map<UnitDimension, Integer> baseDimensions = new HashMap<>();
278                for (Map.Entry<? extends Unit, Integer> entry : pseudoUnits.entrySet()) {
279                        baseDimensions.put(new UnitDimension(entry.getKey()), entry.getValue());
280                }
281                return baseDimensions;
282        }
283
284        @Override
285        public String toString() {
286                return pseudoUnit.toString();
287        }
288
289        @Override
290        public boolean equals(Object obj) {
291                if (this == obj) {
292                        return true;
293                }
294                if (obj instanceof UnitDimension) {
295                        UnitDimension other = (UnitDimension) obj;
296                        return Objects.equals(pseudoUnit, other.pseudoUnit);
297                }
298                return false;
299        }
300
301        @Override
302        public int hashCode() {
303                return Objects.hashCode(pseudoUnit);
304        }
305}