001/* 002 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences. 003 * Copyright (C) 2007 - JScience (http://jscience.org/) 004 * All rights reserved. 005 * 006 * Permission to use, copy, modify, and distribute this software is 007 * freely granted, provided that this notice is preserved. 008 */ 009package javax.measure; 010 011import java.math.BigDecimal; 012import java.math.MathContext; 013 014import javax.measure.converter.AddConverter; 015import javax.measure.converter.RationalConverter; 016import javax.measure.converter.UnitConverter; 017import javax.measure.quantity.Quantity; 018import javax.measure.unit.Unit; 019 020/** 021 * <p> This class represents a measure whose value is an arbitrary-precision 022 * decimal number.</p> 023 * 024 * <p> When converting, applications may supply the 025 * <code>java.math.Context</code>:[code] 026 * DecimalMeasure<Velocity> c = DecimalMeasure.valueOf("299792458 m/s"); 027 * DecimalMeasure<Velocity> milesPerHour = c.to(MILES_PER_HOUR, MathContext.DECIMAL128); 028 * System.out.println(milesPerHour); 029 * 030 * > 670616629.3843951324266284896206156 mph 031 * [/code] 032 * 033 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 034 * @version 4.3, October 3, 2007 035 */ 036public class DecimalMeasure<Q extends Quantity> extends Measure<BigDecimal, Q> { 037 038 /** 039 * Holds the BigDecimal value. 040 */ 041 private final BigDecimal _value; 042 043 /** 044 * Holds the unit. 045 */ 046 private final Unit<Q> _unit; 047 048 /** 049 * Creates a decimal measure for the specified number stated in the 050 * specified unit. 051 */ 052 public DecimalMeasure(BigDecimal value, Unit<Q> unit) { 053 _value = value; 054 _unit = unit; 055 } 056 057 /** 058 * Returns the decimal measure for the specified number stated in the 059 * specified unit. 060 * 061 * @param decimal the measurement value. 062 * @param unit the measurement unit. 063 */ 064 public static <Q extends Quantity> DecimalMeasure<Q> valueOf( 065 BigDecimal decimal, Unit<Q> unit) { 066 return new DecimalMeasure<Q>(decimal, unit); 067 } 068 069 /** 070 * Returns the decimal measure for the specified textual representation. 071 * This method first reads the <code>BigDecimal</code> value, then 072 * the unit if any (value and unit should be separated by white spaces). 073 * 074 * @param csq the decimal measure representation (including unit if any). 075 * @throws NumberFormatException if the specified character sequence is 076 * not a valid representation of decimal measure. 077 */ 078 @SuppressWarnings("unchecked") 079 public static <Q extends Quantity> DecimalMeasure<Q> valueOf(CharSequence csq) { 080 String str = csq.toString(); 081 int numberLength = str.length(); 082 int unitStartIndex = -1; 083 for (int i=0; i < str.length(); i++) { 084 if (Character.isWhitespace(str.charAt(i))) { 085 for (int j=i+1; j < str.length(); j++) { 086 if (!Character.isWhitespace(str.charAt(j))) { 087 unitStartIndex = j; 088 break; 089 } 090 } 091 numberLength = i; 092 break; 093 } 094 } 095 BigDecimal decimal = new BigDecimal(str.substring(0, numberLength)); 096 Unit unit = Unit.ONE; 097 if (unitStartIndex > 0) { 098 unit = Unit.valueOf(str.substring(unitStartIndex)); 099 } 100 return new DecimalMeasure<Q>(decimal, unit); 101 } 102 103 @Override 104 public Unit<Q> getUnit() { 105 return _unit; 106 } 107 108 @Override 109 public BigDecimal getValue() { 110 return _value; 111 } 112 113 /** 114 * Returns the decimal measure equivalent to this measure but stated in the 115 * specified unit. This method will raise an ArithmeticException if the 116 * resulting measure does not have a terminating decimal expansion. 117 * 118 * @param unit the new measurement unit. 119 * @return the measure stated in the specified unit. 120 * @throws ArithmeticException if the converted measure value does not have 121 * a terminating decimal expansion 122 * @see #to(Unit, MathContext) 123 */ 124 @Override 125 public DecimalMeasure<Q> to(Unit<Q> unit) { 126 return to(unit, null); 127 } 128 129 /** 130 * Returns the decimal measure equivalent to this measure but stated in the 131 * specified unit, the conversion is performed using the specified math 132 * context. 133 * 134 * @param unit the new measurement unit. 135 * @param mathContext the mathContext used to convert 136 * <code>BigDecimal</code> values or <code>null</code> if none. 137 * @return the measure stated in the specified unit. 138 * @throws ArithmeticException if the result is inexact but the 139 * rounding mode is <code>MathContext.UNNECESSARY</code> or 140 * <code>mathContext.precision == 0</tt> and the quotient has a 141 * non-terminating decimal expansion. 142 */ 143 public DecimalMeasure<Q> to(Unit<Q> unit, MathContext mathContext) { 144 if ((unit == _unit) || (unit.equals(_unit))) 145 return this; 146 UnitConverter cvtr = _unit.getConverterTo(unit); 147 if (cvtr instanceof RationalConverter) { 148 RationalConverter factor = (RationalConverter) cvtr; 149 BigDecimal dividend = BigDecimal.valueOf(factor.getDividend()); 150 BigDecimal divisor = BigDecimal.valueOf(factor.getDivisor()); 151 BigDecimal result = mathContext == null ? 152 _value.multiply(dividend).divide(divisor) : 153 _value.multiply(dividend, mathContext).divide(divisor, mathContext); 154 return new DecimalMeasure<Q>(result, unit); 155 } else if (cvtr.isLinear()) { 156 BigDecimal factor = BigDecimal.valueOf(cvtr.convert(1.0)); 157 BigDecimal result = mathContext == null ? 158 _value.multiply(factor) : _value.multiply(factor, mathContext); 159 return new DecimalMeasure<Q>(result, unit); 160 } else if (cvtr instanceof AddConverter) { 161 BigDecimal offset = BigDecimal.valueOf(((AddConverter)cvtr).getOffset()); 162 BigDecimal result = mathContext == null ? 163 _value.add(offset) : _value.add(offset, mathContext); 164 return new DecimalMeasure<Q>(result, unit); 165 } else { // Non-linear and not an offset, convert the double value. 166 BigDecimal result = BigDecimal.valueOf(cvtr.convert(_value.doubleValue())); 167 return new DecimalMeasure<Q>(result, unit); 168 } 169 } 170 171 public double doubleValue(Unit<Q> unit) { 172 if ((unit == _unit) || (unit.equals(_unit))) 173 return _value.doubleValue(); 174 return _unit.getConverterTo(unit).convert(_value.doubleValue()); 175 } 176 177 private static final long serialVersionUID = 1L; 178}