001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2024, 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.6 Oct 6, 2024 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 /** 126 * Holds the measurement unit 127 */ 128 private final Unit<Q> unit; 129 130 /** 131 * Holds the measurement scale 132 */ 133 private final Scale scale; 134 135 /** 136 * Holds a dimensionless quantity of none (exact). 137 */ 138 public static final Quantity<Dimensionless> NONE = Quantities.getQuantity(0, AbstractUnit.ONE); 139 140 /** 141 * Holds a dimensionless quantity of one (exact). 142 */ 143 public static final Quantity<Dimensionless> ONE = Quantities.getQuantity(1, AbstractUnit.ONE); 144 145 /** 146 * Constructor. 147 * @param unit a unit 148 * @param scale the scale, absolute or relative 149 */ 150 protected AbstractQuantity(Unit<Q> unit, Scale scale) { 151 this.unit = unit; 152 this.scale = scale; 153 } 154 155 /** 156 * Constructor. Applies {@code ABSOLUTE} {@code Scale} if none was given. 157 * @param unit a unit 158 */ 159 protected AbstractQuantity(Unit<Q> unit) { 160 this(unit, ABSOLUTE); 161 } 162 163 /** 164 * Returns the numeric value of the quantity. 165 * 166 * @return the quantity value. 167 */ 168 @Override 169 public abstract Number getValue(); 170 171 /** 172 * Returns the measurement unit. 173 * 174 * @return the measurement unit. 175 */ 176 @Override 177 public Unit<Q> getUnit() { 178 return unit; 179 } 180 181 /** 182 * Returns the absolute or relative scale. 183 * 184 * @return the scale. 185 */ 186 @Override 187 public Scale getScale() { 188 return scale; 189 } 190 191 /** 192 * Returns this quantity after conversion to specified unit. The default implementation returns 193 * <code>NumberQuantity.of(doubleValue(unit), unit)</code> . If this quantity is already stated in the specified unit, then this quantity is 194 * returned and no conversion is performed. 195 * 196 * @param anotherUnit 197 * the unit in which the returned quantity is stated. 198 * @return this quantity or a new quantity equivalent to this quantity stated in the specified unit. 199 * @throws ArithmeticException 200 * if the result is inexact and the quotient has a non-terminating decimal expansion. 201 */ 202 @Override 203 public ComparableQuantity<Q> to(Unit<Q> anotherUnit) { 204 if (anotherUnit.equals(this.getUnit())) { 205 return this; 206 } 207 return ScaleHelper.convertTo(this, anotherUnit); 208 } 209 210 @Override 211 public boolean isGreaterThan(Quantity<Q> that) { 212 return this.compareTo(that) > 0; 213 } 214 215 @Override 216 public boolean isGreaterThanOrEqualTo(Quantity<Q> that) { 217 return this.compareTo(that) >= 0; 218 } 219 220 @Override 221 public boolean isLessThan(Quantity<Q> that) { 222 return this.compareTo(that) < 0; 223 } 224 225 @Override 226 public boolean isLessThanOrEqualTo(Quantity<Q> that) { 227 return this.compareTo(that) <= 0; 228 } 229 230 @Override 231 public boolean isEquivalentTo(Quantity<Q> that) { 232 return this.compareTo(that) == 0; 233 } 234 235 /** 236 * Compares this quantity to the specified quantity. The default implementation compares the value of both this quantity and the specified quantity 237 * 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. 238 * 239 * @param that {@code Quantity} to which this {@code AbstractQuantity} is to be compared. 240 * 241 * @implNote 242 * Note: this class uses implicit unit conversion that is inconsistent with {@code equals}. 243 * 244 * @return a negative integer, zero, or a positive integer as this quantity is less than, equal/equivalent to, or greater than the specified Measurement 245 * quantity. 246 * @see {@link tech.units.indriya.spi.NumberSystem#compare} 247 */ 248 @Override 249 public int compareTo(Quantity<Q> that) { 250 if (this.getUnit().equals(that.getUnit())) { 251 return numberSystem().compare(this.getValue(), that.getValue()); 252 } 253 return numberSystem().compare(this.getValue(), that.to(this.getUnit()).getValue()); 254 } 255 256 /** 257 * Compares this quantity against the specified object for <b>strict</b> equality (same unit and same amount). 258 * 259 * <p> 260 * Similarly to the {@link BigDecimal#equals} method which consider 2.0 and 2.00 as different objects because of different internal scales, 261 * quantities such as <code>Quantities.getQuantity(3.0, KILOGRAM)</code> <code>Quantities.getQuantity(3, KILOGRAM)</code> and 262 * <code>Quantities.getQuantity("3 kg")</code> might not be considered equals because of possible differences in their implementations. 263 * </p> 264 * 265 * <p> 266 * To compare quantities stated using different units or using different amount implementations the {@link #compareTo compareTo} or 267 * {@link #isEquivalentTo} methods should be used. 268 * </p> 269 * 270 * @param obj 271 * the object to compare with. 272 * @return <code>this.getUnit.equals(obj.getUnit()) 273 * && this.getScale().equals(obj.getScale() 274 * && this.getValue().equals(obj.getValue())</code> 275 */ 276 @Override 277 public boolean equals(Object obj) { 278 if (this == obj) { 279 return true; 280 } 281 if (obj instanceof Quantity<?>) { 282 Quantity<?> that = (Quantity<?>) obj; 283 return Objects.equals(getUnit(), that.getUnit()) && 284 Objects.equals(getScale(), that.getScale()) && 285 Objects.equals(getValue(), that.getValue()); 286 } 287 return false; 288 } 289 290 /** 291 * Returns the hash code for this quantity. 292 * 293 * @return the hash code value. 294 */ 295 @Override 296 public int hashCode() { 297 return Objects.hash(getUnit(), getScale(), getValue()); 298 } 299 300 /** 301 * Returns the <code>String</code> representation of this quantity. The string produced for a given quantity is always the same; it is not 302 * 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, 303 * etc. Locale-sensitive quantity formatting and parsing is handled by the {@link QuantityFormat} implementations and its subclasses. 304 * 305 * @return <code>SimpleQuantityFormat.getInstance().format(this)</code> 306 */ 307 @Override 308 public String toString() { 309 return SimpleQuantityFormat.getInstance().format(this); 310 } 311 312 @Override 313 public <T extends Quantity<T>, E extends Quantity<E>> ComparableQuantity<E> 314 divide(Quantity<T> that, Class<E> asTypeQuantity) { 315 return divide(Objects.requireNonNull(that)) 316 .asType(Objects.requireNonNull(asTypeQuantity)); 317 } 318 319 @Override 320 public <T extends Quantity<T>, E extends Quantity<E>> ComparableQuantity<E> 321 multiply(Quantity<T> that, Class<E> asTypeQuantity) { 322 return multiply(Objects.requireNonNull(that)) 323 .asType(Objects.requireNonNull(asTypeQuantity)); 324 } 325 326 @Override 327 public <T extends Quantity<T>> ComparableQuantity<T> inverse(Class<T> quantityClass) { 328 return inverse().asType(quantityClass); 329 } 330 331 /** 332 * Casts this quantity to a parameterized quantity of specified nature or throw a <code>ClassCastException</code> if the dimension of the 333 * specified quantity and its unit's dimension do not match. For example:<br> 334 * <code> 335 * Quantity<Length> length = AbstractQuantity.parse("2 km").asType(Length.class); 336 * </code> 337 * 338 * @param type 339 * the quantity class identifying the nature of the quantity. 340 * @return this quantity parameterized with the specified type. 341 * @throws ClassCastException 342 * if the dimension of this unit is different from the specified quantity dimension. 343 * @throws UnsupportedOperationException 344 * if the specified quantity class does not have a public static field named "UNIT" holding the SI unit for the quantity. 345 * @see Unit#asType(Class) 346 */ 347 public final <T extends Quantity<T>> ComparableQuantity<T> 348 asType(Class<T> type) throws ClassCastException { 349 this.getUnit().asType(type); // ClassCastException if dimension mismatches. 350 return (ComparableQuantity<T>) this; 351 } 352 353 /** 354 * Returns the quantity of unknown type corresponding to the specified representation. This method can be used to parse dimensionless 355 * quantities.<br> 356 * <code> 357 * Quantity<Dimensionless> proportion = AbstractQuantity.parse("0.234").asType(Dimensionless.class); 358 * </code> 359 * 360 * <p> 361 * <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 362 * 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. 363 * </p> 364 * 365 * @param csq 366 * the decimal value and its unit (if any) separated by space(s). 367 * @return <code>SimpleQuantityFormat.getInstance().parse(csq)</code> 368 */ 369 public static Quantity<?> parse(CharSequence csq) { 370 return SimpleQuantityFormat.getInstance().parse(csq); 371 } 372 373 /** @deprecated seems unused */ 374 protected boolean hasFraction(double value) { 375 return Math.round(value) != value; 376 } 377 378 /** @deprecated seems unused */ 379 protected boolean hasFraction(BigDecimal value) { 380 return value.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) != 0; 381 } 382 383 private NumberSystem numberSystem() { 384 return Calculus.currentNumberSystem(); 385 } 386}