001/* 002 * Units of Measurement Reference Implementation 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.units.ri; 031 032import java.util.Comparator; 033import java.util.Objects; 034 035import javax.measure.Quantity; 036import javax.measure.Unit; 037import javax.measure.quantity.Dimensionless; 038 039import tec.units.ri.format.QuantityFormat; 040import tec.units.ri.function.NaturalOrder; 041import tec.units.ri.quantity.NumberQuantity; 042import tec.uom.lib.common.function.UnitSupplier; 043import tec.uom.lib.common.function.ValueSupplier; 044 045/** 046 * <p> 047 * This class represents the immutable result of a scalar measurement stated in a known unit. 048 * </p> 049 * 050 * <p> 051 * <code> 052 * public static final Quantity<Velocity> C = NumberQuantity.parse("299792458 m/s").asType(Velocity.class); 053 * // Speed of Light (exact). 054 * </code> 055 * </p> 056 * 057 * <p> 058 * Quantities can be converted to different units.<br> 059 * <code> 060 * Quantity<Velocity> milesPerHour = C.to(MILES_PER_HOUR); // Use double implementation (fast). 061 * System.out.println(milesPerHour); 062 * 063 * > 670616629.3843951 m/h 064 * </code> 065 * </p> 066 * 067 * <p> 068 * Applications may sub-class {@link AbstractQuantity} for particular quantity types.<br> 069 * <code> 070 * // Quantity of type Mass based on double primitive types.<br> 071 * public class MassAmount extends AbstractQuantity<Mass> {<br> 072 * private final double kilograms; // Internal SI representation.<br> 073 * private Mass(double kg) { kilograms = kg; }<br> 074 * public static Mass of(double value, Unit<Mass> unit) {<br> 075 * return new Mass(unit.getConverterTo(SI.KILOGRAM).convert(value));<br> 076 * }<br> 077 * public Unit<Mass> getUnit() { return SI.KILOGRAM; }<br> 078 * public Double getValue() { return kilograms; }<br> 079 * ...<br> 080 * }<br> 081 * <p> 082 * // Complex numbers measurements.<br> 083 * public class ComplexQuantity 084 * <Q extends Quantity>extends AbstractQuantity 085 * <Q>{<br> 086 * public Complex getValue() { ... } // Assuming Complex is a Number.<br> 087 * ...<br> 088 * }<br> 089 * <br> 090 * // Specializations of complex numbers measurements.<br> 091 * public final class Current extends ComplexQuantity<ElectricCurrent> {...}<br> 092 * public final class Tension extends ComplexQuantity<ElectricPotential> {...} <br> 093 * </code> 094 * </p> 095 * 096 * <p> 097 * All instances of this class shall be immutable. 098 * </p> 099 * 100 * @author <a href="mailto:units@catmedia.us">Werner Keil</a> 101 * @version 1.0.2, May 28, 2017 102 * @since 1.0 103 */ 104public abstract class AbstractQuantity<Q extends Quantity<Q>> implements Quantity<Q>, Comparable<Quantity<Q>>, UnitSupplier<Q>, ValueSupplier<Number> { 105 106 /** 107 * 108 */ 109 // private static final long serialVersionUID = -4993173119977931016L; 110 111 private final Unit<Q> unit; 112 113 /** 114 * Holds a dimensionless quantity of none (exact). 115 */ 116 public static final Quantity<Dimensionless> NONE = NumberQuantity.of(0, AbstractUnit.ONE); 117 118 /** 119 * Holds a dimensionless quantity of one (exact). 120 */ 121 public static final Quantity<Dimensionless> ONE = NumberQuantity.of(1, AbstractUnit.ONE); 122 123 /** 124 * constructor. 125 */ 126 protected AbstractQuantity(Unit<Q> unit) { 127 this.unit = unit; 128 } 129 130 /** 131 * Returns the measurement numeric value. 132 * 133 * @return the measurement value. 134 */ 135 public abstract Number getValue(); 136 137 /** 138 * Returns the measurement unit. 139 * 140 * @return the measurement unit. 141 */ 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 public Quantity<Q> to(Unit<Q> unit) { 168 if (unit.equals(this.getUnit())) { 169 return this; 170 } 171 return NumberQuantity.of(doubleValue(unit), unit); 172 } 173 174 /** 175 * Compares this measure to the specified Measurement quantity. The default implementation compares the {@link AbstractQuantity#doubleValue(Unit)} 176 * of both this measure and the specified Measurement stated in the same unit (this measure's {@link #getUnit() unit}). 177 * 178 * @return a negative integer, zero, or a positive integer as this measure is less than, equal to, or greater than the specified Measurement 179 * quantity. 180 * @see {@link NaturalOrder} 181 */ 182 public int compareTo(Quantity<Q> that) { 183 final Comparator<Quantity<Q>> comparator = new NaturalOrder<Q>(); 184 return comparator.compare(this, that); 185 } 186 187 /** 188 * Compares this measure against the specified object for <b>strict</b> equality (same unit and same amount). 189 * 190 * <p> 191 * Similarly to the {@link BigDecimal#equals} method which consider 2.0 and 2.00 as different objects because of different internal scales, 192 * measurements such as <code>Measure.valueOf(3.0, KILOGRAM)</code> <code>Measure.valueOf(3, KILOGRAM)</code> and 193 * <code>Quantities.getQuantity("3 kg")</code> might not be considered equals because of possible differences in their implementations. 194 * </p> 195 * 196 * <p> 197 * To compare measures stated using different units or using different amount implementations the {@link #compareTo compareTo} or 198 * {@link #equals(javax.measure.Measurement, double, javax.measure.unit.Unit) equals(Measurement, epsilon, epsilonUnit)} methods should be used. 199 * </p> 200 * 201 * @param obj 202 * the object to compare with. 203 * @return <code>this.getUnit.equals(obj.getUnit()) 204 * && this.getValue().equals(obj.getValue())</code> 205 */ 206 @Override 207 public boolean equals(Object obj) { 208 if (!(obj instanceof AbstractQuantity<?>)) { 209 return false; 210 } 211 AbstractQuantity<?> that = (AbstractQuantity<?>) obj; 212 return this.getUnit().equals(that.getUnit()) && this.getValue().equals(that.getValue()); 213 } 214 215 /** 216 * Compares this measure and the specified Measurement to the given accuracy. Measurements are considered approximately equals if their absolute 217 * differences when stated in the same specified unit is less than the specified epsilon. 218 * 219 * @param that 220 * the Measurement to compare with. 221 * @param epsilon 222 * the absolute error stated in epsilonUnit. 223 * @param epsilonUnit 224 * the epsilon unit. 225 * @return <code>abs(this.doubleValue(epsilonUnit) - that.doubleValue(epsilonUnit)) <= epsilon</code> 226 */ 227 public boolean equals(AbstractQuantity<Q> that, double epsilon, Unit<Q> epsilonUnit) { 228 return Math.abs(this.doubleValue(epsilonUnit) - that.doubleValue(epsilonUnit)) <= epsilon; 229 } 230 231 /** 232 * Returns the hash code for this quantity. 233 * 234 * @return the hash code value. 235 */ 236 @Override 237 public int hashCode() { 238 return getUnit().hashCode() + getValue().hashCode(); 239 } 240 241 final boolean isBig() { 242 return false; 243 } 244 245 /** 246 * Returns the <code>String</code> representation of this quantity. The string produced for a given quantity is always the same; it is not affected 247 * 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. 248 * Locale-sensitive quantity formatting and parsing is handled by the {@link QuantityFormat} class and its subclasses. 249 * 250 * @return <code>UnitFormat.getInternational().format(this)</code> 251 */ 252 @Override 253 public String toString() { 254 // return MeasureFormat.getStandard().format(this); TODO improve 255 // MeasureFormat 256 // return String.valueOf(getValue()) + " " + String.valueOf(getUnit()); 257 return QuantityFormat.getInstance().format(this); 258 } 259 260 public abstract double doubleValue(Unit<Q> unit) throws ArithmeticException; 261 262 protected long longValue(Unit<Q> unit) throws ArithmeticException { 263 double result = doubleValue(unit); 264 if ((result < Long.MIN_VALUE) || (result > Long.MAX_VALUE)) { 265 throw new ArithmeticException("Overflow (" + result + ")"); 266 } 267 return (long) result; 268 } 269 270 protected float floatValue(Unit<Q> unit) { 271 return (float) doubleValue(unit); 272 } 273 274 /** 275 * Casts this quantity to a parameterized unit of specified nature or throw a <code>ClassCastException</code> if the dimension of the specified 276 * quantity and this measure unit's dimension do not match. For example: <br> 277 * <code> 278 * Measure<Length> length = Quantities.getQuantity("2 km").asType(Length.class); 279 * </code> 280 * 281 * @param type 282 * the quantity class identifying the nature of the quantity. 283 * @return this quantity parameterized with the specified type. 284 * @throws ClassCastException 285 * if the dimension of this unit is different from the specified quantity dimension. 286 * @throws UnsupportedOperationException 287 * if the specified quantity class does not have a public static field named "UNIT" holding the SI unit for the quantity. 288 * @see Unit#asType(Class) 289 */ 290 @SuppressWarnings("unchecked") 291 public final <T extends Quantity<T>> Quantity<T> asType(Class<T> type) throws ClassCastException { 292 this.getUnit().asType(type); // Raises ClassCastException is dimension 293 // mismatches. 294 return (Quantity<T>) this; 295 } 296 297 /** 298 * Returns the quantity of unknown type corresponding to the specified representation. This method can be used to parse dimensionless quantities.<br/> 299 * <code> 300 * Quantity<Dimensionless> proportion = AbstractQuantity.parse("0.234").asType(Dimensionless.class); 301 * </code> 302 * 303 * <p> 304 * Note: This method handles only {@link tec.units.ri.SimpleUnitFormat.UnitFormat#getStandard standard} unit format. 305 * </p> 306 * 307 * @param csq 308 * the decimal value and its unit (if any) separated by space(s). 309 * @return <code>QuantityFormat.getInstance().parse(csq)</code> 310 */ 311 public static Quantity<?> parse(CharSequence csq) { 312 return QuantityFormat.getInstance().parse(csq); 313 } 314 315 /** 316 * Utility class for number comparison and equality 317 */ 318 protected static final class Equalizer { 319 320 /** 321 * Converts a number to {@link Double} 322 * 323 * @param value 324 * the value to be converted 325 * @return the value converted 326 */ 327 public static Double toDouble(Number value) { 328 if (Double.class.isInstance(value)) { 329 return Double.class.cast(value); 330 } 331 return value.doubleValue(); 332 } 333 334 /** 335 * Check if the both value has equality number, in other words, 1 is equals to 1.0000 and 1.0. 336 * 337 * If the first value is a <type>Number</type> of either <type>Double</type>, <type>Float</type>, <type>Integer</type>, <type>Long</type>, 338 * <type>Short</type> or <type>Byte</type> it is compared using the respective <code>*value()</code> method of <type>Number</type>. Otherwise it 339 * is checked, if {@link Double#compareTo(Object)} is equal to zero. 340 * 341 * @param valueA 342 * the value a 343 * @param valueB 344 * the value B 345 * @return {@link Double#compareTo(Object)} == zero 346 */ 347 public static boolean hasEquality(Number valueA, Number valueB) { 348 Objects.requireNonNull(valueA); 349 Objects.requireNonNull(valueB); 350 351 if (valueA instanceof Double && valueB instanceof Double) { 352 return valueA.doubleValue() == valueB.doubleValue(); 353 } else if (valueA instanceof Float && valueB instanceof Float) { 354 return valueA.floatValue() == valueB.floatValue(); 355 } else if (valueA instanceof Integer && valueB instanceof Integer) { 356 return valueA.intValue() == valueB.intValue(); 357 } else if (valueA instanceof Long && valueB instanceof Long) { 358 return valueA.longValue() == valueB.longValue(); 359 } else if (valueA instanceof Short && valueB instanceof Short) { 360 return valueA.shortValue() == valueB.shortValue(); 361 } else if (valueA instanceof Byte && valueB instanceof Byte) { 362 return valueA.byteValue() == valueB.byteValue(); 363 } 364 return toDouble(valueA).compareTo(toDouble(valueB)) == 0; 365 } 366 } 367}