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 static javax.measure.Quantity.Scale.ABSOLUTE; 033 034import java.math.BigDecimal; 035import java.util.Objects; 036 037import javax.measure.Quantity; 038import javax.measure.Unit; 039import javax.measure.UnitConverter; 040import javax.measure.format.QuantityFormat; 041import javax.measure.quantity.Dimensionless; 042 043import tech.units.indriya.format.SimpleQuantityFormat; 044import tech.units.indriya.format.SimpleUnitFormat; 045import tech.units.indriya.function.Calculus; 046import tech.units.indriya.quantity.Quantities; 047import tech.units.indriya.spi.NumberSystem; 048import tech.uom.lib.common.function.UnitSupplier; 049import tech.uom.lib.common.function.ValueSupplier; 050import tech.uom.lib.common.util.NaturalQuantityComparator; 051 052/** 053 * <p> 054 * This class represents the immutable result of a scalar measurement stated in a known unit. 055 * </p> 056 * 057 * <p> 058 * To avoid any loss of precision, known exact quantities (e.g. physical constants) should not be created from <code>double</code> constants but from 059 * their decimal representation.<br> 060 * <code> 061 * public static final Quantity<Velocity> C = NumberQuantity.parse("299792458 m/s").asType(Velocity.class); 062 * // Speed of Light (exact). 063 * </code> 064 * </p> 065 * 066 * <p> 067 * Quantities can be converted to different units.<br> 068 * <code> 069 * Quantity<Velocity> milesPerHour = C.to(MILES_PER_HOUR); // Use double implementation (fast). 070 * System.out.println(milesPerHour); 071 * 072 * > 670616629.3843951 m/h 073 * </code> 074 * </p> 075 * 076 * <p> 077 * Applications may sub-class {@link AbstractQuantity} for particular quantity types.<br> 078 * <code> 079 * // Quantity of type Mass based on double primitive types.<br> 080 * public class MassAmount extends AbstractQuantity<Mass> {<br> 081 * private final double kilograms; // Internal SI representation.<br> 082 * private Mass(double kg) { kilograms = kg; }<br> 083 * public static Mass of(double value, Unit<Mass> unit) {<br> 084 * return new Mass(unit.getConverterTo(SI.KILOGRAM).convert(value));<br> 085 * }<br> 086 * public Unit<Mass> getUnit() { return SI.KILOGRAM; }<br> 087 * public Double getValue() { return kilograms; }<br> 088 * ...<br> 089 * }<br> 090 * <p> 091 * // Complex numbers measurements.<br> 092 * public class ComplexQuantity 093 * <Q extends Quantity>extends AbstractQuantity 094 * <Q>{<br> 095 * public Complex getValue() { ... } // Assuming Complex is a Number.<br> 096 * ...<br> 097 * }<br> 098 * <br> 099 * // Specializations of complex numbers quantities.<br> 100 * public final class Current extends ComplexQuantity<ElectricCurrent> {...}<br> 101 * public final class Tension extends ComplexQuantity<ElectricPotential> {...} <br> 102 * </code> 103 * </p> 104 * 105 * <p> 106 * All instances of this class shall be immutable. 107 * </p> 108 * 109 * @author <a href="mailto:werner@uom.technology">Werner Keil</a> 110 * @author Andi Huber 111 * @version 1.11, Jun 9, 2019 112 * @since 1.0 113 */ 114@SuppressWarnings("unchecked") 115public abstract class AbstractQuantity<Q extends Quantity<Q>> implements ComparableQuantity<Q>, UnitSupplier<Q>, ValueSupplier<Number> { 116 117 /** 118 * 119 */ 120 private static final long serialVersionUID = 293852425369811882L; 121 122 private final Unit<Q> unit; 123 124 private final Scale scale; 125 126 /** 127 * Holds a dimensionless quantity of none (exact). 128 */ 129 public static final Quantity<Dimensionless> NONE = Quantities.getQuantity(0, AbstractUnit.ONE); 130 131 /** 132 * Holds a dimensionless quantity of one (exact). 133 */ 134 public static final Quantity<Dimensionless> ONE = Quantities.getQuantity(1, AbstractUnit.ONE); 135 136 /** 137 * Constructor. 138 * @param unit a unit 139 * @param sca the scale, absolute or relative 140 */ 141 protected AbstractQuantity(Unit<Q> unit, Scale sca) { 142 this.unit = unit; 143 this.scale = sca; 144 } 145 146 /** 147 * Constructor. Applies {@code ABSOLUTE} {@code Scale} if none was given. 148 * @param unit a unit 149 */ 150 protected AbstractQuantity(Unit<Q> unit) { 151 this(unit, ABSOLUTE); 152 } 153 154 /** 155 * Returns the numeric value of the quantity. 156 * 157 * @return the quantity value. 158 */ 159 @Override 160 public abstract Number getValue(); 161 162 /** 163 * Returns the measurement unit. 164 * 165 * @return the measurement unit. 166 */ 167 @Override 168 public Unit<Q> getUnit() { 169 return unit; 170 } 171 172 /** 173 * Returns the absolute or relative scale. 174 * 175 * @return the scale. 176 */ 177 @Override 178 public Scale getScale() { 179 return scale; 180 } 181 182 /** 183 * Returns this quantity after conversion to specified unit. The default implementation returns 184 * <code>NumberQuantity.of(doubleValue(unit), unit)</code> . If this quantity is already stated in the specified unit, then this quantity is 185 * returned and no conversion is performed. 186 * 187 * @param anotherUnit 188 * the unit in which the returned quantity is stated. 189 * @return this quantity or a new quantity equivalent to this quantity stated in the specified unit. 190 * @throws ArithmeticException 191 * if the result is inexact and the quotient has a non-terminating decimal expansion. 192 */ 193 @Override 194 public ComparableQuantity<Q> to(Unit<Q> anotherUnit) { 195 if (anotherUnit.equals(this.getUnit())) { 196 return this; 197 } 198 UnitConverter t = getUnit().getConverterTo(anotherUnit); 199 Number convertedValue = t.convert(getValue()); 200 return Quantities.getQuantity(convertedValue, anotherUnit); 201 } 202 203 @Override 204 public boolean isGreaterThan(Quantity<Q> that) { 205 return this.compareTo(that) > 0; 206 } 207 208 @Override 209 public boolean isGreaterThanOrEqualTo(Quantity<Q> that) { 210 return this.compareTo(that) >= 0; 211 } 212 213 @Override 214 public boolean isLessThan(Quantity<Q> that) { 215 return this.compareTo(that) < 0; 216 } 217 218 @Override 219 public boolean isLessThanOrEqualTo(Quantity<Q> that) { 220 return this.compareTo(that) <= 0; 221 } 222 223 @Override 224 public boolean isEquivalentTo(Quantity<Q> that) { 225 return this.compareTo(that) == 0; 226 } 227 228 /** 229 * 230 * FIXME[220] update java-doc 231 * Compares this quantity to the specified quantity. The default implementation compares the {@link AbstractQuantity#doubleValue(Unit)} 232 * of both this quantity and the specified quantity stated in the same unit (this quantity's {@link #getUnit() unit}). 233 * 234 * @return a negative integer, zero, or a positive integer as this quantity is less than, equal to, or greater than the specified Measurement 235 * quantity. 236 * @see {@link NaturalQuantityComparator} 237 */ 238 @Override 239 public int compareTo(Quantity<Q> that) { 240 if (this.getUnit().equals(that.getUnit())) { 241 return numberSystem().compare(this.getValue(), that.getValue()); 242 } 243 return numberSystem().compare(this.getValue(), that.to(this.getUnit()).getValue()); 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.getLevel().equals(obj.getLevel() 264 * && this.getValue().equals(obj.getValue())</code> 265 */ 266 @Override 267 public boolean equals(Object obj) { 268 if (this == obj) { 269 return true; 270 } 271 if (obj instanceof Quantity<?>) { 272 Quantity<?> that = (Quantity<?>) obj; 273 return Objects.equals(getUnit(), that.getUnit()) && 274 Objects.equals(getScale(), that.getScale()) && 275 Objects.equals(getValue(), that.getValue()); 276 } 277 return false; 278 } 279 280 /** 281 * Returns the hash code for this quantity. 282 * 283 * @return the hash code value. 284 */ 285 @Override 286 public int hashCode() { 287 return Objects.hash(getUnit(), getScale(), getValue()); 288 } 289 290 /** 291 * Returns the <code>String</code> representation of this quantity. The string produced for a given quantity is always the same; it is not 292 * affected by locale. This means that it can be used as a canonical string representation for exchanging quantity, or as a key for a Hashtable, 293 * etc. Locale-sensitive quantity formatting and parsing is handled by the {@link QuantityFormat} implementations and its subclasses. 294 * 295 * @return <code>SimpleQuantityFormat.getInstance().format(this)</code> 296 */ 297 @Override 298 public String toString() { 299 return SimpleQuantityFormat.getInstance().format(this); 300 } 301 302 @Override 303 public <T extends Quantity<T>, E extends Quantity<E>> ComparableQuantity<E> divide(Quantity<T> that, Class<E> asTypeQuantity) { 304 305 return divide(Objects.requireNonNull(that)).asType(Objects.requireNonNull(asTypeQuantity)); 306 307 } 308 309 @Override 310 public <T extends Quantity<T>, E extends Quantity<E>> ComparableQuantity<E> multiply(Quantity<T> that, Class<E> asTypeQuantity) { 311 return multiply(Objects.requireNonNull(that)).asType(Objects.requireNonNull(asTypeQuantity)); 312 } 313 314 @Override 315 public <T extends Quantity<T>> ComparableQuantity<T> inverse(Class<T> quantityClass) { 316 return inverse().asType(quantityClass); 317 } 318 319 /** 320 * Casts this quantity to a parameterized quantity of specified nature or throw a <code>ClassCastException</code> if the dimension of the 321 * specified quantity and its unit's dimension do not match. For example:<br> 322 * <code> 323 * Quantity<Length> length = AbstractQuantity.parse("2 km").asType(Length.class); 324 * </code> 325 * 326 * @param type 327 * the quantity class identifying the nature of the quantity. 328 * @return this quantity parameterized with the specified type. 329 * @throws ClassCastException 330 * if the dimension of this unit is different from the specified quantity dimension. 331 * @throws UnsupportedOperationException 332 * if the specified quantity class does not have a public static field named "UNIT" holding the SI unit for the quantity. 333 * @see Unit#asType(Class) 334 */ 335 public final <T extends Quantity<T>> ComparableQuantity<T> asType(Class<T> type) throws ClassCastException { 336 this.getUnit().asType(type); // Raises ClassCastException if dimension 337 // mismatches. 338 return (ComparableQuantity<T>) this; 339 } 340 341 /** 342 * Returns the quantity of unknown type corresponding to the specified representation. This method can be used to parse dimensionless 343 * quantities.<br> 344 * <code> 345 * Quatity<Dimensionless> proportion = AbstractQuantity.parse("0.234").asType(Dimensionless.class); 346 * </code> 347 * 348 * <p> 349 * Note: This method handles only {@link SimpleUnitFormat#getStandard standard} unit format. Locale-sensitive quantity parsing is currently not 350 * supported. 351 * </p> 352 * 353 * @param csq 354 * the decimal value and its unit (if any) separated by space(s). 355 * @return <code>QuantityFormat.getInstance().parse(csq)</code> 356 */ 357 public static Quantity<?> parse(CharSequence csq) { 358 return SimpleQuantityFormat.getInstance().parse(csq); 359 } 360 361 protected boolean hasFraction(double value) { 362 return Math.round(value) != value; 363 } 364 365 protected boolean hasFraction(BigDecimal value) { 366 return value.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) != 0; 367 } 368 369 protected NumberSystem numberSystem() { 370 return Calculus.currentNumberSystem(); 371 } 372}