task4/solution04/src/org/apidesign/apifest08/currency/DatedCompositeConvertorImpl.java
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/DatedCompositeConvertorImpl.java Fri Oct 17 17:40:14 2008 +0200
1.3 @@ -0,0 +1,319 @@
1.4 +package org.apidesign.apifest08.currency;
1.5 +
1.6 +import java.math.BigDecimal;
1.7 +import java.util.Currency;
1.8 +import java.util.Date;
1.9 +import java.util.HashMap;
1.10 +import java.util.Map;
1.11 +import java.util.Set;
1.12 +
1.13 +
1.14 +final class DatedCompositeConvertorImpl
1.15 + implements TimedConvertor
1.16 +{
1.17 + /**
1.18 + * The convertors that are supported.
1.19 + */
1.20 + private final DatedConvertor[] convertors;
1.21 +
1.22 + /**
1.23 + * Keeps track of what convertors to use to convert between currencies.
1.24 + */
1.25 + private final Map<DateRange, Map<Currency, Map<Currency, Convertor>>> datedConversions;
1.26 +
1.27 + {
1.28 + datedConversions = new HashMap<DateRange, Map<Currency, Map<Currency, Convertor>>>();
1.29 + }
1.30 +
1.31 + /**
1.32 + * Construct a ComositeConvertorImpl with the specified convertors.
1.33 + * This will result in all possible conversions between the supplied currencies being made.
1.34 + *
1.35 + * @param cs the convertors to use.
1.36 + * @throws IllegalArgumentException if any of the items in cs are null.
1.37 + */
1.38 + DatedCompositeConvertorImpl(Convertor ... cs)
1.39 + {
1.40 + int i;
1.41 +
1.42 + convertors = new DatedConvertor[cs.length];
1.43 + i = 0;
1.44 +
1.45 + for(final Convertor c : cs)
1.46 + {
1.47 + if(!(c instanceof DatedConvertor))
1.48 + {
1.49 + throw new IllegalArgumentException("cs must only contain DatedConvertors");
1.50 + }
1.51 +
1.52 + convertors[i] = (DatedConvertor)c;
1.53 + i++;
1.54 + }
1.55 +
1.56 + // track all of the known conversion
1.57 + for(final DatedConvertor convertor : convertors)
1.58 + {
1.59 + final Set<Currency> currencies;
1.60 + Map<Currency, Convertor> possible;
1.61 + DateRange range;
1.62 +
1.63 + if(convertor == null)
1.64 + {
1.65 + throw new IllegalArgumentException("cs cannot contain null");
1.66 + }
1.67 +
1.68 + currencies = convertor.getCurrencies();
1.69 + range = convertor.getDateRange();
1.70 +
1.71 + for(final Currency currency : currencies)
1.72 + {
1.73 + Map<Currency, Map<Currency, Convertor>> possibleConversions;
1.74 +
1.75 + possibleConversions = datedConversions.get(range);
1.76 +
1.77 + if(possibleConversions == null)
1.78 + {
1.79 + possibleConversions = new HashMap<Currency, Map<Currency, Convertor>>();
1.80 + datedConversions.put(range, possibleConversions);
1.81 + }
1.82 +
1.83 + possible = possibleConversions.get(currency);
1.84 +
1.85 + if(possible == null)
1.86 + {
1.87 + possible = new HashMap<Currency, Convertor>();
1.88 + possibleConversions.put(currency, possible);
1.89 + }
1.90 +
1.91 + for(final Currency c : currencies)
1.92 + {
1.93 + possible.put(c, convertor);
1.94 + }
1.95 + }
1.96 + }
1.97 +
1.98 +
1.99 + /*
1.100 + // make up conversions that can be derived... eg:
1.101 + // we have:
1.102 + // USD <-> CAD
1.103 + // CAD <-> CZK
1.104 + // SSK <-> GBP
1.105 + // we can derive:
1.106 + // USD <-> CZK
1.107 + // we cannot derive:
1.108 + // USD <-> GBP
1.109 + // CAD <-> GBP
1.110 + // CZK <-> GBP
1.111 + //
1.112 + // NOTE: no attempt is made to deal with dates for DatedConvertors... nothing we can do about it.
1.113 + do
1.114 + {
1.115 + newConvertors = 0;
1.116 +
1.117 + // todo... need to loop this until all the ones that can be handled are done.
1.118 + for(final Currency from : getCurrencies())
1.119 + {
1.120 + for(final Currency to : getCurrencies())
1.121 + {
1.122 + if(!(canConvert(from, to)))
1.123 + {
1.124 + final Set<Currency> fromCurrencies;
1.125 + final Set<Currency> toCurrencies;
1.126 + final Set<Currency> common;
1.127 + Map<Currency, Map<Currency, Convertor>> possibleConversions;
1.128 +
1.129 + possibleConversions.get(range);
1.130 + fromCurrencies = possibleConversions.get(from).keySet();
1.131 + toCurrencies = possibleConversions.get(to).keySet();
1.132 + common = new HashSet<Currency>();
1.133 +
1.134 + for(final Currency currency : fromCurrencies)
1.135 + {
1.136 + if(toCurrencies.contains(currency))
1.137 + {
1.138 + common.add(currency);
1.139 + }
1.140 + }
1.141 +
1.142 + for(final Currency currency : common)
1.143 + {
1.144 + final Convertor convertor;
1.145 +
1.146 + convertor = createConvertor(from, to, currency);
1.147 + possibleConversions.get(from).put(to, convertor);
1.148 + possibleConversions.get(to).put(from, convertor);
1.149 + newConvertors++;
1.150 + }
1.151 + }
1.152 + }
1.153 + }
1.154 + }
1.155 + while(newConvertors > 0);
1.156 + */
1.157 + }
1.158 +
1.159 + /**
1.160 + * Check to see if converting between the two currencies is possible.
1.161 + *
1.162 + * @param from the currency to convert from.
1.163 + * @param to the currency to convert to.
1.164 + * @return true if the conversion is possible.
1.165 + * @throws IllegalArgumentException if either from or to are null.
1.166 + */
1.167 + public boolean canConvert(final Currency from, final Currency to)
1.168 + {
1.169 + throw new UnsupportedOperationException();
1.170 + }
1.171 +
1.172 + /**
1.173 + * Get the currencies that the convertor supports. Just because a currency is
1.174 + * supported does not mean that canConvert will return true.
1.175 + *
1.176 + * @return the supported currencies.
1.177 + */
1.178 + public Set<Currency> getCurrencies()
1.179 + {
1.180 + throw new UnsupportedOperationException();
1.181 + }
1.182 +
1.183 + /**
1.184 + * Get the conversion rate between two currencies.
1.185 + *
1.186 + * @param from the currency to convert from.
1.187 + * @param to the currency to convert to.
1.188 + * @return the conversion rate between the two currencies.
1.189 + * @throws IllegalArgumentException if either from or to is null.
1.190 + * @throws InvalidConversionException if canConvert would return false.
1.191 + */
1.192 + public BigDecimal getConversionRate(final Currency from, final Currency to)
1.193 + throws InvalidConversionException
1.194 + {
1.195 + throw new UnsupportedOperationException();
1.196 + }
1.197 +
1.198 + private Convertor getConvertor(final Currency from, final Currency to, final Date date)
1.199 + {
1.200 + Map<Currency, Map<Currency, Convertor>> possibleConversions;
1.201 + final Map<Currency, Convertor> possible;
1.202 + Convertor convertor;
1.203 +
1.204 + if(from == null)
1.205 + {
1.206 + throw new IllegalArgumentException("from cannot be null");
1.207 + }
1.208 +
1.209 + if(to == null)
1.210 + {
1.211 + throw new IllegalArgumentException("to cannot be null");
1.212 + }
1.213 +
1.214 + possibleConversions = null;
1.215 +
1.216 + for(final DateRange range : datedConversions.keySet())
1.217 + {
1.218 + if(range.isInRange(date))
1.219 + {
1.220 + possibleConversions = datedConversions.get(range);
1.221 + break;
1.222 + }
1.223 + }
1.224 +
1.225 + if(possibleConversions == null)
1.226 + {
1.227 + return (null);
1.228 + }
1.229 +
1.230 + possible = possibleConversions.get(from);
1.231 +
1.232 +
1.233 + if(possible == null)
1.234 + {
1.235 + return (null);
1.236 + }
1.237 +
1.238 +
1.239 + convertor = possible.get(to);
1.240 +
1.241 +
1.242 + if(convertor == null)
1.243 + {
1.244 + return (null);
1.245 + }
1.246 +
1.247 + return (convertor);
1.248 + }
1.249 +
1.250 + /**
1.251 + * Convert an amount from one currency to another.
1.252 + *
1.253 + * @param from the currency to convert from.
1.254 + * @param to the currency to convert to.
1.255 + * @param amount the amount to convert.
1.256 + * @return the converted amount.
1.257 + * @throws IllegalArgumentException if any of the arguments are null.
1.258 + * @throws InvalidConversionException if either from or to are not valid for the convertor.
1.259 + */
1.260 + public BigDecimal convert(final Currency from,
1.261 + final Currency to,
1.262 + final BigDecimal amount)
1.263 + throws InvalidConversionException
1.264 + {
1.265 + throw new InvalidConversionException("No date for the conversion", from);
1.266 + }
1.267 +
1.268 + public BigDecimal convert(final Currency from,
1.269 + final Currency to,
1.270 + final BigDecimal amount,
1.271 + final Date date)
1.272 + throws InvalidConversionException
1.273 + {
1.274 + final Convertor convertor;
1.275 + final BigDecimal total;
1.276 +
1.277 + convertor = getConvertor(from, to, date);
1.278 +
1.279 + if(convertor == null)
1.280 + {
1.281 + throw new InvalidConversionException("cannot convert", from);
1.282 + }
1.283 +
1.284 + if(canConvert(from, to, date))
1.285 + {
1.286 + final TimedConvertor timeConvertor;
1.287 +
1.288 + timeConvertor = (TimedConvertor)convertor;
1.289 + total = timeConvertor.convert(from, to, amount, date);
1.290 + }
1.291 + else
1.292 + {
1.293 + throw new InvalidConversionException("cannot convert", from);
1.294 + }
1.295 +
1.296 + return (total);
1.297 + }
1.298 +
1.299 + public boolean canConvert(final Currency from,
1.300 + final Currency to,
1.301 + final Date date)
1.302 + {
1.303 + Convertor convertor;
1.304 + final boolean retVal;
1.305 +
1.306 + convertor = getConvertor(from, to, date);
1.307 +
1.308 + if(convertor != null)
1.309 + {
1.310 + final TimedConvertor timeConvertor;
1.311 +
1.312 + timeConvertor = (TimedConvertor)convertor;
1.313 + retVal = timeConvertor.canConvert(from, to, date);
1.314 + }
1.315 + else
1.316 + {
1.317 + retVal = false;
1.318 + }
1.319 +
1.320 + return (retVal);
1.321 + }
1.322 +}