Java tutorial
//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; } }