001/* 002 * Units of Measurement Implementation for Java SE 003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil, V2COM. 004 * 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without modification, 008 * are permitted provided that the following conditions are met: 009 * 010 * 1. Redistributions of source code must retain the above copyright notice, 011 * this list of conditions and the following disclaimer. 012 * 013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 014 * and the following disclaimer in the documentation and/or other materials provided with the distribution. 015 * 016 * 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products 017 * derived from this software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package tec.uom.se; 031 032import tec.uom.se.format.SimpleUnitFormat; 033import tec.uom.se.function.AddConverter; 034import tec.uom.se.function.MultiplyConverter; 035import tec.uom.se.function.RationalConverter; 036import tec.uom.se.quantity.QuantityDimension; 037import tec.uom.se.spi.DimensionalModel; 038import tec.uom.se.unit.AlternateUnit; 039import tec.uom.se.unit.AnnotatedUnit; 040import tec.uom.se.unit.ProductUnit; 041import tec.uom.se.unit.TransformedUnit; 042 043import javax.measure.*; 044import javax.measure.quantity.Dimensionless; 045 046import java.io.Serializable; 047import java.math.BigInteger; 048import java.util.HashMap; 049import java.util.Map; 050import java.lang.reflect.ParameterizedType; 051import java.lang.reflect.Type; 052 053/** 054 * <p> 055 * The class represents units founded on the seven <b>SI</b> base units for seven base quantities assumed to be mutually independent. 056 * </p> 057 * 058 * <p> 059 * For all physics units, unit conversions are symmetrical: <code>u1.getConverterTo(u2).equals(u2.getConverterTo(u1).inverse())</code>. Non-physical 060 * units (e.g. currency units) for which conversion is not symmetrical should have their own separate class hierarchy and are considered distinct 061 * (e.g. financial units), although they can always be combined with physics units (e.g. "€/Kg", "$/h"). 062 * </p> 063 * 064 * @see <a href= "http://en.wikipedia.org/wiki/International_System_of_Units">Wikipedia: International System of Units</a> 065 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 066 * @author <a href="mailto:units@catmedia.us">Werner Keil</a> 067 * @version 1.0.6, April 24, 2017 068 * @since 1.0 069 */ 070public abstract class AbstractUnit<Q extends Quantity<Q>> implements Unit<Q>, Comparable<Unit<Q>>, Serializable { 071 072 /** 073 * 074 */ 075 private static final long serialVersionUID = -4344589505537030204L; 076 077 /** 078 * Holds the dimensionless unit <code>ONE</code>. 079 * 080 * @see <a href= "https://en.wikipedia.org/wiki/Natural_units#Choosing_constants_to_normalize"> Wikipedia: Natural Units - Choosing constants to 081 * normalize</a> 082 * @see <a href= "http://www.av8n.com/physics/dimensionless-units.htm">Units of Dimension One</a> 083 */ 084 public static final Unit<Dimensionless> ONE = new ProductUnit<>(); 085 086 /** 087 * Holds the name. 088 */ 089 protected String name; 090 091 /** 092 * Holds the symbol. 093 */ 094 private String symbol; 095 096 /** 097 * Holds the unique symbols collection (base units or alternate units). 098 */ 099 protected static final Map<String, Unit<?>> SYMBOL_TO_UNIT = new HashMap<>(); 100 101 /** 102 * DefaultQuantityFactory constructor. 103 */ 104 protected AbstractUnit() { 105 } 106 107 protected Type getActualType() { 108 ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass(); 109 return parameterizedType.getActualTypeArguments()[0].getClass().getGenericInterfaces()[0]; 110 } 111 112 /** 113 * Indicates if this unit belongs to the set of coherent SI units (unscaled SI units). 114 * 115 * The base and coherent derived units of the SI form a coherent set, designated the set of coherent SI units. The word coherent is used here in the 116 * following sense: when coherent units are used, equations between the numerical values of quantities take exactly the same form as the equations 117 * between the quantities themselves. Thus if only units from a coherent set are used, conversion factors between units are never required. 118 * 119 * @return <code>equals(toSystemUnit())</code> 120 */ 121 public boolean isSystemUnit() { 122 Unit<Q> si = this.toSystemUnit(); 123 return (this == si) || this.equals(si); 124 } 125 126 /** 127 * Returns the unscaled {@link SI} unit from which this unit is derived. 128 * 129 * The SI unit can be be used to identify a quantity given the unit. For example:<code> static boolean isAngularVelocity(AbstractUnit<?> unit) { 130 * return unit.toSystemUnit().equals(RADIAN.divide(SECOND)); } assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. </code> 131 * 132 * @return the unscaled metric unit from which this unit is derived. 133 */ 134 protected abstract Unit<Q> toSystemUnit(); 135 136 /** 137 * Returns the converter from this unit to its unscaled {@link #toSysemUnit System Unit} unit. 138 * 139 * @return <code>getConverterTo(this.toSystemUnit())</code> 140 * @see #toSI 141 */ 142 public abstract UnitConverter getSystemConverter(); 143 144 /** 145 * Annotates the specified unit. Annotation does not change the unit semantic. Annotations are often written between curly braces behind units. For 146 * example: 147 * <code> AbstractUnit<Volume> PERCENT_VOL = Units.PERCENT.annotate("vol"); // "%{vol}" AbstractUnit<Mass> KG_TOTAL = 148 * Units.KILOGRAM.annotate("total"); // "kg{total}" AbstractUnit<Dimensionless> RED_BLOOD_CELLS = Units.ONE.annotate("RBC"); // "{RBC}" </code> 149 * 150 * Note: Annotation of system units are not considered themselves as system units. 151 * 152 * @param annotation 153 * the unit annotation. 154 * @return the annotated unit. 155 */ 156 public AnnotatedUnit<Q> annotate(String annotation) { 157 return new AnnotatedUnit<>(this, annotation); 158 } 159 160 /** 161 * Returns the abstract unit represented by the specified characters as per default format. 162 * 163 * Locale-sensitive unit parsing could be handled using {@link LocalUnitFormat} in subclasses of AbstractUnit. 164 * 165 * <p> 166 * Note: The standard format supports dimensionless units.<code> AbstractUnit<Dimensionless> PERCENT = 167 * AbstractUnit.parse("100").inverse().asType(Dimensionless.class); </code> 168 * </p> 169 * 170 * @param charSequence 171 * the character sequence to parse. 172 * @return <code>SimpleUnitFormat.getInstance().parse(csq, new ParsePosition(0))</code> 173 * @throws ParserException 174 * if the specified character sequence cannot be correctly parsed (e.g. not UCUM compliant). 175 */ 176 public static Unit<?> parse(CharSequence charSequence) { 177 return SimpleUnitFormat.getInstance().parse(charSequence); 178 } 179 180 /** 181 * Returns the standard representation of this physics unit. The string produced for a given unit is always the same; it is not affected by the 182 * locale. It can be used as a canonical string representation for exchanging units, or as a key for a Hashtable, etc. 183 * 184 * Locale-sensitive unit parsing could be handled using {@link LocalUnitFormat} in subclasses of AbstractUnit. 185 * 186 * @return <code>SimpleUnitFormat.getInstance().format(this)</code> 187 */ 188 @Override 189 public String toString() { 190 return SimpleUnitFormat.getInstance().format(this); 191 } 192 193 // /////////////////////////////////////////////////////// 194 // Implements org.unitsofmeasurement.Unit<Q> interface // 195 // /////////////////////////////////////////////////////// 196 197 /** 198 * Returns the system unit (unscaled SI unit) from which this unit is derived. They can be be used to identify a quantity given the unit. For 199 * example:<br> 200 * <code> static boolean isAngularVelocity(AbstractUnit<?> unit) {<br> return unit.getSystemUnit().equals(RADIAN.divide(SECOND));<br>} 201 * <br>assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. </code> 202 * 203 * @return the unscaled metric unit from which this unit is derived. 204 */ 205 @Override 206 public final Unit<Q> getSystemUnit() { 207 return toSystemUnit(); 208 } 209 210 /** 211 * Indicates if this unit is compatible with the unit specified. To be compatible both units must be physics units having the same fundamental 212 * dimension. 213 * 214 * @param that 215 * the other unit. 216 * @return <code>true</code> if this unit and that unit have equals fundamental dimension according to the current physics model; <code>false</code> 217 * otherwise. 218 */ 219 @Override 220 public final boolean isCompatible(Unit<?> that) { 221 if ((this == that) || this.equals(that)) 222 return true; 223 if (!(that instanceof AbstractUnit)) 224 return false; 225 Dimension thisDimension = this.getDimension(); 226 Dimension thatDimension = that.getDimension(); 227 if (thisDimension.equals(thatDimension)) 228 return true; 229 DimensionalModel model = DimensionalModel.current(); // Use 230 // dimensional 231 // analysis 232 // model. 233 return model.getFundamentalDimension(thisDimension).equals(model.getFundamentalDimension(thatDimension)); 234 } 235 236 /** 237 * Casts this unit to a parameterized unit of specified nature or throw a ClassCastException if the dimension of the specified quantity and this 238 * unit's dimension do not match (regardless whether or not the dimensions are independent or not). 239 * 240 * @param type 241 * the quantity class identifying the nature of the unit. 242 * @throws ClassCastException 243 * if the dimension of this unit is different from the SI dimension of the specified type. 244 * @see Units#getUnit(Class) 245 */ 246 @SuppressWarnings("unchecked") 247 @Override 248 public final <T extends Quantity<T>> AbstractUnit<T> asType(Class<T> type) { 249 Dimension typeDimension = QuantityDimension.of(type); 250 if ((typeDimension != null) && (!typeDimension.equals(this.getDimension()))) 251 throw new ClassCastException("The unit: " + this + " is not compatible with quantities of type " + type); 252 return (AbstractUnit<T>) this; 253 } 254 255 @Override 256 public abstract Map<? extends Unit<?>, Integer> getBaseUnits(); 257 258 @Override 259 public abstract Dimension getDimension(); 260 261 protected void setName(String name) { 262 this.name = name; 263 } 264 265 public String getName() { 266 return name; 267 } 268 269 public String getSymbol() { 270 return symbol; 271 } 272 273 protected void setSymbol(String s) { 274 this.symbol = s; 275 } 276 277 @Override 278 public final UnitConverter getConverterTo(Unit<Q> that) throws UnconvertibleException { 279 if ((this == that) || this.equals(that)) 280 return AbstractConverter.IDENTITY; // Shortcut. 281 Unit<Q> thisSystemUnit = this.getSystemUnit(); 282 Unit<Q> thatSystemUnit = that.getSystemUnit(); 283 if (!thisSystemUnit.equals(thatSystemUnit)) 284 try { 285 return getConverterToAny(that); 286 } catch (IncommensurableException e) { 287 throw new UnconvertibleException(e); 288 } 289 UnitConverter thisToSI = this.getSystemConverter(); 290 UnitConverter thatToSI = that.getConverterTo(thatSystemUnit); 291 return thatToSI.inverse().concatenate(thisToSI); 292 } 293 294 @SuppressWarnings("rawtypes") 295 @Override 296 public final UnitConverter getConverterToAny(Unit<?> that) throws IncommensurableException, UnconvertibleException { 297 if (!isCompatible(that)) 298 throw new IncommensurableException(this + " is not compatible with " + that); 299 AbstractUnit thatAbstr = (AbstractUnit) that; // Since both units are 300 // compatible they must 301 // be both physics 302 // units. 303 DimensionalModel model = DimensionalModel.current(); 304 Unit thisSystemUnit = this.getSystemUnit(); 305 UnitConverter thisToDimension = model.getDimensionalTransform(thisSystemUnit.getDimension()).concatenate(this.getSystemConverter()); 306 Unit thatSystemUnit = thatAbstr.getSystemUnit(); 307 UnitConverter thatToDimension = model.getDimensionalTransform(thatSystemUnit.getDimension()).concatenate(thatAbstr.getSystemConverter()); 308 return thatToDimension.inverse().concatenate(thisToDimension); 309 } 310 311 @SuppressWarnings({ "rawtypes", "unchecked" }) 312 @Override 313 public final Unit<Q> alternate(String symbol) { 314 return new AlternateUnit(this, symbol); 315 } 316 317 @Override 318 public final Unit<Q> transform(UnitConverter operation) { 319 Unit<Q> systemUnit = this.getSystemUnit(); 320 UnitConverter cvtr; 321 if (this.isSystemUnit()) { 322 cvtr = this.getSystemConverter().concatenate(operation); 323 } else { 324 cvtr = operation; 325 } 326 if (cvtr.equals(AbstractConverter.IDENTITY)) { 327 return systemUnit; 328 } else { 329 return new TransformedUnit<>(null, this, systemUnit, cvtr); 330 } 331 } 332 333 @Override 334 public final Unit<Q> shift(double offset) { 335 if (offset == 0) 336 return this; 337 return transform(new AddConverter(offset)); 338 } 339 340 @Override 341 public final Unit<Q> multiply(double factor) { 342 if (factor == 1) 343 return this; 344 if (isLongValue(factor)) 345 return transform(new RationalConverter(BigInteger.valueOf((long) factor), BigInteger.ONE)); 346 return transform(new MultiplyConverter(factor)); 347 } 348 349 private static boolean isLongValue(double value) { 350 return !((value < Long.MIN_VALUE) || (value > Long.MAX_VALUE)) && Math.floor(value) == value; 351 } 352 353 /** 354 * Returns the product of this unit with the one specified. 355 * 356 * <p> 357 * Note: If the specified unit (that) is not a physical unit, then <code>that.multiply(this)</code> is returned. 358 * </p> 359 * 360 * @param that 361 * the unit multiplicand. 362 * @return <code>this * that</code> 363 */ 364 @Override 365 public final Unit<?> multiply(Unit<?> that) { 366 if (that instanceof AbstractUnit) 367 return multiply((AbstractUnit<?>) that); 368 // return that.multiply(this); // Commutatif. 369 return ProductUnit.getProductInstance(this, that); 370 } 371 372 /** 373 * Returns the product of this physical unit with the one specified. 374 * 375 * @param that 376 * the physical unit multiplicand. 377 * @return <code>this * that</code> 378 */ 379 protected final Unit<?> multiply(AbstractUnit<?> that) { 380 if (this.equals(ONE)) 381 return that; 382 if (that.equals(ONE)) 383 return this; 384 return ProductUnit.getProductInstance(this, that); 385 } 386 387 /** 388 * Returns the inverse of this physical unit. 389 * 390 * @return <code>1 / this</code> 391 */ 392 @Override 393 public final Unit<?> inverse() { 394 if (this.equals(ONE)) 395 return this; 396 return ProductUnit.getQuotientInstance(ONE, this); 397 } 398 399 /** 400 * Returns the result of dividing this unit by the specifified divisor. If the factor is an integer value, the division is exact. For example: 401 * 402 * <pre> 403 * <code> 404 * QUART = GALLON_LIQUID_US.divide(4); // Exact definition. 405 * </code> 406 * </pre> 407 * 408 * @param divisor 409 * the divisor value. 410 * @return this unit divided by the specified divisor. 411 */ 412 @Override 413 public final Unit<Q> divide(double divisor) { 414 if (divisor == 1) 415 return this; 416 if (isLongValue(divisor)) 417 return transform(new RationalConverter(BigInteger.ONE, BigInteger.valueOf((long) divisor))); 418 return transform(new MultiplyConverter(1.0 / divisor)); 419 } 420 421 /** 422 * Returns the quotient of this unit with the one specified. 423 * 424 * @param that 425 * the unit divisor. 426 * @return <code>this.multiply(that.inverse())</code> 427 */ 428 @Override 429 public final Unit<?> divide(Unit<?> that) { 430 return this.multiply(that.inverse()); 431 } 432 433 /** 434 * Returns the quotient of this physical unit with the one specified. 435 * 436 * @param that 437 * the physical unit divisor. 438 * @return <code>this.multiply(that.inverse())</code> 439 */ 440 protected final Unit<?> divide(AbstractUnit<?> that) { 441 return this.multiply(that.inverse()); 442 } 443 444 /** 445 * Returns a unit equals to the given root of this unit. 446 * 447 * @param n 448 * the root's order. 449 * @return the result of taking the given root of this unit. 450 * @throws ArithmeticException 451 * if <code>n == 0</code> or if this operation would result in an unit with a fractional exponent. 452 */ 453 @Override 454 public final Unit<?> root(int n) { 455 if (n > 0) 456 return ProductUnit.getRootInstance(this, n); 457 else if (n == 0) 458 throw new ArithmeticException("Root's order of zero"); 459 else 460 // n < 0 461 return ONE.divide(this.root(-n)); 462 } 463 464 /** 465 * Returns a unit equals to this unit raised to an exponent. 466 * 467 * @param n 468 * the exponent. 469 * @return the result of raising this unit to the exponent. 470 */ 471 @Override 472 public final Unit<?> pow(int n) { 473 if (n > 0) 474 return this.multiply(this.pow(n - 1)); 475 else if (n == 0) 476 return ONE; 477 else 478 // n < 0 479 return ONE.divide(this.pow(-n)); 480 } 481 482 /** 483 * Compares this unit to the specified unit. The default implementation compares the name and symbol of both this unit and the specified unit. 484 * 485 * @return a negative integer, zero, or a positive integer as this unit is less than, equal to, or greater than the specified unit. 486 */ 487 public int compareTo(Unit<Q> that) { 488 if (name != null && getSymbol() != null) { 489 return name.compareTo(that.getName()) + getSymbol().compareTo(that.getSymbol()); 490 } else if (name == null) { 491 if (getSymbol() != null && that.getSymbol() != null) { 492 return getSymbol().compareTo(that.getSymbol()); 493 } else { 494 return -1; 495 } 496 } else if (getSymbol() == null) { 497 if (name != null) { 498 return name.compareTo(that.getName()); 499 } else { 500 return -1; 501 } 502 } else { 503 return -1; 504 } 505 } 506 507 // ////////////////////////////////////////////////////////////// 508 // Ensures that sub-classes implements hashCode/equals method. 509 // ////////////////////////////////////////////////////////////// 510 511 @Override 512 public abstract int hashCode(); 513 514 @Override 515 public abstract boolean equals(Object that); 516}