//package org.groovyflow.util;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.List;
import java.util.Locale;
public class Money implements Comparable, java.io.Serializable {
private static final String ZERO_STRING = "0";
private static final Money ZERO_MONEY = new Money(ZERO_STRING);
private BigDecimal delegate;
public static Money getMoneyThatHasZeroValue(){
return ZERO_MONEY;
}
public static Money getInfiniteMoney(){
Long x = Long.MAX_VALUE;
return new Money(x.toString());
}
public Money(String val) {
this.delegate = new BigDecimal(val);
if(delegate.scale() > 2)
throw new IllegalArgumentException("Money can't have scale > 2");
delegate.setScale(2);
}
public Money(BigDecimal value){
this(value.toString());
}
public Money add(Money val){
return new Money(delegate.add(val.delegate));
}
public Money subtract(Money val){
return new Money(delegate.subtract(val.delegate));
}
public BigDecimal multiply(BigDecimal val){
return delegate.multiply(val);
}
/**
* returns Money . If roundCeiling is true we rounded up to
* the nearest cent, otherwise we round down. Note that rounding toward the ceiling
* always rounds to positive infinity (so, for example, -$0.031 becomes
* -$0.03). When roundCeiling is false we round toward the floor, so in that case
* -$0.031 becomes-$0.04. This is exactly as BigDecimal.ROUND_CEILING and
* BigDecimal.ROUND_FLOOR behave.
* @see java.math.BigDecimal.ROUND_CEILING
*/
public Money multiplyAndRound(BigDecimal val, boolean roundCeiling){
BigDecimal product = delegate.multiply(val);
int rounding = roundCeiling ? BigDecimal.ROUND_CEILING : BigDecimal.ROUND_FLOOR;
return new Money(product.setScale(2, rounding));
}
/**
* Sets scale to 2 and returns a Money object.
*/
public Money divideAndReturnMoney(BigDecimal val, int roundingMode){
return new Money(delegate.divide(val, 2, roundingMode));
}
/**
*Round the return value before turning it into a Money object by passing it into the Money constructor.
*/
public BigDecimal divide(BigDecimal val, int scale, int roundingMode) {
return delegate.divide(val, scale, roundingMode);
}
public BigDecimal divide(Money val, int scale, int roundingMode){
return divide(val.delegate, scale, roundingMode);
}
public BigDecimal getBigDecimalValue(){
return new BigDecimal(delegate.toString());
}
// Comparison Operations
public boolean gt(Money val){
return compareTo(val) > 0;
}
public boolean gtEq(Money val){
return compareTo(val) >= 0;
}
public boolean lt(Money val){
return compareTo(val) < 0;
}
public boolean ltEq(Money val){
return compareTo(val) <= 0;
}
public boolean gtZero(){
return gt(ZERO_MONEY);
}
public boolean gtEqZero(){
return gtEq(ZERO_MONEY);
}
public boolean ltZero(){
return lt(ZERO_MONEY);
}
public boolean ltEqZero(){
return ltEq(ZERO_MONEY);
}
public int compareTo(Money val){
return delegate.compareTo(val.delegate);
}
public int compareTo(Object o) {
return compareTo((Money)o);
}
/**
* Will return true if x is a Money object and x's private BigDecimal delegate
* has the same value as our private BigDecimal delegate, regardless of scale.
* A subtle point: BigDecimal's .equal() requires that the scale of the compared
* BigDecimals are the same, while the current class's .equals does not require that.
* In fact, this .equals behaves like BigDecimal's .compareTo().
*/
public boolean equals(Object x){
if(!(x instanceof Money))
return false;
Money brother = (Money) x;
return (delegate.compareTo(brother.delegate) == 0);
}
public boolean equalsZeroMoney(){
return this.equals(ZERO_MONEY);
}
public Money negate(){
return ZERO_MONEY.subtract(this);
}
/**
* Returns -1 if this is less than zero money, 0 if equal to zero money, 1 if greater than zero money.
*/
public int compareToZeroMoney(){
return this.compareTo(ZERO_MONEY);
}
public Money min(Money val){
return new Money((delegate.min(val.delegate)).toString());
}
public Money max(Money val){
return new Money((delegate.max(val.delegate)).toString());
}
public Money abs(){
return new Money( delegate.abs().toString() );
}
public int hashCode() {
return delegate.hashCode();
}
/**
*Prints money with two decimal points.
*/
public String toString(){
if(delegate == null)
return null;
//setting scale to 2 won't really force scale to 2 if we have something like 10 or 10.0, so
//we have to do the following.
int realScale = delegate.scale();
if(realScale == 2)
return delegate.toString();
else if(realScale == 1)
return delegate.toString() + "0";
else if (realScale == 0)
return delegate.toString() + ".00";
else
throw new RuntimeException("Scale of money object is > 2, should never happen, Money object is faulty.");
}
/**
* Front end re-wrote displayAsDollars so that it displays a negative amount without the
* negative sign. If you want something sensible, use displayAsDollarsCorrectly instead.
*/
public String displayAsDollarsCorrectly(){
if ( delegate.signum() < 0 ) {
this.delegate = delegate.negate();
String dis = "-$" + this.toString();
this.delegate = this.delegate.negate();
return dis;
} else {
return "$" + toString();
}
}
public String displayAsDollars(){
NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US);
if ( delegate.signum() < 0 ) {
this.delegate = delegate.negate();
//String dis = "-$" + this.toString();
this.delegate = this.delegate.negate();
//return dis;
return nf.format(this.delegate);
} else {
return nf.format(this.delegate);
//return "$" + toString();
}
}
public int intValue(){
return delegate.intValue();
}
/**
*Null elements in the argument array will not cause things to blow up with a NullPointerException.
*Instead they will be ignored, because we foresee some circumstances in which a caller
*might have a sparsely populated array it wants summed up. Note that call this class's
*add(Money) method one at a time does not, as of this writing, share this behavior. Instead
*it will just blow up.
*/
public static Money add(Money[] moneys){
//Attempt to save on object creation by adding up the BigDecimal
//delegates. So rather than creating a Money and a BigDecimal
//with each element of the sum, we're just creating a BigDecimal.
BigDecimal total = new BigDecimal("0");
for(int i = 0; i < moneys.length; i++){
if(moneys[i] != null){
total = total.add(moneys[i].getBigDecimalValue());
}
}
return new Money(total);
}
public static Money add(List moneys){
Money[] arr = (Money[]) moneys.toArray(new Money[moneys.size()]);
return add(arr);
}
public static Money returnNullAsZero(Money money){
return (money == null) ? getMoneyThatHasZeroValue() : money;
}
}