blob: 2c9465623b67ffe857ceed96c7ade18a2946576d [file] [log] [blame]
/**
* 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;
}
}