| /** |
| * Copyright (c) 2005, 2014, Werner Keil and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Werner Keil, Jean-Marie Dautelle - initial API and implementation |
| */ |
| package org.eclipse.uomo.business.money; |
| |
| import java.math.BigInteger; |
| import java.math.MathContext; |
| import java.text.FieldPosition; |
| import java.text.NumberFormat; |
| |
| import org.eclipse.uomo.business.internal.CurrencyUnit; |
| import org.eclipse.uomo.business.internal.MonetaryAmount; |
| import org.eclipse.uomo.business.internal.MonetaryOperator; |
| import org.eclipse.uomo.business.types.Quantity; |
| import org.eclipse.uomo.core.UOMoRuntimeException; |
| import tech.units.indriya.AbstractConverter; |
| import javax.measure.Quantity; |
| import org.eclipse.uomo.units.impl.converter.RationalConverter; |
| import org.unitsofmeasurement.unit.IncommensurableException; |
| import org.unitsofmeasurement.unit.UnconvertibleException; |
| import javax.measure.Unit; |
| import javax.measure.UnitConverter; |
| |
| import com.ibm.icu.math.BigDecimal; |
| import com.ibm.icu.util.Currency; |
| |
| /** |
| * This class represents an amount of money specified in a given |
| * {@link Currency} (convenience method). |
| * |
| * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> |
| * @author <a href="mailto:units@catmedia.us">Werner Keil</a> |
| * @version 0.8, $Date: 2014-08-03 $ |
| */ |
| public class MoneyAmount extends QuantityAmount<Quantity> implements Quantity, MonetaryAmount, |
| Comparable<MonetaryAmount> { |
| |
| /** |
| * Holds the base unit for money quantities (symbol "$"). |
| */ |
| public final static MoneyUnit<Quantity> UNIT = new MoneyUnit<Quantity>( |
| "$"); |
| |
| /** |
| * Creates a money amount always on the heap independently from the current |
| * {@link javolution.context.AllocatorContext allocator context}. To allow |
| * for custom object allocation policies, static factory methods |
| * <code>valueOf(...)</code> are recommended. |
| * |
| * @param value |
| * the value stated in the specified currency. |
| * @param currency |
| * the currency in which the value is stated. |
| */ |
| public MoneyAmount(Number value, MoneyUnit unit) { |
| super(value, unit); |
| } |
| |
| /** |
| * Returns the money amount corresponding to the specified Number value |
| * and currency. |
| * |
| * @param value |
| * the value stated in the specified currency. |
| * @param currency |
| * the currency in which the value is stated. |
| * @return the corresponding amount. |
| */ |
| public static MoneyAmount of(Number value, CurrencyUnit currency) { |
| return new MoneyAmount(value, (MoneyUnit)currency); |
| } |
| |
| /** |
| * Returns the money amount corresponding to the specified Number value |
| * and currency. |
| * |
| * @param value |
| * the value stated in the specified currency. |
| * @param currency |
| * the currency in which the value is stated. |
| * @return the corresponding amount. |
| */ |
| // static MoneyAmount of(Number value, java.util.Currency currency) { |
| // MoneyAmount amount = new MoneyAmount(value, currency); |
| // return amount; |
| // } |
| |
| /** |
| * Returns the money amount corresponding to the specified Number value |
| * and currency. |
| * |
| * @param value |
| * the value stated in the specified currency. |
| * @param currency |
| * the currency in which the value is stated. |
| * @return the corresponding amount. |
| */ |
| public static MoneyAmount of(Number value, Unit<?> currency) { |
| return new MoneyAmount(value, (MoneyUnit) currency); |
| } |
| |
| /** |
| * Returns the money amount corresponding to the specified value and cents. |
| * |
| * @param value |
| * the integer value in the specified currency. |
| * @param cents |
| * the cents value in the specified currency. |
| * @param currency |
| * the currency in which the value and cents are stated. |
| * @return the corresponding amount. |
| */ |
| public static MoneyAmount of(long value, int cents, Currency currency) { |
| return new MoneyAmount(BigDecimal.valueOf(value * 100 |
| + cents, -2), (MoneyUnit) currency); |
| } |
| |
| /** |
| * Returns the money amount corresponding to the specified generic amount. |
| * |
| * @param amount |
| * the raw amount. |
| * @return the corresponding money amount stated in an existing |
| * {@link Currency}. |
| * @throws ClassCastException |
| * if the SI unit of the specified amount is not a |
| * {@link Currency}. |
| */ |
| public static MoneyAmount of(QuantityAmount<Quantity> amount) { |
| // MoneyAmount amountSI = amount.toSI(); |
| return MoneyAmount.of(BigDecimal.valueOf(amount.getValue() |
| .doubleValue()), amount.unit().getSystemUnit()); |
| } |
| |
| |
| /** |
| * Returns the money amount corresponding to the specified Number value |
| * and currency code. |
| * |
| * @param value |
| * the value stated in the specified currency. |
| * @param currency |
| * the currency in which the value is stated. |
| * @return the corresponding amount. |
| */ |
| public static MoneyAmount of(String code, Number value) { |
| return new MoneyAmount(value, MoneyUnit.of(code)); |
| } |
| |
| /** |
| * Overrides the default {@link #toString()} to show only the currency |
| * {@link Currency#getFractionDigits() fraction digits} of the associated |
| * currency (e.g. rounding to closest cents). |
| * |
| * @return the string representation of this money amount. |
| */ |
| public String toStringLocale() { |
| BigDecimal value = (BigDecimal) this.getNumber(); |
| // int digits = ((Currency) this.getUnit()).getDefaultFractionDigits(); |
| // int exponent = value.getExponent(); |
| // LargeInteger significand = value.getSignificand(); |
| // int scale = exponent + digits; |
| // significand = significand.E(scale); |
| // exponent -= scale; |
| // value = BigDecimal.valueOf(significand, exponent); |
| NumberFormat numberFormat = NumberFormat.getInstance(); |
| StringBuffer tmp = new StringBuffer(); |
| numberFormat.format(value, tmp, new FieldPosition(0)); |
| tmp.append(' '); |
| tmp.append(this.unit().toString()); |
| return tmp.toString(); |
| } |
| |
| // public MoneyAmount opposite() { |
| // return MoneyAmount.valueOf(_value.opposite(), getCurrency()); |
| // } |
| |
| protected MoneyAmount plus(Quantity that) { |
| // Measure<BigDecimal, ?> amount = that.to((Unit) getCurrency()); |
| return MoneyAmount.of(this.getNumber().doubleValue() |
| + that.value().doubleValue(), getCurrency()); |
| } |
| |
| protected MoneyAmount plus(MoneyAmount that) { |
| // Measure<BigDecimal, ?> amount = that.to((Unit) getCurrency()); |
| return MoneyAmount.of(this.getNumber().doubleValue() |
| + that.getNumber().doubleValue(), getCurrency()); |
| } |
| |
| protected MoneyAmount minus(MoneyAmount that) { |
| // MoneyAmount amount = that.to((Unit) getCurrency()); |
| return MoneyAmount.of(this.getNumber().doubleValue() |
| - that.getNumber().doubleValue(), getCurrency()); |
| } |
| |
| public MoneyAmount multiply(Number that) { |
| return MoneyAmount.of( |
| getNumber().doubleValue() * that.doubleValue(), getCurrency()); |
| } |
| |
| public MoneyAmount multiply(long n) { |
| return MoneyAmount.of(getNumber().longValue() * n, |
| getCurrency()); |
| } |
| |
| public MoneyAmount multiply(MonetaryAmount that) { |
| MoneyAmount ma = (MoneyAmount)that; |
| return times(ma); |
| } |
| |
| protected MoneyAmount times(MoneyAmount that) { |
| Unit<?> unit = unit().multiply(that.unit()); |
| return MoneyAmount.of(((BigDecimal) getNumber()) |
| .multiply((BigDecimal) that.getNumber()), unit); |
| } |
| |
| public MoneyAmount pow(int exp) { |
| return MoneyAmount.of(((BigDecimal) getNumber()).pow(BigDecimal |
| .valueOf(exp)), unit().pow(exp)); |
| } |
| |
| // protected MoneyAmount inverse() { |
| // return MoneyAmount.valueOf(((BigDecimal) getNumber()).inverse(), |
| // unit().inverse()); |
| // } |
| |
| public MoneyAmount divide(long n) { |
| return MoneyAmount.of(getNumber().longValue() / n, |
| getCurrency()); |
| } |
| |
| protected MoneyAmount divide(MoneyAmount that) { |
| Unit<?> unit = unit().divide(that.unit()); |
| return MoneyAmount.of(((BigDecimal) getNumber()) |
| .divide((BigDecimal) that.getNumber()), unit); |
| } |
| |
| public MoneyAmount copy() { |
| return MoneyAmount.of(getNumber(), getCurrency()); |
| } |
| |
| /** |
| * Get the unit (convenience to avoid cast). |
| * |
| * @draft UOMo 0.5 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| @SuppressWarnings("unchecked") |
| public MoneyUnit<Quantity> unit() { |
| return (MoneyUnit<Quantity>) getUnit(); |
| } |
| |
| public int compareTo(Quantity o) { |
| // TODO Auto-generated method stub |
| return 0; |
| } |
| |
| public double doubleValue(Unit<Quantity> unit) { |
| Unit<Quantity> myUnit = unit(); |
| try { |
| UnitConverter converter = unit.getConverterToAny(myUnit); |
| return converter.convert(getNumber().doubleValue()); |
| } catch (UnconvertibleException e) { |
| throw e; |
| } catch (IncommensurableException e) { |
| throw new IllegalArgumentException(e.getMessage()); |
| } |
| } |
| |
| public long longValue(Unit<Quantity> unit) throws ArithmeticException { |
| Unit<Quantity> myUnit = unit(); |
| try { |
| UnitConverter converter = unit.getConverterToAny(myUnit); |
| return (converter.convert(BigDecimal.valueOf(super.getNumber() |
| .longValue())).longValue()); |
| } catch (UnconvertibleException e) { |
| throw e; |
| } catch (IncommensurableException e) { |
| throw new IllegalArgumentException(e.getMessage()); |
| } |
| } |
| |
| public Quantity<Quantity> divide(Quantity<?> that) { |
| return divide((MoneyAmount) that); |
| } |
| |
| public Quantity<Quantity> multiply(Quantity<?> that) { |
| return multiply((MonetaryAmount) that); |
| } |
| |
| public Quantity<Quantity> to(Unit<Quantity> unit) { |
| return to(unit, MathContext.DECIMAL32); |
| } |
| |
| protected Quantity<Quantity> to(Unit<Quantity> unit, MathContext ctx) { |
| if (this.unit().equals(unit)) |
| return this; |
| UnitConverter cvtr = this.unit().getConverterTo(unit); |
| if (cvtr == AbstractConverter.IDENTITY) |
| return (Quantity<Quantity>) of(this.getNumber(), unit); |
| return (Quantity<Quantity>) of(convert(this.getNumber(), cvtr, ctx), |
| unit); |
| } |
| |
| // Try to convert the specified value. |
| private static Number convert(Number value, UnitConverter cvtr, |
| MathContext ctx) { |
| if (cvtr instanceof RationalConverter) { // Try converting through Field |
| // methods. |
| RationalConverter rCvtr = (RationalConverter) cvtr; |
| BigInteger dividend = rCvtr.getDividend(); |
| BigInteger divisor = rCvtr.getDivisor(); |
| if (dividend.abs().compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) |
| throw new ArithmeticException("Multiplier overflow"); |
| if (divisor.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) |
| throw new ArithmeticException("Divisor overflow"); |
| return (value.longValue() * dividend.longValue()) |
| / (divisor.longValue()); |
| } else if (cvtr instanceof AbstractConverter.Compound |
| && cvtr.isLinear()) { // Do it in two parts. |
| AbstractConverter.Compound compound = (AbstractConverter.Compound) cvtr; |
| Number firstConversion = convert(value, compound.getRight(), ctx); |
| Number secondConversion = convert(firstConversion, |
| compound.getLeft(), ctx); |
| return secondConversion; |
| } else { // Try using BigDecimal as intermediate. |
| BigDecimal decimalValue = BigDecimal.valueOf(value.doubleValue()); |
| Number newValue = cvtr.convert(decimalValue.toBigDecimal(), ctx); |
| return newValue; |
| // if (((FieldNumber)value) instanceof Decimal) |
| // return (N)((FieldNumber)Decimal.valueOf(newValue)); |
| // if (((FieldNumber)value) instanceof Float64) |
| // return (N)((FieldNumber)Float64.valueOf(newValue.doubleValue())); |
| // throw new ArithmeticException( |
| // "Generic amount conversion not implemented for amount of type " + |
| // value.getClass()); |
| } |
| } |
| |
| private final BigDecimal bigNumber() { |
| if (getNumber() instanceof BigDecimal) { |
| return (BigDecimal)getNumber(); |
| } else { |
| throw new UOMoRuntimeException( |
| new IllegalArgumentException("Cannot represent as BigDecimal")); |
| } |
| } |
| |
| /** |
| * Generate a 'preference neutral' string from Money value. |
| * |
| * @return java.lang.String |
| */ |
| public String serialize() { |
| return null; |
| } |
| |
| public Number value() { |
| return getNumber(); |
| } |
| |
| public Quantity<? extends Quantity<Quantity>> inverse() { |
| final Quantity<? extends Quantity<Quantity>> m = of(value(), unit() |
| .inverse()); |
| return m; |
| } |
| |
| public int compareTo(MonetaryAmount o) { |
| // TODO Auto-generated method stub |
| return 0; |
| } |
| |
| |
| public MonetaryAmount abs() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| public MonetaryAmount add(Number augend) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount divide(MonetaryAmount divisor) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount divide(Number divisor) { |
| // TODO Auto-generated method stub |
| //return of((BigDecimal)value()).divide((BigDecimal)divisor)); |
| return null; |
| } |
| |
| |
| public MonetaryAmount[] divideAndRemainder(MonetaryAmount divisor) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount[] divideAndRemainder(Number divisor) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount divideToIntegralValue(MonetaryAmount divisor) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount divideToIntegralValue(Number divisor) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount negate() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount plus() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| public MonetaryAmount subtract(Number subtrahend) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount ulp() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount remainder(MonetaryAmount divisor) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount remainder(Number divisor) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount scaleByPowerOfTen(int n) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public boolean isZero() { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| |
| public boolean isPositive() { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| |
| public boolean isPositiveOrZero() { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| |
| public boolean isNegative() { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| |
| public boolean isNegativeOrZero() { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| public MonetaryAmount from(Number amount) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| public MonetaryAmount from(CurrencyUnit currency, Number amount) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| public MonetaryAmount with(MonetaryOperator operator) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#getScale() |
| */ |
| public int getScale() { |
| return this.bigNumber().scale(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#getPrecision() |
| */ |
| public int getPrecision() { |
| return bigNumber().toBigDecimal().precision(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#intValue() |
| */ |
| public int intValue() { |
| return this.getNumber().intValue(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#intValueExact() |
| */ |
| public int intValueExact() { |
| return bigNumber().intValueExact(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#longValue() |
| */ |
| public long longValue() { |
| return this.getNumber().longValue(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#longValueExact() |
| */ |
| public long longValueExact() { |
| return bigNumber().longValueExact(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#floatValue() |
| */ |
| public float floatValue() { |
| return this.getNumber().floatValue(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#doubleValue() |
| */ |
| public double doubleValue() { |
| return this.getNumber().doubleValue(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#byteValue() |
| */ |
| public byte byteValue() { |
| return this.getNumber().byteValue(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#shortValue() |
| */ |
| public short shortValue() { |
| return getNumber().shortValue(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#shortValueExact() |
| */ |
| public short shortValueExact() { |
| return bigNumber().shortValueExact(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#signum() |
| */ |
| |
| public int signum() { |
| return bigNumber().signum(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#toEngineeringString() |
| */ |
| public String toEngineeringString() { |
| return getCurrency().getCurrencyCode() + ' ' |
| + bigNumber().toBigDecimal().toEngineeringString(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see MonetaryAmount#toPlainString() |
| */ |
| public String toPlainString() { |
| return getCurrency().getCurrencyCode() + ' ' |
| + bigNumber().toBigDecimal().toPlainString(); |
| } |
| |
| public boolean isLessThan(MonetaryAmount amount) { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| |
| public boolean isLessThanOrEqualTo(MonetaryAmount amount) { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| |
| public boolean isGreaterThan(MonetaryAmount amount) { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| |
| public boolean isGreaterThanOrEqualTo(MonetaryAmount amount) { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| |
| public boolean isEqualTo(MonetaryAmount amount) { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| |
| public boolean isNotEqualTo(MonetaryAmount amount) { |
| return !getNumber().equals(amount); |
| } |
| |
| public <T> T asType(Class<T> type) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| public Class<?> getNumberType() { |
| return getNumber().getClass(); |
| } |
| |
| public CurrencyUnit getCurrency() { |
| return (CurrencyUnit)unit(); |
| } |
| |
| @Override |
| public Quantity add(Quantity<Quantity> that) { |
| return plus((Quantity) that); |
| } |
| |
| @Override |
| public MonetaryAmount add(MonetaryAmount augend) { |
| return plus((MoneyAmount) augend); |
| } |
| |
| @Override |
| public Quantity<Quantity> subtract(Quantity<Quantity> that) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public MonetaryAmount subtract(MonetaryAmount subtrahend) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| } |