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