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