task4/solution04/src/org/apidesign/apifest08/currency/DatedCompositeConvertorImpl.java
1 package org.apidesign.apifest08.currency;
3 import java.math.BigDecimal;
4 import java.util.Currency;
6 import java.util.HashMap;
11 final class DatedCompositeConvertorImpl
12 implements TimedConvertor
15 * The convertors that are supported.
17 private final DatedConvertor[] convertors;
20 * Keeps track of what convertors to use to convert between currencies.
22 private final Map<DateRange, Map<Currency, Map<Currency, Convertor>>> datedConversions;
25 datedConversions = new HashMap<DateRange, Map<Currency, Map<Currency, Convertor>>>();
29 * Construct a ComositeConvertorImpl with the specified convertors.
30 * This will result in all possible conversions between the supplied currencies being made.
32 * @param cs the convertors to use.
33 * @throws IllegalArgumentException if any of the items in cs are null.
35 DatedCompositeConvertorImpl(Convertor ... cs)
39 convertors = new DatedConvertor[cs.length];
42 for(final Convertor c : cs)
44 if(!(c instanceof DatedConvertor))
46 throw new IllegalArgumentException("cs must only contain DatedConvertors");
49 convertors[i] = (DatedConvertor)c;
53 // track all of the known conversion
54 for(final DatedConvertor convertor : convertors)
56 final Set<Currency> currencies;
57 Map<Currency, Convertor> possible;
62 throw new IllegalArgumentException("cs cannot contain null");
65 currencies = convertor.getCurrencies();
66 range = convertor.getDateRange();
68 for(final Currency currency : currencies)
70 Map<Currency, Map<Currency, Convertor>> possibleConversions;
72 possibleConversions = datedConversions.get(range);
74 if(possibleConversions == null)
76 possibleConversions = new HashMap<Currency, Map<Currency, Convertor>>();
77 datedConversions.put(range, possibleConversions);
80 possible = possibleConversions.get(currency);
84 possible = new HashMap<Currency, Convertor>();
85 possibleConversions.put(currency, possible);
88 for(final Currency c : currencies)
90 possible.put(c, convertor);
97 // make up conversions that can be derived... eg:
109 // NOTE: no attempt is made to deal with dates for DatedConvertors... nothing we can do about it.
114 // todo... need to loop this until all the ones that can be handled are done.
115 for(final Currency from : getCurrencies())
117 for(final Currency to : getCurrencies())
119 if(!(canConvert(from, to)))
121 final Set<Currency> fromCurrencies;
122 final Set<Currency> toCurrencies;
123 final Set<Currency> common;
124 Map<Currency, Map<Currency, Convertor>> possibleConversions;
126 possibleConversions.get(range);
127 fromCurrencies = possibleConversions.get(from).keySet();
128 toCurrencies = possibleConversions.get(to).keySet();
129 common = new HashSet<Currency>();
131 for(final Currency currency : fromCurrencies)
133 if(toCurrencies.contains(currency))
135 common.add(currency);
139 for(final Currency currency : common)
141 final Convertor convertor;
143 convertor = createConvertor(from, to, currency);
144 possibleConversions.get(from).put(to, convertor);
145 possibleConversions.get(to).put(from, convertor);
152 while(newConvertors > 0);
157 * Check to see if converting between the two currencies is possible.
159 * @param from the currency to convert from.
160 * @param to the currency to convert to.
161 * @return true if the conversion is possible.
162 * @throws IllegalArgumentException if either from or to are null.
164 public boolean canConvert(final Currency from, final Currency to)
166 throw new UnsupportedOperationException();
170 * Get the currencies that the convertor supports. Just because a currency is
171 * supported does not mean that canConvert will return true.
173 * @return the supported currencies.
175 public Set<Currency> getCurrencies()
177 throw new UnsupportedOperationException();
181 * Get the conversion rate between two currencies.
183 * @param from the currency to convert from.
184 * @param to the currency to convert to.
185 * @return the conversion rate between the two currencies.
186 * @throws IllegalArgumentException if either from or to is null.
187 * @throws InvalidConversionException if canConvert would return false.
189 public BigDecimal getConversionRate(final Currency from, final Currency to)
190 throws InvalidConversionException
192 throw new UnsupportedOperationException();
195 private Convertor getConvertor(final Currency from, final Currency to, final Date date)
197 Map<Currency, Map<Currency, Convertor>> possibleConversions;
198 final Map<Currency, Convertor> possible;
203 throw new IllegalArgumentException("from cannot be null");
208 throw new IllegalArgumentException("to cannot be null");
211 possibleConversions = null;
213 for(final DateRange range : datedConversions.keySet())
215 if(range.isInRange(date))
217 possibleConversions = datedConversions.get(range);
222 if(possibleConversions == null)
227 possible = possibleConversions.get(from);
236 convertor = possible.get(to);
239 if(convertor == null)
248 * Convert an amount from one currency to another.
250 * @param from the currency to convert from.
251 * @param to the currency to convert to.
252 * @param amount the amount to convert.
253 * @return the converted amount.
254 * @throws IllegalArgumentException if any of the arguments are null.
255 * @throws InvalidConversionException if either from or to are not valid for the convertor.
257 public BigDecimal convert(final Currency from,
259 final BigDecimal amount)
260 throws InvalidConversionException
262 throw new InvalidConversionException("No date for the conversion", from);
265 public BigDecimal convert(final Currency from,
267 final BigDecimal amount,
269 throws InvalidConversionException
271 final Convertor convertor;
272 final BigDecimal total;
274 convertor = getConvertor(from, to, date);
276 if(convertor == null)
278 throw new InvalidConversionException("cannot convert", from);
281 if(canConvert(from, to, date))
283 final TimedConvertor timeConvertor;
285 timeConvertor = (TimedConvertor)convertor;
286 total = timeConvertor.convert(from, to, amount, date);
290 throw new InvalidConversionException("cannot convert", from);
296 public boolean canConvert(final Currency from,
301 final boolean retVal;
303 convertor = getConvertor(from, to, date);
305 if(convertor != null)
307 final TimedConvertor timeConvertor;
309 timeConvertor = (TimedConvertor)convertor;
310 retVal = timeConvertor.canConvert(from, to, date);