1 package org.apidesign.apifest08.currency;
4 import java.math.BigDecimal;
5 import java.math.MathContext;
6 import java.math.RoundingMode;
7 import java.util.Collections;
8 import java.util.Currency;
9 import java.util.HashSet;
14 * Convert between two currencies.
16 * @author D'Arcy Smith
19 final class ConvertorImpl
23 * The currency to cvonvert from.
25 private final Currency currencyA;
28 * The currency to cvonvert from.
30 private final Currency currencyB;
33 * The echange rate between a and b.
35 private final BigDecimal currencyARate;
38 * The echange rate between b and a.
40 private final BigDecimal currencyBRate;
43 * Constructs a convertor with the specified currencies.
45 * @param a the currency to convert from.
46 * @param aRate the exchage rage between from and to.
47 * @param b the currency to convert to.
48 * @param bRate the exchage rage between to and from.
49 * @throws IllegalArgumentException if either any of the arguments are null or if either rate <= 0.
51 public ConvertorImpl(final Currency a,
52 final BigDecimal aRate,
54 final BigDecimal bRate)
58 throw new IllegalArgumentException("a cannot be null");
63 throw new IllegalArgumentException("b cannot be null");
68 throw new IllegalArgumentException("aRate cannot be null");
73 throw new IllegalArgumentException("bRate cannot be null");
76 if(aRate.compareTo(BigDecimal.ZERO) <= 0)
78 throw new IllegalArgumentException("aRate must be > 0, was: " + aRate);
81 if(bRate.compareTo(BigDecimal.ZERO) <= 0)
83 throw new IllegalArgumentException("bRate must be > 0, was: " + bRate);
88 currencyARate = aRate;
89 currencyBRate = bRate;
93 * Convert an amount from one currency to another.
95 * @param from the currency to convert from.
96 * @param to the currency to convert to.
97 * @param amount the amount to convert.
98 * @return the converted amount.
99 * @throws IllegalArgumentException if any of the arguments are null.
100 * @throws InvalidConversionException if either from or to are not equal to the currencies passed to the constructor.
102 public BigDecimal convert(final Currency from,
104 final BigDecimal amount)
105 throws InvalidConversionException
107 final BigDecimal result;
111 throw new IllegalArgumentException("amount cannot be null");
116 throw new IllegalArgumentException("from cannot be null");
121 throw new IllegalArgumentException("to cannot be null");
124 if(!(from.equals(currencyA)) && (!(from.equals(currencyB))))
126 throw new InvalidConversionException("cannot convert from: " + from.getCurrencyCode(), from, currencyA, currencyB);
129 if(!(to.equals(currencyA)) && (!(to.equals(currencyB))))
131 throw new InvalidConversionException("cannot convert to: " + to.getCurrencyCode(), to, currencyA, currencyB);
134 result = amount.multiply(getConversionRate(from, to));
136 return (result.setScale(2, RoundingMode.HALF_DOWN));
140 * Check to see if converting between the two currencies is possible.
142 * @param from the currency to convert from.
143 * @param to the currency to convert to.
144 * @return true if the conversion is possible.
146 public boolean canConvert(final Currency from, final Currency to)
148 return ((from.equals(currencyA) || from.equals(currencyB)) &&
149 (to.equals(currencyA) || to.equals(currencyB)));
153 * Get the currencies that the convertor supports.
155 * @return the supported currencies.
157 public Set<Currency> getCurrencies()
159 final Set<Currency> currencies;
161 currencies = new HashSet<Currency>();
162 currencies.add(currencyA);
163 currencies.add(currencyB);
165 return (Collections.unmodifiableSet(currencies));
169 * Get the conversion rate between two currencies.
171 * @param from the currency to convert from.
172 * @param to the currency to convert to.
173 * @return the conversion rate between the two currencies.
174 * @throws InvalidConversionException if canConvert would return false.
176 public BigDecimal getConversionRate(final Currency from,
178 throws InvalidConversionException
180 final BigDecimal rate;
184 rate = BigDecimal.ONE;
188 final BigDecimal rateX;
189 final BigDecimal rateY;
190 final BigDecimal temp;
192 if(from.equals(currencyA))
194 rateX = currencyARate;
195 rateY = currencyBRate;
199 rateX = currencyBRate;
200 rateY = currencyARate;
203 temp = BigDecimal.ONE.divide(rateX, MathContext.DECIMAL64);
204 rate = temp.multiply(rateY);
207 return (rate.setScale(20, RoundingMode.HALF_EVEN));
211 * Check to see if two ConvertorImpls are equal.
213 * @param obj the object to check
214 * @return if the ConvertorImpls are not the same (cuyrrencies and rates).
217 public boolean equals(Object obj)
224 if (getClass() != obj.getClass())
229 final ConvertorImpl other = (ConvertorImpl) obj;
231 // it would be nice if NetBeans could chck to see if the variable is final and guaranteed not to be null... but that
232 // would likely be tricky... but in a NetBeans engineer reads that see if you can do it :-)
233 if (this.currencyA != other.currencyA && (this.currencyA == null || !this.currencyA.equals(other.currencyA)))
238 if (this.currencyB != other.currencyB && (this.currencyB == null || !this.currencyB.equals(other.currencyB)))
243 if (this.currencyARate != other.currencyARate && (this.currencyARate == null || !this.currencyARate.equals(other.currencyARate)))
248 if (this.currencyBRate != other.currencyBRate && (this.currencyBRate == null || !this.currencyBRate.equals(other.currencyBRate)))
257 * Get the hashCode of the Convertor.
259 * @return the hashCode of the convertor.
262 public int hashCode()
265 hash = 37 * hash + (this.currencyA != null ? this.currencyA.hashCode() : 0);
266 hash = 37 * hash + (this.currencyB != null ? this.currencyB.hashCode() : 0);
267 hash = 37 * hash + (this.currencyARate != null ? this.currencyARate.hashCode() : 0);
268 hash = 37 * hash + (this.currencyBRate != null ? this.currencyBRate.hashCode() : 0);
273 * Get the currencyCode of both currencies.
275 * @return the currency codes of both currencies.
278 public String toString()
280 return (currencyA.getCurrencyCode() + " to " + currencyB.getCurrencyCode());