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