1.1 --- a/task4/solution06/src/org/apidesign/apifest08/currency/Convertor.java Sat Oct 11 23:38:46 2008 +0200
1.2 +++ b/task4/solution06/src/org/apidesign/apifest08/currency/Convertor.java Fri Oct 17 17:32:34 2008 +0200
1.3 @@ -5,8 +5,12 @@
1.4 import java.math.BigDecimal;
1.5 import java.math.RoundingMode;
1.6 import java.util.ArrayList;
1.7 +import java.util.Calendar;
1.8 import java.util.Currency;
1.9 +import java.util.Date;
1.10 +import java.util.GregorianCalendar;
1.11 import java.util.List;
1.12 +import java.util.TimeZone;
1.13
1.14 /**
1.15 * Currency covertor.
1.16 @@ -27,7 +31,7 @@
1.17 notNull(currencyFirst, "currencyFirst");
1.18 notNull(currencySecond, "currencySecond");
1.19 notNull(rateValue, "rateValue");
1.20 - convertorDelegates.add(new ConvertorDelegate(new StaticRateProvider(rateValue), currencyFirst, currencySecond));
1.21 + convertorDelegates.add(new ConvertorDelegate(new StaticRateProvider(rateValue), currencyFirst, currencySecond, null, null));
1.22 }
1.23
1.24 /**
1.25 @@ -42,7 +46,23 @@
1.26 notNull(currencyFirst, "currencyFirst");
1.27 notNull(currencySecond, "currencySecond");
1.28 notNull(rateProvider, "rateProvider");
1.29 - convertorDelegates.add(new ConvertorDelegate(rateProvider, currencyFirst, currencySecond));
1.30 + convertorDelegates.add(new ConvertorDelegate(rateProvider, currencyFirst, currencySecond, null, null));
1.31 + }
1.32 +
1.33 + /**
1.34 + * Create new instance of the convertor. The associated rate(s) is timely limited
1.35 + * by a given 'from' and 'till'.
1.36 + * @param convertor
1.37 + * @param from
1.38 + * @param till
1.39 + */
1.40 + public Convertor(Convertor convertor, Date from, Date till) {
1.41 + notNull(convertor, "convertor");
1.42 + notNull(from, "from");
1.43 + notNull(till, "till");
1.44 + for(ConvertorDelegate delegate: convertor.convertorDelegates) {
1.45 + convertorDelegates.add(new ConvertorDelegate(delegate.rateProvider, delegate.first, delegate.second, from, till));
1.46 + }
1.47 }
1.48
1.49 /**
1.50 @@ -57,13 +77,16 @@
1.51
1.52 for(Convertor convertor: convertors) {
1.53 if(convertor != null) {
1.54 - convertorDelegates.addAll(convertor.convertorDelegates);
1.55 + for(ConvertorDelegate delegate: convertor.convertorDelegates) {
1.56 + convertorDelegates.add(new ConvertorDelegate(delegate.rateProvider, delegate.first, delegate.second, delegate.from, delegate.till));
1.57 + }
1.58 }
1.59 }
1.60 }
1.61
1.62 /**
1.63 - * Converts an amount value between the two currencies of this converter.
1.64 + * Converts an amount value between the two currencies of this converter. The rate is taken
1.65 + * for current time.
1.66 *
1.67 * @param amount an amount
1.68 * @param fromCurrency an amount currency
1.69 @@ -74,42 +97,64 @@
1.70 * @throws UnsupportedConversionException if the conversion between a given currencies is not supported.
1.71 */
1.72 public Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency) throws ConversionException {
1.73 + return convert(amount, fromCurrency, toCurrency, new Date(System.currentTimeMillis()));
1.74 + }
1.75 +
1.76 + /**
1.77 + * Converts an amount value between the two currencies of this converter and its
1.78 + * associated rate for a given time.
1.79 + *
1.80 + * @param amount an amount
1.81 + * @param fromCurrency an amount currency
1.82 + * @param toCurrency to a target currency
1.83 + * @param time time
1.84 + * @return a converted amount value
1.85 + *
1.86 + * @throws ConversionException if the conversion fails
1.87 + * @throws UnsupportedConversionException if the conversion between a given currencies and time is not supported.
1.88 + */
1.89 + public Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency, Date time) throws ConversionException {
1.90 notNull(amount, "amount");
1.91 notNull(fromCurrency, "fromCurrency");
1.92 - notNull(toCurrency, "toCurrency");
1.93 + notNull(toCurrency, "toCurrency");
1.94 + notNull(time, "time");
1.95 +
1.96 ConvertorDelegate appropriateDelegate = null;
1.97 -
1.98 //try find an appropriate delegate for conversion
1.99 for(ConvertorDelegate delegate : convertorDelegates) {
1.100 - if(delegate.isConversionSupported(fromCurrency, toCurrency)) {
1.101 - appropriateDelegate = delegate;
1.102 - break;
1.103 + if(delegate.isConversionSupported(fromCurrency, toCurrency) && delegate.isConversionSupported(time)) {
1.104 + appropriateDelegate = delegate;
1.105 }
1.106 }
1.107 -
1.108 if(appropriateDelegate == null) {
1.109 - throw new UnsupportedConversionException(fromCurrency, toCurrency);
1.110 + throw new UnsupportedConversionException(fromCurrency, toCurrency, time);
1.111 }
1.112
1.113 - return appropriateDelegate.convert(amount, fromCurrency, toCurrency);
1.114 + return appropriateDelegate.convert(amount, fromCurrency, toCurrency);
1.115 }
1.116
1.117 /**
1.118 * Internal delegate implements a logic for conversion between two currencies
1.119 - * and vice versa.
1.120 + * and vice versa. There could be time limitation for the associated rate.
1.121 + *
1.122 * @see #isConversionSupported(Currency, Currency)
1.123 */
1.124 private static class ConvertorDelegate {
1.125 +
1.126 private final Currency first;
1.127 private final Currency second;
1.128 private final RateProvider rateProvider;
1.129 + private final Date from;
1.130 + private final Date till;
1.131 public static final BigDecimal one = new BigDecimal(1);
1.132
1.133
1.134 - private ConvertorDelegate(RateProvider rateProvider, Currency currencyFirst, Currency currencySecond) {
1.135 + private ConvertorDelegate(RateProvider rateProvider, Currency currencyFirst, Currency currencySecond, Date from, Date till) {
1.136 this.rateProvider = rateProvider;
1.137 this.first = currencyFirst;
1.138 this.second = currencySecond;
1.139 + this.from = from;
1.140 + this.till = till;
1.141 }
1.142
1.143 private Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency) throws ConversionException {
1.144 @@ -147,6 +192,38 @@
1.145 private boolean isConversionSupported(Currency fromCurrency, Currency toCurrency) {
1.146 return ((fromCurrency == first || fromCurrency == second) && (toCurrency == first || toCurrency == second));
1.147 }
1.148 +
1.149 + /**
1.150 + * @return <code>true</code> if the delegate is able to convert in a given
1.151 + * time.
1.152 + */
1.153 + private boolean isConversionSupported(Date time) {
1.154 + boolean retVal;
1.155 + if(this.from != null && this.till != null) {
1.156 + retVal = getDateInGMT(from).getTime() <= getDateInGMT(time).getTime() && getDateInGMT(till).getTime() >= getDateInGMT(time).getTime();
1.157 + } else {
1.158 + retVal = true; //delegate is applicable "for eternity"
1.159 + }
1.160 + return retVal;
1.161 + }
1.162 +
1.163 + private Date getDateInGMT(Date currentDate) {
1.164 + TimeZone tz = TimeZone.getTimeZone("GMT");
1.165 +
1.166 + Calendar mbCal = new GregorianCalendar(tz);
1.167 + mbCal.setTimeInMillis(currentDate.getTime());
1.168 +
1.169 + Calendar cal = Calendar.getInstance();
1.170 + cal.set(Calendar.YEAR, mbCal.get(Calendar.YEAR));
1.171 + cal.set(Calendar.MONTH, mbCal.get(Calendar.MONTH));
1.172 + cal.set(Calendar.DAY_OF_MONTH, mbCal.get(Calendar.DAY_OF_MONTH));
1.173 + cal.set(Calendar.HOUR_OF_DAY, mbCal.get(Calendar.HOUR_OF_DAY));
1.174 + cal.set(Calendar.MINUTE, mbCal.get(Calendar.MINUTE));
1.175 + cal.set(Calendar.SECOND, mbCal.get(Calendar.SECOND));
1.176 + cal.set(Calendar.MILLISECOND, mbCal.get(Calendar.MILLISECOND));
1.177 +
1.178 + return cal.getTime();
1.179 + }
1.180 }
1.181
1.182 /**