# HG changeset patch # User Jaroslav Tulach # Date 1224257752 -7200 # Node ID bf7622ec1713c3d5ecc1cb2e78babbb011960609 # Parent aa3f99f845efe167820af6133ebb31822bf23fb1 Solution 14, task4 diff -r aa3f99f845ef -r bf7622ec1713 task4/solution14/src/org/apidesign/apifest08/currency/Convertor.java --- a/task4/solution14/src/org/apidesign/apifest08/currency/Convertor.java Fri Oct 17 17:34:40 2008 +0200 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/Convertor.java Fri Oct 17 17:35:52 2008 +0200 @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -22,7 +23,7 @@ private Rate rate = null; //version 2 field - private List currencyRates = null; + private List currencyRates = null; //version - for compatible mode private int instanceVersion = 0; //compatible mode because of problem with empty currency and CZE -> CZE (1:2) rate @@ -47,9 +48,9 @@ throw new IllegalArgumentException("CurrencyRates cannot be empty."); } Set> currencies = new HashSet>(); - List curRates = new ArrayList(); + List curRates = new ArrayList(); for (int i = 0; i < currencyRate.length; i++) { - CurrencyRate curRat = currencyRate[i]; + final CurrencyRate curRat = currencyRate[i]; if (curRat == null) { throw new IllegalArgumentException("Parameter cannot be null."); } @@ -59,13 +60,66 @@ throw new IllegalArgumentException("Pair of currencies in a currency rate cannot be defined twice"); } currencies.add(curPair); - + + if (curRat instanceof TimeLimitedCurrencyRate) { + curRates.add((TimeLimitedCurrencyRate)curRat); + } else { + curRates.add(new TimeLimitedCurrencyRate() { //create delegate which implements TimeLimitedCurrencyRate + public long getFromTime() { + return Long.MIN_VALUE; + } + + public long getToTime() { + return Long.MAX_VALUE; + } + + public String getCurrency1() { + return curRat.getCurrency1(); + } + + public String getCurrency2() { + return curRat.getCurrency2(); + } + + public Rate getRate() { + return curRat.getRate(); + } + }); + } + } + this.currencyRates = Collections.unmodifiableList(curRates); + } + + Convertor(final int fakeParameter, final TimeLimitedCurrencyRate ... currencyRate) { //use fake parameter just to specify which constructor should be invoked + instanceVersion = 3; + + if (currencyRate == null) { + throw new IllegalArgumentException("Parameter cannot be null."); + } + List curRates = new ArrayList(); + for (int i = 0; i < currencyRate.length; i++) { + final TimeLimitedCurrencyRate curRat = currencyRate[i]; + if (curRat == null) { + throw new IllegalArgumentException("Parameter cannot be null."); + } + curRates.add(curRat); } this.currencyRates = Collections.unmodifiableList(curRates); } public double convert(String fromCurrency, String toCurrency, int amount) { + return convert(fromCurrency, toCurrency, amount, System.currentTimeMillis()); + } + + public double convert(String fromCurrency, String toCurrency, int amount, Date date) { + if (date == null) { + throw new IllegalArgumentException("Date cannot be null"); + } + return convert(fromCurrency, toCurrency, amount, date.getTime()); + } + + public double convert(String fromCurrency, String toCurrency, int amount, long time) { if (instanceVersion == 1) { if ((fromCurrency == null) || (toCurrency == null)) { throw new IllegalArgumentException("All arguments have to be non-null."); @@ -81,27 +135,42 @@ } } else { //instanceVersion >= 2 //find suitable convertor - for (CurrencyRate curRate : currencyRates) { - if ((curRate.getCurrency1().equals(fromCurrency))&& - (curRate.getCurrency2().equals(toCurrency))) + for (TimeLimitedCurrencyRate curRate : currencyRates) { + if ((curRate.getCurrency1().equals(fromCurrency))&& + (curRate.getCurrency2().equals(toCurrency))&& + (curRate.getFromTime() <= time) && + (curRate.getToTime() >= time)) { return curRate.getRate().convertAtoB(amount); } } //suitable convertor not found, try to find inverse convertor - for (CurrencyRate curRate : currencyRates) { + for (TimeLimitedCurrencyRate curRate : currencyRates) { if ((curRate.getCurrency2().equals(fromCurrency))&& - (curRate.getCurrency1().equals(toCurrency))) + (curRate.getCurrency1().equals(toCurrency))&& + (curRate.getFromTime() <= time) && + (curRate.getToTime() >= time)) { return curRate.getRate().convertBtoA(amount); } - } + } //even inverse convertor not found throw new IllegalArgumentException("Cannot work with selected currencies."); } } public double convert(String fromCurrency, String toCurrency, double amount) { + return convert(fromCurrency, toCurrency, amount, System.currentTimeMillis()); + } + + public double convert(String fromCurrency, String toCurrency, double amount, Date date) { + if (date == null) { + throw new IllegalArgumentException("Date cannot be null"); + } + return convert(fromCurrency, toCurrency, amount, date.getTime()); + } + + public double convert(String fromCurrency, String toCurrency, double amount, long time) { if (instanceVersion == 1) { if ((fromCurrency == null) || (toCurrency == null)) { throw new IllegalArgumentException("All arguments have to be non-null."); @@ -117,21 +186,25 @@ } } else { //instanceVersion >= 2 //find suitable convertor - for (CurrencyRate curRate : currencyRates) { - if ((curRate.getCurrency1().equals(fromCurrency))&& - (curRate.getCurrency2().equals(toCurrency))) + for (TimeLimitedCurrencyRate curRate : currencyRates) { + if ((curRate.getCurrency1().equals(fromCurrency))&& + (curRate.getCurrency2().equals(toCurrency))&& + (curRate.getFromTime() <= time) && + (curRate.getToTime() >= time)) { return curRate.getRate().convertAtoB(amount); } } //suitable convertor not found, try to find inverse convertor - for (CurrencyRate curRate : currencyRates) { + for (TimeLimitedCurrencyRate curRate : currencyRates) { if ((curRate.getCurrency2().equals(fromCurrency))&& - (curRate.getCurrency1().equals(toCurrency))) + (curRate.getCurrency1().equals(toCurrency))&& + (curRate.getFromTime() <= time) && + (curRate.getToTime() >= time)) { return curRate.getRate().convertBtoA(amount); } - } + } //even inverse convertor not found throw new IllegalArgumentException("Cannot work with selected currencies."); } @@ -148,7 +221,19 @@ ret.add(new CurrencyRateImpl(currency1, currency2, rate)); //here it checks that currency rate is not nonsense return Collections.unmodifiableCollection(ret); } else { //instanceVersion >= 2 - return currencyRates; + List ret = new ArrayList(currencyRates); + return Collections.unmodifiableCollection(ret); + } + } + + public Collection getTimeLimitedCurrencyRates() { + if (instanceVersion == 1) { + List ret = new ArrayList(); + ret.add(new CurrencyRateImpl(currency1, currency2, rate)); //here it checks that currency rate is not nonsense + return Collections.unmodifiableCollection(ret); + } else { //instanceVersion >= 2 + List ret = new ArrayList(currencyRates); + return Collections.unmodifiableCollection(ret); } } diff -r aa3f99f845ef -r bf7622ec1713 task4/solution14/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- a/task4/solution14/src/org/apidesign/apifest08/currency/ConvertorFactory.java Fri Oct 17 17:34:40 2008 +0200 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/ConvertorFactory.java Fri Oct 17 17:35:52 2008 +0200 @@ -2,6 +2,8 @@ package org.apidesign.apifest08.currency; import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; import java.util.List; public final class ConvertorFactory { @@ -63,4 +65,54 @@ return new Convertor(currRates.toArray(new CurrencyRate[0])); } + public Convertor mergeConvertorsIgnoreEqualCurrencies(Convertor ... convertors) { + if (convertors == null) { + throw new IllegalArgumentException("Parameter cannot be null."); + } + if (convertors.length == 0) { + throw new IllegalArgumentException("Convertors cannot be empty."); + } + List currRates = new ArrayList(); + for (Convertor convertor : convertors) { + if (convertor == null) { + throw new IllegalArgumentException("Parameter cannot be null."); + } + currRates.addAll(convertor.getTimeLimitedCurrencyRates()); + } + + return new Convertor(4, currRates.toArray(new TimeLimitedCurrencyRate[0])); + } + + public Convertor limitConvertor(Convertor convertor, Date fromDate, Date toDate) { + if ((convertor == null)||(fromDate == null)||(toDate == null)) { + throw new IllegalArgumentException("Parameter cannot be null"); + } + Collection timeLimitedCurrencyRates = convertor.getTimeLimitedCurrencyRates(); + List filteredRates = new ArrayList(); + for (final TimeLimitedCurrencyRate timeLimitedCurrencyRate : timeLimitedCurrencyRates) { + final long newFrom = java.lang.Math.max(fromDate.getTime(), timeLimitedCurrencyRate.getFromTime()); + final long newTo = java.lang.Math.min(toDate.getTime(), timeLimitedCurrencyRate.getToTime()); + if (newTo >= newFrom) { + filteredRates.add(new TimeLimitedCurrencyRate() { //create delegate + public long getFromTime() { + return newFrom; + } + public long getToTime() { + return newTo; + } + public String getCurrency1() { + return timeLimitedCurrencyRate.getCurrency1(); + } + public String getCurrency2() { + return timeLimitedCurrencyRate.getCurrency2(); + } + public Rate getRate() { + return timeLimitedCurrencyRate.getRate(); + } + }); + } + } + return new Convertor(4, filteredRates.toArray(new TimeLimitedCurrencyRate[0])); + } + } diff -r aa3f99f845ef -r bf7622ec1713 task4/solution14/src/org/apidesign/apifest08/currency/CurrencyRateFactory.java --- a/task4/solution14/src/org/apidesign/apifest08/currency/CurrencyRateFactory.java Fri Oct 17 17:34:40 2008 +0200 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/CurrencyRateFactory.java Fri Oct 17 17:35:52 2008 +0200 @@ -21,5 +21,17 @@ public CurrencyRate createCurrencyRate(final String currency1, final String currency2, double amount1, double amount2) { return new CurrencyRateImpl(currency1, currency2, new Rate(amount1, amount2)); } + + public TimeLimitedCurrencyRate createCurrencyRateTimeLimited(final String currency1, final String currency2, final Rate rate, long fromTime, long toTime) { + return new CurrencyRateImpl(currency1, currency2, rate, fromTime, toTime); + } + + public TimeLimitedCurrencyRate createCurrencyRateTimeLimited(final String currency1, final String currency2, int amount1, int amount2, long fromTime, long toTime) { + return new CurrencyRateImpl(currency1, currency2, new Rate(amount1, amount2), fromTime, toTime); + } + + public TimeLimitedCurrencyRate createCurrencyRateTimeLimited(final String currency1, final String currency2, double amount1, double amount2, long fromTime, long toTime) { + return new CurrencyRateImpl(currency1, currency2, new Rate(amount1, amount2), fromTime, toTime); + } } diff -r aa3f99f845ef -r bf7622ec1713 task4/solution14/src/org/apidesign/apifest08/currency/CurrencyRateImpl.java --- a/task4/solution14/src/org/apidesign/apifest08/currency/CurrencyRateImpl.java Fri Oct 17 17:34:40 2008 +0200 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/CurrencyRateImpl.java Fri Oct 17 17:35:52 2008 +0200 @@ -1,10 +1,12 @@ package org.apidesign.apifest08.currency; -public final class CurrencyRateImpl implements CurrencyRate { +public final class CurrencyRateImpl implements CurrencyRate, TimeLimitedCurrencyRate { private String currency1; private String currency2; private Rate rate; + private long fromTime; + private long toTime; CurrencyRateImpl(final String currency1, final String currency2, final Rate rate) { if ((currency1 == null)||(currency2 == null) || (rate == null)) { @@ -20,6 +22,29 @@ this.currency1 = currency1; this.currency2 = currency2; this.rate = rate; + this.fromTime = Long.MIN_VALUE; + this.toTime = Long.MAX_VALUE; + } + + CurrencyRateImpl(final String currency1, final String currency2, final Rate rate, final long fromTime, final long toTime) { + if ((currency1 == null)||(currency2 == null) || (rate == null)) { + throw new IllegalArgumentException("Argument cannot be null."); + } + if ("".equals(currency1) || "".equals(currency2)) { + throw new IllegalArgumentException("Name of currency cannot be empty string"); + } + if (currency1.equals(currency2)) { + throw new IllegalArgumentException("Currencies in rate cannot be the same"); + } + if (fromTime > toTime) { + throw new IllegalArgumentException("Invalid time range"); + } + + this.currency1 = currency1; + this.currency2 = currency2; + this.rate = rate; + this.fromTime = fromTime; + this.toTime = toTime; } public String getCurrency1() { @@ -34,4 +59,12 @@ return rate; } + public long getFromTime() { + return fromTime; + } + + public long getToTime() { + return toTime; + } + } diff -r aa3f99f845ef -r bf7622ec1713 task4/solution14/src/org/apidesign/apifest08/currency/TimeLimitedCurrencyRate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/TimeLimitedCurrencyRate.java Fri Oct 17 17:35:52 2008 +0200 @@ -0,0 +1,9 @@ +package org.apidesign.apifest08.currency; + + +public interface TimeLimitedCurrencyRate extends CurrencyRate { + + public long getFromTime(); + public long getToTime(); + +} diff -r aa3f99f845ef -r bf7622ec1713 task4/solution14/test/org/apidesign/apifest08/test/Task2Test.java --- a/task4/solution14/test/org/apidesign/apifest08/test/Task2Test.java Fri Oct 17 17:34:40 2008 +0200 +++ b/task4/solution14/test/org/apidesign/apifest08/test/Task2Test.java Fri Oct 17 17:35:52 2008 +0200 @@ -86,7 +86,7 @@ * API clients to code anything complex. */ public static Convertor merge(Convertor one, Convertor two) { - return ConvertorFactory.newInstance().mergeConvertors(one,two); + return ConvertorFactory.newInstance().mergeConvertorsIgnoreEqualCurrencies(one,two); } /** Join the convertors from previous task, Task1Test and show that it diff -r aa3f99f845ef -r bf7622ec1713 task4/solution14/test/org/apidesign/apifest08/test/Task4Test.java --- a/task4/solution14/test/org/apidesign/apifest08/test/Task4Test.java Fri Oct 17 17:34:40 2008 +0200 +++ b/task4/solution14/test/org/apidesign/apifest08/test/Task4Test.java Fri Oct 17 17:35:52 2008 +0200 @@ -1,8 +1,10 @@ package org.apidesign.apifest08.test; import java.util.Date; +import java.util.GregorianCalendar; import junit.framework.TestCase; import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.ConvertorFactory; /** 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,19 +47,14 @@ * @return new convertor */ public static Convertor limitTo(Convertor old, Date from, Date till) { - return null; + return ConvertorFactory.newInstance().limitConvertor(old, from, till); } public void testCompositionOfLimitedConvertors() throws Exception { - if (Boolean.getBoolean("ignore.failing")) { - // 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 + Date d1 = (new GregorianCalendar(2008, 9, 1, 0, 0)).getTime(); // 2008-10-01 0:00 GMT + Date d2 = (new GregorianCalendar(2008, 9, 2, 0, 0)).getTime(); // 2008-10-02 0:00 GMT + Date d3 = (new GregorianCalendar(2008, 9, 3, 0, 0)).getTime(); // 2008-10-03 0:00 GMT Convertor c = Task2Test.merge( limitTo(Task1Test.createCZKtoUSD(), d1, d2), @@ -66,36 +63,77 @@ // convert $5 to CZK using c: // cannot convert as no rate is applicable to current date + try { + c.convert("USD", "CZK", 5); + fail(); + } catch (IllegalArgumentException e) { + //ok + } // convert $8 to CZK using c: // cannot convert as no rate is applicable to current date + try { + c.convert("USD", "CZK", 8); + fail(); + } catch (IllegalArgumentException e) { + //ok + } // convert 1003CZK to USD using c: // cannot convert as no rate is applicable to current date + try { + c.convert("CZK", "USD", 1003); + fail(); + } catch (IllegalArgumentException e) { + //ok + } // convert 16CZK using c: // cannot convert as no rate is applicable to current date + try { + c.convert("CZK", "USD", 16); + fail(); + } catch (IllegalArgumentException e) { + //ok + } // convert 500SKK to CZK using c: // cannot convert as no rate is applicable to current date + try { + c.convert("SKK", "CZK", 500); + fail(); + } catch (IllegalArgumentException e) { + //ok + } // convert $5 to CZK using c at 2008-10-01 6:00 GMT: // assertEquals("Result is 85 CZK"); + assertEquals("Result is 85 CZK", 85.0, c.convert("USD", "CZK", 5, (new GregorianCalendar(2008,9,1,6,0)).getTime())); // convert $8 to CZK using c at 2008-10-01 6:00 GMT: // assertEquals("Result is 136 CZK"); + assertEquals("Result is 136 CZK", 136.0, c.convert("USD", "CZK", 8, (new GregorianCalendar(2008,9,1,6,0)).getTime())); // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT: // assertEquals("Result is 59 USD"); + assertEquals("Result is 59 USD", 59.0, c.convert("CZK", "USD", 1003.0, (new GregorianCalendar(2008, 9, 1, 6, 0)).getTime())); // convert 16CZK using c at 2008-10-02 9:00 GMT: // assertEquals("Result is 20 SKK"); + assertEquals("Result is 20 SKK", 20.0, c.convert("CZK", "SKK", 16.0, (new GregorianCalendar(2008, 9, 2, 9, 0)).getTime())); // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT: // assertEquals("Result is 400 CZK"); + assertEquals("Result is 400 CZK", 400.0, c.convert("SKK", "CZK", 500.0, (new GregorianCalendar(2008, 9, 2, 9, 0)).getTime())); // 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("SKK", "CZK", 500.0, (new GregorianCalendar(2008, 9, 1, 6, 0)).getTime()); + fail(); + } catch (IllegalArgumentException e) { + //ok + } } /** Create convertor that understands two currencies, CZK and @@ -104,18 +142,13 @@ * @return prepared convertor ready for converting SKK to CZK and CZK to SKK */ public static Convertor createSKKtoCZK2() { - return null; + return ConvertorFactory.newInstance().createConvertor("SKK", "CZK", 100, 90); } public void testDateConvetorWithTwoDifferentRates() throws Exception { - if (Boolean.getBoolean("ignore.failing")) { - // 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 + Date d1 = (new GregorianCalendar(2008,9,1,0,0)).getTime(); // 2008-10-01 0:00 GMT + Date d2 = (new GregorianCalendar(2008,9,2,0,0)).getTime(); // 2008-10-02 0:00 GMT + Date d3 = (new GregorianCalendar(2008,9,6,0,0)).getTime(); // 2008-10-03 0:00 GMT Convertor c = Task2Test.merge( limitTo(createSKKtoCZK2(), d1, d2), @@ -124,9 +157,11 @@ // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT: // assertEquals("Result is 400 CZK"); + assertEquals("Result is 400 CZK", 400.0, c.convert("SKK", "CZK", 500.0, (new GregorianCalendar(2008, 9, 2, 9, 0)).getTime())); // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT: // assertEquals("Result is 450 CZK"); + assertEquals("Result is 450 CZK", 450.0, c.convert("SKK", "CZK", 500.0, (new GregorianCalendar(2008, 9, 1, 6, 0)).getTime())); } }