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