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;
012
013import javax.measure.converter.RationalConverter;
014import javax.measure.converter.UnitConverter;
015import javax.measure.quantity.Dimensionless;
016
017/**
018 * <p> This class represents the dimension of an unit. Two units <code>u1</code>
019 *     and <code>u2</code> are {@link Unit#isCompatible compatible} if and
020 *     only if <code>(u1.getDimension().equals(u2.getDimension())))</code>
021 *     </p>
022 *     
023 * <p> Instances of this class are immutable.</p>
024 *
025 * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
026 * @version 3.1, April 22, 2006
027 * @see <a href="http://en.wikipedia.org/wiki/Dimensional_analysis">
028 *      Wikipedia: Dimensional Analysis</a>
029 */
030public final class Dimension implements Serializable {
031
032    /**
033     * Holds the current physical model.
034     */
035    private static Model CurrentModel = Model.STANDARD;
036
037    /**
038     * Holds dimensionless.
039     */
040    public static final Dimension NONE = new Dimension(Unit.ONE);
041
042    /**
043     * Holds length dimension (L).
044     */
045    public static final Dimension LENGTH = new Dimension('L');
046
047    /**
048     * Holds mass dimension (M).
049     */
050    public static final Dimension MASS = new Dimension('M');
051
052    /**
053     * Holds time dimension (T).
054     */
055    public static final Dimension TIME = new Dimension('T');
056
057    /**
058     * Holds electric current dimension (I).
059     */
060    public static final Dimension ELECTRIC_CURRENT = new Dimension('I');
061
062    /**
063     * Holds temperature dimension (θ).
064     */
065    public static final Dimension TEMPERATURE = new Dimension('θ');
066
067    /**
068     * Holds amount of substance dimension (N).
069     */
070    public static final Dimension AMOUNT_OF_SUBSTANCE = new Dimension('N');
071
072    /**
073     * Holds the pseudo unit associated to this dimension.
074     */
075    private final Unit<?> _pseudoUnit;
076
077    /**
078     * Creates a new dimension associated to the specified symbol.
079     * 
080     * @param symbol the associated symbol.
081     */
082    public Dimension(char symbol) {
083        _pseudoUnit = new BaseUnit<Dimensionless>("[" + symbol + "]");
084    }
085
086    /**
087     * Creates a dimension having the specified pseudo-unit 
088     * (base unit or product of base unit).
089     * 
090     * @param pseudoUnit the pseudo-unit identifying this dimension.
091     */
092    private Dimension(Unit<?> pseudoUnit) {
093        _pseudoUnit = pseudoUnit;
094    }
095
096    /**
097     * Returns the product of this dimension with the one specified.
098     *
099     * @param  that the dimension multiplicand.
100     * @return <code>this * that</code>
101     */
102    public final Dimension times(Dimension that) {
103        return new Dimension(this._pseudoUnit.times(that._pseudoUnit));
104    }
105
106    /**
107     * Returns the quotient of this dimension with the one specified.
108     *
109     * @param  that the dimension divisor.
110     * @return <code>this / that</code>
111     */
112    public final Dimension divide(Dimension that) {
113        return new Dimension(this._pseudoUnit.divide(that._pseudoUnit));
114    }
115
116    /**
117     * Returns this dimension raised to an exponent.
118     *
119     * @param  n the exponent.
120     * @return the result of raising this dimension to the exponent.
121     */
122    public final Dimension pow(int n) {
123        return new Dimension(this._pseudoUnit.pow(n));
124    }
125
126    /**
127     * Returns the given root of this dimension.
128     *
129     * @param  n the root's order.
130     * @return the result of taking the given root of this dimension.
131     * @throws ArithmeticException if <code>n == 0</code>.
132     */
133    public final Dimension root(int n) {
134        return new Dimension(this._pseudoUnit.root(n));
135    }
136
137    /**
138     * Returns the representation of this dimension.
139     *
140     * @return the representation of this dimension.
141     */
142    public String toString() {
143        return _pseudoUnit.toString();
144    }
145
146    /**
147     * Indicates if the specified dimension is equals to the one specified.
148     *
149     * @param that the object to compare to.
150     * @return <code>true</code> if this dimension is equals to that dimension;
151     *         <code>false</code> otherwise.
152     */
153    public boolean equals(Object that) {
154        if (this == that)
155            return true;
156        return (that instanceof Dimension)
157                && _pseudoUnit.equals(((Dimension) that)._pseudoUnit);
158    }
159
160    /**
161     * Returns the hash code for this dimension.
162     *
163     * @return this dimension hashcode value.
164     */
165    public int hashCode() {
166        return _pseudoUnit.hashCode();
167    }
168
169    /**
170     * Sets the model used to determinate the units dimensions.
171     *  
172     * @param model the new model to be used when calculating unit dimensions.
173     */
174    public static void setModel(Model model) {
175        Dimension.CurrentModel = model;
176    }
177
178    /**
179     * Returns the model used to determinate the units dimensions
180     * (default {@link Model#STANDARD STANDARD}).
181     *  
182     * @return the model used when calculating unit dimensions.
183     */
184    public static Model getModel() {
185        return Dimension.CurrentModel;
186    }
187
188    /**
189     * This interface represents the mapping between {@link BaseUnit base units}
190     * and {@link Dimension dimensions}. Custom models may allow
191     * conversions not possible using the {@link #STANDARD standard} model.
192     * For example:[code]
193     * public static void main(String[] args) {
194     *     Dimension.Model relativistic = new Dimension.Model() {
195     *         RationalConverter meterToSecond = new RationalConverter(1, 299792458); // 1/c
196     *   
197     *         public Dimension getDimension(BaseUnit unit) {
198     *             if (unit.equals(SI.METER)) return Dimension.TIME;
199     *             return Dimension.Model.STANDARD.getDimension(unit);
200     *         }
201     *
202     *         public UnitConverter getTransform(BaseUnit unit) {
203     *             if (unit.equals(SI.METER)) return meterToSecond;
204     *             return Dimension.Model.STANDARD.getTransform(unit);
205     *         }};
206     *     Dimension.setModel(relativistic);
207     * 
208     *     // Converts 1.0 GeV (energy) to kg (mass).
209     *     System.out.println(Unit.valueOf("GeV").getConverterTo(KILOGRAM).convert(1.0));
210     * }
211     *   
212     * > 1.7826617302520883E-27[/code]
213     */
214    public interface Model {
215
216        /**
217         * Holds the standard model (default).
218         */
219        public Model STANDARD = new Model() {
220
221            public Dimension getDimension(BaseUnit<?> unit) {
222                if (unit.equals(SI.METRE)) return Dimension.LENGTH;
223                if (unit.equals(SI.KILOGRAM)) return Dimension.MASS;
224                if (unit.equals(SI.KELVIN)) return Dimension.TEMPERATURE;
225                if (unit.equals(SI.SECOND)) return Dimension.TIME;
226                if (unit.equals(SI.AMPERE)) return Dimension.ELECTRIC_CURRENT;
227                if (unit.equals(SI.MOLE)) return Dimension.AMOUNT_OF_SUBSTANCE;
228                if (unit.equals(SI.CANDELA)) return SI.WATT.getDimension();
229                return new Dimension(new BaseUnit<Dimensionless>("[" + unit.getSymbol() + "]"));
230            }
231            
232            public UnitConverter getTransform(BaseUnit<?> unit) {
233                if (unit.equals(SI.CANDELA)) return new RationalConverter(1, 683);
234                return UnitConverter.IDENTITY;
235            }
236        };
237
238        /**
239         * Returns the dimension of the specified base unit (a dimension 
240         * particular to the base unit if the base unit is not recognized).
241         * 
242         * @param unit the base unit for which the dimension is returned.
243         * @return the dimension of the specified unit.
244         */
245        Dimension getDimension(BaseUnit<?> unit);
246
247        /**
248         * Returns the normalization transform of the specified base unit
249         * ({@link UnitConverter#IDENTITY IDENTITY} if the base unit is 
250         * not recognized).
251         * 
252         * @param unit the base unit for which the transform is returned.
253         * @return the normalization transform.
254         */
255        UnitConverter getTransform(BaseUnit<?> unit);
256    }
257
258    private static final long serialVersionUID = 1L;
259}