1.1 --- a/task4/solution06/src/org/apidesign/apifest08/currency/Convertor.java Fri Oct 17 17:31:48 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 /**
2.1 --- a/task4/solution06/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java Fri Oct 17 17:31:48 2008 +0200
2.2 +++ b/task4/solution06/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java Fri Oct 17 17:32:34 2008 +0200
2.3 @@ -1,6 +1,7 @@
2.4 package org.apidesign.apifest08.currency;
2.5
2.6 import java.util.Currency;
2.7 +import java.util.Date;
2.8
2.9 public final class UnsupportedConversionException extends ConversionException{
2.10
2.11 @@ -14,6 +15,12 @@
2.12 this.from = from;
2.13 this.to = to;
2.14 }
2.15 +
2.16 + public UnsupportedConversionException(Currency from, Currency to, Date time) {
2.17 + super("Conversion from the currency " + from + " to the currency " + to + " or vice versa in not supported for time " + time );
2.18 + this.from = from;
2.19 + this.to = to;
2.20 + }
2.21
2.22 public Currency getFrom() {
2.23 return from;
3.1 --- a/task4/solution06/test/org/apidesign/apifest08/test/Task4Test.java Fri Oct 17 17:31:48 2008 +0200
3.2 +++ b/task4/solution06/test/org/apidesign/apifest08/test/Task4Test.java Fri Oct 17 17:32:34 2008 +0200
3.3 @@ -1,8 +1,13 @@
3.4 package org.apidesign.apifest08.test;
3.5
3.6 +import java.math.BigDecimal;
3.7 +import java.text.SimpleDateFormat;
3.8 import java.util.Date;
3.9 import junit.framework.TestCase;
3.10 import org.apidesign.apifest08.currency.Convertor;
3.11 +import org.apidesign.apifest08.currency.UnsupportedConversionException;
3.12 +
3.13 +import static org.apidesign.apifest08.test.Currencies.*;
3.14
3.15 /** The exchange rates are not always the same. They are changing. However
3.16 * as in order to predict the future, one needs to understand own past. That is
3.17 @@ -45,7 +50,7 @@
3.18 * @return new convertor
3.19 */
3.20 public static Convertor limitTo(Convertor old, Date from, Date till) {
3.21 - return null;
3.22 + return new Convertor(old, from, till);
3.23 }
3.24
3.25
3.26 @@ -54,10 +59,11 @@
3.27 // implement me! then delete this if statement
3.28 return;
3.29 }
3.30 -
3.31 - Date d1 = null; // 2008-10-01 0:00 GMT
3.32 - Date d2 = null; // 2008-10-02 0:00 GMT
3.33 - Date d3 = null; // 2008-10-03 0:00 GMT
3.34 + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
3.35 +
3.36 + Date d1 = df.parse("2008-10-01 0:00 GMT");
3.37 + Date d2 = df.parse("2008-10-02 0:00 GMT");
3.38 + Date d3 = df.parse("2008-10-03 0:00 GMT");
3.39
3.40 Convertor c = Task2Test.merge(
3.41 limitTo(Task1Test.createCZKtoUSD(), d1, d2),
3.42 @@ -65,37 +71,67 @@
3.43 );
3.44
3.45 // convert $5 to CZK using c:
3.46 - // cannot convert as no rate is applicable to current date
3.47 + try {
3.48 + c.convert(new BigDecimal(5), USD , CZK);
3.49 + fail("cannot convert as no rate is applicable to current date");
3.50 + } catch(UnsupportedConversionException e) {
3.51 + //expected
3.52 + }
3.53
3.54 // convert $8 to CZK using c:
3.55 - // cannot convert as no rate is applicable to current date
3.56 + try {
3.57 + c.convert(new BigDecimal(8), USD , CZK);
3.58 + fail("cannot convert as no rate is applicable to current date");
3.59 + } catch(UnsupportedConversionException e) {
3.60 + //expected
3.61 + }
3.62
3.63 // convert 1003CZK to USD using c:
3.64 - // cannot convert as no rate is applicable to current date
3.65 + try {
3.66 + c.convert(new BigDecimal(1003), CZK, USD);
3.67 + fail("cannot convert as no rate is applicable to current date");
3.68 + } catch(UnsupportedConversionException e) {
3.69 + //expected
3.70 + }
3.71
3.72 // convert 16CZK using c:
3.73 - // cannot convert as no rate is applicable to current date
3.74 + try {
3.75 + c.convert(new BigDecimal(16), CZK, USD);
3.76 + fail("cannot convert as no rate is applicable to current date");
3.77 + } catch(UnsupportedConversionException e) {
3.78 + //expected
3.79 + }
3.80
3.81 // convert 500SKK to CZK using c:
3.82 - // cannot convert as no rate is applicable to current date
3.83 + try {
3.84 + c.convert(new BigDecimal(500), SKK, CZK);
3.85 + fail("cannot convert as no rate is applicable to current date");
3.86 + } catch(UnsupportedConversionException e) {
3.87 + //expected
3.88 + }
3.89
3.90 - // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
3.91 - // assertEquals("Result is 85 CZK");
3.92 + // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
3.93 + assertEquals("Result is 85 CZK", 85, c.convert(new BigDecimal(5), USD, CZK, df.parse("2008-10-01 6:00 GMT")).getValue().intValue());
3.94
3.95 // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
3.96 - // assertEquals("Result is 136 CZK");
3.97 -
3.98 + assertEquals("Result is 136 CZK", 136, c.convert(new BigDecimal(8), USD, CZK, df.parse("2008-10-01 6:00 GMT")).getValue().intValue());
3.99 +
3.100 // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
3.101 - // assertEquals("Result is 59 USD");
3.102 + assertEquals("Result is 59 USD", 59, c.convert(new BigDecimal(1003), CZK, USD, df.parse("2008-10-01 6:00 GMT")).getValue().intValue());
3.103
3.104 // convert 16CZK using c at 2008-10-02 9:00 GMT:
3.105 - // assertEquals("Result is 20 SKK");
3.106 + assertEquals("Result is 20 SKK", 20, c.convert(new BigDecimal(16), CZK, SKK, df.parse("2008-10-02 9:00 GMT")).getValue().intValue());
3.107
3.108 // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
3.109 - // assertEquals("Result is 400 CZK");
3.110 + assertEquals("Result is 400 CZK", 400, c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-02 9:00 GMT")).getValue().intValue());
3.111
3.112 // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
3.113 - // cannot convert as no rate is applicable to current date
3.114 + try {
3.115 + c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-01 6:00 GMT"));
3.116 + fail("cannot convert as no rate is applicable to current date");
3.117 + } catch(UnsupportedConversionException e) {
3.118 + //expected
3.119 + }
3.120 }
3.121
3.122 /** Create convertor that understands two currencies, CZK and
3.123 @@ -104,7 +140,7 @@
3.124 * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
3.125 */
3.126 public static Convertor createSKKtoCZK2() {
3.127 - return null;
3.128 + return new Convertor(new BigDecimal("0.9"), SKK, CZK);
3.129 }
3.130
3.131 public void testDateConvetorWithTwoDifferentRates() throws Exception {
3.132 @@ -112,10 +148,11 @@
3.133 // implement me! then delete this if statement
3.134 return;
3.135 }
3.136 -
3.137 - Date d1 = null; // 2008-10-01 0:00 GMT
3.138 - Date d2 = null; // 2008-10-02 0:00 GMT
3.139 - Date d3 = null; // 2008-10-03 0:00 GMT
3.140 + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
3.141 +
3.142 + Date d1 = df.parse("2008-10-01 0:00 GMT");
3.143 + Date d2 = df.parse("2008-10-02 0:00 GMT");
3.144 + Date d3 = df.parse("2008-10-03 0:00 GMT");
3.145
3.146 Convertor c = Task2Test.merge(
3.147 limitTo(createSKKtoCZK2(), d1, d2),
3.148 @@ -123,10 +160,10 @@
3.149 );
3.150
3.151 // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
3.152 - // assertEquals("Result is 400 CZK");
3.153 + assertEquals("Result is 400 CZK", 400, c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-02 9:00 GMT")).getValue().intValue());
3.154
3.155 // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
3.156 - // assertEquals("Result is 450 CZK");
3.157 + assertEquals("Result is 450 CZK", 450, c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-01 6:00 GMT")).getValue().intValue());
3.158 }
3.159
3.160 }