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