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 convert from.
25 private final Currency currencyA;
28 * The currency to convert to.
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.
145 * @throws IllegalArgumentException if either from or to are null.
147 public boolean canConvert(final Currency from, final Currency to)
151 throw new IllegalArgumentException("from cannot be null");
156 throw new IllegalArgumentException("to cannot be null");
159 return ((from.equals(currencyA) || from.equals(currencyB)) &&
160 (to.equals(currencyA) || to.equals(currencyB)));
164 * Get the currencies that the convertor supports.
166 * @return the supported currencies.
168 public Set<Currency> getCurrencies()
170 final Set<Currency> currencies;
172 currencies = new HashSet<Currency>();
173 currencies.add(currencyA);
174 currencies.add(currencyB);
176 return (Collections.unmodifiableSet(currencies));
180 * Get the conversion rate between two currencies.
182 * @param from the currency to convert from.
183 * @param to the currency to convert to.
184 * @return the conversion rate between the two currencies.
185 * @throws InvalidConversionException if canConvert would return false.
186 * @throws IllegalArgumentException if either from or to are null.
188 public BigDecimal getConversionRate(final Currency from,
190 throws InvalidConversionException
192 final BigDecimal rate;
196 throw new IllegalArgumentException("from cannot be null");
201 throw new IllegalArgumentException("to cannot be null");
206 rate = BigDecimal.ONE;
210 final BigDecimal rateX;
211 final BigDecimal rateY;
212 final BigDecimal temp;
214 if(from.equals(currencyA))
216 rateX = currencyARate;
217 rateY = currencyBRate;
221 rateX = currencyBRate;
222 rateY = currencyARate;
225 temp = BigDecimal.ONE.divide(rateX, MathContext.DECIMAL64);
226 rate = temp.multiply(rateY);
229 return (rate.setScale(20, RoundingMode.HALF_EVEN));
233 * Check to see if two ConvertorImpls are equal.
235 * @param obj the object to check
236 * @return if the ConvertorImpls are not the same (cuyrrencies and rates).
239 public boolean equals(Object obj)
246 if (getClass() != obj.getClass())
251 final ConvertorImpl other = (ConvertorImpl) obj;
253 // it would be nice if NetBeans could chck to see if the variable is final and guaranteed not to be null... but that
254 // would likely be tricky... but in a NetBeans engineer reads that see if you can do it :-)
255 if (this.currencyA != other.currencyA && (this.currencyA == null || !this.currencyA.equals(other.currencyA)))
260 if (this.currencyB != other.currencyB && (this.currencyB == null || !this.currencyB.equals(other.currencyB)))
265 if (this.currencyARate != other.currencyARate && (this.currencyARate == null || !this.currencyARate.equals(other.currencyARate)))
270 if (this.currencyBRate != other.currencyBRate && (this.currencyBRate == null || !this.currencyBRate.equals(other.currencyBRate)))
279 * Get the hashCode of the Convertor.
281 * @return the hashCode of the convertor.
284 public int hashCode()
287 hash = 37 * hash + (this.currencyA != null ? this.currencyA.hashCode() : 0);
288 hash = 37 * hash + (this.currencyB != null ? this.currencyB.hashCode() : 0);
289 hash = 37 * hash + (this.currencyARate != null ? this.currencyARate.hashCode() : 0);
290 hash = 37 * hash + (this.currencyBRate != null ? this.currencyBRate.hashCode() : 0);
295 * Get the currencyCode of both currencies.
297 * @return the currency codes of both currencies.
300 public String toString()
302 return (currencyA.getCurrencyCode() + " to " + currencyB.getCurrencyCode());