japod@23
|
1 |
package org.apidesign.apifest08.currency;
|
japod@23
|
2 |
|
japod@23
|
3 |
import java.math.BigDecimal;
|
japod@23
|
4 |
import java.math.MathContext;
|
japod@23
|
5 |
import java.math.RoundingMode;
|
japod@23
|
6 |
|
japod@23
|
7 |
/** Convertor able to convert amount from one currency to other currency.
|
japod@23
|
8 |
*
|
japod@23
|
9 |
* Exchange rate is provided by {@link ExchangeRateProvider}.
|
japod@23
|
10 |
*/
|
japod@23
|
11 |
public class Convertor {
|
japod@23
|
12 |
boolean remainderAllowed = false; //if false, remained is not allowed (should be true ideally, but can't handle it now)
|
japod@23
|
13 |
ExchangeRateProvider exchangeRateProvider;
|
japod@23
|
14 |
|
japod@23
|
15 |
private Convertor() {
|
japod@23
|
16 |
|
japod@23
|
17 |
}
|
japod@23
|
18 |
|
japod@23
|
19 |
/**
|
japod@23
|
20 |
* Static method used to create convertor.
|
japod@23
|
21 |
*
|
japod@23
|
22 |
* @param exchangeRateProvider {@link ExchangeRateProvider} used to get exchange rate.
|
japod@23
|
23 |
*
|
japod@23
|
24 |
* @return Returns <code>Convertor</code> which can be used to convert money.
|
japod@23
|
25 |
*/
|
japod@23
|
26 |
public static Convertor createConvertor(ExchangeRateProvider exchangeRateProvider) {
|
japod@23
|
27 |
Convertor c = new Convertor();
|
japod@23
|
28 |
|
japod@23
|
29 |
c.exchangeRateProvider = exchangeRateProvider;
|
japod@23
|
30 |
return c;
|
japod@23
|
31 |
}
|
japod@23
|
32 |
|
japod@23
|
33 |
/**
|
japod@23
|
34 |
* Convert <code>amount</code> from <code>fromCurrency</code> to <code>toCurrency</code> as specified
|
japod@23
|
35 |
* in <code>ExchangeRateProvider</code>.
|
japod@23
|
36 |
*
|
japod@23
|
37 |
* @param amount Amount which should be converted. Can't be negative value (can be zero or positive).
|
japod@23
|
38 |
* @return Return <code>ConversionResult</code> which holds conversion result.
|
japod@23
|
39 |
*/
|
japod@23
|
40 |
public ConversionResult convert(BigDecimal amount) {
|
japod@23
|
41 |
return convertValue(amount, false);
|
japod@23
|
42 |
}
|
japod@23
|
43 |
|
japod@23
|
44 |
/**
|
japod@23
|
45 |
* Convert <code>amount</code> from <code>toCurrency</code> to <code>fromCurrency</code> as specified
|
japod@23
|
46 |
* in <code>ExchangeRateProvider</code>. This is <em>reverted</em> order than suggested by names of currency fields in <code>ExchangeRate</code>.
|
japod@23
|
47 |
*
|
japod@23
|
48 |
* @param amount Amount which should be converted. Can't be negative value (can be zero or positive).
|
japod@23
|
49 |
* @return Return <code>ConversionResult</code> which holds conversion result.
|
japod@23
|
50 |
*/
|
japod@23
|
51 |
public ConversionResult convertBack(BigDecimal amount) {
|
japod@23
|
52 |
return convertValue(amount, true);
|
japod@23
|
53 |
}
|
japod@23
|
54 |
|
japod@23
|
55 |
private ConversionResult convertValue(BigDecimal amount, boolean convertBack) throws RuntimeException {
|
japod@23
|
56 |
ConversionResult result = new ConversionResult();
|
japod@23
|
57 |
|
japod@23
|
58 |
ExchangeRate rate = exchangeRateProvider.getExchangeRate();
|
japod@23
|
59 |
int fromFranctionDigits = exchangeRateProvider.getFromCurrency().getDefaultFractionDigits();
|
japod@23
|
60 |
int toFractionDigits = exchangeRateProvider.getToCurrency().getDefaultFractionDigits();
|
japod@23
|
61 |
|
japod@23
|
62 |
if (toFractionDigits!=2) {
|
japod@23
|
63 |
throw new RuntimeException("Can't process currency with defaultFractionDigits!=2, "+exchangeRateProvider.getToCurrency()+" has "+toFractionDigits+" defaultFractionDigits");
|
japod@23
|
64 |
}
|
japod@23
|
65 |
if (fromFranctionDigits!=2) {
|
japod@23
|
66 |
throw new RuntimeException("Can't process currency with defaultFractionDigits!=2, "+exchangeRateProvider.getFromCurrency()+" has "+fromFranctionDigits+" defaultFractionDigits");
|
japod@23
|
67 |
}
|
japod@23
|
68 |
|
japod@23
|
69 |
if (amount.signum()==-1) {
|
japod@23
|
70 |
throw new RuntimeException("Can convert only non-negative value, current value is "+amount);
|
japod@23
|
71 |
}
|
japod@23
|
72 |
|
japod@23
|
73 |
|
japod@23
|
74 |
MathContext context = new MathContext(0, RoundingMode.DOWN);
|
japod@23
|
75 |
|
japod@23
|
76 |
BigDecimal from;
|
japod@23
|
77 |
BigDecimal to;
|
japod@23
|
78 |
if (convertBack) {
|
japod@23
|
79 |
//converting in reverted way
|
japod@23
|
80 |
to = rate.getFromValue();
|
japod@23
|
81 |
from = rate.getToValue();
|
japod@23
|
82 |
} else {
|
japod@23
|
83 |
//converting in mornak way
|
japod@23
|
84 |
from = rate.getFromValue();
|
japod@23
|
85 |
to = rate.getToValue();
|
japod@23
|
86 |
}
|
japod@23
|
87 |
|
japod@23
|
88 |
BigDecimal amountCent = amount.movePointRight(2);
|
japod@23
|
89 |
|
japod@23
|
90 |
final BigDecimal multiplied = amountCent.multiply(to,context);
|
japod@23
|
91 |
BigDecimal[] division = multiplied.divideAndRemainder(from,context);
|
japod@23
|
92 |
|
japod@23
|
93 |
if (!remainderAllowed && !(BigDecimal.ZERO.equals(division[1]))) {
|
japod@23
|
94 |
throw new RuntimeException("Remained is not allowed - remaining amount is " + division[1]+ " cents");
|
japod@23
|
95 |
} else {
|
japod@23
|
96 |
result.setRemainder(BigDecimal.ZERO);
|
japod@23
|
97 |
}
|
japod@23
|
98 |
|
japod@23
|
99 |
final BigDecimal converted = division[0].movePointLeft(2);
|
japod@23
|
100 |
result.setConverted(converted);
|
japod@23
|
101 |
//result.setRemainder(...);
|
japod@23
|
102 |
|
japod@23
|
103 |
return result;
|
japod@23
|
104 |
}
|
japod@23
|
105 |
}
|