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}