# HG changeset patch # User Jaroslav Tulach # Date 1224257554 -7200 # Node ID 51f7b894eba62798f160e9cff24e6a1b4b8233bc # Parent 20d332739f60c856e82a585b310ca1540576200e Solution 6, task 4 diff -r 20d332739f60 -r 51f7b894eba6 task4/solution06/src/org/apidesign/apifest08/currency/Convertor.java --- a/task4/solution06/src/org/apidesign/apifest08/currency/Convertor.java Fri Oct 17 17:31:48 2008 +0200 +++ b/task4/solution06/src/org/apidesign/apifest08/currency/Convertor.java Fri Oct 17 17:32:34 2008 +0200 @@ -5,8 +5,12 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; +import java.util.Calendar; import java.util.Currency; +import java.util.Date; +import java.util.GregorianCalendar; import java.util.List; +import java.util.TimeZone; /** * Currency covertor. @@ -27,7 +31,7 @@ notNull(currencyFirst, "currencyFirst"); notNull(currencySecond, "currencySecond"); notNull(rateValue, "rateValue"); - convertorDelegates.add(new ConvertorDelegate(new StaticRateProvider(rateValue), currencyFirst, currencySecond)); + convertorDelegates.add(new ConvertorDelegate(new StaticRateProvider(rateValue), currencyFirst, currencySecond, null, null)); } /** @@ -42,7 +46,23 @@ notNull(currencyFirst, "currencyFirst"); notNull(currencySecond, "currencySecond"); notNull(rateProvider, "rateProvider"); - convertorDelegates.add(new ConvertorDelegate(rateProvider, currencyFirst, currencySecond)); + convertorDelegates.add(new ConvertorDelegate(rateProvider, currencyFirst, currencySecond, null, null)); + } + + /** + * Create new instance of the convertor. The associated rate(s) is timely limited + * by a given 'from' and 'till'. + * @param convertor + * @param from + * @param till + */ + public Convertor(Convertor convertor, Date from, Date till) { + notNull(convertor, "convertor"); + notNull(from, "from"); + notNull(till, "till"); + for(ConvertorDelegate delegate: convertor.convertorDelegates) { + convertorDelegates.add(new ConvertorDelegate(delegate.rateProvider, delegate.first, delegate.second, from, till)); + } } /** @@ -57,13 +77,16 @@ for(Convertor convertor: convertors) { if(convertor != null) { - convertorDelegates.addAll(convertor.convertorDelegates); + for(ConvertorDelegate delegate: convertor.convertorDelegates) { + convertorDelegates.add(new ConvertorDelegate(delegate.rateProvider, delegate.first, delegate.second, delegate.from, delegate.till)); + } } } } /** - * Converts an amount value between the two currencies of this converter. + * Converts an amount value between the two currencies of this converter. The rate is taken + * for current time. * * @param amount an amount * @param fromCurrency an amount currency @@ -74,42 +97,64 @@ * @throws UnsupportedConversionException if the conversion between a given currencies is not supported. */ public Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency) throws ConversionException { + return convert(amount, fromCurrency, toCurrency, new Date(System.currentTimeMillis())); + } + + /** + * Converts an amount value between the two currencies of this converter and its + * associated rate for a given time. + * + * @param amount an amount + * @param fromCurrency an amount currency + * @param toCurrency to a target currency + * @param time time + * @return a converted amount value + * + * @throws ConversionException if the conversion fails + * @throws UnsupportedConversionException if the conversion between a given currencies and time is not supported. + */ + public Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency, Date time) throws ConversionException { notNull(amount, "amount"); notNull(fromCurrency, "fromCurrency"); - notNull(toCurrency, "toCurrency"); + notNull(toCurrency, "toCurrency"); + notNull(time, "time"); + ConvertorDelegate appropriateDelegate = null; - //try find an appropriate delegate for conversion for(ConvertorDelegate delegate : convertorDelegates) { - if(delegate.isConversionSupported(fromCurrency, toCurrency)) { - appropriateDelegate = delegate; - break; + if(delegate.isConversionSupported(fromCurrency, toCurrency) && delegate.isConversionSupported(time)) { + appropriateDelegate = delegate; } } - if(appropriateDelegate == null) { - throw new UnsupportedConversionException(fromCurrency, toCurrency); + throw new UnsupportedConversionException(fromCurrency, toCurrency, time); } - return appropriateDelegate.convert(amount, fromCurrency, toCurrency); + return appropriateDelegate.convert(amount, fromCurrency, toCurrency); } /** * Internal delegate implements a logic for conversion between two currencies - * and vice versa. + * and vice versa. There could be time limitation for the associated rate. + * * @see #isConversionSupported(Currency, Currency) */ private static class ConvertorDelegate { + private final Currency first; private final Currency second; private final RateProvider rateProvider; + private final Date from; + private final Date till; public static final BigDecimal one = new BigDecimal(1); - private ConvertorDelegate(RateProvider rateProvider, Currency currencyFirst, Currency currencySecond) { + private ConvertorDelegate(RateProvider rateProvider, Currency currencyFirst, Currency currencySecond, Date from, Date till) { this.rateProvider = rateProvider; this.first = currencyFirst; this.second = currencySecond; + this.from = from; + this.till = till; } private Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency) throws ConversionException { @@ -147,6 +192,38 @@ private boolean isConversionSupported(Currency fromCurrency, Currency toCurrency) { return ((fromCurrency == first || fromCurrency == second) && (toCurrency == first || toCurrency == second)); } + + /** + * @return true if the delegate is able to convert in a given + * time. + */ + private boolean isConversionSupported(Date time) { + boolean retVal; + if(this.from != null && this.till != null) { + retVal = getDateInGMT(from).getTime() <= getDateInGMT(time).getTime() && getDateInGMT(till).getTime() >= getDateInGMT(time).getTime(); + } else { + retVal = true; //delegate is applicable "for eternity" + } + return retVal; + } + + private Date getDateInGMT(Date currentDate) { + TimeZone tz = TimeZone.getTimeZone("GMT"); + + Calendar mbCal = new GregorianCalendar(tz); + mbCal.setTimeInMillis(currentDate.getTime()); + + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.YEAR, mbCal.get(Calendar.YEAR)); + cal.set(Calendar.MONTH, mbCal.get(Calendar.MONTH)); + cal.set(Calendar.DAY_OF_MONTH, mbCal.get(Calendar.DAY_OF_MONTH)); + cal.set(Calendar.HOUR_OF_DAY, mbCal.get(Calendar.HOUR_OF_DAY)); + cal.set(Calendar.MINUTE, mbCal.get(Calendar.MINUTE)); + cal.set(Calendar.SECOND, mbCal.get(Calendar.SECOND)); + cal.set(Calendar.MILLISECOND, mbCal.get(Calendar.MILLISECOND)); + + return cal.getTime(); + } } /** diff -r 20d332739f60 -r 51f7b894eba6 task4/solution06/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java --- a/task4/solution06/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java Fri Oct 17 17:31:48 2008 +0200 +++ b/task4/solution06/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java Fri Oct 17 17:32:34 2008 +0200 @@ -1,6 +1,7 @@ package org.apidesign.apifest08.currency; import java.util.Currency; +import java.util.Date; public final class UnsupportedConversionException extends ConversionException{ @@ -14,6 +15,12 @@ this.from = from; this.to = to; } + + public UnsupportedConversionException(Currency from, Currency to, Date time) { + super("Conversion from the currency " + from + " to the currency " + to + " or vice versa in not supported for time " + time ); + this.from = from; + this.to = to; + } public Currency getFrom() { return from; diff -r 20d332739f60 -r 51f7b894eba6 task4/solution06/test/org/apidesign/apifest08/test/Task4Test.java --- a/task4/solution06/test/org/apidesign/apifest08/test/Task4Test.java Fri Oct 17 17:31:48 2008 +0200 +++ b/task4/solution06/test/org/apidesign/apifest08/test/Task4Test.java Fri Oct 17 17:32:34 2008 +0200 @@ -1,8 +1,13 @@ package org.apidesign.apifest08.test; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; import java.util.Date; import junit.framework.TestCase; import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.UnsupportedConversionException; + +import static org.apidesign.apifest08.test.Currencies.*; /** The exchange rates are not always the same. They are changing. However * as in order to predict the future, one needs to understand own past. That is @@ -45,7 +50,7 @@ * @return new convertor */ public static Convertor limitTo(Convertor old, Date from, Date till) { - return null; + return new Convertor(old, from, till); } @@ -54,10 +59,11 @@ // implement me! then delete this if statement return; } - - Date d1 = null; // 2008-10-01 0:00 GMT - Date d2 = null; // 2008-10-02 0:00 GMT - Date d3 = null; // 2008-10-03 0:00 GMT + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz"); + + Date d1 = df.parse("2008-10-01 0:00 GMT"); + Date d2 = df.parse("2008-10-02 0:00 GMT"); + Date d3 = df.parse("2008-10-03 0:00 GMT"); Convertor c = Task2Test.merge( limitTo(Task1Test.createCZKtoUSD(), d1, d2), @@ -65,37 +71,67 @@ ); // convert $5 to CZK using c: - // cannot convert as no rate is applicable to current date + try { + c.convert(new BigDecimal(5), USD , CZK); + fail("cannot convert as no rate is applicable to current date"); + } catch(UnsupportedConversionException e) { + //expected + } // convert $8 to CZK using c: - // cannot convert as no rate is applicable to current date + try { + c.convert(new BigDecimal(8), USD , CZK); + fail("cannot convert as no rate is applicable to current date"); + } catch(UnsupportedConversionException e) { + //expected + } // convert 1003CZK to USD using c: - // cannot convert as no rate is applicable to current date + try { + c.convert(new BigDecimal(1003), CZK, USD); + fail("cannot convert as no rate is applicable to current date"); + } catch(UnsupportedConversionException e) { + //expected + } // convert 16CZK using c: - // cannot convert as no rate is applicable to current date + try { + c.convert(new BigDecimal(16), CZK, USD); + fail("cannot convert as no rate is applicable to current date"); + } catch(UnsupportedConversionException e) { + //expected + } // convert 500SKK to CZK using c: - // cannot convert as no rate is applicable to current date + try { + c.convert(new BigDecimal(500), SKK, CZK); + fail("cannot convert as no rate is applicable to current date"); + } catch(UnsupportedConversionException e) { + //expected + } - // convert $5 to CZK using c at 2008-10-01 6:00 GMT: - // assertEquals("Result is 85 CZK"); + // convert $5 to CZK using c at 2008-10-01 6:00 GMT: + assertEquals("Result is 85 CZK", 85, c.convert(new BigDecimal(5), USD, CZK, df.parse("2008-10-01 6:00 GMT")).getValue().intValue()); // convert $8 to CZK using c at 2008-10-01 6:00 GMT: - // assertEquals("Result is 136 CZK"); - + assertEquals("Result is 136 CZK", 136, c.convert(new BigDecimal(8), USD, CZK, df.parse("2008-10-01 6:00 GMT")).getValue().intValue()); + // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT: - // assertEquals("Result is 59 USD"); + assertEquals("Result is 59 USD", 59, c.convert(new BigDecimal(1003), CZK, USD, df.parse("2008-10-01 6:00 GMT")).getValue().intValue()); // convert 16CZK using c at 2008-10-02 9:00 GMT: - // assertEquals("Result is 20 SKK"); + assertEquals("Result is 20 SKK", 20, c.convert(new BigDecimal(16), CZK, SKK, df.parse("2008-10-02 9:00 GMT")).getValue().intValue()); // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT: - // assertEquals("Result is 400 CZK"); + assertEquals("Result is 400 CZK", 400, c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-02 9:00 GMT")).getValue().intValue()); // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT: - // cannot convert as no rate is applicable to current date + try { + c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-01 6:00 GMT")); + fail("cannot convert as no rate is applicable to current date"); + } catch(UnsupportedConversionException e) { + //expected + } } /** Create convertor that understands two currencies, CZK and @@ -104,7 +140,7 @@ * @return prepared convertor ready for converting SKK to CZK and CZK to SKK */ public static Convertor createSKKtoCZK2() { - return null; + return new Convertor(new BigDecimal("0.9"), SKK, CZK); } public void testDateConvetorWithTwoDifferentRates() throws Exception { @@ -112,10 +148,11 @@ // implement me! then delete this if statement return; } - - Date d1 = null; // 2008-10-01 0:00 GMT - Date d2 = null; // 2008-10-02 0:00 GMT - Date d3 = null; // 2008-10-03 0:00 GMT + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz"); + + Date d1 = df.parse("2008-10-01 0:00 GMT"); + Date d2 = df.parse("2008-10-02 0:00 GMT"); + Date d3 = df.parse("2008-10-03 0:00 GMT"); Convertor c = Task2Test.merge( limitTo(createSKKtoCZK2(), d1, d2), @@ -123,10 +160,10 @@ ); // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT: - // assertEquals("Result is 400 CZK"); + assertEquals("Result is 400 CZK", 400, c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-02 9:00 GMT")).getValue().intValue()); // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT: - // assertEquals("Result is 450 CZK"); + assertEquals("Result is 450 CZK", 450, c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-01 6:00 GMT")).getValue().intValue()); } }