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 java.math.BigDecimal; 033import java.math.BigInteger; 034import java.math.MathContext; 035import java.util.Comparator; 036import java.util.Objects; 037 038import javax.measure.Quantity; 039import javax.measure.Unit; 040import javax.measure.UnitConverter; 041import javax.measure.quantity.Dimensionless; 042 043import tec.uom.lib.common.function.UnitSupplier; 044import tec.uom.lib.common.function.ValueSupplier; 045import tec.uom.se.format.QuantityFormat; 046import tec.uom.se.function.NaturalOrder; 047import tec.uom.se.quantity.Quantities; 048 049/** 050 * <p> 051 * This class represents the immutable result of a scalar measurement stated in a known unit. 052 * </p> 053 * 054 * <p> 055 * To avoid any loss of precision, known exact quantities (e.g. physical constants) should not be created from <code>double</code> constants but from 056 * their decimal representation.<br/> 057 * <code> 058 * public static final Quantity<Velocity> C = NumberQuantity.parse("299792458 m/s").asType(Velocity.class); 059 * // Speed of Light (exact). 060 * </code> 061 * </p> 062 * 063 * <p> 064 * Quantities can be converted to different units.<br> 065 * <code> 066 * Quantity<Velocity> milesPerHour = C.to(MILES_PER_HOUR); // Use double implementation (fast). 067 * System.out.println(milesPerHour); 068 * 069 * > 670616629.3843951 m/h 070 * </code> 071 * </p> 072 * 073 * <p> 074 * Applications may sub-class {@link AbstractQuantity} for particular quantity types.<br> 075 * <code> 076 * // Quantity of type Mass based on double primitive types.<br> 077 * public class MassAmount extends AbstractQuantity<Mass> {<br> 078 * private final double kilograms; // Internal SI representation.<br> 079 * private Mass(double kg) { kilograms = kg; }<br> 080 * public static Mass of(double value, Unit<Mass> unit) {<br> 081 * return new Mass(unit.getConverterTo(SI.KILOGRAM).convert(value));<br> 082 * }<br> 083 * public Unit<Mass> getUnit() { return SI.KILOGRAM; }<br> 084 * public Double getValue() { return kilograms; }<br> 085 * ...<br> 086 * }<br> 087 * <p> 088 * // Complex numbers measurements.<br> 089 * public class ComplexQuantity 090 * <Q extends Quantity>extends AbstractQuantity 091 * <Q>{<br> 092 * public Complex getValue() { ... } // Assuming Complex is a Number.<br> 093 * ...<br> 094 * }<br> 095 * <br> 096 * // Specializations of complex numbers measurements.<br> 097 * public final class Current extends ComplexQuantity<ElectricCurrent> {...}<br> 098 * public final class Tension extends ComplexQuantity<ElectricPotential> {...} <br> 099 * </code> 100 * </p> 101 * 102 * <p> 103 * All instances of this class shall be immutable. 104 * </p> 105 * 106 * @author <a href="mailto:werner@uom.technology">Werner Keil</a> 107 * @version 1.0.5, May 31, 2017 108 * @since 1.0 109 */ 110@SuppressWarnings("unchecked") 111public abstract class AbstractQuantity<Q extends Quantity<Q>> implements ComparableQuantity<Q>, UnitSupplier<Q>, ValueSupplier<Number> { 112 113 /** 114 * 115 */ 116 private static final long serialVersionUID = 293852425369811882L; 117 118 private final Unit<Q> unit; 119 120 /** 121 * Holds a dimensionless quantity of none (exact). 122 */ 123 public static final Quantity<Dimensionless> NONE = Quantities.getQuantity(0, AbstractUnit.ONE); 124 125 /** 126 * Holds a dimensionless quantity of one (exact). 127 */ 128 public static final Quantity<Dimensionless> ONE = Quantities.getQuantity(1, AbstractUnit.ONE); 129 130 /** 131 * constructor. 132 */ 133 protected AbstractQuantity(Unit<Q> unit) { 134 this.unit = unit; 135 } 136 137 /** 138 * Returns the numeric value of the quantity. 139 * 140 * @return the quantity value. 141 */ 142 @Override 143 public abstract Number getValue(); 144 145 /** 146 * Returns the measurement unit. 147 * 148 * @return the measurement unit. 149 */ 150 @Override 151 public Unit<Q> getUnit() { 152 return unit; 153 } 154 155 /** 156 * Convenient method equivalent to {@link #to(javax.measure.unit.Unit) to(this.getUnit().toSI())}. 157 * 158 * @return this measure or a new measure equivalent to this measure but stated in SI units. 159 * @throws ArithmeticException 160 * if the result is inexact and the quotient has a non-terminating decimal expansion. 161 */ 162 public Quantity<Q> toSI() { 163 return to(this.getUnit().getSystemUnit()); 164 } 165 166 /** 167 * Returns this measure after conversion to specified unit. The default implementation returns <code>Measure.valueOf(doubleValue(unit), unit)</code> 168 * . If this measure is already stated in the specified unit, then this measure is returned and no conversion is performed. 169 * 170 * @param unit 171 * the unit in which the returned measure is stated. 172 * @return this measure or a new measure equivalent to this measure but stated in the specified unit. 173 * @throws ArithmeticException 174 * if the result is inexact and the quotient has a non-terminating decimal expansion. 175 */ 176 @Override 177 public ComparableQuantity<Q> to(Unit<Q> unit) { 178 if (unit.equals(this.getUnit())) { 179 return this; 180 } 181 UnitConverter t = getUnit().getConverterTo(unit); 182 Number convertedValue = t.convert(getValue()); 183 return Quantities.getQuantity(convertedValue, unit); 184 } 185 186 /** 187 * Returns this measure after conversion to specified unit. The default implementation returns 188 * <code>Measure.valueOf(decimalValue(unit, ctx), unit)</code>. If this measure is already stated in the specified unit, then this measure is 189 * returned and no conversion is performed. 190 * 191 * @param unit 192 * the unit in which the returned measure is stated. 193 * @param ctx 194 * the math context to use for conversion. 195 * @return this measure or a new measure equivalent to this measure but stated in the specified unit. 196 * @throws ArithmeticException 197 * if the result is inexact but the rounding mode is <code>UNNECESSARY</code> or <code>mathContext.precision == 0</code> and the quotient 198 * has a non-terminating decimal expansion. 199 */ 200 public Quantity<Q> to(Unit<Q> unit, MathContext ctx) { 201 if (unit.equals(this.getUnit())) { 202 return this; 203 } 204 return Quantities.getQuantity(decimalValue(unit, ctx), unit); 205 } 206 207 @Override 208 public boolean isGreaterThan(Quantity<Q> that) { 209 return this.compareTo(that) > 0; 210 } 211 212 @Override 213 public boolean isGreaterThanOrEqualTo(Quantity<Q> that) { 214 return this.compareTo(that) >= 0; 215 } 216 217 @Override 218 public boolean isLessThan(Quantity<Q> that) { 219 return this.compareTo(that) < 0; 220 } 221 222 @Override 223 public boolean isLessThanOrEqualTo(Quantity<Q> that) { 224 return this.compareTo(that) <= 0; 225 } 226 227 @Override 228 public boolean isEquivalentTo(Quantity<Q> that) { 229 return this.compareTo(that) == 0; 230 } 231 232 /** 233 * Compares this measure to the specified Measurement quantity. The default implementation compares the {@link AbstractQuantity#doubleValue(Unit)} 234 * of both this measure and the specified Measurement stated in the same unit (this measure's {@link #getUnit() unit}). 235 * 236 * @return a negative integer, zero, or a positive integer as this measure is less than, equal to, or greater than the specified Measurement 237 * quantity. 238 * @see {@link NaturalOrder} 239 */ 240 @Override 241 public int compareTo(Quantity<Q> that) { 242 final Comparator<Quantity<Q>> comparator = new NaturalOrder<>(); 243 return comparator.compare(this, that); 244 } 245 246 /** 247 * Compares this quantity against the specified object for <b>strict</b> equality (same unit and same amount). 248 * 249 * <p> 250 * Similarly to the {@link BigDecimal#equals} method which consider 2.0 and 2.00 as different objects because of different internal scales, 251 * quantities such as <code>Quantities.getQuantity(3.0, KILOGRAM)</code> <code>Quantities.getQuantity(3, KILOGRAM)</code> and 252 * <code>Quantities.getQuantity("3 kg")</code> might not be considered equals because of possible differences in their implementations. 253 * </p> 254 * 255 * <p> 256 * To compare quantities stated using different units or using different amount implementations the {@link #compareTo compareTo} or 257 * {@link #equals(javax.measure.Quantity, double, javax.measure.unit.Unit) equals(Quantity, epsilon, epsilonUnit)} methods should be used. 258 * </p> 259 * 260 * @param obj 261 * the object to compare with. 262 * @return <code>this.getUnit.equals(obj.getUnit()) 263 * && this.getValue().equals(obj.getValue())</code> 264 */ 265 @Override 266 public boolean equals(Object obj) { 267 if (this == obj) { 268 return true; 269 } 270 if (obj instanceof AbstractQuantity<?>) { 271 AbstractQuantity<?> that = (AbstractQuantity<?>) obj; 272 return Objects.equals(getUnit(), that.getUnit()) && Objects.equals(getValue(), that.getValue()); 273 } 274 return false; 275 } 276 277 /** 278 * Compares this quantity and the specified quantity to the given accuracy. Quantities are considered approximately equals if their absolute 279 * differences when stated in the same specified unit is less than the specified epsilon. 280 * 281 * @param that 282 * the quantity to compare with. 283 * @param epsilon 284 * the absolute error stated in epsilonUnit. 285 * @param epsilonUnit 286 * the epsilon unit. 287 * @return <code>abs(this.doubleValue(epsilonUnit) - that.doubleValue(epsilonUnit)) <= epsilon</code> 288 */ 289 public boolean equals(AbstractQuantity<Q> that, double epsilon, Unit<Q> epsilonUnit) { 290 return Math.abs(this.doubleValue(epsilonUnit) - that.doubleValue(epsilonUnit)) <= epsilon; 291 } 292 293 /** 294 * Returns the hash code for this quantity. 295 * 296 * @return the hash code value. 297 */ 298 @Override 299 public int hashCode() { 300 return Objects.hash(getUnit(), getValue()); 301 } 302 303 public abstract boolean isBig(); 304 305 /** 306 * Returns the <code>String</code> representation of this quantity. The string produced for a given quantity is always the same; it is not affected 307 * by locale. This means that it can be used as a canonical string representation for exchanging quantity, or as a key for a Hashtable, etc. 308 * Locale-sensitive quantity formatting and parsing is handled by the {@link MeasurementFormat} class and its subclasses. 309 * 310 * @return <code>UnitFormat.getInternational().format(this)</code> 311 */ 312 @Override 313 public String toString() { 314 return String.valueOf(getValue()) + " " + String.valueOf(getUnit()); 315 } 316 317 public abstract BigDecimal decimalValue(Unit<Q> unit, MathContext ctx) throws ArithmeticException; 318 319 public abstract double doubleValue(Unit<Q> unit) throws ArithmeticException; 320 321 public final int intValue(Unit<Q> unit) throws ArithmeticException { 322 long longValue = longValue(unit); 323 if ((longValue < Integer.MIN_VALUE) || (longValue > Integer.MAX_VALUE)) { 324 throw new ArithmeticException("Cannot convert " + longValue + " to int (overflow)"); 325 } 326 return (int) longValue; 327 } 328 329 protected long longValue(Unit<Q> unit) throws ArithmeticException { 330 double result = doubleValue(unit); 331 if ((result < Long.MIN_VALUE) || (result > Long.MAX_VALUE)) { 332 throw new ArithmeticException("Overflow (" + result + ")"); 333 } 334 return (long) result; 335 } 336 337 protected final float floatValue(Unit<Q> unit) { 338 return (float) doubleValue(unit); 339 } 340 341 @Override 342 public <T extends Quantity<T>, E extends Quantity<E>> ComparableQuantity<E> divide(Quantity<T> that, Class<E> asTypeQuantity) { 343 344 return divide(Objects.requireNonNull(that)).asType(Objects.requireNonNull(asTypeQuantity)); 345 346 } 347 348 @Override 349 public <T extends Quantity<T>, E extends Quantity<E>> ComparableQuantity<E> multiply(Quantity<T> that, Class<E> asTypeQuantity) { 350 return multiply(Objects.requireNonNull(that)).asType(Objects.requireNonNull(asTypeQuantity)); 351 } 352 353 @Override 354 public <T extends Quantity<T>> ComparableQuantity<T> inverse(Class<T> quantityClass) { 355 return inverse().asType(quantityClass); 356 } 357 358 /** 359 * Casts this quantity to a parameterized quantity of specified nature or throw a <code>ClassCastException</code> if the dimension of the specified 360 * quantity and its unit's dimension do not match. For example:<br/> 361 * <code> 362 * Quantity<Length> length = AbstractQuantity.parse("2 km").asType(Length.class); 363 * </code> 364 * 365 * @param type 366 * the quantity class identifying the nature of the quantity. 367 * @return this quantity parameterized with the specified type. 368 * @throws ClassCastException 369 * if the dimension of this unit is different from the specified quantity dimension. 370 * @throws UnsupportedOperationException 371 * if the specified quantity class does not have a public static field named "UNIT" holding the SI unit for the quantity. 372 * @see Unit#asType(Class) 373 */ 374 public final <T extends Quantity<T>> ComparableQuantity<T> asType(Class<T> type) throws ClassCastException { 375 this.getUnit().asType(type); // Raises ClassCastException if dimension 376 // mismatches. 377 return (ComparableQuantity<T>) this; 378 } 379 380 /** 381 * Returns the quantity of unknown type corresponding to the specified representation. This method can be used to parse dimensionless quantities.<br/> 382 * <code> 383 * Quatity<Dimensionless> proportion = AbstractQuantity.parse("0.234").asType(Dimensionless.class); 384 * </code> 385 * 386 * <p> 387 * Note: This method handles only {@link SimpleUnitFormat#getStandard standard} unit format. Locale-sensitive quantity parsing is currently not 388 * supported. 389 * </p> 390 * 391 * @param csq 392 * the decimal value and its unit (if any) separated by space(s). 393 * @return <code>QuantityFormat.getInstance().parse(csq)</code> 394 */ 395 public static Quantity<?> parse(CharSequence csq) { 396 return QuantityFormat.getInstance().parse(csq); 397 } 398 399 /** 400 * Utility class for number comparison and equality 401 */ 402 protected static final class Equalizer { 403 404 /** 405 * Converts a number to {@link BigDecimal} 406 * 407 * @param value 408 * the value to be converted 409 * @return the value converted 410 */ 411 public static BigDecimal toBigDecimal(Number value) { 412 if (BigDecimal.class.isInstance(value)) { 413 return BigDecimal.class.cast(value); 414 } else if (BigInteger.class.isInstance(value)) { 415 return new BigDecimal(BigInteger.class.cast(value)); 416 } 417 return BigDecimal.valueOf(value.doubleValue()); 418 } 419 420 /** 421 * Check if the both value has equality number, in other words, 1 is equals to 1.0000 and 1.0. 422 * 423 * If the first value is a <type>Number</type> of either <type>Double</type>, <type>Float</type>, <type>Integer</type>, <type>Long</type>, 424 * <type>Short</type> or <type>Byte</type> it is compared using the respective <code>*value()</code> method of <type>Number</type>. Otherwise it 425 * is checked, if {@link BigDecimal#compareTo(Object)} is equal to zero. 426 * 427 * @param valueA 428 * the value a 429 * @param valueB 430 * the value B 431 * @return {@link BigDecimal#compareTo(Object)} == zero 432 */ 433 public static boolean hasEquality(Number valueA, Number valueB) { 434 Objects.requireNonNull(valueA); 435 Objects.requireNonNull(valueB); 436 437 if (valueA instanceof Double && valueB instanceof Double) { 438 return valueA.doubleValue() == valueB.doubleValue(); 439 } else if (valueA instanceof Float && valueB instanceof Float) { 440 return valueA.floatValue() == valueB.floatValue(); 441 } else if (valueA instanceof Integer && valueB instanceof Integer) { 442 return valueA.intValue() == valueB.intValue(); 443 } else if (valueA instanceof Long && valueB instanceof Long) { 444 return valueA.longValue() == valueB.longValue(); 445 } else if (valueA instanceof Short && valueB instanceof Short) { 446 return valueA.shortValue() == valueB.shortValue(); 447 } else if (valueA instanceof Byte && valueB instanceof Byte) { 448 return valueA.byteValue() == valueB.byteValue(); 449 } 450 return toBigDecimal(valueA).compareTo(toBigDecimal(valueB)) == 0; 451 } 452 } 453}