1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/task3/solution13/src/org/apidesign/apifest08/currency/Convertor.java Tue Oct 07 11:05:34 2008 +0200
1.3 @@ -0,0 +1,213 @@
1.4 +package org.apidesign.apifest08.currency;
1.5 +
1.6 +import java.math.BigDecimal;
1.7 +import java.math.MathContext;
1.8 +import java.math.RoundingMode;
1.9 +
1.10 +/** Convertor able to convert amount from one currency to other currency.
1.11 + * <p>
1.12 + * Conversion method are:
1.13 + * <ul>
1.14 + * <li>{@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal)} - convert using exchange rate specified in exchange rate provide. <em>Do not try use reverted excahnage rate</em>.
1.15 + * <li>{@link #convertWithReversibleRates(ConvertorCurrency, ConvertorCurrency, BigDecimal)} - convert using exchange rate specified in exchange rate provide. <em>Try use reverted excahnage rate</em>.
1.16 + * </ul>
1.17 + *
1.18 + * Exchange rate is provided by {@link ExchangeRateProvider}.
1.19 + */
1.20 +public class Convertor {
1.21 + private Convertor[] convertors;
1.22 +
1.23 + /** Create new <code>Convertor</code> as merge of provided convertors. Merged convertor will use
1.24 + * provided convertors to convert between currencies.
1.25 + * <p>
1.26 + * Only one should be able to provide conversion between currencies. If more than one convertos
1.27 + * are able to convert currency, one of conversions will be used (it is not defined which).
1.28 + *
1.29 + * @since version 2
1.30 + * @param convertors Convertor used to create merge-convertor.
1.31 + * @return Returns new convertor instance.
1.32 + */
1.33 + public static Convertor createConvertorAsMerge(Convertor[] convertors) {
1.34 + return new Convertor(convertors);
1.35 + }
1.36 +
1.37 + boolean remainderAllowed = true; //if false, remained is not allowed (should be true ideally, but can't handle it now)
1.38 + ExchangeRateProvider exchangeRateProvider;
1.39 +
1.40 + /** Create simle convertor.
1.41 + */
1.42 + private Convertor() {
1.43 + this.convertors=new Convertor[0];
1.44 + }
1.45 +
1.46 + /** Create merge convertor.
1.47 + */
1.48 + private Convertor(Convertor[] convertors) {
1.49 + this.convertors = convertors;
1.50 + }
1.51 +
1.52 + /**
1.53 + * Create new <code>Convertor</code> using <code>ExchangeRateProvider</code>.
1.54 + *
1.55 + * @param exchangeRateProvider {@link ExchangeRateProvider} used to get exchange rate.
1.56 + *
1.57 + * @return Returns <code>Convertor</code> which can be used to convert money.
1.58 + * @since version1
1.59 + */
1.60 + public static Convertor createConvertor(ExchangeRateProvider exchangeRateProvider) {
1.61 + Convertor c = new Convertor();
1.62 +
1.63 + c.exchangeRateProvider = exchangeRateProvider;
1.64 + return c;
1.65 + }
1.66 +
1.67 + /**
1.68 + * Convert <code>amount</code> from <code>fromCurrency</code> to <code>toCurrency</code> as specified
1.69 + * in <code>ExchangeRateProvider</code>.
1.70 + *
1.71 + * @param amount Amount which should be converted. Can't be negative value (can be zero or positive).
1.72 + * @return Return <code>ConversionResult</code> which holds conversion result.
1.73 + * @since version1
1.74 + * @deprecated since version2. Use {@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal) } - explicitly specify conversion currencies.
1.75 + */
1.76 + public ConversionResult convert(BigDecimal amount) {
1.77 + return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, false,false);
1.78 + }
1.79 +
1.80 + /**
1.81 + * Convert <code>amount</code> from <code>toCurrency</code> to <code>fromCurrency</code> as specified
1.82 + * in <code>ExchangeRateProvider</code>. This is <em>reverted</em> order than suggested by names of currency fields in <code>ExchangeRate</code>.
1.83 + *
1.84 + * @param amount Amount which should be converted. Can't be negative value (can be zero or positive).
1.85 + * @return Return <code>ConversionResult</code> which holds conversion result.
1.86 + * @since version1
1.87 + * @deprecated since version2. Use {@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal) } - explicitly specify conversion currencies.
1.88 + */
1.89 + public ConversionResult convertBack(BigDecimal amount) {
1.90 + return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, true,false);
1.91 + }
1.92 +
1.93 + private ConversionResult convertUsingSimpleConvertor(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, boolean reversibleExRate, BigDecimal amount, boolean convertBack) throws ConversionNotSupportedException, RuntimeException {
1.94 + ConversionResult result = new ConversionResult();
1.95 +
1.96 + //ExchangeRate rate = exchangeRateProvider.getExchangeRate();
1.97 + ExchangeRate rate;
1.98 + if (reversibleExRate) {
1.99 + rate = exchangeRateProvider.getReversibleExchangeRate(fromCurrency, toCurrency);
1.100 + } else {
1.101 + rate = exchangeRateProvider.getExchangeRate(fromCurrency, toCurrency);
1.102 + }
1.103 + if (rate == null) {
1.104 + return null;
1.105 + }
1.106 +
1.107 + int fromFranctionDigits = fromCurrency.getDefaultFractionDigits();
1.108 + int toFractionDigits = toCurrency.getDefaultFractionDigits();
1.109 +
1.110 + if (toFractionDigits != 2) {
1.111 + throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + exchangeRateProvider.getToCurrency() + " has " + toFractionDigits + " defaultFractionDigits");
1.112 + }
1.113 + if (fromFranctionDigits != 2) {
1.114 + throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + exchangeRateProvider.getFromCurrency() + " has " + fromFranctionDigits + " defaultFractionDigits");
1.115 + }
1.116 +
1.117 + if (amount.signum() == -1) {
1.118 + throw new RuntimeException("Can convert only non-negative value, current value is " + amount);
1.119 + }
1.120 +
1.121 +
1.122 + MathContext context = new MathContext(0, RoundingMode.DOWN);
1.123 +
1.124 + BigDecimal from;
1.125 + BigDecimal to;
1.126 + if (convertBack) {
1.127 + //converting in reverted way
1.128 + to = rate.getFromValue();
1.129 + from = rate.getToValue();
1.130 + } else {
1.131 + //converting in normal way
1.132 + from = rate.getFromValue();
1.133 + to = rate.getToValue();
1.134 + }
1.135 +
1.136 + BigDecimal amountCent = amount.movePointRight(2);
1.137 +
1.138 + final BigDecimal multiplied = amountCent.multiply(to, context);
1.139 + BigDecimal[] division = multiplied.divideAndRemainder(from, context);
1.140 +
1.141 + if (!remainderAllowed && !(BigDecimal.ZERO.equals(division[1]))) {
1.142 + throw new RuntimeException("Remained is not allowed - remaining amount is " + division[1] + " cents");
1.143 + } else {
1.144 + result.setRemainder(BigDecimal.ZERO);
1.145 + }
1.146 +
1.147 + final BigDecimal converted = division[0].movePointLeft(2);
1.148 + result.setConverted(converted);
1.149 + //result.setRemainder(...);
1.150 + return result;
1.151 + }
1.152 +
1.153 +
1.154 + private ConversionResult convertValue(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency,BigDecimal amount, boolean convertBack,boolean reversibleExRate) throws RuntimeException {
1.155 + //result.setRemainder(...);
1.156 + if (convertors.length==0) {
1.157 + return convertUsingSimpleConvertor(fromCurrency, toCurrency, reversibleExRate, amount, convertBack);
1.158 + } else {
1.159 + ConversionResult result = null;
1.160 + for (int i = 0;i<convertors.length;i++) {
1.161 + Convertor subConvertor = convertors[i];
1.162 + result = subConvertor.convertValue(fromCurrency, toCurrency, amount, convertBack, reversibleExRate);
1.163 + if (result!=null) {
1.164 + break;
1.165 + }
1.166 + }
1.167 + return result;
1.168 + }
1.169 + }
1.170 +
1.171 + /**
1.172 + * Convert <code>value</code> from <code>fromCurrency</code> to <code>toCurrency</code>.
1.173 + * <p>
1.174 + * Exchange rate is provided by exchange rate provider which was specified when Convertor was created.
1.175 + * This method is using only exchange rate from->to and not trying to use reverted excange rate to->from.
1.176 + *
1.177 + * @param fromCurrency Source currency to convert from.
1.178 + * @param toCurrency Target currency to convert to.
1.179 + * @param value Value in source currency which should be converted.
1.180 + * @return Return conversion result.
1.181 + * @since version2
1.182 + * @throws ConversionNotSupportedException If conversion from <code>fromCurrency</code> to <code>toCurrency</code> is not supported.
1.183 + */
1.184 + public ConversionResult convert(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value) {
1.185 + ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,false);
1.186 + if (result==null) {
1.187 + //throw new ConversionNotSupportedException("Conversion from " + fromCurrency + " to " + toCurrency + " is not supported");
1.188 + throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),false);
1.189 + }
1.190 + return result;
1.191 + }
1.192 +
1.193 + /**
1.194 + * Convert <code>value</code> from <code>fromCurrency</code> to <code>toCurrency</code>.
1.195 + * Exchange rate is provided by exchange rate provider which was specified when Convertor was created.
1.196 + * <p>
1.197 + * This method is using only exchange rate from->to and if not found, it is trying to use reverted excange rate to->from.
1.198 + *
1.199 + * @param fromCurrency Source currency to convert from.
1.200 + * @param toCurrency Target currency to convert to.
1.201 + * @param value Value in source currency which should be converted.
1.202 + * @return Return conversion result.
1.203 + * @since version2
1.204 + * @throws ConversionNotSupportedException If conversion from <code>fromCurrency</code> to <code>toCurrency</code>
1.205 + * is not supported and neither conversion from <code>toCurrency</code> to <code>fromCurrency</code> is not supported.
1.206 + */
1.207 + public ConversionResult convertWithReversibleRates(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value) {
1.208 + ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,true);
1.209 + if (result==null) {
1.210 + //throw new ConversionNotSupportedException("Neither onversion nor reverted conversion from " + fromCurrency + " to " + toCurrency + " is not supported,");
1.211 + throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),true);
1.212 + }
1.213 + return result;
1.214 + }
1.215 +
1.216 +}