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