KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jscience > physics > units > Unit


1 /*
2  * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
3  * Copyright (C) 2005 - JScience (http://jscience.org/)
4  * All rights reserved.
5  *
6  * Permission to use, copy, modify, and distribute this software is
7  * freely granted, provided that this notice is preserved.
8  */

9 package org.jscience.physics.units;
10
11 import java.io.Serializable JavaDoc;
12 import java.util.Set JavaDoc;
13
14 import org.jscience.physics.quantities.Angle;
15 import org.jscience.physics.quantities.Quantity;
16 import org.jscience.physics.quantities.Dimensionless;
17
18 import javolution.util.FastComparator;
19 import javolution.util.FastMap;
20 import javolution.xml.XmlElement;
21 import javolution.xml.XmlFormat;
22 import javolution.lang.MathLib;
23 import javolution.lang.PersistentReference;
24 import javolution.lang.Text;
25
26 /**
27  * <p> This class represents a unit of physical quantity.</p>
28  *
29  * <p> It is helpful to think of instances of this class as recording the
30  * history by which they are created. Thus, for example, the string
31  * "g/kg" (which is a dimensionless unit) would result from invoking
32  * the method toString() on a unit that was created by dividing a
33  * gram unit by a kilogram unit. Yet, "kg" divided by "kg" returns
34  * {@link #ONE} and not "kg/kg" due to automatic unit factorization.</p>
35  *
36  * <p> This class supports the multiplication of offsets units. The result is
37  * usually a unit not convertible to its system unit. Such units may
38  * appear in derivative quantities. For example °C/m is a unit of
39  * gradient, which is common in atmospheric and oceanographic research.</p>
40  *
41  * <p> Units raised at rational powers are also supported. For example
42  * the cubic root of "liter" is a unit compatible with meter.</p>
43  *
44  * <p> Instances of this class (and sub-classes) are immutable and unique.</p>
45  *
46  * @author <a HREF="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
47  * @author <a HREF="mailto:steve@unidata.ucar.edu">Steve Emmerson</a>
48  * @author Martin Desruisseaux
49  * @version 1.1, May 24, 2005
50  */

