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