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