001    package org.nakedobjects.applib.value;
002    
003    import java.math.BigDecimal;
004    
005    import org.nakedobjects.applib.annotation.Value;
006    
007    
008    @Value(semanticsProviderName = "org.nakedobjects.metamodel.value.MoneyValueSemanticsProvider")
009    public class Money extends Magnitude {
010    
011        private static final long serialVersionUID = 1L;
012        private static final int[] cents = new int[] { 1, 10, 100, 100 };
013        private final long amount;
014        private final String currency;
015    
016        public Money(final double amount, final String currency) {
017            assertCurrencySet(currency);
018            this.currency = currency.toUpperCase();
019            this.amount = Math.round(amount * centFactor());
020        }
021    
022        public Money(final long amount, final String currency) {
023            assertCurrencySet(currency);
024            this.currency = currency.toUpperCase();
025            this.amount = amount * centFactor();
026        }
027    
028        private void assertCurrencySet(final String currency) {
029            if (currency == null || currency.equals("")) {
030                throw new IllegalArgumentException("Currency must be specified");
031            }
032            if (currency.length() != 3) {
033                throw new IllegalArgumentException("Invalid currency code '" + currency + "'");
034            }
035        }
036    
037        /**
038         * Add the specified money to this money.
039         */
040        public Money add(final Money money) {
041            assertSameCurrency(money);
042            return newMoney(amount + money.amount);
043        }
044    
045        private void assertSameCurrency(final Money money) {
046            if (!money.getCurrency().equals(getCurrency())) {
047                throw new IllegalArgumentException("Not the same currency: " + getCurrency() + " & " + money.getCurrency());
048            }
049        }
050    
051        private int centFactor() {
052            return cents[getFractionalDigits()];
053        }
054    
055        /**
056         * Returns this value as a double.
057         */
058        public double doubleValue() {
059            return amount / (double) centFactor();
060        }
061    
062        /**
063         * Returns this value as a float.
064         */
065        public float floatValue() {
066            return amount;
067        }
068    
069        public BigDecimal getAmount() {
070            return BigDecimal.valueOf(amount, getFractionalDigits());
071        }
072    
073        public String getCurrency() {
074            return currency;
075        }
076    
077        private int getFractionalDigits() {
078            return 2;
079        }
080    
081        public boolean hasSameCurrency(final Money money) {
082            return currency.equals(money.currency);
083        }
084    
085        /**
086         * Returns this value as an int.
087         */
088        public int intValue() {
089            return (int) amount;
090        }
091    
092        @Override
093        public boolean isEqualTo(final Magnitude magnitude) {
094            if (magnitude instanceof Money && hasSameCurrency((Money) magnitude)) {
095                return ((Money) magnitude).amount == amount;
096            } else {
097                throw new IllegalArgumentException("Parameter must be of type Money and have the same currency");
098            }
099        }
100    
101        public boolean isGreaterThanZero() {
102            return amount > 0;
103        }
104    
105        @Override
106        public boolean isLessThan(final Magnitude magnitude) {
107            if (magnitude instanceof Money && hasSameCurrency((Money) magnitude)) {
108                return amount < ((Money) magnitude).amount;
109            } else {
110                throw new IllegalArgumentException("Parameter must be of type Money and have the same currency");
111            }
112        }
113    
114        /**
115         * Returns true if this value is less than zero.
116         */
117        public boolean isLessThanZero() {
118            return amount < 0;
119        }
120    
121        public boolean isZero() {
122            return amount == 0;
123        }
124    
125        /**
126         * Returns this value as an long.
127         */
128        public long longValue() {
129            return amount;
130        }
131    
132        private Money newMoney(final long amount) {
133            return new Money(amount / (centFactor() * 1.0), this.currency);
134        }
135    
136        /**
137         * Subtract the specified amount from this value.
138         */
139        public Money subtract(final Money money) {
140            assertSameCurrency(money);
141            return newMoney(amount - money.amount);
142        }
143    
144        @Override
145        public boolean equals(final Object other) {
146            if (this == other) {
147                return true;
148            }
149            if (other == null) {
150                return false;
151            }
152            return other.getClass() == this.getClass() && equals((Money) other);
153        }
154    
155        public boolean equals(final Money other) {
156            return other.currency.equals(currency) && other.amount == amount;
157        }
158    
159        @Override
160        public int hashCode() {
161            return (int) amount;
162        }
163    
164        @Override
165        public String toString() {
166            return amount / (centFactor() * 1.0) + " " + currency;
167        }
168    }
169    // Copyright (c) Naked Objects Group Ltd.