japod@55
|
1 |
package org.apidesign.apifest08.currency;
|
japod@55
|
2 |
|
japod@55
|
3 |
|
japod@55
|
4 |
import java.math.BigDecimal;
|
jaroslav@69
|
5 |
import java.math.RoundingMode;
|
jaroslav@69
|
6 |
import java.util.Collections;
|
japod@55
|
7 |
import java.util.Currency;
|
jaroslav@69
|
8 |
import java.util.HashSet;
|
jaroslav@69
|
9 |
import java.util.Set;
|
japod@55
|
10 |
|
japod@55
|
11 |
|
japod@55
|
12 |
/**
|
japod@55
|
13 |
* The exchange rate between two currencies.
|
japod@55
|
14 |
*
|
japod@55
|
15 |
* @author D'Arcy Smith
|
jaroslav@69
|
16 |
* @version 1.1
|
japod@55
|
17 |
*/
|
japod@55
|
18 |
public final class ExchangeRate
|
japod@55
|
19 |
{
|
japod@55
|
20 |
/**
|
japod@55
|
21 |
*
|
japod@55
|
22 |
*/
|
japod@55
|
23 |
private final Currency currencyA;
|
japod@55
|
24 |
|
japod@55
|
25 |
/**
|
japod@55
|
26 |
*
|
japod@55
|
27 |
*/
|
japod@55
|
28 |
private final Currency currencyB;
|
japod@55
|
29 |
|
japod@55
|
30 |
/**
|
japod@55
|
31 |
*
|
japod@55
|
32 |
*/
|
japod@55
|
33 |
private final BigDecimal rateAtoB;
|
japod@55
|
34 |
|
japod@55
|
35 |
/**
|
japod@55
|
36 |
*
|
japod@55
|
37 |
*/
|
japod@55
|
38 |
private final BigDecimal rateBtoA;
|
japod@55
|
39 |
|
japod@55
|
40 |
/**
|
japod@55
|
41 |
* Construct an ExchangeRate with the specified values.
|
japod@55
|
42 |
*
|
japod@55
|
43 |
* @param a the first currency
|
japod@55
|
44 |
* @param b the second currency
|
japod@55
|
45 |
* @param ra the rate to convert a to b
|
japod@55
|
46 |
* @param rb the rate to covertt b to a
|
japod@55
|
47 |
* @throws IllegalArgumentException if any parameter is null.
|
japod@55
|
48 |
*/
|
japod@55
|
49 |
public ExchangeRate(final Currency a,
|
japod@55
|
50 |
final Currency b,
|
japod@55
|
51 |
final BigDecimal ra,
|
japod@55
|
52 |
final BigDecimal rb)
|
japod@55
|
53 |
{
|
japod@55
|
54 |
if(a == null)
|
japod@55
|
55 |
{
|
japod@55
|
56 |
throw new IllegalArgumentException("a cannot be null");
|
japod@55
|
57 |
}
|
japod@55
|
58 |
|
japod@55
|
59 |
if(b == null)
|
japod@55
|
60 |
{
|
japod@55
|
61 |
throw new IllegalArgumentException("b cannot be null");
|
japod@55
|
62 |
}
|
japod@55
|
63 |
|
japod@55
|
64 |
if(ra == null)
|
japod@55
|
65 |
{
|
japod@55
|
66 |
throw new IllegalArgumentException("ra cannot be null");
|
japod@55
|
67 |
}
|
japod@55
|
68 |
|
japod@55
|
69 |
if(rb == null)
|
japod@55
|
70 |
{
|
japod@55
|
71 |
throw new IllegalArgumentException("rb cannot be null");
|
japod@55
|
72 |
}
|
japod@55
|
73 |
|
japod@55
|
74 |
if(ra.compareTo(BigDecimal.ZERO) <= 0)
|
japod@55
|
75 |
{
|
japod@55
|
76 |
throw new IllegalArgumentException("ra cannot be <= 0, was: " + ra);
|
japod@55
|
77 |
}
|
japod@55
|
78 |
|
japod@55
|
79 |
if(rb.compareTo(BigDecimal.ZERO) <= 0)
|
japod@55
|
80 |
{
|
japod@55
|
81 |
throw new IllegalArgumentException("rb cannot be <= 0, was: " + ra);
|
japod@55
|
82 |
}
|
japod@55
|
83 |
|
japod@55
|
84 |
currencyA = a;
|
japod@55
|
85 |
currencyB = b;
|
japod@55
|
86 |
rateAtoB = ra;
|
japod@55
|
87 |
rateBtoA = rb;
|
japod@55
|
88 |
}
|
japod@55
|
89 |
|
japod@55
|
90 |
/**
|
japod@55
|
91 |
* Get the first currency.
|
japod@55
|
92 |
*
|
japod@55
|
93 |
* @return the first currency.
|
japod@55
|
94 |
*/
|
japod@55
|
95 |
public Currency getCurrencyA()
|
japod@55
|
96 |
{
|
japod@55
|
97 |
return currencyA;
|
japod@55
|
98 |
}
|
japod@55
|
99 |
|
japod@55
|
100 |
/**
|
japod@55
|
101 |
* Get the second currency.
|
japod@55
|
102 |
*
|
japod@55
|
103 |
* @return the second currency.
|
japod@55
|
104 |
*/
|
japod@55
|
105 |
public Currency getCurrencyB()
|
japod@55
|
106 |
{
|
japod@55
|
107 |
return currencyB;
|
japod@55
|
108 |
}
|
japod@55
|
109 |
|
japod@55
|
110 |
/**
|
japod@55
|
111 |
* Get the conversion rate from currencyA to currencyB.
|
japod@55
|
112 |
*
|
japod@55
|
113 |
* @return the conversion rate from currencyA to currencyB.
|
japod@55
|
114 |
*/
|
japod@55
|
115 |
public BigDecimal getRateAtoB()
|
japod@55
|
116 |
{
|
japod@55
|
117 |
return rateAtoB;
|
japod@55
|
118 |
}
|
japod@55
|
119 |
|
japod@55
|
120 |
/**
|
japod@55
|
121 |
* Get the conversion rate from currencyB to currencyA.
|
japod@55
|
122 |
*
|
japod@55
|
123 |
* @return the conversion rate from currencyB to currencyA.
|
japod@55
|
124 |
*/
|
japod@55
|
125 |
public BigDecimal getRateBtoA()
|
japod@55
|
126 |
{
|
japod@55
|
127 |
return rateBtoA;
|
japod@55
|
128 |
}
|
jaroslav@69
|
129 |
|
jaroslav@69
|
130 |
public static ExchangeRate getExchangeRate(final Currency a,
|
jaroslav@69
|
131 |
final Currency b,
|
jaroslav@69
|
132 |
final BigDecimal va,
|
jaroslav@69
|
133 |
final BigDecimal vb)
|
jaroslav@69
|
134 |
{
|
jaroslav@69
|
135 |
final BigDecimal rateAtoB;
|
jaroslav@69
|
136 |
final BigDecimal rateBtoA;
|
jaroslav@69
|
137 |
final ExchangeRate rate;
|
jaroslav@69
|
138 |
|
jaroslav@69
|
139 |
if(a == null)
|
jaroslav@69
|
140 |
{
|
jaroslav@69
|
141 |
throw new IllegalArgumentException("a cannot be null");
|
jaroslav@69
|
142 |
}
|
jaroslav@69
|
143 |
|
jaroslav@69
|
144 |
if(b == null)
|
jaroslav@69
|
145 |
{
|
jaroslav@69
|
146 |
throw new IllegalArgumentException("b cannot be null");
|
jaroslav@69
|
147 |
}
|
jaroslav@69
|
148 |
|
jaroslav@69
|
149 |
if(a.equals(b))
|
jaroslav@69
|
150 |
{
|
jaroslav@69
|
151 |
rateAtoB = BigDecimal.ONE;
|
jaroslav@69
|
152 |
rateBtoA = BigDecimal.ONE;
|
jaroslav@69
|
153 |
}
|
jaroslav@69
|
154 |
else
|
jaroslav@69
|
155 |
{
|
jaroslav@69
|
156 |
rateAtoB = vb.divide(va, 20, RoundingMode.HALF_DOWN);
|
jaroslav@69
|
157 |
rateBtoA = va.divide(vb, 20, RoundingMode.HALF_DOWN);
|
jaroslav@69
|
158 |
}
|
jaroslav@69
|
159 |
|
jaroslav@69
|
160 |
rate = new ExchangeRate(a,
|
jaroslav@69
|
161 |
b,
|
jaroslav@69
|
162 |
rateAtoB.setScale(20, RoundingMode.HALF_EVEN),
|
jaroslav@69
|
163 |
rateBtoA.setScale(20, RoundingMode.HALF_EVEN));
|
jaroslav@69
|
164 |
|
jaroslav@69
|
165 |
return (rate);
|
jaroslav@69
|
166 |
}
|
jaroslav@69
|
167 |
|
jaroslav@69
|
168 |
public BigDecimal convert(final Currency from,
|
jaroslav@69
|
169 |
final Currency to,
|
jaroslav@69
|
170 |
final BigDecimal amount)
|
jaroslav@69
|
171 |
throws InvalidConversionException
|
jaroslav@69
|
172 |
{
|
jaroslav@69
|
173 |
final BigDecimal result;
|
jaroslav@69
|
174 |
|
jaroslav@69
|
175 |
if(amount == null)
|
jaroslav@69
|
176 |
{
|
jaroslav@69
|
177 |
throw new IllegalArgumentException("amount cannot be null");
|
jaroslav@69
|
178 |
}
|
jaroslav@69
|
179 |
|
jaroslav@69
|
180 |
if(from == null)
|
jaroslav@69
|
181 |
{
|
jaroslav@69
|
182 |
throw new IllegalArgumentException("from cannot be null");
|
jaroslav@69
|
183 |
}
|
jaroslav@69
|
184 |
|
jaroslav@69
|
185 |
if(to == null)
|
jaroslav@69
|
186 |
{
|
jaroslav@69
|
187 |
throw new IllegalArgumentException("to cannot be null");
|
jaroslav@69
|
188 |
}
|
jaroslav@69
|
189 |
|
jaroslav@69
|
190 |
if(!(from.equals(currencyA)) && (!(from.equals(currencyB))))
|
jaroslav@69
|
191 |
{
|
jaroslav@69
|
192 |
throw new InvalidConversionException("cannot convert from: " + from.getCurrencyCode(), from, currencyA, currencyB);
|
jaroslav@69
|
193 |
}
|
jaroslav@69
|
194 |
|
jaroslav@69
|
195 |
if(!(to.equals(currencyA)) && (!(to.equals(currencyB))))
|
jaroslav@69
|
196 |
{
|
jaroslav@69
|
197 |
throw new InvalidConversionException("cannot convert to: " + to.getCurrencyCode(), to, currencyA, currencyB);
|
jaroslav@69
|
198 |
}
|
jaroslav@69
|
199 |
|
jaroslav@69
|
200 |
result = amount.multiply(getConversionRate(from, to));
|
jaroslav@69
|
201 |
|
jaroslav@69
|
202 |
return (result.setScale(2, RoundingMode.HALF_DOWN));
|
jaroslav@69
|
203 |
}
|
jaroslav@69
|
204 |
|
jaroslav@69
|
205 |
/**
|
jaroslav@69
|
206 |
* Check to see if converting between the two currencies is possible.
|
jaroslav@69
|
207 |
*
|
jaroslav@69
|
208 |
* @param from the currency to convert from.
|
jaroslav@69
|
209 |
* @param to the currency to convert to.
|
jaroslav@69
|
210 |
* @return true if the conversion is possible.
|
jaroslav@69
|
211 |
* @throws IllegalArgumentException if either from or to are null.
|
jaroslav@69
|
212 |
*/
|
jaroslav@69
|
213 |
public boolean canConvert(final Currency from, final Currency to)
|
jaroslav@69
|
214 |
{
|
jaroslav@69
|
215 |
if(from == null)
|
jaroslav@69
|
216 |
{
|
jaroslav@69
|
217 |
throw new IllegalArgumentException("from cannot be null");
|
jaroslav@69
|
218 |
}
|
jaroslav@69
|
219 |
|
jaroslav@69
|
220 |
if(to == null)
|
jaroslav@69
|
221 |
{
|
jaroslav@69
|
222 |
throw new IllegalArgumentException("to cannot be null");
|
jaroslav@69
|
223 |
}
|
jaroslav@69
|
224 |
|
jaroslav@69
|
225 |
return ((from.equals(currencyA) || from.equals(currencyB)) &&
|
jaroslav@69
|
226 |
(to.equals(currencyA) || to.equals(currencyB)));
|
jaroslav@69
|
227 |
}
|
jaroslav@69
|
228 |
/**
|
jaroslav@69
|
229 |
* Get the currencies that the convertor supports.
|
jaroslav@69
|
230 |
*
|
jaroslav@69
|
231 |
* @return the supported currencies.
|
jaroslav@69
|
232 |
*/
|
jaroslav@69
|
233 |
public Set<Currency> getCurrencies()
|
jaroslav@69
|
234 |
{
|
jaroslav@69
|
235 |
final Set<Currency> currencies;
|
jaroslav@69
|
236 |
|
jaroslav@69
|
237 |
currencies = new HashSet<Currency>();
|
jaroslav@69
|
238 |
currencies.add(currencyA);
|
jaroslav@69
|
239 |
currencies.add(currencyB);
|
jaroslav@69
|
240 |
|
jaroslav@69
|
241 |
return (Collections.unmodifiableSet(currencies));
|
jaroslav@69
|
242 |
}
|
jaroslav@69
|
243 |
|
jaroslav@69
|
244 |
/**
|
jaroslav@69
|
245 |
* Get the conversion rate between two currencies.
|
jaroslav@69
|
246 |
*
|
jaroslav@69
|
247 |
* @param from the currency to convert from.
|
jaroslav@69
|
248 |
* @param to the currency to convert to.
|
jaroslav@69
|
249 |
* @return the conversion rate between the two currencies.
|
jaroslav@69
|
250 |
* @throws InvalidConversionException if canConvert would return false.
|
jaroslav@69
|
251 |
* @throws IllegalArgumentException if either from or to are null.
|
jaroslav@69
|
252 |
*/
|
jaroslav@69
|
253 |
public BigDecimal getConversionRate(final Currency from,
|
jaroslav@69
|
254 |
final Currency to)
|
jaroslav@69
|
255 |
throws InvalidConversionException
|
jaroslav@69
|
256 |
{
|
jaroslav@69
|
257 |
final BigDecimal rate;
|
jaroslav@69
|
258 |
|
jaroslav@69
|
259 |
if(from == null)
|
jaroslav@69
|
260 |
{
|
jaroslav@69
|
261 |
throw new IllegalArgumentException("from cannot be null");
|
jaroslav@69
|
262 |
}
|
jaroslav@69
|
263 |
|
jaroslav@69
|
264 |
if(to == null)
|
jaroslav@69
|
265 |
{
|
jaroslav@69
|
266 |
throw new IllegalArgumentException("to cannot be null");
|
jaroslav@69
|
267 |
}
|
jaroslav@69
|
268 |
|
jaroslav@69
|
269 |
if(from.equals(to))
|
jaroslav@69
|
270 |
{
|
jaroslav@69
|
271 |
rate = BigDecimal.ONE;
|
jaroslav@69
|
272 |
}
|
jaroslav@69
|
273 |
else
|
jaroslav@69
|
274 |
{
|
jaroslav@69
|
275 |
if(from.equals(currencyA))
|
jaroslav@69
|
276 |
{
|
jaroslav@69
|
277 |
rate = rateAtoB;
|
jaroslav@69
|
278 |
}
|
jaroslav@69
|
279 |
else
|
jaroslav@69
|
280 |
{
|
jaroslav@69
|
281 |
rate = rateBtoA;
|
jaroslav@69
|
282 |
}
|
jaroslav@69
|
283 |
}
|
jaroslav@69
|
284 |
|
jaroslav@69
|
285 |
return (rate);
|
jaroslav@69
|
286 |
}
|
jaroslav@69
|
287 |
|
jaroslav@69
|
288 |
public String toString()
|
jaroslav@69
|
289 |
{
|
jaroslav@69
|
290 |
return (rateAtoB + " : " + rateBtoA);
|
jaroslav@69
|
291 |
}
|
jaroslav@69
|
292 |
|
jaroslav@69
|
293 |
@Override
|
jaroslav@69
|
294 |
public boolean equals(Object obj) {
|
jaroslav@69
|
295 |
if (obj == null) {
|
jaroslav@69
|
296 |
return false;
|
jaroslav@69
|
297 |
}
|
jaroslav@69
|
298 |
if (getClass() != obj.getClass()) {
|
jaroslav@69
|
299 |
return false;
|
jaroslav@69
|
300 |
}
|
jaroslav@69
|
301 |
final ExchangeRate other = (ExchangeRate) obj;
|
jaroslav@69
|
302 |
if (this.currencyA != other.currencyA && (this.currencyA == null || !this.currencyA.equals(other.currencyA))) {
|
jaroslav@69
|
303 |
return false;
|
jaroslav@69
|
304 |
}
|
jaroslav@69
|
305 |
if (this.currencyB != other.currencyB && (this.currencyB == null || !this.currencyB.equals(other.currencyB))) {
|
jaroslav@69
|
306 |
return false;
|
jaroslav@69
|
307 |
}
|
jaroslav@69
|
308 |
if (this.rateAtoB != other.rateAtoB && (this.rateAtoB == null || !this.rateAtoB.equals(other.rateAtoB))) {
|
jaroslav@69
|
309 |
return false;
|
jaroslav@69
|
310 |
}
|
jaroslav@69
|
311 |
if (this.rateBtoA != other.rateBtoA && (this.rateBtoA == null || !this.rateBtoA.equals(other.rateBtoA))) {
|
jaroslav@69
|
312 |
return false;
|
jaroslav@69
|
313 |
}
|
jaroslav@69
|
314 |
return true;
|
jaroslav@69
|
315 |
}
|
jaroslav@69
|
316 |
|
jaroslav@69
|
317 |
@Override
|
jaroslav@69
|
318 |
public int hashCode() {
|
jaroslav@69
|
319 |
int hash = 7;
|
jaroslav@69
|
320 |
hash = 97 * hash + (this.currencyA != null ? this.currencyA.hashCode() : 0);
|
jaroslav@69
|
321 |
hash = 97 * hash + (this.currencyB != null ? this.currencyB.hashCode() : 0);
|
jaroslav@69
|
322 |
hash = 97 * hash + (this.rateAtoB != null ? this.rateAtoB.hashCode() : 0);
|
jaroslav@69
|
323 |
hash = 97 * hash + (this.rateBtoA != null ? this.rateBtoA.hashCode() : 0);
|
jaroslav@69
|
324 |
return hash;
|
jaroslav@69
|
325 |
}
|
japod@55
|
326 |
}
|