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: */
009: package javax.measure;
010:
011: import java.math.BigDecimal;
012: import java.math.MathContext;
013:
014: import javax.measure.converter.AddConverter;
015: import javax.measure.converter.RationalConverter;
016: import javax.measure.converter.UnitConverter;
017: import javax.measure.quantity.Quantity;
018: import 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: */
036: public class DecimalMeasure<Q extends Quantity> extends
037: Measure<BigDecimal, Q> {
038:
039: /**
040: * Holds the BigDecimal value.
041: */
042: private final BigDecimal _value;
043:
044: /**
045: * Holds the unit.
046: */
047: private final Unit<Q> _unit;
048:
049: /**
050: * Creates a decimal measure for the specified number stated in the
051: * specified unit.
052: */
053: public DecimalMeasure(BigDecimal value, Unit<Q> unit) {
054: _value = value;
055: _unit = unit;
056: }
057:
058: /**
059: * Returns the decimal measure for the specified number stated in the
060: * specified unit.
061: *
062: * @param decimal the measurement value.
063: * @param unit the measurement unit.
064: */
065: public static <Q extends Quantity> DecimalMeasure<Q> valueOf(
066: BigDecimal decimal, Unit<Q> unit) {
067: return new DecimalMeasure<Q>(decimal, unit);
068: }
069:
070: /**
071: * Returns the decimal measure for the specified textual representation.
072: * This method first reads the <code>BigDecimal</code> value, then
073: * the unit if any (value and unit should be separated by white spaces).
074: *
075: * @param csq the decimal measure representation (including unit if any).
076: * @throws NumberFormatException if the specified character sequence is
077: * not a valid representation of decimal measure.
078: */
079: @SuppressWarnings("unchecked")
080: public static <Q extends Quantity> DecimalMeasure<Q> valueOf(
081: CharSequence csq) {
082: String str = csq.toString();
083: int numberLength = str.length();
084: int unitStartIndex = -1;
085: for (int i = 0; i < str.length(); i++) {
086: if (Character.isWhitespace(str.charAt(i))) {
087: for (int j = i + 1; j < str.length(); j++) {
088: if (!Character.isWhitespace(str.charAt(j))) {
089: unitStartIndex = j;
090: break;
091: }
092: }
093: numberLength = i;
094: break;
095: }
096: }
097: BigDecimal decimal = new BigDecimal(str.substring(0,
098: numberLength));
099: Unit unit = Unit.ONE;
100: if (unitStartIndex > 0) {
101: unit = Unit.valueOf(str.substring(unitStartIndex));
102: }
103: return new DecimalMeasure<Q>(decimal, unit);
104: }
105:
106: @Override
107: public Unit<Q> getUnit() {
108: return _unit;
109: }
110:
111: @Override
112: public BigDecimal getValue() {
113: return _value;
114: }
115:
116: /**
117: * Returns the decimal measure equivalent to this measure but stated in the
118: * specified unit. This method will raise an ArithmeticException if the
119: * resulting measure does not have a terminating decimal expansion.
120: *
121: * @param unit the new measurement unit.
122: * @return the measure stated in the specified unit.
123: * @throws ArithmeticException if the converted measure value does not have
124: * a terminating decimal expansion
125: * @see #to(Unit, MathContext)
126: */
127: @Override
128: public DecimalMeasure<Q> to(Unit<Q> unit) {
129: return to(unit, null);
130: }
131:
132: /**
133: * Returns the decimal measure equivalent to this measure but stated in the
134: * specified unit, the conversion is performed using the specified math
135: * context.
136: *
137: * @param unit the new measurement unit.
138: * @param mathContext the mathContext used to convert
139: * <code>BigDecimal</code> values or <code>null</code> if none.
140: * @return the measure stated in the specified unit.
141: * @throws ArithmeticException if the result is inexact but the
142: * rounding mode is <code>MathContext.UNNECESSARY</code> or
143: * <code>mathContext.precision == 0</tt> and the quotient has a
144: * non-terminating decimal expansion.
145: */
146: public DecimalMeasure<Q> to(Unit<Q> unit, MathContext mathContext) {
147: if ((unit == _unit) || (unit.equals(_unit)))
148: return this ;
149: UnitConverter cvtr = _unit.getConverterTo(unit);
150: if (cvtr instanceof RationalConverter) {
151: RationalConverter factor = (RationalConverter) cvtr;
152: BigDecimal dividend = BigDecimal.valueOf(factor
153: .getDividend());
154: BigDecimal divisor = BigDecimal
155: .valueOf(factor.getDivisor());
156: BigDecimal result = mathContext == null ? _value.multiply(
157: dividend).divide(divisor) : _value.multiply(
158: dividend, mathContext).divide(divisor, mathContext);
159: return new DecimalMeasure<Q>(result, unit);
160: } else if (cvtr.isLinear()) {
161: BigDecimal factor = BigDecimal.valueOf(cvtr.convert(1.0));
162: BigDecimal result = mathContext == null ? _value
163: .multiply(factor) : _value.multiply(factor,
164: mathContext);
165: return new DecimalMeasure<Q>(result, unit);
166: } else if (cvtr instanceof AddConverter) {
167: BigDecimal offset = BigDecimal
168: .valueOf(((AddConverter) cvtr).getOffset());
169: BigDecimal result = mathContext == null ? _value
170: .add(offset) : _value.add(offset, mathContext);
171: return new DecimalMeasure<Q>(result, unit);
172: } else { // Non-linear and not an offset, convert the double value.
173: BigDecimal result = BigDecimal.valueOf(cvtr.convert(_value
174: .doubleValue()));
175: return new DecimalMeasure<Q>(result, unit);
176: }
177: }
178:
179: public double doubleValue(Unit<Q> unit) {
180: if ((unit == _unit) || (unit.equals(_unit)))
181: return _value.doubleValue();
182: return _unit.getConverterTo(unit).convert(_value.doubleValue());
183: }
184:
185: private static final long serialVersionUID = 1L;
186: }
|