001/*
002 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
003 * Copyright (C) 2006 - JScience (http://jscience.org/)
004 * All rights reserved.
005 * 
006 * Permission to use, copy, modify, and distribute this software is
007 * freely granted, provided that this notice is preserved.
008 */
009package javax.measure.unit;
010
011import java.io.Serializable;
012import java.text.ParseException;
013import java.text.ParsePosition;
014import java.util.HashMap;
015
016import javax.measure.MeasureFormat;
017import javax.measure.converter.AddConverter;
018import javax.measure.converter.ConversionException;
019import javax.measure.converter.MultiplyConverter;
020import javax.measure.converter.RationalConverter;
021import javax.measure.converter.UnitConverter;
022import javax.measure.quantity.Dimensionless;
023import javax.measure.quantity.Quantity;
024
025/**
026 * <p> This class represents a determinate {@link javax.measure.quantity.Quantity
027 *     quantity} (as of length, time, heat, or value) adopted as a standard
028 *     of measurement.</p>
029 *
030 * <p> It is helpful to think of instances of this class as recording the
031 *     history by which they are created. Thus, for example, the string
032 *     "g/kg" (which is a dimensionless unit) would result from invoking
033 *     the method toString() on a unit that was created by dividing a
034 *     gram unit by a kilogram unit. Yet, "kg" divided by "kg" returns
035 *     {@link #ONE} and not "kg/kg" due to automatic unit factorization.</p>
036 *
037 * <p> This class supports the multiplication of offsets units. The result is
038 *     usually a unit not convertible to its {@link #getStandardUnit standard unit}.
039 *     Such units may appear in derivative quantities. For example °C/m is an 
040 *     unit of gradient, which is common in atmospheric and oceanographic
041 *     research.</p>
042 *
043 * <p> Units raised at rational powers are also supported. For example
044 *     the cubic root of "liter" is a unit compatible with meter.</p>
045 *     
046 * <p> Instances of this class are immutable.</p>
047 *
048 * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
049 * @author  <a href="mailto:steve@unidata.ucar.edu">Steve Emmerson</a>
050 * @author  Martin Desruisseaux
051 * @version 3.2, August 28, 2006
052 * @see <a href="http://en.wikipedia.org/wiki/Units_of_measurement">
053 *       Wikipedia: Units of measurement</a>
054 */
055public abstract class Unit<Q extends Quantity> implements Serializable {
056
057    /**
058     * Holds the dimensionless unit <code>ONE</code>.
059     */
060    public static final Unit<Dimensionless> ONE = new ProductUnit<Dimensionless>();
061
062    /**
063     * Holds the unique symbols collection (base unit or alternate units).
064     */
065    static final HashMap<String, Unit<?>> SYMBOL_TO_UNIT = new HashMap<String, Unit<?>>();
066
067    /**
068     * Default constructor.
069     */
070    protected Unit() {
071    }
072
073    //////////////////////////////////////////////////////
074    // Contract methods (for sub-classes to implement). //
075    //////////////////////////////////////////////////////
076
077    /**
078     * Returns the {@link BaseUnit base unit}, {@link AlternateUnit alternate
079     * unit} or product of base units and alternate units this unit is derived
080     * from. The standard unit identifies the "type" of 
081     * {@link javax.measure.quantity.Quantity quantity} for which this unit is employed.
082     * For example:[code]
083     *    boolean isAngularVelocity(Unit<?> u) {
084     *       return u.getStandardUnit().equals(RADIAN.divide(SECOND));
085     *    }
086     *    assert(REVOLUTION.divide(MINUTE).isAngularVelocity());  
087     * [/code]
088     * 
089     * <p><i> Note: Having the same system unit is not sufficient to ensure
090     *              that a converter exists between the two units
091     *              (e.g. °C/m and K/m).</i></p>
092     * @return the system unit this unit is derived from.
093     */
094    public abstract Unit<? super Q> getStandardUnit();
095
096    
097    /**
098     * Returns the converter from this unit to its system unit.
099     * 
100     * @return <code>this.getConverterTo(this.getSystemUnit())</code>
101     */
102    public abstract UnitConverter toStandardUnit();
103
104    /**
105     * Returns the hash code for this unit.
106     *
107     * @return this unit hashcode value.
108     */
109    public abstract int hashCode();
110
111    /**
112     * Indicates if the specified unit can be considered equals to 
113     * the one specified.
114     *
115     * @param that the object to compare to.
116     * @return <code>true</code> if this unit is considered equal to 
117     *         that unit; <code>false</code> otherwise.
118     */
119    public abstract boolean equals(Object that);
120
121    /**
122     * Indicates if this unit is a standard unit (base units and 
123     * alternate units are standard units). The standard unit identifies 
124     * the "type" of {@link javax.measure.quantity.Quantity quantity} for 
125     * which the unit is employed.
126     * 
127     * @return <code>getStandardUnit().equals(this)</code>
128     */
129    public boolean isStandardUnit() {
130        return getStandardUnit().equals(this);
131    }
132
133    /**
134     * Indicates if this unit is compatible with the unit specified.
135     * Units don't need to be equals to be compatible. For example:[code]
136     *     RADIAN.equals(ONE) == false
137     *     RADIAN.isCompatible(ONE) == true
138     * [/code]
139     * @param  that the other unit.
140     * @return <code>this.getDimension().equals(that.getDimension())</code>
141     * @see #getDimension()
142     */
143    public final boolean isCompatible(Unit<?> that) {
144        return (this == that)
145                || this.getStandardUnit().equals(that.getStandardUnit())
146                || this.getDimension().equals(that.getDimension());
147    }
148
149    /**
150     * Casts this unit to a parameterized unit of specified nature or 
151     * throw a <code>ClassCastException</code> if the dimension of the 
152     * specified quantity and this unit's dimension do not match.
153     * For example:[code]
154     * Unit<Length> LIGHT_YEAR = NonSI.C.times(NonSI.YEAR).asType(Length.class);
155     * [/code]
156     * 
157     * @param  type the quantity class identifying the nature of the unit.
158     * @return this unit parameterized with the specified type.
159     * @throws ClassCastException if the dimension of this unit is different 
160     *         from the specified quantity dimension.
161     * @throws UnsupportedOperationException if the specified quantity class
162     *         does not have a public static field named "UNIT" holding the 
163     *         standard unit for the quantity.
164     */
165    @SuppressWarnings("unchecked")
166    public final <T extends Quantity> Unit<T> asType(Class<T> type) throws ClassCastException {
167        Dimension dim1 = this.getDimension();
168        Unit<T> u = null;
169        try {
170            u = (Unit<T>)type.getField("UNIT").get(null);
171        } catch (Exception e) {
172            throw new Error(e);
173        }
174        Dimension dim2 = u.getDimension();
175        if (!dim1.equals(dim2))
176            throw new ClassCastException();
177        return (Unit<T>)this;
178    }
179   
180    /**
181     * Returns the dimension of this unit (depends upon the current 
182     * dimensional {@link Dimension.Model model}). 
183     *
184     * @return the dimension of this unit for the current model.
185     */
186    public final Dimension getDimension() {
187        Unit<?> systemUnit = this.getStandardUnit();
188        if (systemUnit instanceof BaseUnit)
189            return Dimension.getModel().getDimension((BaseUnit<?>) systemUnit);
190        if (systemUnit instanceof AlternateUnit)
191            return ((AlternateUnit<?>) systemUnit).getParent().getDimension();
192        // Product of units.
193        ProductUnit<?> productUnit = (ProductUnit<?>) systemUnit;
194        Dimension dimension = Dimension.NONE;
195        for (int i = 0; i < productUnit.getUnitCount(); i++) {
196            Unit<?> unit = productUnit.getUnit(i);
197            Dimension d = unit.getDimension().pow(productUnit.getUnitPow(i))
198                    .root(productUnit.getUnitRoot(i));
199            dimension = dimension.times(d);
200        }
201        return dimension;
202    }
203
204    /**
205     * Returns a converter of numeric values from this unit to another unit.
206     *
207     * @param  that the unit to which to convert the numeric values.
208     * @return the converter from this unit to <code>that</code> unit.
209     * @throws ConversionException if the conveter cannot be constructed
210     *         (e.g. <code>!this.isCompatible(that)</code>).
211     */
212    public final UnitConverter getConverterTo(Unit<?> that)
213            throws ConversionException {
214        if (this.equals(that))
215            return UnitConverter.IDENTITY;
216        Unit<?> thisSystemUnit = this.getStandardUnit();
217        Unit<?> thatSystemUnit = that.getStandardUnit();
218        if (thisSystemUnit.equals(thatSystemUnit))
219            return that.toStandardUnit().inverse().concatenate(
220                    this.toStandardUnit());
221        // Use dimensional transforms.
222        if (!thisSystemUnit.getDimension()
223                .equals(thatSystemUnit.getDimension()))
224            throw new ConversionException(this + " is not compatible with "
225                    + that);
226        // Transform between SystemUnit and BaseUnits is Identity. 
227        UnitConverter thisTransform = this.toStandardUnit().concatenate(
228                transformOf(this.getBaseUnits()));
229        UnitConverter thatTransform = that.toStandardUnit().concatenate(
230                transformOf(that.getBaseUnits()));
231        return thatTransform.inverse().concatenate(thisTransform);
232    }
233
234    private Unit<?> getBaseUnits() {
235        Unit<?> systemUnit = this.getStandardUnit();
236        if (systemUnit instanceof BaseUnit) return systemUnit;
237        if (systemUnit instanceof AlternateUnit) 
238            return ((AlternateUnit<?>)systemUnit).getParent().getBaseUnits();
239        if (systemUnit instanceof ProductUnit) {
240            ProductUnit<?> productUnit = (ProductUnit<?>)systemUnit;
241            Unit<?> baseUnits = ONE;
242            for (int i = 0; i < productUnit.getUnitCount(); i++) {
243                Unit<?> unit = productUnit.getUnit(i).getBaseUnits();
244                unit = unit.pow(productUnit.getUnitPow(i));
245                unit = unit.root(productUnit.getUnitRoot(i));
246                baseUnits = baseUnits.times(unit);
247            }
248            return baseUnits;
249        } else {
250            throw new InternalError(
251                    "System Unit cannot be an instance of " + this.getClass());            
252        }        
253    }
254    private static UnitConverter transformOf(Unit<?> baseUnits) {
255        if (baseUnits instanceof BaseUnit)
256            return Dimension.getModel().getTransform((BaseUnit<?>) baseUnits);
257        // Product of units.
258        ProductUnit<?> productUnit = (ProductUnit<?>) baseUnits;
259        UnitConverter converter = UnitConverter.IDENTITY;
260        for (int i = 0; i < productUnit.getUnitCount(); i++) {
261            Unit<?> unit = productUnit.getUnit(i);
262            UnitConverter cvtr = transformOf(unit);
263            if (!cvtr.isLinear())
264                throw new ConversionException(baseUnits
265                        + " is non-linear, cannot convert");
266            if (productUnit.getUnitRoot(i) != 1)
267                throw new ConversionException(productUnit
268                        + " holds a base unit with fractional exponent");
269            int pow = productUnit.getUnitPow(i);
270            if (pow < 0) { // Negative power.
271                pow = -pow;
272                cvtr = cvtr.inverse();
273            }
274            for (int j = 0; j < pow; j++) {
275                converter = converter.concatenate(cvtr);
276            }
277        }
278        return converter;
279    }
280
281    /**
282     * Returns a unit equivalent to this unit but used in expressions to 
283     * distinguish between quantities of a different nature but of the same
284     * dimensions.
285     *     
286     * <p> Examples of alternate units:[code]
287     *     Unit<Angle> RADIAN = ONE.alternate("rad");
288     *     Unit<Force> NEWTON = METER.times(KILOGRAM).divide(SECOND.pow(2)).alternate("N");
289     *     Unit<Pressure> PASCAL = NEWTON.divide(METER.pow(2)).alternate("Pa");
290     *     [/code]</p>
291     *
292     * @param symbol the new symbol for the alternate unit.
293     * @return the alternate unit.
294     * @throws UnsupportedOperationException if this unit is not a standard unit.
295     * @throws IllegalArgumentException if the specified symbol is already 
296     *         associated to a different unit.
297     */
298    public final <A extends Quantity> AlternateUnit<A> alternate(String symbol) {
299        return new AlternateUnit<A>(symbol, this);
300    }
301
302    /**
303     * Returns the combination of this unit with the specified sub-unit.
304     * Compound units are typically used for formatting purpose. 
305     * Examples of compound units:[code]
306     *   HOUR_MINUTE = NonSI.HOUR.compound(NonSI.MINUTE);
307     *   DEGREE_MINUTE_SECOND_ANGLE = NonSI.DEGREE_ANGLE.compound(
308     *       NonSI.DEGREE_MINUTE).compound(NonSI.SECOND_ANGLE);
309     *  [/code]
310     *
311     * @param  subunit the sub-unit to combine with this unit.
312     * @return the corresponding compound unit.
313     */
314    public final CompoundUnit<Q> compound(Unit<Q> subunit) {
315        return new CompoundUnit<Q>(this, subunit);
316    }
317
318    /**
319     * Returns the unit derived from this unit using the specified converter.
320     * The converter does not need to be linear. For example:[code]
321     * Unit<Dimensionless> DECIBEL = Unit.ONE.transform(
322     *     new LogConverter(10).inverse().concatenate(
323     *           new RationalConverter(1, 10)));[/code]
324     *
325     * @param operation the converter from the transformed unit to this unit.
326     * @return the unit after the specified transformation.
327     */
328    public final Unit<Q> transform(UnitConverter operation) {
329        if (this instanceof TransformedUnit) {
330            TransformedUnit<Q> tf = (TransformedUnit<Q>) this;
331            Unit<Q> parent = tf.getParentUnit();
332            UnitConverter toParent = tf.toParentUnit().concatenate(operation);
333            if (toParent == UnitConverter.IDENTITY)
334                return parent;
335            return new TransformedUnit<Q>(parent, toParent);
336        }
337        if (operation == UnitConverter.IDENTITY) 
338            return this;
339        return new TransformedUnit<Q>(this, operation);
340    }
341
342    /**
343     * Returns the result of adding an offset to this unit. The returned unit
344     * is convertible with all units that are convertible with this unit.
345     *
346     * @param  offset the offset added (expressed in this unit,
347     *         e.g. <code>CELSIUS = KELVIN.plus(273.15)</code>).
348     * @return <code>this.transform(new AddConverter(offset))</code>
349     */
350    public final Unit<Q> plus(double offset) {
351        return transform(new AddConverter(offset));
352    }
353
354    /**
355     * Returns the result of multiplying this unit by an exact factor. 
356     *
357     * @param  factor the exact scale factor
358     *         (e.g. <code>KILOMETER = METER.times(1000)</code>).
359     * @return <code>this.transform(new RationalConverter(factor, 1))</code>
360     */
361    public final Unit<Q> times(long factor) {
362        return transform(new RationalConverter(factor, 1));
363    }
364
365    /**
366     * Returns the result of multiplying this unit by a an approximate factor
367     *
368     * @param  factor the approximate factor (e.g. 
369     *         <code>ELECTRON_MASS = KILOGRAM.times(9.10938188e-31)</code>).
370     * @return <code>this.transform(new MultiplyConverter(factor))</code>
371     */
372    public final Unit<Q> times(double factor) {
373        return transform(new MultiplyConverter(factor));
374    }
375
376    /**
377     * Returns the product of this unit with the one specified.
378     *
379     * @param  that the unit multiplicand.
380     * @return <code>this * that</code>
381     */
382    public final Unit<? extends Quantity> times(Unit<?> that) {
383        return ProductUnit.getProductInstance(this, that);
384    }
385
386    /**
387     * Returns the inverse of this unit.
388     *
389     * @return <code>1 / this</code>
390     */
391    public final Unit<? extends Quantity> inverse() {
392        return ProductUnit.getQuotientInstance(ONE, this);
393    }
394
395    /**
396     * Returns the result of dividing this unit by an exact divisor.
397     *
398     * @param  divisor the exact divisor.
399     *         (e.g. <code>QUART = GALLON_LIQUID_US.divide(4)</code>).
400     * @return <code>this.transform(new RationalConverter(1 , divisor))</code>
401     */
402    public final Unit<Q> divide(long divisor) {
403        return transform(new RationalConverter(1, divisor));
404    }
405
406    /**
407     * Returns the result of dividing this unit by an approximate divisor.
408     *
409     * @param  divisor the approximate divisor.
410     * @return <code>this.transform(new MultiplyConverter(1.0 / divisor))</code>
411     */
412    public final Unit<Q> divide(double divisor) {
413        return transform(new MultiplyConverter(1.0 / divisor));
414    }
415
416    /**
417     * Returns the quotient of this unit with the one specified.
418     *
419     * @param  that the unit divisor.
420     * @return <code>this / that</code>
421     */
422    public final Unit<? extends Quantity> divide(Unit<?> that) {
423        return this.times(that.inverse());
424    }
425
426    /**
427     * Returns a unit equals to the given root of this unit.
428     *
429     * @param  n the root's order.
430     * @return the result of taking the given root of this unit.
431     * @throws ArithmeticException if <code>n == 0</code>.
432     */
433    public final Unit<? extends Quantity> root(int n) {
434        if (n > 0) {
435            return ProductUnit.getRootInstance(this, n);
436        } else if (n == 0) {
437            throw new ArithmeticException("Root's order of zero");
438        } else { // n < 0
439            return ONE.divide(this.root(-n));
440        }
441    }
442
443    /**
444     * Returns a unit equals to this unit raised to an exponent.
445     *
446     * @param  n the exponent.
447     * @return the result of raising this unit to the exponent.
448     */
449    public final Unit<? extends Quantity> pow(int n) {
450        if (n > 0) {
451            return this.times(this.pow(n - 1));
452        } else if (n == 0) {
453            return ONE;
454        } else { // n < 0
455            return ONE.divide(this.pow(-n));
456        }
457    }
458
459    /**
460     * Returns a unit instance that is defined from the specified
461     * character sequence using the {@link UnitFormat#getInstance()
462     * standard unit format}.
463     * <p> Examples of valid entries (all for meters per second squared) are:
464     *     <code><ul>
465     *       <li>m*s-2</li>
466     *       <li>m/s²</li>
467     *       <li>m·s-²</li>
468     *       <li>m*s**-2</li>
469     *       <li>m^+1 s^-2</li>
470     *     </ul></code></p>
471     *
472     * @param  csq the character sequence to parse.
473     * @return <code>UnitFormat.getStandardInstance().parse(csq, new ParsePosition(0))</code>
474     * @throws IllegalArgumentException if the specified character sequence
475     *         cannot be correctly parsed (e.g. symbol unknown).
476     */
477    public static Unit<? extends Quantity> valueOf(CharSequence csq) {
478        try {
479            return UnitFormat.getInstance()
480                    .parseProductUnit(csq, new ParsePosition(0));
481        } catch (ParseException e) {
482            throw new IllegalArgumentException(e);
483        }
484    }
485
486    //////////////////////
487    // GENERAL CONTRACT //
488    //////////////////////
489
490    /**
491     * Returns the standard <code>String</code> representation of this unit.
492     * This representation is not affected by locale. Locale-sensitive
493     * unit formatting and parsing is handled by the {@link MeasureFormat} 
494     * class and its subclasses.    
495     *  
496     * @return <code>UnitFormat.getStandardInstance().format(this)</code>
497     */
498    public final String toString() {
499        return UnitFormat.getInstance().format(this);
500    }
501}