1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/task2/solution04/src/org/apidesign/apifest08/currency/CompositeConvertorImpl.java Tue Oct 07 00:21:03 2008 +0200
1.3 @@ -0,0 +1,314 @@
1.4 +package org.apidesign.apifest08.currency;
1.5 +
1.6 +
1.7 +import java.math.BigDecimal;
1.8 +import java.math.RoundingMode;
1.9 +import java.util.Collections;
1.10 +import java.util.Currency;
1.11 +import java.util.HashMap;
1.12 +import java.util.HashSet;
1.13 +import java.util.Map;
1.14 +import java.util.Set;
1.15 +
1.16 +
1.17 +/**
1.18 + * A composite convertor allows conversions between many currencies by forwarding conversion requests to stored convertors.
1.19 + * A composite convertor will build all possible conversions that are allowed by the underlying set of convertors.
1.20 + *
1.21 + * @author D'Arcy Smith
1.22 + * @verson 1.0
1.23 + */
1.24 +final class CompositeConvertorImpl
1.25 + implements Convertor
1.26 +{
1.27 + /**
1.28 + * The convertors that are supported.
1.29 + */
1.30 + private final Convertor[] convertors;
1.31 +
1.32 + /**
1.33 + * Keeps track of what convertors to use to convert between currencies.
1.34 + */
1.35 + private final Map<Currency, Map<Currency, Convertor>> possibleConversions;
1.36 +
1.37 + {
1.38 + possibleConversions = new HashMap<Currency, Map<Currency, Convertor>>();
1.39 + }
1.40 +
1.41 + /**
1.42 + * Construct a ComositeConvertorImpl with the specified convertors.
1.43 + * This will result in all possible conversions between the supplied currencies being made.
1.44 + *
1.45 + * @param cs the convertors to use.
1.46 + * @throws IllegalArgumentException if any of the items in cs are null.
1.47 + */
1.48 + CompositeConvertorImpl(Convertor ... cs)
1.49 + {
1.50 + int newConvertors;
1.51 +
1.52 + convertors = cs;
1.53 +
1.54 + // track all of the known conversion
1.55 + for(final Convertor convertor : convertors)
1.56 + {
1.57 + final Set<Currency> currencies;
1.58 + Map<Currency, Convertor> possible;
1.59 +
1.60 + if(convertor == null)
1.61 + {
1.62 + throw new IllegalArgumentException("cs cannot contain null");
1.63 + }
1.64 +
1.65 + currencies = convertor.getCurrencies();
1.66 +
1.67 + for(final Currency currency : currencies)
1.68 + {
1.69 + possible = possibleConversions.get(currency);
1.70 +
1.71 + if(possible == null)
1.72 + {
1.73 + possible = new HashMap<Currency, Convertor>();
1.74 + possibleConversions.put(currency, possible);
1.75 + }
1.76 +
1.77 + for(final Currency c : currencies)
1.78 + {
1.79 + possible.put(c, convertor);
1.80 + }
1.81 + }
1.82 + }
1.83 +
1.84 + // make up conversions that can be derived... eg:
1.85 + // we have:
1.86 + // USD <-> CAD
1.87 + // CAD <-> CZK
1.88 + // SSK <-> GBP
1.89 + // we can derive:
1.90 + // USD <-> CZK
1.91 + // we cannot derive:
1.92 + // USD <-> GBP
1.93 + // CAD <-> GBP
1.94 + // CZK <-> GBP
1.95 + do
1.96 + {
1.97 + newConvertors = 0;
1.98 +
1.99 + // todo... need to loop this until all the ones that can be handled are done.
1.100 + for(final Currency from : getCurrencies())
1.101 + {
1.102 + for(final Currency to : getCurrencies())
1.103 + {
1.104 + if(!(canConvert(from, to)))
1.105 + {
1.106 + final Set<Currency> fromCurrencies;
1.107 + final Set<Currency> toCurrencies;
1.108 + final Set<Currency> common;
1.109 +
1.110 + fromCurrencies = possibleConversions.get(from).keySet();
1.111 + toCurrencies = possibleConversions.get(to).keySet();
1.112 + common = new HashSet<Currency>();
1.113 +
1.114 + for(final Currency currency : fromCurrencies)
1.115 + {
1.116 + if(toCurrencies.contains(currency))
1.117 + {
1.118 + common.add(currency);
1.119 + }
1.120 + }
1.121 +
1.122 + for(final Currency currency : common)
1.123 + {
1.124 + final Convertor convertor;
1.125 +
1.126 + convertor = createConvertor(from, to, currency);
1.127 + possibleConversions.get(from).put(to, convertor);
1.128 + possibleConversions.get(to).put(from, convertor);
1.129 + newConvertors++;
1.130 + }
1.131 + }
1.132 + }
1.133 + }
1.134 + }
1.135 + while(newConvertors > 0);
1.136 + }
1.137 +
1.138 + /**
1.139 + * Check to see if converting between the two currencies is possible.
1.140 + *
1.141 + * @param from the currency to convert from.
1.142 + * @param to the currency to convert to.
1.143 + * @return true if the conversion is possible.
1.144 + * @throws IllegalArgumentException if either from or to are null.
1.145 + */
1.146 + public boolean canConvert(final Currency from, final Currency to)
1.147 + {
1.148 + final Map<Currency, Convertor> possible;
1.149 +
1.150 + if(from == null)
1.151 + {
1.152 + throw new IllegalArgumentException("from cannot be null");
1.153 + }
1.154 +
1.155 + if(to == null)
1.156 + {
1.157 + throw new IllegalArgumentException("to cannot be null");
1.158 + }
1.159 +
1.160 + possible = possibleConversions.get(from);
1.161 +
1.162 + if(possible.containsKey(to))
1.163 + {
1.164 + return (true);
1.165 + }
1.166 +
1.167 + return (false);
1.168 + }
1.169 +
1.170 + /**
1.171 + * Get the currencies that the convertor supports. Just because a currency is
1.172 + * supported does not mean that canConvert will return true.
1.173 + *
1.174 + * @return the supported currencies.
1.175 + */
1.176 + public Set<Currency> getCurrencies()
1.177 + {
1.178 + final Set<Currency> currencies;
1.179 +
1.180 + currencies = possibleConversions.keySet();
1.181 +
1.182 + return (Collections.unmodifiableSet(currencies));
1.183 + }
1.184 +
1.185 + /**
1.186 + * Get the conversion rate between two currencies.
1.187 + *
1.188 + * @param from the currency to convert from.
1.189 + * @param to the currency to convert to.
1.190 + * @return the conversion rate between the two currencies.
1.191 + * @throws IllegalArgumentException if either from or to is null.
1.192 + * @throws InvalidConversionException if canConvert would return false.
1.193 + */
1.194 + public BigDecimal getConversionRate(final Currency from, final Currency to)
1.195 + throws InvalidConversionException
1.196 + {
1.197 + final Map<Currency, Convertor> possible;
1.198 + Convertor convertor;
1.199 +
1.200 + if(from == null)
1.201 + {
1.202 + throw new IllegalArgumentException("from cannot be null");
1.203 + }
1.204 +
1.205 + if(to == null)
1.206 + {
1.207 + throw new IllegalArgumentException("to cannot be null");
1.208 + }
1.209 +
1.210 + if(!(canConvert(from, to)))
1.211 + {
1.212 + throw new InvalidConversionException("cannot convert", to);
1.213 + }
1.214 +
1.215 + possible = possibleConversions.get(from);
1.216 + convertor = possible.get(to);
1.217 +
1.218 + if(convertor == null)
1.219 + {
1.220 + throw new Error();
1.221 + }
1.222 +
1.223 + return (convertor.getConversionRate(from, to));
1.224 + }
1.225 +
1.226 + /**
1.227 + * Convert an amount from one currency to another.
1.228 + *
1.229 + * @param from the currency to convert from.
1.230 + * @param to the currency to convert to.
1.231 + * @param amount the amount to convert.
1.232 + * @return the converted amount.
1.233 + * @throws IllegalArgumentException if any of the arguments are null.
1.234 + * @throws InvalidConversionException if either from or to are not valid for the convertor.
1.235 + */
1.236 + public BigDecimal convert(final Currency from,
1.237 + final Currency to,
1.238 + final BigDecimal amount)
1.239 + throws InvalidConversionException
1.240 + {
1.241 + final BigDecimal result;
1.242 +
1.243 + if(amount == null)
1.244 + {
1.245 + throw new IllegalArgumentException("amount cannot be null");
1.246 + }
1.247 +
1.248 + if(from == null)
1.249 + {
1.250 + throw new IllegalArgumentException("from cannot be null");
1.251 + }
1.252 +
1.253 + if(to == null)
1.254 + {
1.255 + throw new IllegalArgumentException("to cannot be null");
1.256 + }
1.257 +
1.258 + result = amount.multiply(getConversionRate(from, to));
1.259 +
1.260 + return (result.setScale(2, RoundingMode.HALF_DOWN));
1.261 + }
1.262 +
1.263 + /**
1.264 + * Create a convertor between two currencies using another currency that is able to convert between both.
1.265 + *
1.266 + * @param from the currency to convert from.
1.267 + * @param to the currency to convert to.
1.268 + * @param intermediary the currency to use as a go-between.
1.269 + * @return a Convertor that is able to convert between from an to.
1.270 + * @throws IllegalArgumentException if any of the arguments are null.
1.271 + */
1.272 + private Convertor createConvertor(final Currency from,
1.273 + final Currency to,
1.274 + final Currency intermediary)
1.275 + {
1.276 + final Convertor fromIntermediary;
1.277 + final Convertor toIntermediary;
1.278 +
1.279 + if(from == null)
1.280 + {
1.281 + throw new IllegalArgumentException("from cannot be null");
1.282 + }
1.283 +
1.284 + if(to == null)
1.285 + {
1.286 + throw new IllegalArgumentException("to cannot be null");
1.287 + }
1.288 +
1.289 + if(intermediary == null)
1.290 + {
1.291 + throw new IllegalArgumentException("intermediary cannot be null");
1.292 + }
1.293 +
1.294 + fromIntermediary = possibleConversions.get(from).get(intermediary);
1.295 + toIntermediary = possibleConversions.get(to).get(intermediary);
1.296 +
1.297 + try
1.298 + {
1.299 + final BigDecimal fromRate;
1.300 + final BigDecimal toRate;
1.301 + final BigDecimal rate;
1.302 + final Convertor convertor;
1.303 +
1.304 + fromRate = fromIntermediary.getConversionRate(from, intermediary);
1.305 + toRate = toIntermediary.getConversionRate(intermediary, to);
1.306 + rate = fromRate.multiply(toRate);
1.307 +
1.308 + convertor = ConvertorFactory.getConvertor(from, BigDecimal.ONE, to, rate);
1.309 +
1.310 + return (convertor);
1.311 + }
1.312 + catch (InvalidConversionException ex)
1.313 + {
1.314 + throw new Error();
1.315 + }
1.316 + }
1.317 +}
2.1 --- a/task2/solution04/src/org/apidesign/apifest08/currency/ConverterImpl.java Tue Oct 07 00:19:37 2008 +0200
2.2 +++ b/task2/solution04/src/org/apidesign/apifest08/currency/ConverterImpl.java Tue Oct 07 00:21:03 2008 +0200
2.3 @@ -4,7 +4,10 @@
2.4 import java.math.BigDecimal;
2.5 import java.math.MathContext;
2.6 import java.math.RoundingMode;
2.7 +import java.util.Collections;
2.8 import java.util.Currency;
2.9 +import java.util.HashSet;
2.10 +import java.util.Set;
2.11
2.12
2.13 /**
2.14 @@ -128,12 +131,59 @@
2.15 throw new InvalidConversionException("cannot convert to: " + to.getCurrencyCode(), to, currencyA, currencyB);
2.16 }
2.17
2.18 - // converting between the same currency is no converstion at all.
2.19 + result = amount.multiply(getConversionRate(from, to));
2.20 +
2.21 + return (result.setScale(2, RoundingMode.HALF_DOWN));
2.22 + }
2.23 +
2.24 + /**
2.25 + * Check to see if converting between the two currencies is possible.
2.26 + *
2.27 + * @param from the currency to convert from.
2.28 + * @param to the currency to convert to.
2.29 + * @return true if the conversion is possible.
2.30 + */
2.31 + public boolean canConvert(final Currency from, final Currency to)
2.32 + {
2.33 + return ((from.equals(currencyA) || from.equals(currencyB)) &&
2.34 + (to.equals(currencyA) || to.equals(currencyB)));
2.35 + }
2.36 +
2.37 + /**
2.38 + * Get the currencies that the convertor supports.
2.39 + *
2.40 + * @return the supported currencies.
2.41 + */
2.42 + public Set<Currency> getCurrencies()
2.43 + {
2.44 + final Set<Currency> currencies;
2.45 +
2.46 + currencies = new HashSet<Currency>();
2.47 + currencies.add(currencyA);
2.48 + currencies.add(currencyB);
2.49 +
2.50 + return (Collections.unmodifiableSet(currencies));
2.51 + }
2.52 +
2.53 + /**
2.54 + * Get the conversion rate between two currencies.
2.55 + *
2.56 + * @param from the currency to convert from.
2.57 + * @param to the currency to convert to.
2.58 + * @return the conversion rate between the two currencies.
2.59 + * @throws InvalidConversionException if canConvert would return false.
2.60 + */
2.61 + public BigDecimal getConversionRate(final Currency from,
2.62 + final Currency to)
2.63 + throws InvalidConversionException
2.64 + {
2.65 + final BigDecimal rate;
2.66 +
2.67 if(from.equals(to))
2.68 {
2.69 - result = amount;
2.70 + rate = BigDecimal.ONE;
2.71 }
2.72 - else
2.73 + else
2.74 {
2.75 final BigDecimal rateX;
2.76 final BigDecimal rateY;
2.77 @@ -150,10 +200,83 @@
2.78 rateY = currencyARate;
2.79 }
2.80
2.81 - temp = amount.divide(rateX, MathContext.DECIMAL32);
2.82 - result = temp.multiply(rateY);
2.83 + temp = BigDecimal.ONE.divide(rateX, MathContext.DECIMAL64);
2.84 + rate = temp.multiply(rateY);
2.85 + }
2.86 +
2.87 + return (rate.setScale(20, RoundingMode.HALF_EVEN));
2.88 + }
2.89 +
2.90 + /**
2.91 + * Check to see if two ConvertorImpls are equal.
2.92 + *
2.93 + * @param obj the object to check
2.94 + * @return if the ConvertorImpls are not the same (cuyrrencies and rates).
2.95 + */
2.96 + @Override
2.97 + public boolean equals(Object obj)
2.98 + {
2.99 + if (obj == null)
2.100 + {
2.101 + return false;
2.102 + }
2.103 +
2.104 + if (getClass() != obj.getClass())
2.105 + {
2.106 + return false;
2.107 }
2.108
2.109 - return (result.setScale(2, RoundingMode.HALF_DOWN));
2.110 + final ConvertorImpl other = (ConvertorImpl) obj;
2.111 +
2.112 + // it would be nice if NetBeans could chck to see if the variable is final and guaranteed not to be null... but that
2.113 + // would likely be tricky... but in a NetBeans engineer reads that see if you can do it :-)
2.114 + if (this.currencyA != other.currencyA && (this.currencyA == null || !this.currencyA.equals(other.currencyA)))
2.115 + {
2.116 + return false;
2.117 + }
2.118 +
2.119 + if (this.currencyB != other.currencyB && (this.currencyB == null || !this.currencyB.equals(other.currencyB)))
2.120 + {
2.121 + return false;
2.122 + }
2.123 +
2.124 + if (this.currencyARate != other.currencyARate && (this.currencyARate == null || !this.currencyARate.equals(other.currencyARate)))
2.125 + {
2.126 + return false;
2.127 + }
2.128 +
2.129 + if (this.currencyBRate != other.currencyBRate && (this.currencyBRate == null || !this.currencyBRate.equals(other.currencyBRate)))
2.130 + {
2.131 + return false;
2.132 + }
2.133 +
2.134 + return true;
2.135 + }
2.136 +
2.137 + /**
2.138 + * Get the hashCode of the Convertor.
2.139 + *
2.140 + * @return the hashCode of the convertor.
2.141 + */
2.142 + @Override
2.143 + public int hashCode()
2.144 + {
2.145 + int hash = 7;
2.146 + hash = 37 * hash + (this.currencyA != null ? this.currencyA.hashCode() : 0);
2.147 + hash = 37 * hash + (this.currencyB != null ? this.currencyB.hashCode() : 0);
2.148 + hash = 37 * hash + (this.currencyARate != null ? this.currencyARate.hashCode() : 0);
2.149 + hash = 37 * hash + (this.currencyBRate != null ? this.currencyBRate.hashCode() : 0);
2.150 + return hash;
2.151 + }
2.152 +
2.153 + /**
2.154 + * Get the currencyCode of both currencies.
2.155 + *
2.156 + * @return the currency codes of both currencies.
2.157 + */
2.158 + @Override
2.159 + public String toString()
2.160 + {
2.161 + return (currencyA.getCurrencyCode() + " to " + currencyB.getCurrencyCode());
2.162 }
2.163 }
3.1 --- a/task2/solution04/src/org/apidesign/apifest08/currency/Convertor.java Tue Oct 07 00:19:37 2008 +0200
3.2 +++ b/task2/solution04/src/org/apidesign/apifest08/currency/Convertor.java Tue Oct 07 00:21:03 2008 +0200
3.3 @@ -3,6 +3,7 @@
3.4
3.5 import java.math.BigDecimal;
3.6 import java.util.Currency;
3.7 +import java.util.Set;
3.8
3.9
3.10 /**
3.11 @@ -27,4 +28,32 @@
3.12 Currency to,
3.13 BigDecimal amount)
3.14 throws InvalidConversionException;
3.15 +
3.16 + /**
3.17 + * Check to see if converting between the two currencies is possible.
3.18 + *
3.19 + * @param from the currency to convert from.
3.20 + * @param to the currency to convert to.
3.21 + * @return true if the conversion is possible.
3.22 + */
3.23 + boolean canConvert(Currency from, Currency to);
3.24 +
3.25 + /**
3.26 + * Get the currencies that the convertor supports. Just because a currency is
3.27 + * supported does not mean that canConvert will return true.
3.28 + *
3.29 + * @return the supported currencies.
3.30 + */
3.31 + Set<Currency> getCurrencies();
3.32 +
3.33 + /**
3.34 + * Get the conversion rate between two currencies.
3.35 + *
3.36 + * @param from the currency to convert from.
3.37 + * @param to the currency to convert to.
3.38 + * @return the conversion rate between the two currencies.
3.39 + * @throws InvalidConversionException if canConvert would return false.
3.40 + */
3.41 + BigDecimal getConversionRate(final Currency from, final Currency to)
3.42 + throws InvalidConversionException;
3.43 }
4.1 --- a/task2/solution04/src/org/apidesign/apifest08/currency/ConvertorFactory.java Tue Oct 07 00:19:37 2008 +0200
4.2 +++ b/task2/solution04/src/org/apidesign/apifest08/currency/ConvertorFactory.java Tue Oct 07 00:21:03 2008 +0200
4.3 @@ -1,10 +1,7 @@
4.4 package org.apidesign.apifest08.currency;
4.5
4.6 -import java.lang.ref.WeakReference;
4.7 import java.math.BigDecimal;
4.8 import java.util.Currency;
4.9 -import java.util.Map;
4.10 -import java.util.WeakHashMap;
4.11
4.12
4.13 /**
4.14 @@ -15,15 +12,15 @@
4.15 */
4.16 public final class ConvertorFactory
4.17 {
4.18 - /**
4.19 + /*
4.20 * flyweight so that only one vestion of each converter is created at a time.
4.21 - */
4.22 private final static Map<String, WeakReference<Convertor>> convertors;
4.23
4.24 static
4.25 {
4.26 convertors = new WeakHashMap<String, WeakReference<Convertor>>();
4.27 }
4.28 + */
4.29
4.30 /**
4.31 * Prevent accidental construction.
4.32 @@ -74,7 +71,7 @@
4.33 final Currency b,
4.34 final BigDecimal bRate)
4.35 {
4.36 - final String key;
4.37 + // final String key;
4.38 Convertor convertor;
4.39
4.40 if(a == null)
4.41 @@ -97,6 +94,7 @@
4.42 throw new IllegalArgumentException("bRate cannot be null");
4.43 }
4.44
4.45 + /*
4.46 key = a.getCurrencyCode() + aRate + b.getCurrencyCode() + bRate;
4.47
4.48 // make sure that we don't try to overwrite one
4.49 @@ -107,10 +105,68 @@
4.50 convertor = new ConvertorImpl(a, aRate, b, bRate);
4.51 convertors.put(key, new WeakReference(convertor));
4.52 }
4.53 +
4.54 + convertor = convertors.get(key).get();
4.55 }
4.56 + */
4.57 +
4.58 + convertor = new ConvertorImpl(a, aRate, b, bRate);
4.59 +
4.60 + return (convertor);
4.61 + }
4.62 +
4.63 + public static Convertor mergeConvertors(final Convertor ... cs)
4.64 + {
4.65 + Convertor convertor;
4.66 +
4.67 + /*
4.68 + final String key;
4.69
4.70 - convertor = convertors.get(key).get();
4.71 + // ISSUE: only takes into account the names... not the rates...
4.72 + key = getKey(cs);
4.73 +
4.74 + // make sure that we don't try to overwrite one
4.75 + synchronized(convertors)
4.76 + {
4.77 + if(!(convertors.containsKey(key)))
4.78 + {
4.79 + convertor = new CompositeConvertorImpl(cs);
4.80 + convertors.put(key, new WeakReference(convertor));
4.81 + }
4.82 +
4.83 + convertor = convertors.get(key).get();
4.84 + }
4.85 + */
4.86 +
4.87 + convertor = new CompositeConvertorImpl(cs);
4.88
4.89 return (convertor);
4.90 }
4.91 +
4.92 + /*
4.93 + private static String getKey(final Convertor ... cs)
4.94 + {
4.95 + final Set<Currency> currencies;
4.96 + final StringBuilder builder;
4.97 +
4.98 + currencies = new HashSet<Currency>();
4.99 +
4.100 + for(final Convertor convertor : cs)
4.101 + {
4.102 + final Set<Currency> c;
4.103 +
4.104 + c = convertor.getCurrencies();
4.105 + currencies.addAll(c);
4.106 + }
4.107 +
4.108 + builder = new StringBuilder();
4.109 +
4.110 + for(final Currency currency : currencies)
4.111 + {
4.112 + builder.append(currency.getCurrencyCode());
4.113 + }
4.114 +
4.115 + return (builder.toString());
4.116 + }
4.117 + */
4.118 }
5.1 --- a/task2/solution04/src/org/apidesign/apifest08/currency/InvalidConversionException.java Tue Oct 07 00:19:37 2008 +0200
5.2 +++ b/task2/solution04/src/org/apidesign/apifest08/currency/InvalidConversionException.java Tue Oct 07 00:21:03 2008 +0200
5.3 @@ -28,18 +28,31 @@
5.4 */
5.5 private final Currency currencyB;
5.6
5.7 +
5.8 /**
5.9 - * Construct a new InvalidConversionException wit the specified message.
5.10 + * Construct a new InvalidConversionException with the specified message.
5.11 + *
5.12 + * @param msg the message for getMessage.
5.13 + * @param bad the currency that is not valid.
5.14 + */
5.15 + public InvalidConversionException(final String msg,
5.16 + final Currency bad)
5.17 + {
5.18 + this(msg, bad, null, null);
5.19 + }
5.20 +
5.21 + /**
5.22 + * Construct a new InvalidConversionException with the specified message.
5.23 *
5.24 * @param msg the message for getMessage.
5.25 * @param bad the currency that is not valid.
5.26 * @param a a valid currency.
5.27 * @param b a valid currency.
5.28 */
5.29 - public InvalidConversionException(final String msg,
5.30 - final Currency bad,
5.31 - final Currency a,
5.32 - final Currency b)
5.33 + public InvalidConversionException(final String msg,
5.34 + final Currency bad,
5.35 + final Currency a,
5.36 + final Currency b)
5.37 {
5.38 super(msg);
5.39
6.1 --- a/task2/solution04/test/org/apidesign/apifest08/test/Task1Test.java Tue Oct 07 00:19:37 2008 +0200
6.2 +++ b/task2/solution04/test/org/apidesign/apifest08/test/Task1Test.java Tue Oct 07 00:21:03 2008 +0200
6.3 @@ -3,6 +3,7 @@
6.4
6.5 import java.math.BigDecimal;
6.6 import java.util.Currency;
6.7 +import java.util.Set;
6.8 import junit.framework.TestCase;
6.9 import org.apidesign.apifest08.currency.Convertor;
6.10 import org.apidesign.apifest08.currency.ConvertorFactory;
6.11 @@ -50,7 +51,8 @@
6.12 */
6.13 public static Convertor createCZKtoUSD()
6.14 {
6.15 - return (ConvertorFactory.getConvertor("CZK", BigDecimal.valueOf(17.0), "USD", BigDecimal.valueOf(1)));
6.16 + return (ConvertorFactory.getConvertor("CZK", BigDecimal.valueOf(17.0),
6.17 + "USD", BigDecimal.valueOf(1)));
6.18 }
6.19
6.20 /** Create convertor that understands two currencies, CZK and
6.21 @@ -63,7 +65,8 @@
6.22 */
6.23 public static Convertor createSKKtoCZK()
6.24 {
6.25 - return (ConvertorFactory.getConvertor(Currency.getInstance("SKK"), BigDecimal.valueOf(100), Currency.getInstance("CZK"), BigDecimal.valueOf(80)));
6.26 + return (ConvertorFactory.getConvertor(Currency.getInstance("SKK"), BigDecimal.valueOf(100),
6.27 + Currency.getInstance("CZK"), BigDecimal.valueOf(80)));
6.28 }
6.29
6.30 /** Use the convertor from <code>createCZKtoUSD</code> method and do few conversions
6.31 @@ -180,5 +183,41 @@
6.32 assertEquals(CZK, ex.getCurrencyB());
6.33 }
6.34 }
6.35 +
6.36 + public void testGetCurrencies()
6.37 + {
6.38 + Convertor c;
6.39 + Set<Currency> currencies;
6.40 +
6.41 + c = createSKKtoCZK();
6.42 + currencies = c.getCurrencies();
6.43 + assertEquals(2, currencies.size());
6.44 + assertTrue(currencies.contains(Currency.getInstance("SKK")));
6.45 + assertTrue(currencies.contains(Currency.getInstance("CZK")));
6.46 +
6.47 + c = createCZKtoUSD();
6.48 + currencies = c.getCurrencies();
6.49 + assertEquals(2, currencies.size());
6.50 + assertTrue(currencies.contains(Currency.getInstance("USD")));
6.51 + assertTrue(currencies.contains(Currency.getInstance("CZK")));
6.52 + }
6.53 +
6.54 + public void testGetConverstionRate()
6.55 + throws InvalidConversionException
6.56 + {
6.57 + Convertor c;
6.58 +
6.59 + c = createSKKtoCZK();
6.60 + assertEquals(1.0, c.getConversionRate(Currency.getInstance("CZK"), Currency.getInstance("CZK")).doubleValue());
6.61 + assertEquals(1.0, c.getConversionRate(Currency.getInstance("SKK"), Currency.getInstance("SKK")).doubleValue());
6.62 + assertEquals(0.80, c.getConversionRate(Currency.getInstance("SKK"), Currency.getInstance("CZK")).doubleValue());
6.63 + assertEquals(1.25, c.getConversionRate(Currency.getInstance("CZK"), Currency.getInstance("SKK")).doubleValue());
6.64 +
6.65 + c = createCZKtoUSD();
6.66 + assertEquals(1.0, c.getConversionRate(Currency.getInstance("CZK"), Currency.getInstance("CZK")).doubleValue());
6.67 + assertEquals(1.0, c.getConversionRate(Currency.getInstance("USD"), Currency.getInstance("USD")).doubleValue());
6.68 + assertEquals(1.0/17.0, c.getConversionRate(Currency.getInstance("CZK"), Currency.getInstance("USD")).doubleValue(), 0.00000000000000001);
6.69 + assertEquals(17.0, c.getConversionRate(Currency.getInstance("USD"), Currency.getInstance("CZK")).doubleValue(), 0.00000000000000001);
6.70 + }
6.71 }
6.72
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/task2/solution04/test/org/apidesign/apifest08/test/Task2Test.java Tue Oct 07 00:21:03 2008 +0200
7.3 @@ -0,0 +1,179 @@
7.4 +package org.apidesign.apifest08.test;
7.5 +
7.6 +import java.math.BigDecimal;
7.7 +import java.util.Currency;
7.8 +import java.util.Set;
7.9 +import junit.framework.TestCase;
7.10 +import org.apidesign.apifest08.currency.Convertor;
7.11 +import org.apidesign.apifest08.currency.ConvertorFactory;
7.12 +import org.apidesign.apifest08.currency.InvalidConversionException;
7.13 +
7.14 +
7.15 +/** There are many currencies around the world and many banks manipulate
7.16 + * with more than one or two at the same time. As banks are usually the
7.17 + * best paying clients, which is true even in case of your Convertor API,
7.18 + * it is reasonable to listen to their requests.
7.19 + * <p>
7.20 + * The quest for today is to enhance your existing convertor API to hold
7.21 + * information about many currencies and allow conversions between any of them.
7.22 + * Also, as conversion rates for diferent currencies usually arise from various
7.23 + * bank departments, there is another important need. There is a need to
7.24 + * compose two convertors into one by merging all the information about
7.25 + * currencies they know about.
7.26 + */
7.27 +public class Task2Test extends TestCase
7.28 +{
7.29 + private final static Currency CZK;
7.30 + private final static Currency SKK;
7.31 + private final static Currency USD;
7.32 +
7.33 + static
7.34 + {
7.35 + CZK = Currency.getInstance("CZK");
7.36 + SKK = Currency.getInstance("SKK");
7.37 + USD = Currency.getInstance("USD");
7.38 + }
7.39 +
7.40 + public Task2Test(String testName)
7.41 + {
7.42 + super(testName);
7.43 + }
7.44 +
7.45 + @Override
7.46 + protected void setUp()
7.47 + throws Exception
7.48 + {
7.49 + }
7.50 +
7.51 + @Override
7.52 + protected void tearDown()
7.53 + throws Exception
7.54 + {
7.55 + }
7.56 +
7.57 + // As in Task1Test, keep in mind, that there are three parts
7.58 + // of the whole system:
7.59 + // 1. there is someone who knows the current exchange rate
7.60 + // 2. there is someone who wants to do the conversion
7.61 + // 3. there is the API between 1. and 2. which allows them to communicate
7.62 + //
7.63 + // Please backward compatibly enhance your existing API to support following
7.64 + // usecases:
7.65 + //
7.66 +
7.67 + /** Create convertor that understands two currencies, CZK and
7.68 + * SKK. Make 100 SKK == 75 CZK. This is method for the group of users that
7.69 + * knows the exchange rate, and needs to use the API to create objects
7.70 + * with the exchange rate. Anyone shall be ready to call this method without
7.71 + * any other method being called previously. The API itself shall know
7.72 + * nothing about any rates, before this method is called.
7.73 + */
7.74 + public static Convertor createTripleConvertor() {
7.75 + // Rates: 1USD = 15CZK
7.76 + // Rates: 1USD = 20SKK
7.77 + // Rates: 75CZK = 100SKK
7.78 + Convertor c = ConvertorFactory.mergeConvertors(
7.79 + ConvertorFactory.getConvertor(USD, BigDecimal.ONE, CZK, BigDecimal.valueOf(15.00)),
7.80 + ConvertorFactory.getConvertor(USD, BigDecimal.ONE, SKK, BigDecimal.valueOf(20.00))
7.81 + );
7.82 +
7.83 + return c;
7.84 + }
7.85 +
7.86 + /** Define convertor that understands three currencies. Use it.
7.87 + */
7.88 + public void testConvertorForUSDandCZKandSKK() throws Exception {
7.89 + Convertor c = createTripleConvertor();
7.90 +
7.91 + // convert $5 to CZK using c:
7.92 + // assertEquals("Result is 75 CZK");
7.93 + assertEquals(new BigDecimal("75.00"), c.convert(USD, CZK, BigDecimal.valueOf(5.00)));
7.94 +
7.95 + // convert $5 to SKK using c:
7.96 + // assertEquals("Result is 100 SKK");
7.97 + assertEquals(new BigDecimal("100.00"), c.convert(USD, SKK, BigDecimal.valueOf(5.00)));
7.98 +
7.99 + // convert 200SKK to CZK using c:
7.100 + // assertEquals("Result is 150 CZK");
7.101 + assertEquals(new BigDecimal("150.00"), c.convert(SKK, CZK, BigDecimal.valueOf(200.00)));
7.102 +
7.103 + // convert 200SKK to USK using c:
7.104 + // assertEquals("Result is 10 USD");
7.105 + assertEquals(new BigDecimal("10.00"), c.convert(SKK, USD, BigDecimal.valueOf(200.00)));
7.106 + }
7.107 +
7.108 + /** Merge all currency rates of convertor 1 with convertor 2.
7.109 + * Implement this using your API, preferably this method just delegates
7.110 + * into some API method which does the actual work, without requiring
7.111 + * API clients to code anything complex.
7.112 + */
7.113 + public static Convertor merge(Convertor one, Convertor two) {
7.114 + return ConvertorFactory.mergeConvertors(one, two);
7.115 + }
7.116 +
7.117 + /** Join the convertors from previous task, Task1Test and show that it
7.118 + * can be used to do reasonable conversions.
7.119 + */
7.120 + public void testConvertorComposition() throws Exception {
7.121 + Convertor c = merge(
7.122 + Task1Test.createCZKtoUSD(),
7.123 + Task1Test.createSKKtoCZK()
7.124 + );
7.125 +
7.126 + // convert $5 to CZK using c:
7.127 + // assertEquals("Result is 85 CZK");
7.128 + assertEquals(new BigDecimal("85.00"), c.convert(USD, CZK, BigDecimal.valueOf(5.00)));
7.129 +
7.130 + // convert $8 to CZK using c:
7.131 + // assertEquals("Result is 136 CZK");
7.132 + assertEquals(new BigDecimal("136.00"), c.convert(USD, CZK, BigDecimal.valueOf(8.00)));
7.133 +
7.134 + // convert 1003CZK to USD using c:
7.135 + // assertEquals("Result is 59 USD");
7.136 + assertEquals(new BigDecimal("59.00"), c.convert(CZK, USD, BigDecimal.valueOf(1003.00)));
7.137 +
7.138 + // convert 16CZK using c:
7.139 + // assertEquals("Result is 20 SKK");
7.140 + assertEquals(new BigDecimal("20.00"), c.convert(CZK, SKK, BigDecimal.valueOf(16.00)));
7.141 +
7.142 + // convert 500SKK to CZK using c:
7.143 + // assertEquals("Result is 400 CZK");
7.144 + assertEquals(new BigDecimal("400.00"), c.convert(SKK, CZK, BigDecimal.valueOf(500.00)));
7.145 + }
7.146 +
7.147 + public void testGetCurrencies()
7.148 + {
7.149 + Convertor c = merge(
7.150 + Task1Test.createCZKtoUSD(),
7.151 + Task1Test.createSKKtoCZK()
7.152 + );
7.153 + Set<Currency> currencies;
7.154 +
7.155 + currencies = c.getCurrencies();
7.156 + assertEquals(3, currencies.size());
7.157 + assertTrue(currencies.contains(Currency.getInstance("SKK")));
7.158 + assertTrue(currencies.contains(Currency.getInstance("CZK")));
7.159 + assertTrue(currencies.contains(Currency.getInstance("USD")));
7.160 + }
7.161 +
7.162 + public void testGetConverstionRate()
7.163 + throws InvalidConversionException
7.164 + {
7.165 + Convertor c = merge(
7.166 + Task1Test.createCZKtoUSD(),
7.167 + Task1Test.createSKKtoCZK()
7.168 + );
7.169 +
7.170 + assertEquals(1.0, c.getConversionRate(USD, USD).doubleValue(), 0.0000000000000001);
7.171 + assertEquals(17.0, c.getConversionRate(USD, CZK).doubleValue(), 0.0000000000000001);
7.172 + assertEquals(21.25, c.getConversionRate(USD, SKK).doubleValue(), 0.0000000000000001);
7.173 +
7.174 + assertEquals(1.0 / 17.0, c.getConversionRate(CZK, USD).doubleValue(), 0.0000000000000001);
7.175 + assertEquals(1.0, c.getConversionRate(CZK, CZK).doubleValue(), 0.0000000000000001);
7.176 + assertEquals(1.25, c.getConversionRate(CZK, SKK).doubleValue(), 0.0000000000000001);
7.177 +
7.178 + assertEquals(0.04705882352941176, c.getConversionRate(SKK, USD).doubleValue(), 0.0000000000000001);
7.179 + assertEquals(0.8, c.getConversionRate(SKK, CZK).doubleValue(), 0.0000000000000001);
7.180 + assertEquals(1.0, c.getConversionRate(SKK, SKK).doubleValue(), 0.0000000000000001);
7.181 + }
7.182 +}