51 public abstract class Unit<Q extends Quantity> implements Serializable JavaDoc {
52
53     /**
54      * Holds the unit collection.
55      */

56     private static final FastMap<Unit, Unit> COLLECTION = new FastMap<Unit, Unit>(
57             1024).setKeyComparator(new Unit.Comparator());
58
59     /**
60      * Holds the symbol collection (symbol-unit mapping).
61      */

62     static final FastMap<String JavaDoc, Unit> SYMBOLS = new FastMap<String JavaDoc, Unit>(256)
63             .setKeyComparator(FastComparator.LEXICAL);
64
65     /**
66      * Holds the default XML representation for units.
67      * This representation consists of a <code>"name"</code> attribute
68      * holding the textual representation of the unit.
69      */

70     private static final XmlFormat<Unit> UNIT_XML = new XmlFormat<Unit>(
71             Unit.class) {
72         public void format(Unit unit, XmlElement xml) {
73             xml.setAttribute("name", unit.toText());
74         }
75
76         public Unit parse(XmlElement xml) {
77             return Unit.valueOf(xml.getAttribute("name", Text.EMPTY));
78         }
79     };
80
81     /**
82      * Holds the units multiplication table which might be persistent.
83      */

84     private static final PersistentReference<FastMap<Unit, FastMap<Unit, Unit>>> MULT_TABLE = new PersistentReference<FastMap<Unit, FastMap<Unit, Unit>>>(
85             "org.jscience.physics.units.Unit#MULT_TABLE",
86             new FastMap<Unit, FastMap<Unit, Unit>>().setKeyComparator(FastComparator.DIRECT));
87
88     /**
89      * Holds the dimensionless unit <code>ONE</code>.
90      */

91     public static final Unit<Dimensionless> ONE = new ProductUnit<Dimensionless>();
92     static {
93         COLLECTION.put(ONE, ONE);
94         ONE._hashCode = ONE.hashCodeImpl();
95         ONE._parentUnit = ONE;
96         ONE._toParentUnit = Converter.IDENTITY;
97     }
98
99     /**
100      * Holds the inverse of this unit (if known).
101      */

102     private transient Unit _inverse;
103
104     /**
105      * Holds the hashcode for this unit (calculated once, when the unit
106      * is added to the collection).
107      */

108     private transient int _hashCode;
109
110     /**
111      * Holds this unit associated symbol ("" if none).
112      */

113     String JavaDoc _symbol;
114
115     /**
116      * Holds the parent unit for this unit.
117      */

118     Unit<? super Q> _parentUnit;
119
120     /**
121      * Holds the converter to base units.
122      */

123     Converter _toParentUnit;
124
125     /**
126      * Default constructor.
127      */

128     Unit() {
129     }
130
131     //////////////////////////////////////////////////////
132
// Contract methods (for sub-classes to implement). //
133
//////////////////////////////////////////////////////
134

135     /**
136      * Returns the hash code for this unit (calculated only once as units
137      * are unique).
138      *
139      * @return this unit hashcode value.
140      */

141     protected abstract int hashCodeImpl();
142
143     /**
144      * Indicates if the specified unit can be considered equals to
145      * the one specified (for unicity purpose).
146      *
147      * @return <code>true</code> if this unit is considered equal to
148      * that unit; <code>false</code> otherwise.
149      */

150     protected abstract boolean equalsImpl(Object JavaDoc that);
151
152     /**
153      * Returns the parent units this unit is derived from.
154      *
155      * @return the parent unit or <code>this</code> if this unit is
156      * a base unit or a product of base units.
157      */

158     protected abstract Unit< ? super Q> getParentUnitImpl();
159
160     /**
161      * Returns the converter to the parent unit.
162      *
163      * @return the converter to {@link #getParentUnitImpl()}
164      */

165     protected abstract Converter toParentUnitImpl();
166
167     /////////////////////
168
// Public methods. //
169
/////////////////////
170

171     /**
172      * Returns the unit this unit is derived from.
173      *
174      * <p><i> Note: Having the same parent unit is not sufficient to ensure
175      * that a converter exists between the two units
176      * (e.g. °C/m and K/m).</i></p>
177      * @return the parent unit or <code>this</code> if this unit is
178      * a base unit or a product of base units.
179      */

180     public final Unit<? super Q> getParentUnit() {
181         return _parentUnit;
182     }
183
184     /**
185      * Returns the converter from this unit to its parent unit.
186      *
187      * @return <code>this.getConverterTo(this.getParentUnit())</code>
188      */

189     public final Converter toParentUnit() {
190         return _toParentUnit;
191     }
192
193     /**
194      * Returns the {@link BaseUnit base unit} or product of base units this
195      * unit is derived from.
196      *
197      * <p><i> Note: Having the same base units is not sufficient to ensure
198      * that a converter exists between the two units
199      * (e.g. °C/m and K/m).</i></p>
200      * @return the base units this unit is derived from.
201      */

202     public final Unit<? super Q> getBaseUnits() {
203         if (_parentUnit == this) return this;
204         return _parentUnit.getBaseUnits();
205     }
206
207     /**
208      * Returns the converter from this unit to its base units.
209      *
210      * @return <code>this.getConverterTo(this.getBaseUnits())</code>
211      */

212     public final Converter toBaseUnits() {
213         if (_parentUnit == this) return Converter.IDENTITY;
214         return _parentUnit.toBaseUnits().concatenate(this._toParentUnit);
215     }
216     
217     /**
218      * Indicates if this unit is compatible with the unit specified.
219      * Units don't need to be equals to be compatible. For example:
220      * <pre>
221      * <code>RADIAN.equals(ONE) == false</code>
222      * <code>RADIAN.isCompatible(ONE) == true</code>.
223      * </pre>
224      * @param that the other unit.
225      * @return <code>this.getDimension() == that.getDimension()</code>
226      */

227     public final boolean isCompatible(Unit that) {
228         return (this._parentUnit == that._parentUnit)
229                 || (this.getDimension() == that.getDimension());
230     }
231
232     /**
233      * Returns the {@link javolution.realtime.LocalContext local} dimension
234      * of this unit.
235      *
236      * @return this unit dimension.
237      * @see BaseUnit#setDimension
238      */

239     public final Dimension getDimension() {
240         if (_parentUnit != this)
241             return _parentUnit.getDimension();
242         if (_parentUnit instanceof BaseUnit)
243             return ((BaseUnit< ? super Q>) _parentUnit)._dimension.get();
244         ProductUnit< ? super Q> productUnit = (ProductUnit< ? super Q>) _parentUnit;
245         Dimension dimension = Dimension.NONE;
246         for (int i = 0; i < productUnit.size(); i++) {
247             ProductUnit.Element e = productUnit.get(i);
248             Dimension d = e.getUnit().getDimension().pow(e.getPow()).root(
249                     e.getRoot());
250             dimension = dimension.multiply(d);
251         }
252         return dimension;
253     }
254
255     /**
256      * Returns a converter of numeric values from this unit to another unit.
257      *
258      * @param that the unit to which to convert the numeric values.
259      * @return the converter from this unit to <code>that</code> unit.
260      * @throws ConversionException if the conveter cannot be constructed
261      * (e.g. <code>!this.isCompatible(that)</code>).
262      */

263     public final Converter getConverterTo(Unit that) throws ConversionException {
264         if (this == that)
265             return Converter.IDENTITY;
266         if (this._parentUnit == that._parentUnit)
267             return that._toParentUnit.inverse().concatenate(this._toParentUnit);
268         Converter thisTransform = this.getTransform();
269         Converter thatTransform = that.getTransform();
270         if (this.isCompatible(that))
271             return thatTransform.inverse().concatenate(thisTransform);
272         throw new ConversionException(this + " is not compatible with " + that
273                 + " in current context");
274     }
275
276     private Converter getTransform() { // Intrinsic dimensional transform.
277
if (_parentUnit != this)
278             return _parentUnit.getTransform().concatenate(_toParentUnit);
279         if (_parentUnit instanceof BaseUnit) {
280             Converter transform = ((BaseUnit< ? super Q>) _parentUnit)._transform
281                     .get();
282             return transform.concatenate(_toParentUnit);
283         }
284         ProductUnit< ? super Q> productUnit = (ProductUnit< ? super Q>) _parentUnit;
285         double factor = 1.0;
286         for (int i = 0; i < productUnit.size(); i++) {
287             ProductUnit.Element e = productUnit.get(i);
288             Converter cvtr = e.getUnit().getTransform();
289             if (!cvtr.isLinear())
290                 throw new ConversionException(e.getUnit()
291                         + " is non-linear, cannot convert");
292             factor *= MathLib.pow(cvtr.derivative(0), ((double) e.getPow())
293                     / ((double) e.getRoot()));
294         }
295         return (MathLib.abs(factor - 1.0) < 1e-9) ? _toParentUnit
296                 : new MultiplyConverter(factor).concatenate(_toParentUnit);
297     }
298
299     /**
300      * Returns a unit compatible to this unit except it uses the specified
301      * symbol. The alternate unit can itself be used to form expressions and
302      * symbols for other derived units. For example:
303      * <pre><code>
304      * Unit&lt;Angle&gt; RADIAN = Unit.ONE.alternate("rad");
305      * Unit&lt;Force&gt; NEWTON = METER.multiply(KILOGRAM).divide(SECOND.pow(2)).alternate("N");
306      * Unit&lt;Pressure&gt; PASCAL = NEWTON.divide(METER.pow(2)).alternate("Pa");
307      * </code></pre>
308      *
309      * @param symbol the unique symbol for the alternate unit.
310      * @return <code>AlternateUnit.getInstance(symbol, this)</code>
311      * @throws IllegalArgumentException if the specified symbol is currently
312      * associated to a different unit.
313      */

314     public final <T extends Q> AlternateUnit<T> alternate(String JavaDoc symbol) {
315         AlternateUnit<T> newUnit = new AlternateUnit<T>(symbol, _parentUnit,
316                 _toParentUnit);
317         return (AlternateUnit<T>) getInstance(newUnit); // Ensures unicity.
318
}
319
320     /**
321      * Returns the combination of this unit with the specified sub-unit.
322      * Compound units are typically used for formatting purpose.
323      * Examples of compound units:<pre>
324      * HOUR_MINUTE = NonSI.HOUR.compound(NonSI.MINUTE);
325      * DEGREE_MINUTE_SECOND_ANGLE = NonSI.DEGREE_ANGLE.compound(
326      * NonSI.DEGREE_MINUTE).compound(NonSI.SECOND_ANGLE);</pre>
327      *
328      * @param subunit the sub-unit to combine with this unit.
329      * @return <code>CompoundUnit.getInstance(this, subUnit)</code>
330      */

331     public final CompoundUnit<Q> compound(Unit<Q> subunit) {
332         CompoundUnit<Q> newUnit = new CompoundUnit<Q>(this, subunit);
333         return (CompoundUnit<Q>) getInstance(newUnit); // Ensures unicity.
334
}
335
336     /**
337      * Returns the unit derived from this unit using the specified converter.
338      * The converter does not need to be linear. For example:<pre>
339      * Unit&gt;Dimensionless> DECIBEL = Unit.ONE.transform(
340      * new LogConverter(10).inverse().concatenate(
341      * new MultiplyConverter(0.1))).label("dB");</pre>
342      *
343      * @param operation the converter from the transformed unit to this unit.
344      * @return the unit after the specified transformation.
345      */

346     public final Unit<Q> transform(Converter operation) {
347         TransformedUnit<Q> newUnit
348             = new TransformedUnit<Q>(this, operation);
349         return (Unit<Q>) getInstance(newUnit); // Ensures unicity.
350
}
351
352     /**
353      * Returns the result of adding an offset to this unit. The returned unit
354      * is convertible with all units that are convertible with this unit.
355      *
356      * @param offset the offset added (expressed in this unit,
357      * e.g. <code>CELSIUS = KELVIN.plus(273.15)</code>).
358      * @return <code>this.transform(new AddConverter(offset))</code>
359      */

360     public final Unit<Q> plus(double offset) {
361         return transform(new AddConverter(offset));
362     }
363
364     /**
365      * Returns the result of multiplying this unit by a scale factor. The
366      * returned unit is convertible with all units that are convertible with
367      * this unit.
368      *
369      * @param scale the scale factor
370      * (e.g. <code>KILOMETER = METER.multiply(1000)</code>).
371      * @return <code>this.transform(new MultiplyConverter(scale))</code>
372      */

373     public final Unit<Q> times(double scale) {
374         return transform(new MultiplyConverter(scale));
375     }
376
377     /**
378      * Returns the product of this unit with the one specified.
379      *
380      * @param that the unit multiplicand.
381      * @return <code>this * that</code>
382      */

383     public final <Q extends Quantity> Unit<Q> times(Unit that) {
384         FastMap<Unit, Unit> thisMult = MULT_TABLE.get().get(this);
385         if (thisMult == null) {
386             thisMult = new FastMap<Unit, Unit>().setKeyComparator(FastComparator.DIRECT);
387             synchronized (MULT_TABLE) {
388                 MULT_TABLE.get().put(this, thisMult);
389             }
390         }
391         Unit product = thisMult.get(that);
392         if (product == null) {
393             product = ProductUnit.getProductInstance(this, that);
394             synchronized (thisMult) {
395                 thisMult.put(that, product);
396             }
397         }
398         return product;
399     }
400
401     /**
402      * Returns the inverse of this unit.
403      *
404      * @return <code>1 / this</code>
405      */

406     public final <Q extends Quantity> Unit<Q> inverse() {
407         if (_inverse != null)
408             return _inverse;
409         _inverse = ProductUnit.getQuotientInstance(ONE, this);
410         return _inverse;
411     }
412
413     /**
414      * Returns the quotient of this unit with the one specified.
415      *
416      * @param that the unit divisor.
417      * @return <code>this / that</code>
418      */

419     public final <Q extends Quantity> Unit<Q> divide(Unit that) {
420         if (that._inverse != null)
421             return this.times(that._inverse);
422         that._inverse = ProductUnit.getQuotientInstance(ONE, 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 <Q extends Quantity> Unit<Q> root(int n) {
434         if (n > 0) {
435             return ProductUnit.getRootInstance(this, n);
436         } else if (n == 0) {
437             throw new ArithmeticException JavaDoc("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 <Q extends Quantity> Unit<Q> pow(int n) {
450         if (n > 0) {
451             return this.times(this.pow(n - 1));
452         } else if (n == 0) {
453             return (Unit<Q>) ONE;
454         } else { // n < 0
455
return ONE.divide(this.pow(-n));
456         }
457     }
458
459     /**
460      * Attaches a system-wide label to the specified unit. This method overrides
461      * the previous unit's label (e.g. label from unit database) as units may
462      * only have one label (but multiple aliases). For example:
463      * <pre><code>
464      * DAY.multiply(365).label("year");
465      * Unit FOOT = METER.multiply(0.3048).label("ft");
466      * </code></pre>
467      *
468      * @param label the new label for this unit or <code>null</code>
469      * to detache the previous label (if any).
470      * @return this unit.
471      * @throws IllegalArgumentException if the specified label is a known symbol
472      * or if the specified label is already attached to a different
473      * unit (must be detached first).
474      * @see #alias
475      */

476     public Unit<Q> label(String JavaDoc label) {
477         synchronized (UnitFormat.UNIT_TO_LABEL) {
478             String JavaDoc prevLabel = UnitFormat.UNIT_TO_LABEL.put(this, label);
479             if (prevLabel != null) {
480                 UnitFormat.LABEL_TO_UNIT.put(prevLabel, null);
481             }
482             if (label != null) {
483                 UnitFormat.LABEL_TO_UNIT.put(label, this);
484             }
485             return this;
486         }
487     }
488
489     /**
490      * Attaches a system-wide alias to this unit. Multiple aliases may
491      * be attached to the same unit. Aliases are used during parsing to
492      * recognize different variants of the same unit. For example:
493      * <pre><code>
494      * METER.multiply(0.3048).alias("foot");
495      * METER.multiply(0.3048).alias("feet");
496      * METER.alias("meter");
497      * METER.alias("metre");
498      * </code></pre>
499      *
500      * @param alias the alias being attached to this unit.
501      * @return this unit.
502      */

503     public Unit<Q> alias(String JavaDoc alias) {
504         synchronized (UnitFormat.ALIAS_TO_UNIT) {
505             UnitFormat.ALIAS_TO_UNIT.put(alias, this);
506         }
507         return this;
508     }
509
510     /**
511      * Returns a unit instance that is defined from the specified
512      * character sequence. If the specified character sequence is a
513      * combination of units (e.g. {@link ProductUnit}), then the corresponding
514      * unit is created if not already defined.
515      * <p> Examples of valid entries (all for meters per second squared) are:
516      * <code><ul>
517      * <li>m*s-2</li>
518      * <li>m/s²</li>
519      * <li>m·s-²</li>
520      * <li>m*s**-2</li>
521      * <li>m^+1 s^-2</li>
522      * <li>m&lt;sup&gt;1&lt;/sup&gt;·s&lt;sup&gt;-2&lt;/sup&gt;</li>
523      * </ul></code></p>
524      *
525      * @param csq the character sequence to parse.
526      * @return <code>UnitFormat.current().parse(csq)</code>
527      * @throws IllegalArgumentException if the specified character sequence
528      * cannot be correctly parsed (e.g. symbol unknown).
529      * @see UnitFormat#current
530      */

531     public static Unit valueOf(CharSequence JavaDoc csq) {
532         return UnitFormat.current().parse(csq);
533     }
534
535     /**
536      * Returns a read-only/thread safe set of the currently-defined units.
537      * The collection returned is backed by the actual collection of units
538      * -- so it grows as more units are defined.
539      *
540      * @return an unmodifiable view of the units collection.
541      */

542     public static Set JavaDoc<Unit> getInstances() {
543         return Unit.COLLECTION.unmodifiable().keySet();
544     }
545
546     //////////////////////
547
// GENERAL CONTRACT //
548
//////////////////////
549

550     /**
551      * Returns the textual representation of this unit.
552      *
553      * @return the text representation of this unit using the current format.
554      * @see UnitFormat
555      */

556     public Text toText() {
557         return UnitFormat.current().format(this);
558     }
559
560     /**
561      * Returns the standard <code>String</code> representation of this unit.
562      *
563      * @return <code>toText().toString();</code>
564      */

565     public final String JavaDoc toString() {
566         return toText().toString();
567     }
568
569     /**
570      * This method returns an {@link Unit} from the collection
571      * identified by the specified template; if such units does not
572      * exists the specified template is added to the unit collection
573      * and returned.
574      *
575      * @param template the unit template for comparison.
576      * @return a unit from the collection equals to the specified template;
577      * or the template itself.
578      * @throws IllegalArgumentException if the template uses an intrinsic
579      * symbol already taken by a different unit.
580      * @see Unit#equalsImpl(Object)
581      */

582     protected static <Q extends Quantity> Unit<Q> getInstance(Unit<Q> template) {
583         synchronized (COLLECTION) {
584             Unit<Q> unit = COLLECTION.get(template);
585             if (unit != null)
586                 return unit; // Already exists.
587
if ((template._symbol != null)
588                     && SYMBOLS.containsKey(template._symbol))
589                 throw new IllegalArgumentException JavaDoc("The symbol: "
590                         + template._symbol
591                         + " is currently associated to an instance of "
592                         + unit.getClass());
593             SYMBOLS.put(template._symbol, template);
594             COLLECTION.put(template, template);
595             template._hashCode = template.hashCodeImpl();
596             template._parentUnit = template.getParentUnitImpl();
597             template._toParentUnit = template.toParentUnitImpl();
598             return template;
599         }
600     }
601
602     /**
603      * Indicates if this unit is equal to the object specified.
604      * Units are unique and immutable, therefore users might want to use
605      * <code>==</code> to test for equality.
606      *
607      * @param that the object to compare for equality.
608      * @return <code>this == that</code>
609      */

610     public final boolean equals(Object JavaDoc that) {
611         return this == that;
612     }
613
614     /**
615      * Returns the hash code value for this unit.
616      *
617      * @return the unit hash code.
618      */

619     public final int hashCode() {
620         return _hashCode;
621     }
622
623     /**
624      * Overrides <code>readResolve()</code> to ensure that deserialization
625      * maintains unit's unicity.
626      *
627      * @return a new unit or an existing unit.
628      */

629     protected Object JavaDoc readResolve() {
630         return getInstance(this);
631     }
632
633     /**
634      * This class represents an unit comparator (used to test for unicity).
635      */

636     private static final class Comparator extends FastComparator<Unit> {
637
638         public int hashCodeOf(Unit unit) {
639             return unit.hashCodeImpl();
640         }
641
642         public boolean areEqual(Unit u1, Unit u2) {
643             return u1.equalsImpl(u2);
644         }
645
646         public int compare(Unit u1, Unit u2) {
647             throw new UnsupportedOperationException JavaDoc();
648         }
649
650         private static final long serialVersionUID = 1L;
651     }
652 }
Popular Tags