1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/ConversionNotSupportedException.java Tue Oct 07 01:18:23 2008 +0200
1.3 @@ -0,0 +1,85 @@
1.4 +package org.apidesign.apifest08.currency;
1.5 +
1.6 +/**
1.7 + * Conversion not suported exception. This expecption may optionaly describe which conversion was required and failed.
1.8 + * Required conversion can be found in {@link #getFromCurrecyCode() } and {@link #getToCurrecyCode() }.
1.9 + *
1.10 + * @author arnostvalicek
1.11 + * @since version2
1.12 + */
1.13 +public class ConversionNotSupportedException extends ConvertorException {
1.14 + String from;
1.15 + String to;
1.16 + boolean reversed;
1.17 +
1.18 + public ConversionNotSupportedException() {
1.19 + super();
1.20 + }
1.21 +
1.22 + public ConversionNotSupportedException(String message) {
1.23 + super(message);
1.24 + }
1.25 +
1.26 + public ConversionNotSupportedException(String message, Throwable cause) {
1.27 + super(message, cause);
1.28 + }
1.29 +
1.30 + public ConversionNotSupportedException(Throwable cause) {
1.31 + super(cause);
1.32 + }
1.33 +
1.34 + /**
1.35 + * Create exception witd additional information about currencies which are not supported in coversion.
1.36 + * @param from Code of source currency.
1.37 + * @param to Code of target currency.
1.38 + * @param twoWay Set to <code>false</code> if <em>From->To</em> is not supported.
1.39 + * Set to <code>true</code> if both ways <em>From->To</em> and <em>To->From</em> conversions are not supported.
1.40 + *
1.41 + */
1.42 + public ConversionNotSupportedException(String from, String to, boolean twoWay) {
1.43 + this.from = from;
1.44 + this.to = to;
1.45 + this.reversed = true;
1.46 + }
1.47 +
1.48 + @Override
1.49 + public String toString() {
1.50 + if (from!=null && to !=null) {
1.51 + if (reversed) {
1.52 + return "Neither onversion nor reverted conversion from " + from + " to " + to + " is not supported,";
1.53 + } else {
1.54 + return "Conversion from " + from + " to " + to + " is not supported,";
1.55 + }
1.56 + } else {
1.57 + return super.toString();
1.58 + }
1.59 + }
1.60 +
1.61 + /**
1.62 + * Returns code of source currency. This value may be null.
1.63 + * @return Returns code of source currency.
1.64 + */
1.65 + public String getFromCurrecyCode() {
1.66 + return from;
1.67 + }
1.68 +
1.69 + /**
1.70 + * Returns code of target currency. This value may be null.
1.71 + * @return Returns code of target currency.
1.72 + */ public String getToCurrecyCode() {
1.73 + return to;
1.74 + }
1.75 +
1.76 + /**
1.77 + * Returns if one way of two way conversion is not supported.
1.78 + *
1.79 + * Value <code>false</code> means one way conversion is not supported. Value <code>true</code> means
1.80 + * that two way conversio is not supported.
1.81 + *
1.82 + * @return Returs <code>false</code> for one way conversion, <code>true</code> for two way conversion.
1.83 + */
1.84 + public boolean getTwoWayConversion() {
1.85 + return reversed;
1.86 + }
1.87 +
1.88 +}
2.1 --- a/task2/solution13/src/org/apidesign/apifest08/currency/ConversionResult.java Tue Oct 07 00:47:25 2008 +0200
2.2 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/ConversionResult.java Tue Oct 07 01:18:23 2008 +0200
2.3 @@ -6,8 +6,8 @@
2.4 /**
2.5 * Result of currency conversion. Holds converted value and remainder.
2.6 * <p>
2.7 - * <em>Converter</em> describes value converted to <em>target</em> value. <em>Remainder</em> describes
2.8 - * how much from original <em>amount</em> was not possible to convert. Converted never loses any (small) money
2.9 + * <em>Converted</em> describes value converted to <em>target</em> currenty. <em>Remainder</em> describes
2.10 + * how much from original <em>amount</em> was not possible to convert. Convertor never loses any (small) money
2.11 * in conversion error (rounding), but instead of rounding is converts only as much as possible and keeps rest as remainder.
2.12 *
2.13 * @author arnostvalicek
3.1 --- a/task2/solution13/src/org/apidesign/apifest08/currency/Convertor.java Tue Oct 07 00:47:25 2008 +0200
3.2 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/Convertor.java Tue Oct 07 01:18:23 2008 +0200
3.3 @@ -5,23 +5,54 @@
3.4 import java.math.RoundingMode;
3.5
3.6 /** Convertor able to convert amount from one currency to other currency.
3.7 + * <p>
3.8 + * Conversion method are:
3.9 + * <ul>
3.10 + * <li>{@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal)} - convert using exchange rate specified in exchange rate provide. <em>Do not try use reverted excahnage rate</em>.
3.11 + * <li>{@link #convertWithReversibleRates(ConvertorCurrency, ConvertorCurrency, BigDecimal)} - convert using exchange rate specified in exchange rate provide. <em>Try use reverted excahnage rate</em>.
3.12 + * </ul>
3.13 *
3.14 * Exchange rate is provided by {@link ExchangeRateProvider}.
3.15 */
3.16 public class Convertor {
3.17 - boolean remainderAllowed = false; //if false, remained is not allowed (should be true ideally, but can't handle it now)
3.18 + private Convertor[] convertors;
3.19 +
3.20 + /** Create new <code>Convertor</code> as merge of provided convertors. Merged convertor will use
3.21 + * provided convertors to convert between currencies.
3.22 + * <p>
3.23 + * Only one should be able to provide conversion between currencies. If more than one convertos
3.24 + * are able to convert currency, one of conversions will be used (it is not defined which).
3.25 + *
3.26 + * @since version 2
3.27 + * @param convertors Convertor used to create merge-convertor.
3.28 + * @return Returns new convertor instance.
3.29 + */
3.30 + public static Convertor createConvertorAsMerge(Convertor[] convertors) {
3.31 + return new Convertor(convertors);
3.32 + }
3.33 +
3.34 + boolean remainderAllowed = true; //if false, remained is not allowed (should be true ideally, but can't handle it now)
3.35 ExchangeRateProvider exchangeRateProvider;
3.36
3.37 + /** Create simle convertor.
3.38 + */
3.39 private Convertor() {
3.40 -
3.41 + this.convertors=new Convertor[0];
3.42 + }
3.43 +
3.44 + /** Create merge convertor.
3.45 + */
3.46 + private Convertor(Convertor[] convertors) {
3.47 + this.convertors = convertors;
3.48 }
3.49
3.50 /**
3.51 - * Static method used to create convertor.
3.52 + * Create new <code>Convertor</code> using <code>ExchangeRateProvider</code>.
3.53 *
3.54 * @param exchangeRateProvider {@link ExchangeRateProvider} used to get exchange rate.
3.55 *
3.56 * @return Returns <code>Convertor</code> which can be used to convert money.
3.57 + * @since version1
3.58 */
3.59 public static Convertor createConvertor(ExchangeRateProvider exchangeRateProvider) {
3.60 Convertor c = new Convertor();
3.61 @@ -36,9 +67,11 @@
3.62 *
3.63 * @param amount Amount which should be converted. Can't be negative value (can be zero or positive).
3.64 * @return Return <code>ConversionResult</code> which holds conversion result.
3.65 + * @since version1
3.66 + * @deprecated since version2. Use {@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal) } - explicitly specify conversion currencies.
3.67 */
3.68 public ConversionResult convert(BigDecimal amount) {
3.69 - return convertValue(amount, false);
3.70 + return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, false,false);
3.71 }
3.72
3.73 /**
3.74 @@ -47,32 +80,44 @@
3.75 *
3.76 * @param amount Amount which should be converted. Can't be negative value (can be zero or positive).
3.77 * @return Return <code>ConversionResult</code> which holds conversion result.
3.78 + * @since version1
3.79 + * @deprecated since version2. Use {@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal) } - explicitly specify conversion currencies.
3.80 */
3.81 public ConversionResult convertBack(BigDecimal amount) {
3.82 - return convertValue(amount, true);
3.83 + return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, true,false);
3.84 }
3.85
3.86 - private ConversionResult convertValue(BigDecimal amount, boolean convertBack) throws RuntimeException {
3.87 + private ConversionResult convertUsingSimpleConvertor(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, boolean reversibleExRate, BigDecimal amount, boolean convertBack) throws ConversionNotSupportedException, RuntimeException {
3.88 ConversionResult result = new ConversionResult();
3.89 -
3.90 - ExchangeRate rate = exchangeRateProvider.getExchangeRate();
3.91 - int fromFranctionDigits = exchangeRateProvider.getFromCurrency().getDefaultFractionDigits();
3.92 - int toFractionDigits = exchangeRateProvider.getToCurrency().getDefaultFractionDigits();
3.93 -
3.94 - if (toFractionDigits!=2) {
3.95 - throw new RuntimeException("Can't process currency with defaultFractionDigits!=2, "+exchangeRateProvider.getToCurrency()+" has "+toFractionDigits+" defaultFractionDigits");
3.96 +
3.97 + //ExchangeRate rate = exchangeRateProvider.getExchangeRate();
3.98 + ExchangeRate rate;
3.99 + if (reversibleExRate) {
3.100 + rate = exchangeRateProvider.getReversibleExchangeRate(fromCurrency, toCurrency);
3.101 + } else {
3.102 + rate = exchangeRateProvider.getExchangeRate(fromCurrency, toCurrency);
3.103 }
3.104 - if (fromFranctionDigits!=2) {
3.105 - throw new RuntimeException("Can't process currency with defaultFractionDigits!=2, "+exchangeRateProvider.getFromCurrency()+" has "+fromFranctionDigits+" defaultFractionDigits");
3.106 + if (rate == null) {
3.107 + return null;
3.108 }
3.109
3.110 - if (amount.signum()==-1) {
3.111 - throw new RuntimeException("Can convert only non-negative value, current value is "+amount);
3.112 + int fromFranctionDigits = fromCurrency.getDefaultFractionDigits();
3.113 + int toFractionDigits = toCurrency.getDefaultFractionDigits();
3.114 +
3.115 + if (toFractionDigits != 2) {
3.116 + throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + exchangeRateProvider.getToCurrency() + " has " + toFractionDigits + " defaultFractionDigits");
3.117 }
3.118 -
3.119 -
3.120 + if (fromFranctionDigits != 2) {
3.121 + throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + exchangeRateProvider.getFromCurrency() + " has " + fromFranctionDigits + " defaultFractionDigits");
3.122 + }
3.123 +
3.124 + if (amount.signum() == -1) {
3.125 + throw new RuntimeException("Can convert only non-negative value, current value is " + amount);
3.126 + }
3.127 +
3.128 +
3.129 MathContext context = new MathContext(0, RoundingMode.DOWN);
3.130 -
3.131 +
3.132 BigDecimal from;
3.133 BigDecimal to;
3.134 if (convertBack) {
3.135 @@ -80,26 +125,89 @@
3.136 to = rate.getFromValue();
3.137 from = rate.getToValue();
3.138 } else {
3.139 - //converting in mornak way
3.140 + //converting in normal way
3.141 from = rate.getFromValue();
3.142 to = rate.getToValue();
3.143 }
3.144
3.145 BigDecimal amountCent = amount.movePointRight(2);
3.146 -
3.147 - final BigDecimal multiplied = amountCent.multiply(to,context);
3.148 - BigDecimal[] division = multiplied.divideAndRemainder(from,context);
3.149 +
3.150 + final BigDecimal multiplied = amountCent.multiply(to, context);
3.151 + BigDecimal[] division = multiplied.divideAndRemainder(from, context);
3.152
3.153 if (!remainderAllowed && !(BigDecimal.ZERO.equals(division[1]))) {
3.154 - throw new RuntimeException("Remained is not allowed - remaining amount is " + division[1]+ " cents");
3.155 + throw new RuntimeException("Remained is not allowed - remaining amount is " + division[1] + " cents");
3.156 } else {
3.157 result.setRemainder(BigDecimal.ZERO);
3.158 }
3.159 -
3.160 - final BigDecimal converted = division[0].movePointLeft(2);
3.161 +
3.162 + final BigDecimal converted = division[0].movePointLeft(2);
3.163 result.setConverted(converted);
3.164 //result.setRemainder(...);
3.165 -
3.166 return result;
3.167 }
3.168 +
3.169 +
3.170 + private ConversionResult convertValue(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency,BigDecimal amount, boolean convertBack,boolean reversibleExRate) throws RuntimeException {
3.171 + //result.setRemainder(...);
3.172 + if (convertors.length==0) {
3.173 + return convertUsingSimpleConvertor(fromCurrency, toCurrency, reversibleExRate, amount, convertBack);
3.174 + } else {
3.175 + ConversionResult result = null;
3.176 + for (int i = 0;i<convertors.length;i++) {
3.177 + Convertor subConvertor = convertors[i];
3.178 + result = subConvertor.convertValue(fromCurrency, toCurrency, amount, convertBack, reversibleExRate);
3.179 + if (result!=null) {
3.180 + break;
3.181 + }
3.182 + }
3.183 + return result;
3.184 + }
3.185 + }
3.186 +
3.187 + /**
3.188 + * Convert <code>value</code> from <code>fromCurrency</code> to <code>toCurrency</code>.
3.189 + * <p>
3.190 + * Exchange rate is provided by exchange rate provider which was specified when Convertor was created.
3.191 + * This method is using only exchange rate from->to and not trying to use reverted excange rate to->from.
3.192 + *
3.193 + * @param fromCurrency Source currency to convert from.
3.194 + * @param toCurrency Target currency to convert to.
3.195 + * @param value Value in source currency which should be converted.
3.196 + * @return Return conversion result.
3.197 + * @since version2
3.198 + * @throws ConversionNotSupportedException If conversion from <code>fromCurrency</code> to <code>toCurrency</code> is not supported.
3.199 + */
3.200 + public ConversionResult convert(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value) {
3.201 + ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,false);
3.202 + if (result==null) {
3.203 + //throw new ConversionNotSupportedException("Conversion from " + fromCurrency + " to " + toCurrency + " is not supported");
3.204 + throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),false);
3.205 + }
3.206 + return result;
3.207 + }
3.208 +
3.209 + /**
3.210 + * Convert <code>value</code> from <code>fromCurrency</code> to <code>toCurrency</code>.
3.211 + * Exchange rate is provided by exchange rate provider which was specified when Convertor was created.
3.212 + * <p>
3.213 + * This method is using only exchange rate from->to and if not found, it is trying to use reverted excange rate to->from.
3.214 + *
3.215 + * @param fromCurrency Source currency to convert from.
3.216 + * @param toCurrency Target currency to convert to.
3.217 + * @param value Value in source currency which should be converted.
3.218 + * @return Return conversion result.
3.219 + * @since version2
3.220 + * @throws ConversionNotSupportedException If conversion from <code>fromCurrency</code> to <code>toCurrency</code>
3.221 + * is not supported and neither conversion from <code>toCurrency</code> to <code>fromCurrency</code> is not supported.
3.222 + */
3.223 + public ConversionResult convertWithReversibleRates(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value) {
3.224 + ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,true);
3.225 + if (result==null) {
3.226 + //throw new ConversionNotSupportedException("Neither onversion nor reverted conversion from " + fromCurrency + " to " + toCurrency + " is not supported,");
3.227 + throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),true);
3.228 + }
3.229 + return result;
3.230 + }
3.231 +
3.232 }
4.1 --- a/task2/solution13/src/org/apidesign/apifest08/currency/ConvertorCurrency.java Tue Oct 07 00:47:25 2008 +0200
4.2 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/ConvertorCurrency.java Tue Oct 07 01:18:23 2008 +0200
4.3 @@ -40,7 +40,24 @@
4.4 }
4.5
4.6 @Override
4.7 + public boolean equals(Object obj) {
4.8 + boolean result;
4.9 + if (obj instanceof ConvertorCurrency) {
4.10 + ConvertorCurrency that = (ConvertorCurrency) obj;
4.11 + result = currency.equals(that.currency);
4.12 + } else {
4.13 + result = false;
4.14 + }
4.15 + return result;
4.16 + }
4.17 +
4.18 +
4.19 + @Override
4.20 public String toString() {
4.21 - return getClass() + " based on " + (currency != null ? currency.toString() : "NO-BASE-CURRENCY");
4.22 + return "ConvertorCurrency[" + (currency != null ? currency.toString() : "NO-BASE-CURRENCY")+"]";
4.23 + }
4.24 +
4.25 + String getCurrencyCode() {
4.26 + return currency.getCurrencyCode();
4.27 }
4.28 }
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/ConvertorException.java Tue Oct 07 01:18:23 2008 +0200
5.3 @@ -0,0 +1,25 @@
5.4 +package org.apidesign.apifest08.currency;
5.5 +
5.6 +/**
5.7 + * Common Convertor exception.
5.8 + *
5.9 + * @author arnostvalicek
5.10 + */
5.11 +public class ConvertorException extends RuntimeException {
5.12 +
5.13 + public ConvertorException(Throwable cause) {
5.14 + super(cause);
5.15 + }
5.16 +
5.17 + public ConvertorException(String message, Throwable cause) {
5.18 + super(message, cause);
5.19 + }
5.20 +
5.21 + public ConvertorException(String message) {
5.22 + super(message);
5.23 + }
5.24 +
5.25 + public ConvertorException() {
5.26 + }
5.27 +
5.28 +}
6.1 --- a/task2/solution13/src/org/apidesign/apifest08/currency/ExchangeRate.java Tue Oct 07 00:47:25 2008 +0200
6.2 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/ExchangeRate.java Tue Oct 07 01:18:23 2008 +0200
6.3 @@ -12,20 +12,47 @@
6.4 private BigDecimal numberFor;
6.5 private BigDecimal numberGet;
6.6
6.7 + /**
6.8 + * Constructor for new exchange rate holding two values - <em>from value</em> and <em>to value</em>
6.9 + * @param fromValue Exchange rate <em>from value</em>
6.10 + * @param toValue Exchange rate <em>to value</em>
6.11 + */
6.12 public ExchangeRate(BigDecimal fromValue, BigDecimal toValue) {
6.13 this.numberFor = fromValue;
6.14 this.numberGet = toValue;
6.15 }
6.16 +
6.17 + /**
6.18 + * Create new instance of <code>ExchangeRate</code> based on provided exchange rate, but swapping its
6.19 + * <em>from</em> and <em>to</em> value.
6.20 + * <p>
6.21 + * Provided exchange rate is not chaged, this method returns different instance describing reverted exchange rate.
6.22 + *
6.23 + * @param rate Exchange rate which describes rate to be reverted.
6.24 + * @return Instance of reverted rate.
6.25 + */
6.26 + public static ExchangeRate createRevertedRate(ExchangeRate rate) {
6.27 + ExchangeRate reverted = new ExchangeRate(rate.getToValue(), rate.getFromValue());
6.28 + return reverted;
6.29 + }
6.30
6.31 @Override
6.32 public String toString() {
6.33 return "for "+numberFor+" recieve "+numberGet+" @"+getClass().getName();
6.34 }
6.35
6.36 + /**
6.37 + * Return exchange rate <em>from</em> value stored in this object.
6.38 + * @return Returns <em>from</em> value for this exchange rate.
6.39 + */
6.40 public BigDecimal getFromValue() {
6.41 return numberFor;
6.42 }
6.43
6.44 + /**
6.45 + * Return exchange rate <em>to</em> value stored in this object.
6.46 + * @return Returns <em>to</em> value for this exchange rate.
6.47 + */
6.48 public BigDecimal getToValue() {
6.49 return numberGet;
6.50 }
7.1 --- a/task2/solution13/src/org/apidesign/apifest08/currency/ExchangeRateProvider.java Tue Oct 07 00:47:25 2008 +0200
7.2 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/ExchangeRateProvider.java Tue Oct 07 01:18:23 2008 +0200
7.3 @@ -3,27 +3,33 @@
7.4
7.5 import java.math.BigDecimal;
7.6 import java.util.Date;
7.7 +import java.util.HashMap;
7.8 +import java.util.Map;
7.9
7.10 /**
7.11 - * Exchange rate provider - can provide fixed exchange rate which does not depend
7.12 - * on date (method {@link #getExchangeRate()} ) or exchange rate based on date (method {@link #getExchangeRate(java.util.Date) }).
7.13 + * Exchange rate provider. Provides fixed exchange rate which does not depend
7.14 + * on date (method {@link #getExchangeRate()} ).
7.15 *
7.16 + * Date dependend exchange rate to be implemented.
7.17 *
7.18 * @author arnostvalicek
7.19 */
7.20 public class ExchangeRateProvider {
7.21 BigDecimal fromValue, toValue;
7.22 ConvertorCurrency fromCurrency, toCurrency;
7.23 +
7.24 + Map exchangeRateMap = new HashMap();
7.25
7.26 /**
7.27 * Simple constructor for <code>ExchangeRateProviderM</code> which can provide fixed exchange rate.
7.28 *
7.29 - * Describes conversion <em>from</em> to <em>to</em> currency.
7.30 + * Describes conversion <em>from ONE</em> to <em>to ONE</em> currency.
7.31 *
7.32 * @param fromValue From value. BigDecimal value, precision should be set to currency precision.
7.33 * @param fromCurrency From currency.
7.34 * @param toValue To value. BigDecimal value, precision should be set to currency precision.
7.35 * @param toCurrency To currency.
7.36 + * @deprecated deprecated since task2. Use {@link #createExchangeRateProvider() } instead of this constructor.
7.37 */
7.38 public ExchangeRateProvider(BigDecimal fromValue, ConvertorCurrency fromCurrency, BigDecimal toValue, ConvertorCurrency toCurrency) {
7.39 this.fromValue = fromValue;
7.40 @@ -32,18 +38,89 @@
7.41 this.toCurrency = toCurrency;
7.42 }
7.43
7.44 + private ExchangeRateProvider() {
7.45 +
7.46 + }
7.47 +
7.48 + /**
7.49 + * Static method to create new exchange rate provider. This exchange rate provider does not contain
7.50 + * any exchange rates (this is difference to public constructor).
7.51 + * @return New <code>ExchangeRateProvider</code>
7.52 + */
7.53 + public static ExchangeRateProvider createExchangeRateProvider() {
7.54 + ExchangeRateProvider provider = new ExchangeRateProvider();
7.55 + return provider;
7.56 + }
7.57 +
7.58 + /**
7.59 + * Add new exchange rate to <code></code> to this exchange rate provider.
7.60 + * <p>
7.61 + * Example of specifiing conversion rate: 100 SKK == 80 CZK:<br>
7.62 + * <code>addFixedCurencyRate(ConvertorCurrency.getInstance("SKK"), new BigDecimal(100), ConvertorCurrency.getInstance("CZK"), new BigDecimal(80));</code>
7.63 + * <p>
7.64 + *
7.65 + * @param fromCurrency Source currency.
7.66 + * @param fromValue Valye for from currency.
7.67 + * @param toCurrency Target currency.
7.68 + * @param toValue Value for target currency.
7.69 + */
7.70 + public synchronized void addFixedCurencyRate(ConvertorCurrency fromCurrency, BigDecimal fromValue, ConvertorCurrency toCurrency, BigDecimal toValue) {
7.71 + if (fromValue==null) {
7.72 + throw new NullPointerException("fromValue can't be null");
7.73 + }
7.74 + if (toValue==null) {
7.75 + throw new NullPointerException("toValue can't be null");
7.76 + }
7.77 + Map map2 = (Map) exchangeRateMap.get(fromCurrency);
7.78 + if (map2==null) {
7.79 + map2 = new HashMap();
7.80 + exchangeRateMap.put(fromCurrency, map2);
7.81 + }
7.82 +
7.83 + ExchangeRate rate = new ExchangeRate(fromValue, toValue);
7.84 + map2.put(toCurrency, rate);
7.85 + }
7.86 +
7.87 /**
7.88 * Get fixed exange rate for currencies (from->to).
7.89 * @return Returns exchange rate.
7.90 + * @deprecated deprecated since task2. Use {@link #getExchangeRate(ConvertorCurrency, ConvertorCurrency) }
7.91 */
7.92 public ExchangeRate getExchangeRate() {
7.93 return new ExchangeRate(fromValue, toValue);
7.94 }
7.95 +
7.96 +
7.97 + /**
7.98 + * Get fixed exange rate for currencies (from->to).
7.99 + * @return Returns exchange rate or <code>null</code> if exchange rate not found.
7.100 + */
7.101 + public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
7.102 + return getExchangeRateImpl(fromCurrency, toCurrency);
7.103 + }
7.104 +
7.105 + /**
7.106 + * Get fixed exange rate for currencies (from->to) or reversed exchange rate (to->from).
7.107 + * @return Returns exchange rate or <code>null</code> if exchange rate not found.
7.108 + */
7.109 + public ExchangeRate getReversibleExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
7.110 + ExchangeRate rate = getExchangeRateImpl(fromCurrency, toCurrency);
7.111 + if (rate==null) {
7.112 + ExchangeRate revertedRate = getExchangeRateImpl(toCurrency, fromCurrency);
7.113 + if (revertedRate!=null) {
7.114 + rate = ExchangeRate.createRevertedRate(revertedRate);
7.115 + }
7.116 + }
7.117 + return rate;
7.118 + }
7.119 +
7.120 +
7.121
7.122 /**
7.123 * Get exchange rate for currencies (from->to) based on provided date.
7.124 * @param date Date for which exchange rate should be provided.
7.125 * @return Returns exchange rate
7.126 + * @deprecated deprecated since task2. No real implementation in version2.
7.127 */
7.128 public ExchangeRate getExchangeRate(Date date) {
7.129 return new ExchangeRate(fromValue, toValue);
7.130 @@ -57,6 +134,19 @@
7.131 ConvertorCurrency getToCurrency() {
7.132 return toCurrency;
7.133 }
7.134 +
7.135 + private ExchangeRate getExchangeRateImpl(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
7.136 + if (fromValue != null && toValue != null && fromCurrency.equals(this.fromCurrency) && toCurrency.equals(this.toCurrency)) {
7.137 + return new ExchangeRate(fromValue, toValue);
7.138 + }
7.139 + //return new ExchangeRate(fromValue, toValue);
7.140 + Map map2 = (Map) exchangeRateMap.get(fromCurrency);
7.141 + if (map2 == null) {
7.142 + return null;
7.143 + }
7.144 + ExchangeRate result = (ExchangeRate) map2.get(toCurrency);
7.145 + return result;
7.146 + }
7.147
7.148
7.149 }
8.1 --- a/task2/solution13/test/org/apidesign/apifest08/test/Task1Test.java Tue Oct 07 00:47:25 2008 +0200
8.2 +++ b/task2/solution13/test/org/apidesign/apifest08/test/Task1Test.java Tue Oct 07 01:18:23 2008 +0200
8.3 @@ -6,6 +6,7 @@
8.4 import org.apidesign.apifest08.currency.Convertor;
8.5 import org.apidesign.apifest08.currency.ConvertorCurrency;
8.6 import org.apidesign.apifest08.currency.ExchangeRateProvider;
8.7 +import org.apidesign.apifest08.currency.ConversionNotSupportedException;
8.8
8.9 /** Finish the Convertor API, and then write bodies of methods inside
8.10 * of this class to match the given tasks. To fullfil your task, use the
8.11 @@ -34,7 +35,7 @@
8.12 *
8.13 * @return prepared convertor ready for converting USD to CZK and CZK to USD
8.14 */
8.15 - public Convertor createCZKtoUSD() {
8.16 + public static Convertor createCZKtoUSD() {
8.17 ConvertorCurrency fromCurrency = ConvertorCurrency.getInstance("CZK");
8.18 ConvertorCurrency toCurrency = ConvertorCurrency.getInstance("USD");
8.19 ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(17), fromCurrency, new BigDecimal(1), toCurrency);
8.20 @@ -50,7 +51,7 @@
8.21 *
8.22 * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
8.23 */
8.24 - public Convertor createSKKtoCZK() {
8.25 + public static Convertor createSKKtoCZK() {
8.26 ConvertorCurrency fromCurrency = ConvertorCurrency.getInstance("SKK");
8.27 ConvertorCurrency toCurrency = ConvertorCurrency.getInstance("CZK");
8.28 ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(100), fromCurrency, new BigDecimal(80), toCurrency);
8.29 @@ -59,7 +60,7 @@
8.30 }
8.31
8.32
8.33 - public Convertor createCZKtoYEN() {
8.34 + public static Convertor createCZKtoYEN() {
8.35 ConvertorCurrency fromCurrency = ConvertorCurrency.getInstance("CZK");
8.36 ConvertorCurrency toCurrency = ConvertorCurrency.getInstance("JPY");
8.37 ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(1), fromCurrency, new BigDecimal(1), toCurrency);
8.38 @@ -200,8 +201,21 @@
8.39 public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception {
8.40 Convertor c = createCZKtoUSD();
8.41 // convert $5 to SKK, the API shall say this is not possible
8.42 + try {
8.43 + c.convert(ConvertorCurrency.getInstance("USD"), ConvertorCurrency.getInstance("SKK"), new BigDecimal(5));
8.44 + fail();
8.45 + } catch (ConversionNotSupportedException e) {
8.46 + //expected error;
8.47 + assertEquals("Exception From USD", "USD",e.getFromCurrecyCode());
8.48 + assertEquals("Exception To SKK", "SKK",e.getToCurrecyCode());
8.49 + }
8.50 // convert 500 SKK to CZK, the API shall say this is not possible
8.51 - // ... no api for required call, should be here?
8.52 + try {
8.53 + c.convert(ConvertorCurrency.getInstance("SKK"), ConvertorCurrency.getInstance("CZK"), new BigDecimal(500));
8.54 + fail();
8.55 + } catch (ConversionNotSupportedException e) {
8.56 + assertEquals("Exception From USD", "SKK",e.getFromCurrecyCode());
8.57 + assertEquals("Exception To SKK", "CZK",e.getToCurrecyCode()); }
8.58 }
8.59
8.60
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/task2/solution13/test/org/apidesign/apifest08/test/Task2Test.java Tue Oct 07 01:18:23 2008 +0200
9.3 @@ -0,0 +1,136 @@
9.4 +package org.apidesign.apifest08.test;
9.5 +
9.6 +import java.math.BigDecimal;
9.7 +import junit.framework.TestCase;
9.8 +import org.apidesign.apifest08.currency.Convertor;
9.9 +import org.apidesign.apifest08.currency.ConvertorCurrency;
9.10 +import org.apidesign.apifest08.currency.ExchangeRateProvider;
9.11 +
9.12 +/** There are many currencies around the world and many banks manipulate
9.13 + * with more than one or two at the same time. As banks are usually the
9.14 + * best paying clients, which is true even in case of your Convertor API,
9.15 + * it is reasonable to listen to their requests.
9.16 + * <p>
9.17 + * The quest for today is to enhance your existing convertor API to hold
9.18 + * information about many currencies and allow conversions between any of them.
9.19 + * Also, as conversion rates for diferent currencies usually arise from various
9.20 + * bank departments, there is another important need. There is a need to
9.21 + * compose two convertors into one by merging all the information about
9.22 + * currencies they know about.
9.23 + */
9.24 +public class Task2Test extends TestCase {
9.25 + private static ConvertorCurrency currencyCZK = ConvertorCurrency.getInstance("CZK");
9.26 + private static ConvertorCurrency currencySKK = ConvertorCurrency.getInstance("SKK");
9.27 + private static ConvertorCurrency currencyUSD = ConvertorCurrency.getInstance("USD");
9.28 +
9.29 + public Task2Test(String testName) {
9.30 + super(testName);
9.31 + }
9.32 +
9.33 + @Override
9.34 + protected void setUp() throws Exception {
9.35 +
9.36 + }
9.37 +
9.38 + @Override
9.39 + protected void tearDown() throws Exception {
9.40 + }
9.41 +
9.42 + public static Convertor createUsdToSkkConvertor() {
9.43 + ConvertorCurrency fromCurrency = currencyUSD;
9.44 + ConvertorCurrency toCurrency = currencyUSD;
9.45 + ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(1), fromCurrency, new BigDecimal(20), toCurrency);
9.46 +
9.47 + return Convertor.createConvertor(exchangeRateProvider);
9.48 +
9.49 + }
9.50 +
9.51 + // As in Task1Test, keep in mind, that there are three parts
9.52 + // of the whole system:
9.53 + // 1. there is someone who knows the current exchange rate
9.54 + // 2. there is someone who wants to do the conversion
9.55 + // 3. there is the API between 1. and 2. which allows them to communicate
9.56 + //
9.57 + // Please backward compatibly enhance your existing API to support following
9.58 + // usecases:
9.59 + //
9.60 +
9.61 + /** Create convertor that understands two currencies, CZK and
9.62 + * SKK. Make 100 SKK == 75 CZK. This is method for the group of users that
9.63 + * knows the exchange rate, and needs to use the API to create objects
9.64 + * with the exchange rate. Anyone shall be ready to call this method without
9.65 + * any other method being called previously. The API itself shall know
9.66 + * nothing about any rates, before this method is called.
9.67 + */
9.68 + public static Convertor createTripleConvertor() {
9.69 + ExchangeRateProvider exRateProvider = ExchangeRateProvider.createExchangeRateProvider();
9.70 +
9.71 + // Rates: 1USD = 15CZK
9.72 + exRateProvider.addFixedCurencyRate(currencyUSD, new BigDecimal(1),currencyCZK, new BigDecimal(15));
9.73 +
9.74 + // Rates: 1USD = 20SKK
9.75 + exRateProvider.addFixedCurencyRate(currencyUSD, new BigDecimal(1), currencySKK, new BigDecimal(20));
9.76 +
9.77 + // Rates: 75CZK = 100SKK
9.78 + exRateProvider.addFixedCurencyRate(currencyCZK, new BigDecimal(75), currencySKK, new BigDecimal(100));
9.79 +
9.80 + Convertor c = Convertor.createConvertor(exRateProvider);
9.81 +
9.82 + return c;
9.83 + }
9.84 +
9.85 + /** Define convertor that understands three currencies. Use it.
9.86 + */
9.87 + public void testConvertorForUSDandCZKandSKK() throws Exception {
9.88 + Convertor c = createTripleConvertor();
9.89 +
9.90 + // convert $5 to CZK using c:
9.91 + assertEquals("Result is 75 CZK",new BigDecimal("75.00"),c.convertWithReversibleRates(currencyUSD, currencyCZK, new BigDecimal(5)).getConverted());
9.92 +
9.93 +
9.94 + // convert $5 to SKK using c:
9.95 + assertEquals("Result is 100 SKK",new BigDecimal("100.00"),c.convertWithReversibleRates(currencyUSD, currencySKK, new BigDecimal(5)).getConverted());
9.96 +
9.97 + // convert 200SKK to CZK using c:
9.98 + assertEquals("Result is 150 CZK",new BigDecimal("150.00"),c.convertWithReversibleRates(currencySKK, currencyCZK, new BigDecimal(200)).getConverted());
9.99 +
9.100 + // convert 200SKK to USK using c:
9.101 + // assertEquals("Result is 10 USD");
9.102 + }
9.103 +
9.104 + /** Merge all currency rates of convertor 1 with convertor 2.
9.105 + * Implement this using your API, preferably this method just delegates
9.106 + * into some API method which does the actual work, without requiring
9.107 + * API clients to code anything complex.
9.108 + */
9.109 + public static Convertor merge(Convertor one, Convertor two) {
9.110 + return Convertor.createConvertorAsMerge(new Convertor[]{one, two});
9.111 + }
9.112 +
9.113 + /** Join the convertors from previous task, Task1Test and show that it
9.114 + * can be used to do reasonable conversions.
9.115 + */
9.116 + public void testConvertorComposition() throws Exception {
9.117 + Convertor c = merge(
9.118 + Task1Test.createCZKtoUSD(),
9.119 + Task1Test.createSKKtoCZK()
9.120 + );
9.121 +
9.122 + // convert $5 to CZK using c:
9.123 + assertEquals("Result is 85 CZK",new BigDecimal("85.00"),c.convertWithReversibleRates(currencyUSD, currencyCZK, new BigDecimal(5)).getConverted());
9.124 +
9.125 + // convert $8 to CZK using c:
9.126 + // assertEquals("Result is 136 CZK");
9.127 + assertEquals("Result is 136 CZK",new BigDecimal("136.00"),c.convertWithReversibleRates(currencyUSD, currencyCZK, new BigDecimal(8)).getConverted());
9.128 +
9.129 + // convert 1003CZK to USD using c:
9.130 + assertEquals("Result is 59 USD",new BigDecimal("59.00"),c.convertWithReversibleRates(currencyCZK, currencyUSD, new BigDecimal(1003)).getConverted());
9.131 +
9.132 + // convert 16CZK using c:
9.133 + assertEquals("Result is 20 SKK",new BigDecimal("20.00"),c.convertWithReversibleRates(currencyCZK, currencySKK, new BigDecimal(16)).getConverted());
9.134 +
9.135 + // convert 500SKK to CZK using c:
9.136 + assertEquals("Result is 400 CZK",new BigDecimal("400.00"),c.convertWithReversibleRates(currencySKK, currencyCZK, new BigDecimal(500)).getConverted());
9.137 +
9.138 + }
9.139 +}