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 tec.uom.se.function.AddConverter; 033import tec.uom.se.function.MultiplyConverter; 034import tec.uom.se.function.RationalConverter; 035import tec.uom.se.quantity.QuantityDimension; 036import tec.uom.se.spi.DimensionalModel; 037import tec.uom.se.unit.AlternateUnit; 038import tec.uom.se.unit.AnnotatedUnit; 039import tec.uom.se.unit.ProductUnit; 040import tec.uom.se.unit.TransformedUnit; 041import tec.uom.se.unit.Units; 042 043import javax.measure.*; 044import javax.measure.quantity.Dimensionless; 045import javax.measure.spi.ServiceProvider; 046 047import java.io.Serializable; 048import java.math.BigInteger; 049import java.util.HashMap; 050import java.util.Map; 051import java.lang.reflect.ParameterizedType; 052import java.lang.reflect.Type; 053 054/** 055 * <p> 056 * The class represents units founded on the seven <b>SI</b> base units for seven base quantities assumed to be mutually independent. 057 * </p> 058 * 059 * <p> 060 * For all physics units, unit conversions are symmetrical: <code>u1.getConverterTo(u2).equals(u2.getConverterTo(u1).inverse())</code>. Non-physical 061 * units (e.g. currency units) for which conversion is not symmetrical should have their own separate class hierarchy and are considered distinct 062 * (e.g. financial units), although they can always be combined with physics units (e.g. "€/Kg", "$/h"). 063 * </p> 064 * 065 * @see <a href= "http://en.wikipedia.org/wiki/International_System_of_Units">Wikipedia: International System of Units</a> 066 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 067 * @author <a href="mailto:units@catmedia.us">Werner Keil</a> 068 * @version 1.0.3, February 24, 2017 069 * @since 1.0 070 */ 071public abstract class AbstractUnit<Q extends Quantity<Q>> implements Unit<Q>, Comparable<Unit<Q>>, Serializable { 072 073 /** 074 * 075 */ 076 private static final long serialVersionUID = -4344589505537030204L; 077 078 /** 079 * Holds the dimensionless unit <code>ONE</code>. 080 */ 081 public static final Unit<Dimensionless> ONE = new ProductUnit<>(); 082 083 /** 084 * Holds the name. 085 */ 086 protected String name; 087 088 /** 089 * Holds the symbol. 090 */ 091 private String symbol; 092 093 /** 094 * Holds the unique symbols collection (base units or alternate units). 095 */ 096 protected static final Map<String, Unit<?>> SYMBOL_TO_UNIT = new HashMap<>(); 097 098 /** 099 * DefaultQuantityFactory constructor. 100 */ 101 protected AbstractUnit() { 102 } 103 104 protected Type getActualType() { 105 ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass(); 106 return parameterizedType.getActualTypeArguments()[0].getClass().getGenericInterfaces()[0]; 107 } 108 109 /** 110 * Indicates if this unit belongs to the set of coherent SI units (unscaled SI units). 111 * 112 * The base and coherent derived units of the SI form a coherent set, designated the set of coherent SI units. The word coherent is used here in the 113 * following sense: when coherent units are used, equations between the numerical values of quantities take exactly the same form as the equations 114 * between the quantities themselves. Thus if only units from a coherent set are used, conversion factors between units are never required. 115 * 116 * @return <code>equals(toSystemUnit())</code> 117 */ 118 public boolean isSystemUnit() { 119 AbstractUnit<Q> si = this.toSystemUnit(); 120 return (this == si) || this.equals(si); 121 } 122 123 /** 124 * Returns the unscaled {@link SI} unit from which this unit is derived. 125 * 126 * They SI unit can be be used to identify a quantity given the unit. For example:[code] static boolean isAngularVelocity(AbstractUnit<?> unit) { 127 * return unit.toSI().equals(RADIAN.divide(SECOND)); } assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. [/code] 128 * 129 * @return the unscaled metric unit from which this unit is derived. 130 */ 131 protected abstract AbstractUnit<Q> toSystemUnit(); 132 133 /** 134 * Returns the converter from this unit to its unscaled {@link #toSysemUnit System Unit} unit. 135 * 136 * @return <code>getConverterTo(this.toSystemUnit())</code> 137 * @see #toSI 138 */ 139 public abstract UnitConverter getSystemConverter(); 140 141 /** 142 * Annotates the specified unit. Annotation does not change the unit semantic. Annotations are often written between curly braces behind units. For 143 * example: [code] AbstractUnit<Volume> PERCENT_VOL = Units.PERCENT.annotate("vol"); // "%{vol}" AbstractUnit<Mass> KG_TOTAL = 144 * Units.KILOGRAM.annotate("total"); // "kg{total}" AbstractUnit<Dimensionless> RED_BLOOD_CELLS = Units.ONE.annotate("RBC"); // "{RBC}" [/code] 145 * 146 * Note: Annotation of system units are not considered themselves as system units. 147 * 148 * @param annotation 149 * the unit annotation. 150 * @return the annotated unit. 151 */ 152 public AnnotatedUnit<Q> annotate(String annotation) { 153 return new AnnotatedUnit<>(this, annotation); 154 } 155 156 /** 157 * Returns the abstract unit represented by the specified characters as per standard <a href="http://www.unitsofmeasure.org/">UCUM</a> format. 158 * 159 * Locale-sensitive unit parsing should be handled using the OSGi {@link org.unitsofmeasurement.service.UnitFormatService} or for non-OSGi 160 * applications the {@link LocalUnitFormat} utility class. 161 * 162 * <p> 163 * Note: The standard format supports dimensionless units.[code] AbstractUnit<Dimensionless> PERCENT = 164 * AbstractUnit.parse("100").inverse().asType(Dimensionless.class); [/code] 165 * </p> 166 * 167 * @param charSequence 168 * the character sequence to parse. 169 * @return <code>UCUMFormat.getCaseSensitiveInstance().parse(csq, new ParsePosition(0))</code> 170 * @throws ParserException 171 * if the specified character sequence cannot be correctly parsed (e.g. not UCUM compliant). 172 */ 173 public static Unit<?> parse(CharSequence charSequence) { 174 return ServiceProvider.current().getUnitFormatService().getUnitFormat().parse(charSequence); 175 } 176 177 /** 178 * Returns the standard representation of this physics unit. The string produced for a given unit is always the same; it is not affected by the 179 * locale. It can be used as a canonical string representation for exchanging units, or as a key for a Hashtable, etc. 180 * 181 * Locale-sensitive unit parsing should be handled using the {@link org.unitsofmeasurement.service.UnitFormat} service. 182 * 183 * @return <code>ServiceProvider.current().getUnitFormatService().getUnitFormat().format(this)</code> 184 */ 185 @Override 186 public String toString() { 187 return ServiceProvider.current().getUnitFormatService().getUnitFormat().format(this); 188 } 189 190 // /////////////////////////////////////////////////////// 191 // Implements org.unitsofmeasurement.Unit<Q> interface // 192 // /////////////////////////////////////////////////////// 193 194 /** 195 * Returns the system unit (unscaled SI unit) from which this unit is derived. They can be be used to identify a quantity given the unit. For 196 * example:[code] static boolean isAngularVelocity(AbstractUnit<?> unit) { return unit.getSystemUnit().equals(RADIAN.divide(SECOND)); } 197 * assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. [/code] 198 * 199 * @return the unscaled metric unit from which this unit is derived. 200 */ 201 @Override 202 public final AbstractUnit<Q> getSystemUnit() { 203 return toSystemUnit(); 204 } 205 206 /** 207 * Indicates if this unit is compatible with the unit specified. To be compatible both units must be physics units having the same fundamental 208 * dimension. 209 * 210 * @param that 211 * the other unit. 212 * @return <code>true</code> if this unit and that unit have equals fundamental dimension according to the current physics model; <code>false</code> 213 * otherwise. 214 */ 215 @Override 216 public final boolean isCompatible(Unit<?> that) { 217 if ((this == that) || this.equals(that)) 218 return true; 219 if (!(that instanceof AbstractUnit)) 220 return false; 221 Dimension thisDimension = this.getDimension(); 222 Dimension thatDimension = that.getDimension(); 223 if (thisDimension.equals(thatDimension)) 224 return true; 225 DimensionalModel model = DimensionalModel.current(); // Use 226 // dimensional 227 // analysis 228 // model. 229 return model.getFundamentalDimension(thisDimension).equals(model.getFundamentalDimension(thatDimension)); 230 } 231 232 /** 233 * Casts this unit to a parameterized unit of specified nature or throw a ClassCastException if the dimension of the specified quantity and this 234 * unit's dimension do not match (regardless whether or not the dimensions are independent or not). 235 * 236 * @param type 237 * the quantity class identifying the nature of the unit. 238 * @throws ClassCastException 239 * if the dimension of this unit is different from the SI dimension of the specified type. 240 * @see Units#getUnit(Class) 241 */ 242 @SuppressWarnings("unchecked") 243 @Override 244 public final <T extends Quantity<T>> AbstractUnit<T> asType(Class<T> type) { 245 Dimension typeDimension = QuantityDimension.of(type); 246 if ((typeDimension != null) && (!typeDimension.equals(this.getDimension()))) 247 throw new ClassCastException("The unit: " + this + " is not compatible with quantities of type " + type); 248 return (AbstractUnit<T>) this; 249 } 250 251 @Override 252 public abstract Map<? extends Unit<?>, Integer> getBaseUnits(); 253 254 @Override 255 public abstract Dimension getDimension(); 256 257 protected void setName(String name) { 258 this.name = name; 259 } 260 261 public String getName() { 262 return name; 263 } 264 265 public String getSymbol() { 266 return symbol; 267 } 268 269 protected void setSymbol(String s) { 270 this.symbol = s; 271 } 272 273 @Override 274 public final UnitConverter getConverterTo(Unit<Q> that) throws UnconvertibleException { 275 if ((this == that) || this.equals(that)) 276 return AbstractConverter.IDENTITY; // Shortcut. 277 Unit<Q> thisSystemUnit = this.getSystemUnit(); 278 Unit<Q> thatSystemUnit = that.getSystemUnit(); 279 if (!thisSystemUnit.equals(thatSystemUnit)) 280 try { 281 return getConverterToAny(that); 282 } catch (IncommensurableException e) { 283 throw new UnconvertibleException(e); 284 } 285 UnitConverter thisToSI = this.getSystemConverter(); 286 UnitConverter thatToSI = that.getConverterTo(thatSystemUnit); 287 return thatToSI.inverse().concatenate(thisToSI); 288 } 289 290 @SuppressWarnings("rawtypes") 291 @Override 292 public final UnitConverter getConverterToAny(Unit<?> that) throws IncommensurableException, UnconvertibleException { 293 if (!isCompatible(that)) 294 throw new IncommensurableException(this + " is not compatible with " + that); 295 AbstractUnit thatAbstr = (AbstractUnit) that; // Since both units are 296 // compatible they must 297 // be both physics 298 // units. 299 DimensionalModel model = DimensionalModel.current(); 300 AbstractUnit thisSystemUnit = this.getSystemUnit(); 301 UnitConverter thisToDimension = model.getDimensionalTransform(thisSystemUnit.getDimension()).concatenate(this.getSystemConverter()); 302 AbstractUnit thatSystemUnit = thatAbstr.getSystemUnit(); 303 UnitConverter thatToDimension = model.getDimensionalTransform(thatSystemUnit.getDimension()).concatenate(thatAbstr.getSystemConverter()); 304 return thatToDimension.inverse().concatenate(thisToDimension); 305 } 306 307 @SuppressWarnings({ "rawtypes", "unchecked" }) 308 @Override 309 public final Unit<Q> alternate(String symbol) { 310 return new AlternateUnit(this, symbol); 311 } 312 313 @Override 314 public final AbstractUnit<Q> transform(UnitConverter operation) { 315 AbstractUnit<Q> systemUnit = this.getSystemUnit(); 316 UnitConverter cvtr = this.getSystemConverter().concatenate(operation); 317 if (cvtr.equals(AbstractConverter.IDENTITY)) 318 return systemUnit; 319 return new TransformedUnit<>(systemUnit, cvtr); 320 } 321 322 @Override 323 public final AbstractUnit<Q> shift(double offset) { 324 if (offset == 0) 325 return this; 326 return transform(new AddConverter(offset)); 327 } 328 329 @Override 330 public final AbstractUnit<Q> multiply(double factor) { 331 if (factor == 1) 332 return this; 333 if (isLongValue(factor)) 334 return transform(new RationalConverter(BigInteger.valueOf((long) factor), BigInteger.ONE)); 335 return transform(new MultiplyConverter(factor)); 336 } 337 338 private static boolean isLongValue(double value) { 339 return !((value < Long.MIN_VALUE) || (value > Long.MAX_VALUE)) && Math.floor(value) == value; 340 } 341 342 /** 343 * Returns the product of this unit with the one specified. 344 * 345 * <p> 346 * Note: If the specified unit (that) is not a physical unit, then <code>that.multiply(this)</code> is returned. 347 * </p> 348 * 349 * @param that 350 * the unit multiplicand. 351 * @return <code>this * that</code> 352 */ 353 @Override 354 public final Unit<?> multiply(Unit<?> that) { 355 if (that instanceof AbstractUnit) 356 return multiply((AbstractUnit<?>) that); 357 // return that.multiply(this); // Commutatif. 358 return ProductUnit.getProductInstance(this, that); 359 } 360 361 /** 362 * Returns the product of this physical unit with the one specified. 363 * 364 * @param that 365 * the physical unit multiplicand. 366 * @return <code>this * that</code> 367 */ 368 public final Unit<?> multiply(AbstractUnit<?> that) { 369 if (this.equals(ONE)) 370 return that; 371 if (that.equals(ONE)) 372 return this; 373 return ProductUnit.getProductInstance(this, that); 374 } 375 376 /** 377 * Returns the inverse of this physical unit. 378 * 379 * @return <code>1 / this</code> 380 */ 381 @Override 382 public final Unit<?> inverse() { 383 if (this.equals(ONE)) 384 return this; 385 return ProductUnit.getQuotientInstance(ONE, this); 386 } 387 388 /** 389 * Returns the result of dividing this unit by the specifified divisor. If the factor is an integer value, the division is exact. For example: 390 * 391 * <pre> 392 * <code> 393 * QUART = GALLON_LIQUID_US.divide(4); // Exact definition. 394 * </code> 395 * </pre> 396 * 397 * @param divisor 398 * the divisor value. 399 * @return this unit divided by the specified divisor. 400 */ 401 @Override 402 public final AbstractUnit<Q> divide(double divisor) { 403 if (divisor == 1) 404 return this; 405 if (isLongValue(divisor)) 406 return transform(new RationalConverter(BigInteger.ONE, BigInteger.valueOf((long) divisor))); 407 return transform(new MultiplyConverter(1.0 / divisor)); 408 } 409 410 /** 411 * Returns the quotient of this unit with the one specified. 412 * 413 * @param that 414 * the unit divisor. 415 * @return <code>this.multiply(that.inverse())</code> 416 */ 417 @Override 418 public final Unit<?> divide(Unit<?> that) { 419 return this.multiply(that.inverse()); 420 } 421 422 /** 423 * Returns the quotient of this physical unit with the one specified. 424 * 425 * @param that 426 * the physical unit divisor. 427 * @return <code>this.multiply(that.inverse())</code> 428 */ 429 public final Unit<?> divide(AbstractUnit<?> that) { 430 return this.multiply(that.inverse()); 431 } 432 433 /** 434 * Returns a unit equals to the given root of this unit. 435 * 436 * @param n 437 * the root's order. 438 * @return the result of taking the given root of this unit. 439 * @throws ArithmeticException 440 * if <code>n == 0</code> or if this operation would result in an unit with a fractional exponent. 441 */ 442 @Override 443 public final Unit<?> root(int n) { 444 if (n > 0) 445 return ProductUnit.getRootInstance(this, n); 446 else if (n == 0) 447 throw new ArithmeticException("Root's order of zero"); 448 else 449 // n < 0 450 return ONE.divide(this.root(-n)); 451 } 452 453 /** 454 * Returns a unit equals to this unit raised to an exponent. 455 * 456 * @param n 457 * the exponent. 458 * @return the result of raising this unit to the exponent. 459 */ 460 @Override 461 public final Unit<?> pow(int n) { 462 if (n > 0) 463 return this.multiply(this.pow(n - 1)); 464 else if (n == 0) 465 return ONE; 466 else 467 // n < 0 468 return ONE.divide(this.pow(-n)); 469 } 470 471 /** 472 * Compares this unit to the specified unit. The default implementation compares the name and symbol of both this unit and the specified unit. 473 * 474 * @return a negative integer, zero, or a positive integer as this unit is less than, equal to, or greater than the specified unit. 475 */ 476 public int compareTo(Unit<Q> that) { 477 if (name != null && getSymbol() != null) { 478 return name.compareTo(that.getName()) + getSymbol().compareTo(that.getSymbol()); 479 } else if (name == null) { 480 if (getSymbol() != null && that.getSymbol() != null) { 481 return getSymbol().compareTo(that.getSymbol()); 482 } else { 483 return -1; 484 } 485 } else if (getSymbol() == null) { 486 if (name != null) { 487 return name.compareTo(that.getName()); 488 } else { 489 return -1; 490 } 491 } else { 492 return -1; 493 } 494 } 495 496 // ////////////////////////////////////////////////////////////// 497 // Ensures that sub-classes implements hashCode/equals method. 498 // ////////////////////////////////////////////////////////////// 499 500 @Override 501 public abstract int hashCode(); 502 503 @Override 504 public abstract boolean equals(Object that); 505}