source: osm/applications/editors/josm/plugins/opendata/includes/javax/measure/unit/Unit.java@ 28000

Last change on this file since 28000 was 28000, checked in by donvip, 12 years ago

Import new "opendata" JOSM plugin

File size: 15.6 KB
Line 
1/*
2 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
3 * Copyright (C) 2006 - 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 */
9package javax.measure.unit;
10
11import java.io.Serializable;
12import java.util.HashMap;
13
14import javax.measure.converter.ConversionException;
15import javax.measure.converter.MultiplyConverter;
16import javax.measure.converter.RationalConverter;
17import javax.measure.converter.UnitConverter;
18import javax.measure.quantity.Dimensionless;
19import javax.measure.quantity.Quantity;
20
21/**
22 * <p> This class represents a determinate {@link javax.measure.quantity.Quantity
23 * quantity} (as of length, time, heat, or value) adopted as a standard
24 * of measurement.</p>
25 *
26 * <p> It is helpful to think of instances of this class as recording the
27 * history by which they are created. Thus, for example, the string
28 * "g/kg" (which is a dimensionless unit) would result from invoking
29 * the method toString() on a unit that was created by dividing a
30 * gram unit by a kilogram unit. Yet, "kg" divided by "kg" returns
31 * {@link #ONE} and not "kg/kg" due to automatic unit factorization.</p>
32 *
33 * <p> This class supports the multiplication of offsets units. The result is
34 * usually a unit not convertible to its {@link #getStandardUnit standard unit}.
35 * Such units may appear in derivative quantities. For example °C/m is an
36 * unit of gradient, which is common in atmospheric and oceanographic
37 * research.</p>
38 *
39 * <p> Units raised at rational powers are also supported. For example
40 * the cubic root of "liter" is a unit compatible with meter.</p>
41 *
42 * <p> Instances of this class are immutable.</p>
43 *
44 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
45 * @author <a href="mailto:steve@unidata.ucar.edu">Steve Emmerson</a>
46 * @author Martin Desruisseaux
47 * @version 3.2, August 28, 2006
48 * @see <a href="http://en.wikipedia.org/wiki/Units_of_measurement">
49 * Wikipedia: Units of measurement</a>
50 */
51@SuppressWarnings("serial")
52public abstract class Unit<Q extends Quantity> implements Serializable {
53
54 /**
55 * Holds the dimensionless unit <code>ONE</code>.
56 */
57 public static final Unit<Dimensionless> ONE = new ProductUnit<Dimensionless>();
58
59 /**
60 * Holds the unique symbols collection (base unit or alternate units).
61 */
62 static final HashMap<String, Unit<?>> SYMBOL_TO_UNIT = new HashMap<String, Unit<?>>();
63
64 /**
65 * Default constructor.
66 */
67 protected Unit() {
68 }
69
70 //////////////////////////////////////////////////////
71 // Contract methods (for sub-classes to implement). //
72 //////////////////////////////////////////////////////
73
74 /**
75 * Returns the {@link BaseUnit base unit}, {@link AlternateUnit alternate
76 * unit} or product of base units and alternate units this unit is derived
77 * from. The standard unit identifies the "type" of
78 * {@link javax.measure.quantity.Quantity quantity} for which this unit is employed.
79 * For example:[code]
80 * boolean isAngularVelocity(Unit<?> u) {
81 * return u.getStandardUnit().equals(RADIAN.divide(SECOND));
82 * }
83 * assert(REVOLUTION.divide(MINUTE).isAngularVelocity());
84 * [/code]
85 *
86 * <p><i> Note: Having the same system unit is not sufficient to ensure
87 * that a converter exists between the two units
88 * (e.g. °C/m and K/m).</i></p>
89 * @return the system unit this unit is derived from.
90 */
91 public abstract Unit<? super Q> getStandardUnit();
92
93
94 /**
95 * Returns the converter from this unit to its system unit.
96 *
97 * @return <code>this.getConverterTo(this.getSystemUnit())</code>
98 */
99 public abstract UnitConverter toStandardUnit();
100
101 /**
102 * Returns the hash code for this unit.
103 *
104 * @return this unit hashcode value.
105 */
106 public abstract int hashCode();
107
108 /**
109 * Indicates if the specified unit can be considered equals to
110 * the one specified.
111 *
112 * @param that the object to compare to.
113 * @return <code>true</code> if this unit is considered equal to
114 * that unit; <code>false</code> otherwise.
115 */
116 public abstract boolean equals(Object that);
117
118 /**
119 * Indicates if this unit is a standard unit (base units and
120 * alternate units are standard units). The standard unit identifies
121 * the "type" of {@link javax.measure.quantity.Quantity quantity} for
122 * which the unit is employed.
123 *
124 * @return <code>getStandardUnit().equals(this)</code>
125 */
126 public boolean isStandardUnit() {
127 return getStandardUnit().equals(this);
128 }
129
130 /**
131 * Indicates if this unit is compatible with the unit specified.
132 * Units don't need to be equals to be compatible. For example:[code]
133 * RADIAN.equals(ONE) == false
134 * RADIAN.isCompatible(ONE) == true
135 * [/code]
136 * @param that the other unit.
137 * @return <code>this.getDimension().equals(that.getDimension())</code>
138 * @see #getDimension()
139 */
140 public final boolean isCompatible(Unit<?> that) {
141 return (this == that)
142 || this.getStandardUnit().equals(that.getStandardUnit())
143 || this.getDimension().equals(that.getDimension());
144 }
145
146 /**
147 * Casts this unit to a parameterized unit of specified nature or
148 * throw a <code>ClassCastException</code> if the dimension of the
149 * specified quantity and this unit's dimension do not match.
150 * For example:[code]
151 * Unit<Length> LIGHT_YEAR = NonSI.C.times(NonSI.YEAR).asType(Length.class);
152 * [/code]
153 *
154 * @param type the quantity class identifying the nature of the unit.
155 * @return this unit parameterized with the specified type.
156 * @throws ClassCastException if the dimension of this unit is different
157 * from the specified quantity dimension.
158 * @throws UnsupportedOperationException if the specified quantity class
159 * does not have a public static field named "UNIT" holding the
160 * standard unit for the quantity.
161 */
162 @SuppressWarnings("unchecked")
163 public final <T extends Quantity> Unit<T> asType(Class<T> type) throws ClassCastException {
164 Dimension dim1 = this.getDimension();
165 Unit<T> u = null;
166 try {
167 u = (Unit<T>)type.getField("UNIT").get(null);
168 } catch (Exception e) {
169 throw new Error(e);
170 }
171 Dimension dim2 = u.getDimension();
172 if (!dim1.equals(dim2))
173 throw new ClassCastException();
174 return (Unit<T>)this;
175 }
176
177 /**
178 * Returns the dimension of this unit (depends upon the current
179 * dimensional {@link Dimension.Model model}).
180 *
181 * @return the dimension of this unit for the current model.
182 */
183 public final Dimension getDimension() {
184 Unit<?> systemUnit = this.getStandardUnit();
185 if (systemUnit instanceof BaseUnit)
186 return Dimension.getModel().getDimension((BaseUnit<?>) systemUnit);
187 if (systemUnit instanceof AlternateUnit)
188 return ((AlternateUnit<?>) systemUnit).getParent().getDimension();
189 // Product of units.
190 ProductUnit<?> productUnit = (ProductUnit<?>) systemUnit;
191 Dimension dimension = Dimension.NONE;
192 for (int i = 0; i < productUnit.getUnitCount(); i++) {
193 Unit<?> unit = productUnit.getUnit(i);
194 Dimension d = unit.getDimension().pow(productUnit.getUnitPow(i))
195 .root(productUnit.getUnitRoot(i));
196 dimension = dimension.times(d);
197 }
198 return dimension;
199 }
200
201 /**
202 * Returns a converter of numeric values from this unit to another unit.
203 *
204 * @param that the unit to which to convert the numeric values.
205 * @return the converter from this unit to <code>that</code> unit.
206 * @throws ConversionException if the conveter cannot be constructed
207 * (e.g. <code>!this.isCompatible(that)</code>).
208 */
209 public final UnitConverter getConverterTo(Unit<?> that)
210 throws ConversionException {
211 if (this.equals(that))
212 return UnitConverter.IDENTITY;
213 Unit<?> thisSystemUnit = this.getStandardUnit();
214 Unit<?> thatSystemUnit = that.getStandardUnit();
215 if (thisSystemUnit.equals(thatSystemUnit))
216 return that.toStandardUnit().inverse().concatenate(
217 this.toStandardUnit());
218 // Use dimensional transforms.
219 if (!thisSystemUnit.getDimension()
220 .equals(thatSystemUnit.getDimension()))
221 throw new ConversionException(this + " is not compatible with "
222 + that);
223 // Transform between SystemUnit and BaseUnits is Identity.
224 UnitConverter thisTransform = this.toStandardUnit().concatenate(
225 transformOf(this.getBaseUnits()));
226 UnitConverter thatTransform = that.toStandardUnit().concatenate(
227 transformOf(that.getBaseUnits()));
228 return thatTransform.inverse().concatenate(thisTransform);
229 }
230
231 private Unit<?> getBaseUnits() {
232 Unit<?> systemUnit = this.getStandardUnit();
233 if (systemUnit instanceof BaseUnit) return systemUnit;
234 if (systemUnit instanceof AlternateUnit)
235 return ((AlternateUnit<?>)systemUnit).getParent().getBaseUnits();
236 if (systemUnit instanceof ProductUnit) {
237 ProductUnit<?> productUnit = (ProductUnit<?>)systemUnit;
238 Unit<?> baseUnits = ONE;
239 for (int i = 0; i < productUnit.getUnitCount(); i++) {
240 Unit<?> unit = productUnit.getUnit(i).getBaseUnits();
241 unit = unit.pow(productUnit.getUnitPow(i));
242 unit = unit.root(productUnit.getUnitRoot(i));
243 baseUnits = baseUnits.times(unit);
244 }
245 return baseUnits;
246 } else {
247 throw new InternalError(
248 "System Unit cannot be an instance of " + this.getClass());
249 }
250 }
251 private static UnitConverter transformOf(Unit<?> baseUnits) {
252 if (baseUnits instanceof BaseUnit)
253 return Dimension.getModel().getTransform((BaseUnit<?>) baseUnits);
254 // Product of units.
255 ProductUnit<?> productUnit = (ProductUnit<?>) baseUnits;
256 UnitConverter converter = UnitConverter.IDENTITY;
257 for (int i = 0; i < productUnit.getUnitCount(); i++) {
258 Unit<?> unit = productUnit.getUnit(i);
259 UnitConverter cvtr = transformOf(unit);
260 if (!cvtr.isLinear())
261 throw new ConversionException(baseUnits
262 + " is non-linear, cannot convert");
263 if (productUnit.getUnitRoot(i) != 1)
264 throw new ConversionException(productUnit
265 + " holds a base unit with fractional exponent");
266 int pow = productUnit.getUnitPow(i);
267 if (pow < 0) { // Negative power.
268 pow = -pow;
269 cvtr = cvtr.inverse();
270 }
271 for (int j = 0; j < pow; j++) {
272 converter = converter.concatenate(cvtr);
273 }
274 }
275 return converter;
276 }
277
278 /**
279 * Returns the unit derived from this unit using the specified converter.
280 * The converter does not need to be linear. For example:[code]
281 * Unit<Dimensionless> DECIBEL = Unit.ONE.transform(
282 * new LogConverter(10).inverse().concatenate(
283 * new RationalConverter(1, 10)));[/code]
284 *
285 * @param operation the converter from the transformed unit to this unit.
286 * @return the unit after the specified transformation.
287 */
288 public final Unit<Q> transform(UnitConverter operation) {
289 if (this instanceof TransformedUnit) {
290 TransformedUnit<Q> tf = (TransformedUnit<Q>) this;
291 Unit<Q> parent = tf.getParentUnit();
292 UnitConverter toParent = tf.toParentUnit().concatenate(operation);
293 if (toParent == UnitConverter.IDENTITY)
294 return parent;
295 return new TransformedUnit<Q>(parent, toParent);
296 }
297 if (operation == UnitConverter.IDENTITY)
298 return this;
299 return new TransformedUnit<Q>(this, operation);
300 }
301
302 /**
303 * Returns the result of multiplying this unit by an exact factor.
304 *
305 * @param factor the exact scale factor
306 * (e.g. <code>KILOMETER = METER.times(1000)</code>).
307 * @return <code>this.transform(new RationalConverter(factor, 1))</code>
308 */
309 public final Unit<Q> times(long factor) {
310 return transform(new RationalConverter(factor, 1));
311 }
312
313 /**
314 * Returns the result of multiplying this unit by a an approximate factor
315 *
316 * @param factor the approximate factor (e.g.
317 * <code>ELECTRON_MASS = KILOGRAM.times(9.10938188e-31)</code>).
318 * @return <code>this.transform(new MultiplyConverter(factor))</code>
319 */
320 public final Unit<Q> times(double factor) {
321 return transform(new MultiplyConverter(factor));
322 }
323
324 /**
325 * Returns the product of this unit with the one specified.
326 *
327 * @param that the unit multiplicand.
328 * @return <code>this * that</code>
329 */
330 public final Unit<? extends Quantity> times(Unit<?> that) {
331 return ProductUnit.getProductInstance(this, that);
332 }
333
334 /**
335 * Returns the inverse of this unit.
336 *
337 * @return <code>1 / this</code>
338 */
339 public final Unit<? extends Quantity> inverse() {
340 return ProductUnit.getQuotientInstance(ONE, this);
341 }
342
343 /**
344 * Returns the result of dividing this unit by an exact divisor.
345 *
346 * @param divisor the exact divisor.
347 * (e.g. <code>QUART = GALLON_LIQUID_US.divide(4)</code>).
348 * @return <code>this.transform(new RationalConverter(1 , divisor))</code>
349 */
350 public final Unit<Q> divide(long divisor) {
351 return transform(new RationalConverter(1, divisor));
352 }
353
354 /**
355 * Returns the quotient of this unit with the one specified.
356 *
357 * @param that the unit divisor.
358 * @return <code>this / that</code>
359 */
360 public final Unit<? extends Quantity> divide(Unit<?> that) {
361 return this.times(that.inverse());
362 }
363
364 /**
365 * Returns a unit equals to the given root of this unit.
366 *
367 * @param n the root's order.
368 * @return the result of taking the given root of this unit.
369 * @throws ArithmeticException if <code>n == 0</code>.
370 */
371 public final Unit<? extends Quantity> root(int n) {
372 if (n > 0) {
373 return ProductUnit.getRootInstance(this, n);
374 } else if (n == 0) {
375 throw new ArithmeticException("Root's order of zero");
376 } else { // n < 0
377 return ONE.divide(this.root(-n));
378 }
379 }
380
381 /**
382 * Returns a unit equals to this unit raised to an exponent.
383 *
384 * @param n the exponent.
385 * @return the result of raising this unit to the exponent.
386 */
387 public final Unit<? extends Quantity> pow(int n) {
388 if (n > 0) {
389 return this.times(this.pow(n - 1));
390 } else if (n == 0) {
391 return ONE;
392 } else { // n < 0
393 return ONE.divide(this.pow(-n));
394 }
395 }
396}
Note: See TracBrowser for help on using the repository browser.