001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2021, Jean-Marie Dautelle, Werner Keil, Otavio Santana. 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-385, Indriya nor the names of their 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 tech.units.indriya; 031 032import java.io.Serializable; 033import java.lang.reflect.ParameterizedType; 034import java.lang.reflect.Type; 035import java.util.HashMap; 036import java.util.Map; 037 038import javax.measure.Dimension; 039import javax.measure.IncommensurableException; 040import javax.measure.Prefix; 041import javax.measure.Quantity; 042import javax.measure.UnconvertibleException; 043import javax.measure.Unit; 044import javax.measure.UnitConverter; 045import javax.measure.format.MeasurementParseException; 046import javax.measure.quantity.Dimensionless; 047 048import tech.units.indriya.format.LocalUnitFormat; 049import tech.units.indriya.format.SimpleUnitFormat; 050import tech.units.indriya.function.AbstractConverter; 051import tech.units.indriya.function.AddConverter; 052import tech.units.indriya.function.Calculus; 053import tech.units.indriya.function.MultiplyConverter; 054import tech.units.indriya.function.RationalNumber; 055import tech.units.indriya.internal.function.Calculator; 056import tech.units.indriya.spi.DimensionalModel; 057import tech.units.indriya.unit.AlternateUnit; 058import tech.units.indriya.unit.AnnotatedUnit; 059import tech.units.indriya.unit.ProductUnit; 060import tech.units.indriya.unit.TransformedUnit; 061import tech.units.indriya.unit.UnitDimension; 062import tech.units.indriya.unit.Units; 063import tech.uom.lib.common.function.Nameable; 064import tech.uom.lib.common.function.PrefixOperator; 065import tech.uom.lib.common.function.SymbolSupplier; 066 067/** 068 * <p> 069 * The class represents units founded on the seven <b>SI</b> base units for 070 * seven base quantities assumed to be mutually independent. 071 * </p> 072 * 073 * <p> 074 * For all physics units, unit conversions are symmetrical: 075 * <code>u1.getConverterTo(u2).equals(u2.getConverterTo(u1).inverse())</code>. 076 * Non-physical units (e.g. currency units) for which conversion is not 077 * symmetrical should have their own separate class hierarchy and are considered 078 * distinct (e.g. financial units), although they can always be combined with 079 * physics units (e.g. "€/Kg", "$/h"). 080 * </p> 081 * 082 * @see <a href= 083 * "http://en.wikipedia.org/wiki/International_System_of_Units">Wikipedia: 084 * International System of Units</a> 085 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 086 * @author <a href="mailto:werner@units.tech">Werner Keil</a> 087 * @version 3.2, April 16, 2021 088 * @since 1.0 089 */ 090public abstract class AbstractUnit<Q extends Quantity<Q>> 091 implements Unit<Q>, Comparable<Unit<Q>>, PrefixOperator<Q>, Nameable, Serializable, SymbolSupplier { 092 093 /** 094 * 095 */ 096 private static final long serialVersionUID = -4344589505537030204L; 097 098 /** 099 * Holds the dimensionless unit <code>ONE</code>. 100 * 101 * @see <a href= 102 * "https://en.wikipedia.org/wiki/Natural_units#Choosing_constants_to_normalize"> 103 * Wikipedia: Natural Units - Choosing constants to normalize</a> 104 * @see <a href= "http://www.av8n.com/physics/dimensionless-units.htm">Units of 105 * Dimension One</a> 106 */ 107 public static final Unit<Dimensionless> ONE = new ProductUnit<>(); 108 109 /** 110 * Holds the name. 111 */ 112 protected String name; 113 114 /** 115 * Holds the symbol. 116 */ 117 private String symbol; 118 119 /** 120 * Holds the unique symbols collection (base units or alternate units). 121 */ 122 protected static final transient Map<String, Unit<?>> SYMBOL_TO_UNIT = new HashMap<>(); 123 124 /** 125 * Default constructor. 126 */ 127 protected AbstractUnit() { 128 } 129 130 /** 131 * Constructor setting a symbol. 132 * 133 * @param symbol the unit symbol. 134 */ 135 protected AbstractUnit(String symbol) { 136 this.symbol = symbol; 137 } 138 139 protected Type getActualType() { 140 ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass(); 141 return parameterizedType.getActualTypeArguments()[0].getClass().getGenericInterfaces()[0]; 142 } 143 144 /** 145 * Indicates if this unit belongs to the set of coherent SI units (unscaled SI 146 * units). 147 * 148 * The base and coherent derived units of the SI form a coherent set, designated 149 * the set of coherent SI units. The word coherent is used here in the following 150 * sense: when coherent units are used, equations between the numerical values 151 * of quantities take exactly the same form as the equations between the 152 * quantities themselves. Thus if only units from a coherent set are used, 153 * conversion factors between units are never required. 154 * 155 * @return <code>equals(toSystemUnit())</code> 156 */ 157 public boolean isSystemUnit() { 158 Unit<Q> sys = this.toSystemUnit(); 159 return this == sys || this.equals(sys); 160 } 161 162 /** 163 * Returns the converter from this unit to its unscaled {@link #toSystemUnit 164 * System Unit} unit. 165 * 166 * @return <code>getConverterTo(this.toSystemUnit())</code> 167 * @see #toSystemUnit 168 */ 169 public abstract UnitConverter getSystemConverter(); 170 171 /** 172 * Returns the unscaled {@link SI} unit from which this unit is derived. 173 * 174 * The SI unit can be be used to identify a quantity given the unit. For 175 * example:<code> static boolean isAngularVelocity(AbstractUnit<?> unit) { 176 * return unit.toSystemUnit().equals(RADIAN.divide(SECOND)); } assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. </code> 177 * 178 * @return the unscaled metric unit from which this unit is derived. 179 */ 180 protected abstract Unit<Q> toSystemUnit(); 181 182 /** 183 * Annotates the specified unit. Annotation does not change the unit semantic. 184 * Annotations are often written between curly braces behind units. For 185 * example:<br> 186 * <code> Unit<Volume> PERCENT_VOL = ((AbstractUnit)Units.PERCENT).annotate("vol"); // "%{vol}" Unit<Mass> KG_TOTAL = 187 * ((AbstractUnit)Units.KILOGRAM).annotate("total"); // "kg{total}" Unit<Dimensionless> RED_BLOOD_CELLS = ((AbstractUnit)Units.ONE).annotate("RBC"); // "{RBC}" </code> 188 * 189 * Note: Annotation of system units are not considered themselves as system 190 * units. 191 * 192 * @param annotation the unit annotation. 193 * @return the annotated unit. 194 */ 195 public final Unit<Q> annotate(String annotation) { 196 return new AnnotatedUnit<>(this, annotation); 197 } 198 199 /** 200 * Returns the abstract unit represented by the specified characters as per 201 * default format. 202 * 203 * Locale-sensitive unit parsing could be handled using {@link LocalUnitFormat} 204 * in subclasses of AbstractUnit. 205 * 206 * <p> 207 * Note: The standard format supports dimensionless 208 * units.<code> AbstractUnit<Dimensionless> PERCENT = 209 * AbstractUnit.parse("100").inverse().asType(Dimensionless.class); </code> 210 * </p> 211 * 212 * @param charSequence the character sequence to parse. 213 * @return <code>SimpleUnitFormat.getInstance().parse(csq)</code> 214 * @throws MeasurementParseException if the specified character sequence cannot 215 * be correctly parsed (e.g. not UCUM 216 * compliant). 217 */ 218 public static Unit<?> parse(CharSequence charSequence) { 219 return SimpleUnitFormat.getInstance().parse(charSequence); 220 } 221 222 /** 223 * Returns the standard representation of this physics unit. The string produced 224 * for a given unit is always the same; it is not affected by the locale. It can 225 * be used as a canonical string representation for exchanging units, or as a 226 * key for a Hashtable, etc. 227 * 228 * Locale-sensitive unit parsing could be handled using {@link LocalUnitFormat} 229 * in subclasses of AbstractUnit. 230 * 231 * @return <code>SimpleUnitFormat.getInstance().format(this)</code> 232 */ 233 @Override 234 public String toString() { 235 return SimpleUnitFormat.getInstance().format(this); 236 } 237 238 // /////////////////////////////////////////////////////// 239 // Implements javax.measure.Unit<Q> interface // 240 // /////////////////////////////////////////////////////// 241 242 /** 243 * Returns the system unit (unscaled SI unit) from which this unit is derived. 244 * They can be be used to identify a quantity given the unit. For example:<br> 245 * <code> static boolean isAngularVelocity(AbstractUnit<?> unit) {<br> return unit.getSystemUnit().equals(RADIAN.divide(SECOND));<br>} 246 * <br>assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. </code> 247 * 248 * @return the unscaled metric unit from which this unit is derived. 249 */ 250 @Override 251 public final Unit<Q> getSystemUnit() { 252 return toSystemUnit(); 253 } 254 255 /** 256 * Indicates if this unit is compatible with the unit specified. To be 257 * compatible both units must be physics units having the same fundamental 258 * dimension. 259 * 260 * @param that the other unit. 261 * @return <code>true</code> if this unit and that unit have the same 262 * fundamental dimension according to the current dimensional model; 263 * <code>false</code> otherwise. 264 */ 265 @Override 266 public final boolean isCompatible(Unit<?> that) { 267 return internalIsCompatible(that, true); 268 } 269 270 /** 271 * Casts this unit to a parameterized unit of specified nature or throw a 272 * ClassCastException if the dimension of the specified quantity and this unit's 273 * dimension do not match (regardless whether or not the dimensions are 274 * independent or not). 275 * 276 * @param type the quantity class identifying the nature of the unit. 277 * @throws ClassCastException if the dimension of this unit is different from 278 * the SI dimension of the specified type. 279 * @see Units#getUnit(Class) 280 */ 281 @SuppressWarnings("unchecked") 282 @Override 283 public final <T extends Quantity<T>> Unit<T> asType(Class<T> type) { 284 Dimension typeDimension = UnitDimension.of(type); 285 if (typeDimension != null && !typeDimension.equals(this.getDimension())) 286 throw new ClassCastException("The unit: " + this + " is not compatible with quantities of type " + type); 287 return (Unit<T>) this; 288 } 289 290 @Override 291 public abstract Map<? extends Unit<?>, Integer> getBaseUnits(); 292 293 @Override 294 public abstract Dimension getDimension(); 295 296 protected void setName(String name) { 297 this.name = name; 298 } 299 300 public String getName() { 301 return name; 302 } 303 304 public String getSymbol() { 305 return symbol; 306 } 307 308 protected void setSymbol(String s) { 309 this.symbol = s; 310 } 311 312 @Override 313 public final UnitConverter getConverterTo(Unit<Q> that) throws UnconvertibleException { 314 return internalGetConverterTo(that, true); 315 } 316 317 @SuppressWarnings("rawtypes") 318 @Override 319 public final UnitConverter getConverterToAny(Unit<?> that) throws IncommensurableException, UnconvertibleException { 320 if (!isCompatible(that)) 321 throw new IncommensurableException(this + " is not compatible with " + that); 322 AbstractUnit thatAbstr = (AbstractUnit) that; // Since both units are 323 // compatible they must both be abstract units. 324 final DimensionalModel model = DimensionalModel.current(); 325 Unit thisSystemUnit = this.getSystemUnit(); 326 UnitConverter thisToDimension = model.getDimensionalTransform(thisSystemUnit.getDimension()) 327 .concatenate(this.getSystemConverter()); 328 Unit thatSystemUnit = thatAbstr.getSystemUnit(); 329 UnitConverter thatToDimension = model.getDimensionalTransform(thatSystemUnit.getDimension()) 330 .concatenate(thatAbstr.getSystemConverter()); 331 return thatToDimension.inverse().concatenate(thisToDimension); 332 } 333 334 @Override 335 public final Unit<Q> alternate(String newSymbol) { 336 return new AlternateUnit<>(this, newSymbol); 337 } 338 339 @Override 340 public final Unit<Q> transform(UnitConverter operation) { 341 Unit<Q> systemUnit = this.getSystemUnit(); 342 UnitConverter cvtr; 343 if (this.isSystemUnit()) { 344 cvtr = this.getSystemConverter().concatenate(operation); 345 } else { 346 cvtr = operation; 347 } 348 return cvtr.isIdentity() ? systemUnit : new TransformedUnit<>(null, this, systemUnit, cvtr); 349 } 350 351 @Override 352 public final Unit<Q> shift(Number offset) { 353 if (Calculus.currentNumberSystem().isZero(offset)) 354 return this; 355 return transform(new AddConverter(offset)); 356 } 357 358 @Override 359 public final Unit<Q> multiply(Number factor) { 360 if (Calculus.currentNumberSystem().isOne(factor)) 361 return this; 362 return transform(MultiplyConverter.of(factor)); 363 } 364 365 @Override 366 public Unit<Q> shift(double offset) { 367 return shift(RationalNumber.of(offset)); 368 } 369 370 @Override 371 public Unit<Q> multiply(double multiplier) { 372 return multiply(RationalNumber.of(multiplier)); 373 } 374 375 @Override 376 public Unit<Q> divide(double divisor) { 377 return divide(RationalNumber.of(divisor)); 378 } 379 380 /** 381 * Internal helper for isCompatible 382 */ 383 private final boolean internalIsCompatible(Unit<?> that, boolean checkEquals) { 384 if (checkEquals) { 385 if (this == that || this.equals(that)) 386 return true; 387 } else { 388 if (this == that) 389 return true; 390 } 391 if (!(that instanceof Unit)) 392 return false; 393 Dimension thisDimension = this.getDimension(); 394 Dimension thatDimension = that.getDimension(); 395 if (thisDimension.equals(thatDimension)) 396 return true; 397 DimensionalModel model = DimensionalModel.current(); // Use 398 // dimensional 399 // analysis 400 // model. 401 return model.getFundamentalDimension(thisDimension).equals(model.getFundamentalDimension(thatDimension)); 402 } 403 404 protected final UnitConverter internalGetConverterTo(Unit<Q> that, boolean useEquals) 405 throws UnconvertibleException { 406 if (useEquals) { 407 if (this == that || this.equals(that)) 408 return AbstractConverter.IDENTITY; 409 } else { 410 if (this == that) 411 return AbstractConverter.IDENTITY; 412 } 413 Unit<Q> thisSystemUnit = this.getSystemUnit(); 414 Unit<Q> thatSystemUnit = that.getSystemUnit(); 415 if (!thisSystemUnit.equals(thatSystemUnit)) 416 try { 417 return getConverterToAny(that); 418 } catch (IncommensurableException e) { 419 throw new UnconvertibleException(e); 420 } 421 UnitConverter thisToSI = this.getSystemConverter(); 422 UnitConverter thatToSI = that.getConverterTo(thatSystemUnit); 423 return thatToSI.inverse().concatenate(thisToSI); 424 } 425 426 /** 427 * Returns the product of this physical unit with the one specified. 428 * 429 * @param that the physical unit multiplicand. 430 * @return <code>this * that</code> 431 */ 432 public final Unit<?> multiply(Unit<?> that) { 433 if (this.equals(ONE)) 434 return that; 435 if (that.equals(ONE)) 436 return this; 437 return ProductUnit.ofProduct(this, that); 438 } 439 440 /** 441 * Returns the inverse of this physical unit. 442 * 443 * @return <code>1 / this</code> 444 */ 445 @Override 446 public final Unit<?> inverse() { 447 if (this.equals(ONE)) 448 return this; 449 return ProductUnit.ofQuotient(ONE, this); 450 } 451 452 /** 453 * Returns the result of dividing this unit by the specified divisor. If the 454 * factor is an integer value, the division is exact. For example: 455 * 456 * <pre> 457 * <code> 458 * QUART = GALLON_LIQUID_US.divide(4); // Exact definition. 459 * </code> 460 * </pre> 461 * 462 * @param divisor the divisor value. 463 * @return this unit divided by the specified divisor. 464 */ 465 @Override 466 public final Unit<Q> divide(Number divisor) { 467 if (Calculus.currentNumberSystem().isOne(divisor)) 468 return this; 469 Number factor = Calculator.of(divisor).reciprocal().peek(); 470 return transform(MultiplyConverter.of(factor)); 471 } 472 473 /** 474 * Returns the quotient of this unit with the one specified. 475 * 476 * @param that the unit divisor. 477 * @return <code>this.multiply(that.inverse())</code> 478 */ 479 @Override 480 public final Unit<?> divide(Unit<?> that) { 481 return this.multiply(that.inverse()); 482 } 483 484 /** 485 * Returns a unit equals to the given root of this unit. 486 * 487 * @param n the root's order. 488 * @return the result of taking the given root of this unit. 489 * @throws ArithmeticException if <code>n == 0</code> or if this operation would 490 * result in an unit with a fractional exponent. 491 */ 492 @Override 493 public final Unit<?> root(int n) { 494 if (n > 0) 495 return ProductUnit.ofRoot(this, n); 496 else if (n == 0) 497 throw new ArithmeticException("Root's order of zero"); 498 else 499 // n < 0 500 return ONE.divide(this.root(-n)); 501 } 502 503 /** 504 * Returns a unit equals to this unit raised to an exponent. 505 * 506 * @param n the exponent. 507 * @return the result of raising this unit to the exponent. 508 */ 509 @Override 510 public Unit<?> pow(int n) { 511 if (n > 0) 512 return this.multiply(this.pow(n - 1)); 513 else if (n == 0) 514 return ONE; 515 else 516 // n < 0 517 return ONE.divide(this.pow(-n)); 518 } 519 520 @Override 521 public Unit<Q> prefix(Prefix prefix) { 522 return this.transform(MultiplyConverter.ofPrefix(prefix)); 523 } 524 525 /** 526 * Compares this unit to the specified unit. The default implementation compares 527 * the name and symbol of both this unit and the specified unit, giving 528 * precedence to the symbol. 529 * 530 * @return a negative integer, zero, or a positive integer as this unit is less 531 * than, equal to, or greater than the specified unit. 532 */ 533 @Override 534 public int compareTo(Unit<Q> that) { 535 int symbolComparison = compareToWithPossibleNullValues(getSymbol(), that.getSymbol()); 536 if (symbolComparison == 0) { 537 return compareToWithPossibleNullValues(name, that.getName()); 538 } else { 539 return symbolComparison; 540 } 541 } 542 543 private int compareToWithPossibleNullValues(String a, String b) { 544 if (a == null) { 545 return (b == null) ? 0 : -1; 546 } else { 547 return (b == null) ? 1 : a.compareTo(b); 548 } 549 } 550 551 @Override 552 public boolean isEquivalentTo(Unit<Q> that) { 553 return this.getConverterTo(that).isIdentity(); 554 } 555 556 // ////////////////////////////////////////////////////////////// 557 // Ensures that sub-classes implement the hashCode method. 558 // ////////////////////////////////////////////////////////////// 559 560 @Override 561 public abstract boolean equals(Object obj); 562 563 @Override 564 public abstract int hashCode(); 565 566 /** 567 * Utility class for number comparison and equality 568 */ 569 protected static final class Equalizer { 570 /** 571 * Indicates if this unit is considered equals to the specified object. order). 572 * 573 * @param obj the object to compare for equality. 574 * @return <code>true</code> if <code>this</code> and <code>obj</code> are 575 * considered equal; <code>false</code>otherwise. 576 */ 577 public static boolean areEqual(@SuppressWarnings("rawtypes") Unit u1, 578 @SuppressWarnings("rawtypes") Unit u2) { 579 /* 580 * if (u1 != null && u2 != null) { if (u1.getName() != null && u1.getSymbol() != 581 * null) { return u1.getName().equals(u2.getName()) && 582 * u1.getSymbol().equals(u2.getSymbol()) && u1.internalIsCompatible(u2, false); 583 * } else if (u1.getSymbol() != null) { return 584 * u1.getSymbol().equals(u2.getSymbol()) && u1.internalIsCompatible(u2, false); 585 * } else { return u1.toString().equals(u2.toString()) && 586 * u1.internalIsCompatible(u2, false); } } else { 587 */ 588 if (u1 != null && u1.equals(u2)) 589 return true; 590 return false; 591 } 592 } 593}