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: */
009: package javax.measure.unit;
010:
011: import java.io.Serializable;
012: import java.text.ParseException;
013: import java.text.ParsePosition;
014: import java.util.HashMap;
015:
016: import javax.measure.MeasureFormat;
017: import javax.measure.converter.AddConverter;
018: import javax.measure.converter.ConversionException;
019: import javax.measure.converter.MultiplyConverter;
020: import javax.measure.converter.RationalConverter;
021: import javax.measure.converter.UnitConverter;
022: import javax.measure.quantity.Dimensionless;
023: import 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: */
055: public 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: * Returns the converter from this unit to its system unit.
098: *
099: * @return <code>this.getConverterTo(this.getSystemUnit())</code>
100: */
101: public abstract UnitConverter toStandardUnit();
102:
103: /**
104: * Returns the hash code for this unit.
105: *
106: * @return this unit hashcode value.
107: */
108: public abstract int hashCode();
109:
110: /**
111: * Indicates if the specified unit can be considered equals to
112: * the one specified.
113: *
114: * @param that the object to compare to.
115: * @return <code>true</code> if this unit is considered equal to
116: * that unit; <code>false</code> otherwise.
117: */
118: public abstract boolean equals(Object that);
119:
120: /**
121: * Indicates if this unit is a standard unit (base units and
122: * alternate units are standard units). The standard unit identifies
123: * the "type" of {@link javax.measure.quantity.Quantity quantity} for
124: * which the unit is employed.
125: *
126: * @return <code>getStandardUnit().equals(this)</code>
127: */
128: public boolean isStandardUnit() {
129: return getStandardUnit().equals(this );
130: }
131:
132: /**
133: * Indicates if this unit is compatible with the unit specified.
134: * Units don't need to be equals to be compatible. For example:[code]
135: * RADIAN.equals(ONE) == false
136: * RADIAN.isCompatible(ONE) == true
137: * [/code]
138: * @param that the other unit.
139: * @return <code>this.getDimension().equals(that.getDimension())</code>
140: * @see #getDimension()
141: */
142: public final boolean isCompatible(Unit<?> that) {
143: return (this == that)
144: || this .getStandardUnit()
145: .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)
167: throws ClassCastException {
168: Dimension dim1 = this .getDimension();
169: Unit<T> u = null;
170: try {
171: u = (Unit<T>) type.getField("UNIT").get(null);
172: } catch (Exception e) {
173: throw new Error(e);
174: }
175: Dimension dim2 = u.getDimension();
176: if (!dim1.equals(dim2))
177: throw new ClassCastException();
178: return (Unit<T>) this ;
179: }
180:
181: /**
182: * Returns the dimension of this unit (depends upon the current
183: * dimensional {@link Dimension.Model model}).
184: *
185: * @return the dimension of this unit for the current model.
186: */
187: public final Dimension getDimension() {
188: Unit<?> systemUnit = this .getStandardUnit();
189: if (systemUnit instanceof BaseUnit)
190: return Dimension.getModel().getDimension(
191: (BaseUnit<?>) systemUnit);
192: if (systemUnit instanceof AlternateUnit)
193: return ((AlternateUnit<?>) systemUnit).getParent()
194: .getDimension();
195: // Product of units.
196: ProductUnit<?> productUnit = (ProductUnit<?>) systemUnit;
197: Dimension dimension = Dimension.NONE;
198: for (int i = 0; i < productUnit.getUnitCount(); i++) {
199: Unit<?> unit = productUnit.getUnit(i);
200: Dimension d = unit.getDimension().pow(
201: productUnit.getUnitPow(i)).root(
202: productUnit.getUnitRoot(i));
203: dimension = dimension.times(d);
204: }
205: return dimension;
206: }
207:
208: /**
209: * Returns a converter of numeric values from this unit to another unit.
210: *
211: * @param that the unit to which to convert the numeric values.
212: * @return the converter from this unit to <code>that</code> unit.
213: * @throws ConversionException if the conveter cannot be constructed
214: * (e.g. <code>!this.isCompatible(that)</code>).
215: */
216: public final UnitConverter getConverterTo(Unit<?> that)
217: throws ConversionException {
218: if (this .equals(that))
219: return UnitConverter.IDENTITY;
220: Unit<?> this SystemUnit = this .getStandardUnit();
221: Unit<?> thatSystemUnit = that.getStandardUnit();
222: if (this SystemUnit.equals(thatSystemUnit))
223: return that.toStandardUnit().inverse().concatenate(
224: this .toStandardUnit());
225: // Use dimensional transforms.
226: if (!this SystemUnit.getDimension().equals(
227: thatSystemUnit.getDimension()))
228: throw new ConversionException(this
229: + " is not compatible with " + that);
230: // Transform between SystemUnit and BaseUnits is Identity.
231: UnitConverter this Transform = this .toStandardUnit()
232: .concatenate(transformOf(this .getBaseUnits()));
233: UnitConverter thatTransform = that.toStandardUnit()
234: .concatenate(transformOf(that.getBaseUnits()));
235: return thatTransform.inverse().concatenate(this Transform);
236: }
237:
238: private Unit<?> getBaseUnits() {
239: Unit<?> systemUnit = this .getStandardUnit();
240: if (systemUnit instanceof BaseUnit)
241: return systemUnit;
242: if (systemUnit instanceof AlternateUnit)
243: return ((AlternateUnit<?>) systemUnit).getParent()
244: .getBaseUnits();
245: if (systemUnit instanceof ProductUnit) {
246: ProductUnit<?> productUnit = (ProductUnit<?>) systemUnit;
247: Unit<?> baseUnits = ONE;
248: for (int i = 0; i < productUnit.getUnitCount(); i++) {
249: Unit<?> unit = productUnit.getUnit(i).getBaseUnits();
250: unit = unit.pow(productUnit.getUnitPow(i));
251: unit = unit.root(productUnit.getUnitRoot(i));
252: baseUnits = baseUnits.times(unit);
253: }
254: return baseUnits;
255: } else {
256: throw new InternalError(
257: "System Unit cannot be an instance of "
258: + this .getClass());
259: }
260: }
261:
262: private static UnitConverter transformOf(Unit<?> baseUnits) {
263: if (baseUnits instanceof BaseUnit)
264: return Dimension.getModel().getTransform(
265: (BaseUnit<?>) baseUnits);
266: // Product of units.
267: ProductUnit<?> productUnit = (ProductUnit<?>) baseUnits;
268: UnitConverter converter = UnitConverter.IDENTITY;
269: for (int i = 0; i < productUnit.getUnitCount(); i++) {
270: Unit<?> unit = productUnit.getUnit(i);
271: UnitConverter cvtr = transformOf(unit);
272: if (!cvtr.isLinear())
273: throw new ConversionException(baseUnits
274: + " is non-linear, cannot convert");
275: if (productUnit.getUnitRoot(i) != 1)
276: throw new ConversionException(productUnit
277: + " holds a base unit with fractional exponent");
278: int pow = productUnit.getUnitPow(i);
279: if (pow < 0) { // Negative power.
280: pow = -pow;
281: cvtr = cvtr.inverse();
282: }
283: for (int j = 0; j < pow; j++) {
284: converter = converter.concatenate(cvtr);
285: }
286: }
287: return converter;
288: }
289:
290: /**
291: * Returns a unit equivalent to this unit but used in expressions to
292: * distinguish between quantities of a different nature but of the same
293: * dimensions.
294: *
295: * <p> Examples of alternate units:[code]
296: * Unit<Angle> RADIAN = ONE.alternate("rad");
297: * Unit<Force> NEWTON = METER.times(KILOGRAM).divide(SECOND.pow(2)).alternate("N");
298: * Unit<Pressure> PASCAL = NEWTON.divide(METER.pow(2)).alternate("Pa");
299: * [/code]</p>
300: *
301: * @param symbol the new symbol for the alternate unit.
302: * @return the alternate unit.
303: * @throws UnsupportedOperationException if this unit is not a standard unit.
304: * @throws IllegalArgumentException if the specified symbol is already
305: * associated to a different unit.
306: */
307: public final <A extends Quantity> AlternateUnit<A> alternate(
308: String symbol) {
309: return new AlternateUnit<A>(symbol, this );
310: }
311:
312: /**
313: * Returns the combination of this unit with the specified sub-unit.
314: * Compound units are typically used for formatting purpose.
315: * Examples of compound units:[code]
316: * HOUR_MINUTE = NonSI.HOUR.compound(NonSI.MINUTE);
317: * DEGREE_MINUTE_SECOND_ANGLE = NonSI.DEGREE_ANGLE.compound(
318: * NonSI.DEGREE_MINUTE).compound(NonSI.SECOND_ANGLE);
319: * [/code]
320: *
321: * @param subunit the sub-unit to combine with this unit.
322: * @return the corresponding compound unit.
323: */
324: public final CompoundUnit<Q> compound(Unit<Q> subunit) {
325: return new CompoundUnit<Q>(this , subunit);
326: }
327:
328: /**
329: * Returns the unit derived from this unit using the specified converter.
330: * The converter does not need to be linear. For example:[code]
331: * Unit<Dimensionless> DECIBEL = Unit.ONE.transform(
332: * new LogConverter(10).inverse().concatenate(
333: * new RationalConverter(1, 10)));[/code]
334: *
335: * @param operation the converter from the transformed unit to this unit.
336: * @return the unit after the specified transformation.
337: */
338: public final Unit<Q> transform(UnitConverter operation) {
339: if (this instanceof TransformedUnit) {
340: TransformedUnit<Q> tf = (TransformedUnit<Q>) this ;
341: Unit<Q> parent = tf.getParentUnit();
342: UnitConverter toParent = tf.toParentUnit().concatenate(
343: operation);
344: if (toParent == UnitConverter.IDENTITY)
345: return parent;
346: return new TransformedUnit<Q>(parent, toParent);
347: }
348: if (operation == UnitConverter.IDENTITY)
349: return this ;
350: return new TransformedUnit<Q>(this , operation);
351: }
352:
353: /**
354: * Returns the result of adding an offset to this unit. The returned unit
355: * is convertible with all units that are convertible with this unit.
356: *
357: * @param offset the offset added (expressed in this unit,
358: * e.g. <code>CELSIUS = KELVIN.plus(273.15)</code>).
359: * @return <code>this.transform(new AddConverter(offset))</code>
360: */
361: public final Unit<Q> plus(double offset) {
362: return transform(new AddConverter(offset));
363: }
364:
365: /**
366: * Returns the result of multiplying this unit by an exact factor.
367: *
368: * @param factor the exact scale factor
369: * (e.g. <code>KILOMETER = METER.times(1000)</code>).
370: * @return <code>this.transform(new RationalConverter(factor, 1))</code>
371: */
372: public final Unit<Q> times(long factor) {
373: return transform(new RationalConverter(factor, 1));
374: }
375:
376: /**
377: * Returns the result of multiplying this unit by a an approximate factor
378: *
379: * @param factor the approximate factor (e.g.
380: * <code>ELECTRON_MASS = KILOGRAM.times(9.10938188e-31)</code>).
381: * @return <code>this.transform(new MultiplyConverter(factor))</code>
382: */
383: public final Unit<Q> times(double factor) {
384: return transform(new MultiplyConverter(factor));
385: }
386:
387: /**
388: * Returns the product of this unit with the one specified.
389: *
390: * @param that the unit multiplicand.
391: * @return <code>this * that</code>
392: */
393: public final Unit<? extends Quantity> times(Unit<?> that) {
394: return ProductUnit.getProductInstance(this , that);
395: }
396:
397: /**
398: * Returns the inverse of this unit.
399: *
400: * @return <code>1 / this</code>
401: */
402: public final Unit<? extends Quantity> inverse() {
403: return ProductUnit.getQuotientInstance(ONE, this );
404: }
405:
406: /**
407: * Returns the result of dividing this unit by an exact divisor.
408: *
409: * @param divisor the exact divisor.
410: * (e.g. <code>QUART = GALLON_LIQUID_US.divide(4)</code>).
411: * @return <code>this.transform(new RationalConverter(1 , divisor))</code>
412: */
413: public final Unit<Q> divide(long divisor) {
414: return transform(new RationalConverter(1, divisor));
415: }
416:
417: /**
418: * Returns the result of dividing this unit by an approximate divisor.
419: *
420: * @param divisor the approximate divisor.
421: * @return <code>this.transform(new MultiplyConverter(1.0 / divisor))</code>
422: */
423: public final Unit<Q> divide(double divisor) {
424: return transform(new MultiplyConverter(1.0 / divisor));
425: }
426:
427: /**
428: * Returns the quotient of this unit with the one specified.
429: *
430: * @param that the unit divisor.
431: * @return <code>this / that</code>
432: */
433: public final Unit<? extends Quantity> divide(Unit<?> that) {
434: return this .times(that.inverse());
435: }
436:
437: /**
438: * Returns a unit equals to the given root of this unit.
439: *
440: * @param n the root's order.
441: * @return the result of taking the given root of this unit.
442: * @throws ArithmeticException if <code>n == 0</code>.
443: */
444: public final Unit<? extends Quantity> root(int n) {
445: if (n > 0) {
446: return ProductUnit.getRootInstance(this , n);
447: } else if (n == 0) {
448: throw new ArithmeticException("Root's order of zero");
449: } else { // n < 0
450: return ONE.divide(this .root(-n));
451: }
452: }
453:
454: /**
455: * Returns a unit equals to this unit raised to an exponent.
456: *
457: * @param n the exponent.
458: * @return the result of raising this unit to the exponent.
459: */
460: public final Unit<? extends Quantity> pow(int n) {
461: if (n > 0) {
462: return this .times(this .pow(n - 1));
463: } else if (n == 0) {
464: return ONE;
465: } else { // n < 0
466: return ONE.divide(this .pow(-n));
467: }
468: }
469:
470: /**
471: * Returns a unit instance that is defined from the specified
472: * character sequence using the {@link UnitFormat#getInstance()
473: * standard unit format}.
474: * <p> Examples of valid entries (all for meters per second squared) are:
475: * <code><ul>
476: * <li>m*s-2</li>
477: * <li>m/s²</li>
478: * <li>m·s-²</li>
479: * <li>m*s**-2</li>
480: * <li>m^+1 s^-2</li>
481: * </ul></code></p>
482: *
483: * @param csq the character sequence to parse.
484: * @return <code>UnitFormat.getStandardInstance().parse(csq, new ParsePosition(0))</code>
485: * @throws IllegalArgumentException if the specified character sequence
486: * cannot be correctly parsed (e.g. symbol unknown).
487: */
488: public static Unit<? extends Quantity> valueOf(CharSequence csq) {
489: try {
490: return UnitFormat.getInstance().parseProductUnit(csq,
491: new ParsePosition(0));
492: } catch (ParseException e) {
493: throw new IllegalArgumentException(e);
494: }
495: }
496:
497: //////////////////////
498: // GENERAL CONTRACT //
499: //////////////////////
500:
501: /**
502: * Returns the standard <code>String</code> representation of this unit.
503: * This representation is not affected by locale. Locale-sensitive
504: * unit formatting and parsing is handled by the {@link MeasureFormat}
505: * class and its subclasses.
506: *
507: * @return <code>UnitFormat.getStandardInstance().format(this)</code>
508: */
509: public final String toString() {
510: return UnitFormat.getInstance().format(this);
511: }
512: }
|