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