1.1 --- a/task1/solution12/src/org/apidesign/apifest08/currency/Convertor.java Tue Sep 30 12:01:18 2008 +0200
1.2 +++ b/task1/solution12/src/org/apidesign/apifest08/currency/Convertor.java Tue Sep 30 12:04:26 2008 +0200
1.3 @@ -1,6 +1,11 @@
1.4 package org.apidesign.apifest08.currency;
1.5
1.6 import java.util.Currency;
1.7 +import java.util.Hashtable;
1.8 +
1.9 +import org.apidesign.apifest08.currency.exceptions.ConvertorException;
1.10 +import org.apidesign.apifest08.currency.exceptions.InvalidCurrencyException;
1.11 +import org.apidesign.apifest08.currency.exceptions.UnknownConvertorException;
1.12
1.13 /**
1.14 * This is the skeleton class for your API. You need to make it public, so it is accessible to your client code
1.15 @@ -11,12 +16,56 @@
1.16 */
1.17 public class Convertor {
1.18
1.19 - private Currency currency1;
1.20 - private Currency currency2;
1.21 + private static Hashtable<String, ExchangeRate> exchangeRates;
1.22
1.23 - private Convertor(Currency currency1, Currency currency2) {
1.24 - this.currency1 = currency1;
1.25 - this.currency2 = currency2;
1.26 + private ExchangeRate exchangeRate12;
1.27 + private ExchangeRate exchangeRate21;
1.28 +
1.29 + private Convertor(Currency currency1, Currency currency2) throws UnknownConvertorException {
1.30 + String key12 = currency1.getCurrencyCode() + currency2.getCurrencyCode();
1.31 + String key21 = currency2.getCurrencyCode() + currency1.getCurrencyCode();
1.32 +
1.33 + if (!(exchangeRates.containsKey(key12) && exchangeRates.containsKey(key21))) {
1.34 + throw new UnknownConvertorException("Selected convertor (" + currency1.getCurrencyCode() + "->"
1.35 + + currency2.getCurrencyCode() + ") has not defined any rates!!!");
1.36 + }
1.37 +
1.38 + this.exchangeRate12 = exchangeRates.get(key12);
1.39 + this.exchangeRate21 = exchangeRates.get(key21);
1.40 + }
1.41 +
1.42 + /**
1.43 + * Sets convertor rate for selected currencies.
1.44 + * @param currency1
1.45 + * one of the currencies we want to convert to/from
1.46 + * @param currency2
1.47 + * the other currency
1.48 + * @param rate
1.49 + * exchange rate from currency1 to currency2
1.50 + * @param unit
1.51 + * unit of exchangeRate (USD->CZK - unit=1, you exchange one dollar, SKK->CZK unit=100, exchange rate is for
1.52 + * 100SKK)
1.53 + */
1.54 + public static void setConvertorRates(Currency currency1, Currency currency2, double rate, double unit) {
1.55 + if (currency1 == null || currency2 == null) {
1.56 + throw new ConvertorException("None of the currencies should be null!!!");
1.57 + }
1.58 +
1.59 + if (rate <= 0 || unit <= 0) {
1.60 + throw new ConvertorException("Rate(" + rate + ") and unit(" + unit + ") has to be grater then zero!!!");
1.61 + }
1.62 +
1.63 + if (exchangeRates == null) {
1.64 + exchangeRates = new Hashtable<String, ExchangeRate>();
1.65 + }
1.66 +
1.67 + String key12 = currency1.getCurrencyCode() + currency2.getCurrencyCode();
1.68 + String key21 = currency2.getCurrencyCode() + currency1.getCurrencyCode();
1.69 + double recountedRate = (unit / rate) * unit;
1.70 +
1.71 + exchangeRates.put(key12, new ExchangeRate(currency1, currency2, rate, unit));
1.72 + exchangeRates.put(key21, new ExchangeRate(currency2, currency1, recountedRate, unit));
1.73 +
1.74 }
1.75
1.76 /**
1.77 @@ -26,10 +75,13 @@
1.78 * @param currency2
1.79 * the other currency
1.80 * @return new instance of convertor
1.81 + * @throws UnknownConvertorException
1.82 + * thrown if convertor for selected currencies has not been defined
1.83 */
1.84 - public static Convertor getConvertorInstance(Currency currency1, Currency currency2) {
1.85 - if (currency1 == null || currency2 == null)
1.86 + public static Convertor getConvertorInstance(Currency currency1, Currency currency2) throws UnknownConvertorException {
1.87 + if (currency1 == null || currency2 == null) {
1.88 throw new ConvertorException("None of the currencies should be null!!!");
1.89 + }
1.90 return new Convertor(currency1, currency2);
1.91 }
1.92
1.93 @@ -37,30 +89,32 @@
1.94 * Converts selected amout of selected currency to other currency of this convertor instance.
1.95 * @param amount
1.96 * amount to convert
1.97 - * @param actualCurrency
1.98 + * @param originalCurrency
1.99 * currency of this amount
1.100 + * @param newCurrency
1.101 + * currency to which we want convert
1.102 * @return converted amount
1.103 + * @throws InvalidCurrencyException
1.104 + * while one or both currencies doesn't fit for this convertor
1.105 */
1.106 - public double convert(double amount, Currency actualCurrency) {
1.107 - ExchangeRate exchangeRate = null;
1.108 + public double convert(double amount, Currency originalCurrency, Currency newCurrency) throws InvalidCurrencyException {
1.109 + ExchangeRate actualyUsedExchangeRate = null;
1.110
1.111 - if (actualCurrency == null) {
1.112 - throw new ConvertorException("Selected currency is null!!!");
1.113 + if (originalCurrency == null) {
1.114 + throw new ConvertorException("Original currency is null!!!");
1.115 }
1.116
1.117 - if (currency1.getSymbol().equals(actualCurrency.getSymbol())
1.118 - || currency2.getSymbol().equals(actualCurrency.getSymbol())) {
1.119 - exchangeRate = getExchangeRate(actualCurrency);
1.120 - } else {
1.121 - throw new ConvertorException("This convertor could not be used for selected currency("
1.122 - + actualCurrency.getCurrencyCode() + ") " + this + "!!!");
1.123 + if (newCurrency == null) {
1.124 + throw new ConvertorException("Destination currency is null!!!");
1.125 }
1.126
1.127 - return countResult(exchangeRate, amount);
1.128 + actualyUsedExchangeRate = getExchangeRate(originalCurrency, newCurrency);
1.129 +
1.130 + return countResult(actualyUsedExchangeRate, amount);
1.131 }
1.132
1.133 - private double countResult(ExchangeRate exchangeRate, double amount) {
1.134 - return amount * exchangeRate.getRate() / exchangeRate.getUnit();
1.135 + private double countResult(ExchangeRate actualyUsedExchangeRate, double amount) {
1.136 + return amount * actualyUsedExchangeRate.getRate() / actualyUsedExchangeRate.getUnit();
1.137 }
1.138
1.139 /**
1.140 @@ -69,42 +123,27 @@
1.141 * actual currency we want to convert
1.142 * @return actual exchange rate of this convertor for selected currency
1.143 */
1.144 - private ExchangeRate getExchangeRate(Currency actualCurrency) {
1.145 - ExchangeRate exchangeRate = null;
1.146 + private ExchangeRate getExchangeRate(Currency originalCurrency, Currency newCurrency) throws InvalidCurrencyException {
1.147 + ExchangeRate actualyUsedExchangeRate = null;
1.148
1.149 - if (actualCurrency.getCurrencyCode().equals(currency1.getCurrencyCode())) {
1.150 - exchangeRate = getExchangeRateInstance(actualCurrency, currency2);
1.151 - } else if (actualCurrency.getCurrencyCode().equals(currency2.getCurrencyCode())) {
1.152 - exchangeRate = getExchangeRateInstance(actualCurrency, currency1);
1.153 + if (originalCurrency.getCurrencyCode().equals(exchangeRate12.getOriginalCurrency().getCurrencyCode())
1.154 + && newCurrency.getCurrencyCode().equals(exchangeRate12.getNewCurrency().getCurrencyCode())) {
1.155 + actualyUsedExchangeRate = exchangeRate12;
1.156 + } else if (originalCurrency.getCurrencyCode().equals(exchangeRate21.getOriginalCurrency().getCurrencyCode())
1.157 + && newCurrency.getCurrencyCode().equals(exchangeRate21.getNewCurrency().getCurrencyCode())) {
1.158 + actualyUsedExchangeRate = exchangeRate21;
1.159 + } else {
1.160 + throw new InvalidCurrencyException("This convertor " + this
1.161 + + " could not be used for converting selected currencies (" + originalCurrency.getCurrencyCode() + "->"
1.162 + + newCurrency.getCurrencyCode() + ") !!!");
1.163 }
1.164
1.165 - return exchangeRate;
1.166 - }
1.167 -
1.168 - /**
1.169 - * Returns actual exchange rate between original and destination currency.
1.170 - * @param originalCurrency
1.171 - * original currency, from which we want to convert
1.172 - * @param newCurrency
1.173 - * destination currency, to which we want to convert
1.174 - * @return exchange rate between this two currencies
1.175 - */
1.176 - private ExchangeRate getExchangeRateInstance(Currency originalCurrency, Currency newCurrency) {
1.177 - if ("USD".equals(originalCurrency.getCurrencyCode()) && "CZK".equals(newCurrency.getCurrencyCode())) {
1.178 - return new ExchangeRate(originalCurrency, newCurrency, 17d, 1d);
1.179 - } else if ("CZK".equals(originalCurrency.getCurrencyCode()) && "USD".equals(newCurrency.getCurrencyCode())) {
1.180 - return new ExchangeRate(originalCurrency, newCurrency, 1d / 17d, 1d);
1.181 - } else if ("SKK".equals(originalCurrency.getCurrencyCode()) && "CZK".equals(newCurrency.getCurrencyCode())) {
1.182 - return new ExchangeRate(originalCurrency, newCurrency, 80d, 100d);
1.183 - } else if ("CZK".equals(originalCurrency.getCurrencyCode()) && "SKK".equals(newCurrency.getCurrencyCode())) {
1.184 - return new ExchangeRate(originalCurrency, newCurrency, 100d / (80d / 100d), 100d);
1.185 - } else {
1.186 - throw new ConvertorException("Defined currencies don't have secified rates ["
1.187 - + originalCurrency.getCurrencyCode() + ", " + newCurrency.getCurrencyCode() + "]!!!");
1.188 - }
1.189 + return actualyUsedExchangeRate;
1.190 }
1.191
1.192 public String toString() {
1.193 - return "Converter [" + currency1.getCurrencyCode() + ", " + currency2.getCurrencyCode() + "]";
1.194 + String currency1Code = exchangeRate12.getOriginalCurrency().getCurrencyCode();
1.195 + String currency2Code = exchangeRate12.getNewCurrency().getCurrencyCode();
1.196 + return "Converter [" + currency1Code + "->" + currency2Code + ", " + currency2Code + "->" + currency1Code + "]";
1.197 }
1.198 }
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/task1/solution12/src/org/apidesign/apifest08/currency/exceptions/ConvertorException.java Tue Sep 30 12:04:26 2008 +0200
2.3 @@ -0,0 +1,20 @@
2.4 +package org.apidesign.apifest08.currency.exceptions;
2.5 +
2.6 +public class ConvertorException extends RuntimeException {
2.7 +
2.8 + public ConvertorException() {
2.9 + }
2.10 +
2.11 + public ConvertorException(String message) {
2.12 + super(message);
2.13 + }
2.14 +
2.15 + public ConvertorException(Throwable cause) {
2.16 + super(cause);
2.17 + }
2.18 +
2.19 + public ConvertorException(String message, Throwable cause) {
2.20 + super(message, cause);
2.21 + }
2.22 +
2.23 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/task1/solution12/src/org/apidesign/apifest08/currency/exceptions/InvalidCurrencyException.java Tue Sep 30 12:04:26 2008 +0200
3.3 @@ -0,0 +1,20 @@
3.4 +package org.apidesign.apifest08.currency.exceptions;
3.5 +
3.6 +public class InvalidCurrencyException extends Exception {
3.7 +
3.8 + public InvalidCurrencyException() {
3.9 + }
3.10 +
3.11 + public InvalidCurrencyException(String message) {
3.12 + super(message);
3.13 + }
3.14 +
3.15 + public InvalidCurrencyException(Throwable cause) {
3.16 + super(cause);
3.17 + }
3.18 +
3.19 + public InvalidCurrencyException(String message, Throwable cause) {
3.20 + super(message, cause);
3.21 + }
3.22 +
3.23 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/task1/solution12/src/org/apidesign/apifest08/currency/exceptions/UnknownConvertorException.java Tue Sep 30 12:04:26 2008 +0200
4.3 @@ -0,0 +1,20 @@
4.4 +package org.apidesign.apifest08.currency.exceptions;
4.5 +
4.6 +public class UnknownConvertorException extends Exception {
4.7 +
4.8 + public UnknownConvertorException() {
4.9 + }
4.10 +
4.11 + public UnknownConvertorException(String message) {
4.12 + super(message);
4.13 + }
4.14 +
4.15 + public UnknownConvertorException(Throwable cause) {
4.16 + super(cause);
4.17 + }
4.18 +
4.19 + public UnknownConvertorException(String message, Throwable cause) {
4.20 + super(message, cause);
4.21 + }
4.22 +
4.23 +}
5.1 --- a/task1/solution12/test/org/apidesign/apifest08/test/Task1Test.java Tue Sep 30 12:01:18 2008 +0200
5.2 +++ b/task1/solution12/test/org/apidesign/apifest08/test/Task1Test.java Tue Sep 30 12:04:26 2008 +0200
5.3 @@ -5,6 +5,8 @@
5.4 import junit.framework.TestCase;
5.5
5.6 import org.apidesign.apifest08.currency.Convertor;
5.7 +import org.apidesign.apifest08.currency.exceptions.InvalidCurrencyException;
5.8 +import org.apidesign.apifest08.currency.exceptions.UnknownConvertorException;
5.9
5.10 /**
5.11 * Finish the Convertor API, and then write bodies of methods inside of this class to match the given tasks. To fullfil
5.12 @@ -24,40 +26,81 @@
5.13 protected void tearDown() throws Exception {
5.14 }
5.15
5.16 + //
5.17 + // Imagine that there are three parts of the whole system:
5.18 + // 1. there is someone who knows the current exchange rate
5.19 + // 2. there is someone who wants to do the conversion
5.20 + // 3. there is the API between 1. and 2. which allows them to communicate
5.21 + // Please design such API
5.22 + //
5.23 +
5.24 /**
5.25 - * Create convertor that understands two currencies, CZK and USD. Make 1 USD == 17 CZK. Creation of the convertor
5.26 - * shall not require subclassing of any class or interface on the client side.
5.27 + * Create convertor that understands two currencies, CZK and USD. Make 1 USD == 17 CZK. This is a method provided for
5.28 + * #1 group - e.g. those that know the exchange rate. They somehow need to create the objects from the API and tell
5.29 + * them the exchange rate. The API itself knows nothing about any rates, before the createCZKtoUSD method is called.
5.30 + * Creation of the convertor shall not require subclassing of any class or interface on the client side.
5.31 * @return prepared convertor ready for converting USD to CZK and CZK to USD
5.32 */
5.33 public static Convertor createCZKtoUSD() {
5.34 - return Convertor.getConvertorInstance(Currency.getInstance("CZK"), Currency.getInstance("USD"));
5.35 + // set exchange rates
5.36 + Convertor.setConvertorRates(Currency.getInstance("USD"), Currency.getInstance("CZK"), 17d, 1d);
5.37 +
5.38 + // create new instance
5.39 + Convertor convertor = null;
5.40 + try {
5.41 + convertor = Convertor.getConvertorInstance(Currency.getInstance("USD"), Currency.getInstance("CZK"));
5.42 + } catch (UnknownConvertorException e) {
5.43 + e.printStackTrace();
5.44 + }
5.45 +
5.46 + return convertor;
5.47 }
5.48
5.49 /**
5.50 - * Create convertor that understands two currencies, CZK and SKK. Make 100 SKK == 80 CZK. Creation of the convertor
5.51 - * shall not require subclassing of any class or interface on the client side.
5.52 + * Create convertor that understands two currencies, CZK and SKK. Make 100 SKK == 80 CZK. Again this is method for the
5.53 + * #1 group - it knows the exchange rate, and needs to use the API to create objects with the exchange rate. Anyone
5.54 + * shall be ready to call this method without any other method being called previously. The API itself shall know
5.55 + * nothing about any rates, before this method is called. Creation of the convertor shall not require subclassing of
5.56 + * any class or interface on the client side.
5.57 * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
5.58 */
5.59 public static Convertor createSKKtoCZK() {
5.60 - return Convertor.getConvertorInstance(Currency.getInstance("SKK"), Currency.getInstance("CZK"));
5.61 + // set exchange rates
5.62 + Convertor.setConvertorRates(Currency.getInstance("SKK"), Currency.getInstance("CZK"), 80d, 100d);
5.63 +
5.64 + // create new instance
5.65 + Convertor convertor = null;
5.66 + try {
5.67 + convertor = Convertor.getConvertorInstance(Currency.getInstance("SKK"), Currency.getInstance("CZK"));
5.68 + } catch (UnknownConvertorException e) {
5.69 + e.printStackTrace();
5.70 + }
5.71 +
5.72 + return convertor;
5.73 }
5.74
5.75 + //
5.76 + // now the methods for group #2 follow:
5.77 + // this group knows nothing about exchange rates, but knows how to use
5.78 + // the API to do conversions. It somehow (by calling one of the factory
5.79 + // methods) gets objects from the API and uses them to do the conversions.
5.80 + //
5.81 +
5.82 /**
5.83 * Use the convertor from <code>createCZKtoUSD</code> method and do few conversions with it.
5.84 */
5.85 public void testCurrencyCZKUSD() throws Exception {
5.86 Convertor c = createCZKtoUSD();
5.87 -
5.88 // convert $5 to CZK using c:
5.89 - double result = c.convert(5, Currency.getInstance("USD"));
5.90 + double result = c.convert(5, Currency.getInstance("USD"), Currency.getInstance("CZK"));
5.91 assertEquals("Result is not 85 CZK", 85.0, result);
5.92
5.93 // convert $8 to CZK
5.94 - result = c.convert(8, Currency.getInstance("USD"));
5.95 + result = c.convert(8, Currency.getInstance("USD"), Currency.getInstance("CZK"));
5.96 assertEquals("Result is not 136 CZK", 136.0, result);
5.97
5.98 // convert 1003CZK to USD
5.99 - result = c.convert(1003, Currency.getInstance("CZK"));
5.100 + result = c.convert(1003, Currency.getInstance("CZK"), Currency.getInstance("USD"));
5.101 assertEquals("Result is not 59 USD", 59.0, result);
5.102 }
5.103
5.104 @@ -68,11 +111,64 @@
5.105 Convertor c = createSKKtoCZK();
5.106
5.107 // convert 16CZK using c:
5.108 - double result = c.convert(16, Currency.getInstance("CZK"));
5.109 + double result = c.convert(16, Currency.getInstance("CZK"), Currency.getInstance("SKK"));
5.110 assertEquals("Result is not 20 SKK", 20.0, result);
5.111
5.112 // convert 500SKK to CZK
5.113 - result = c.convert(500, Currency.getInstance("SKK"));
5.114 + result = c.convert(500, Currency.getInstance("SKK"), Currency.getInstance("CZK"));
5.115 assertEquals("Result is not 400 CZK", 400.0, result);
5.116 }
5.117 +
5.118 + /**
5.119 + * Verify that the CZK to USD convertor knows nothing about SKK.
5.120 + */
5.121 + public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception {
5.122 + Convertor c = createCZKtoUSD();
5.123 + boolean exceptionThrown = false;
5.124 +
5.125 + // convert $5 to SKK, the API shall say this is not possible
5.126 + try {
5.127 + c.convert(5, Currency.getInstance("USD"), Currency.getInstance("SKK"));
5.128 + exceptionThrown = false;
5.129 + } catch (InvalidCurrencyException e) {
5.130 + exceptionThrown = true;
5.131 + }
5.132 + assertEquals("It should be impossible to convert to SKK with USD->CZK convertor", true, exceptionThrown);
5.133 +
5.134 + // convert 500 SKK to CZK, the API shall say this is not possible
5.135 + try {
5.136 + c.convert(500, Currency.getInstance("SKK"), Currency.getInstance("CZK"));
5.137 + exceptionThrown = false;
5.138 + } catch (InvalidCurrencyException e) {
5.139 + exceptionThrown = true;
5.140 + }
5.141 + assertEquals("It should be impossible to convert from SKK with USD->CZK convertor", true, exceptionThrown);
5.142 +
5.143 + }
5.144 +
5.145 + /**
5.146 + * Verify that the CZK to SKK convertor knows nothing about USD.
5.147 + */
5.148 + public void testCannotConvertToSKKwithSKKCZKConvertor() throws Exception {
5.149 + Convertor c = createSKKtoCZK();
5.150 + boolean exceptionThrown = false;
5.151 +
5.152 + // convert $5 to SKK, the API shall say this is not possible
5.153 + try {
5.154 + c.convert(5, Currency.getInstance("USD"), Currency.getInstance("SKK"));
5.155 + exceptionThrown = false;
5.156 + } catch (InvalidCurrencyException e) {
5.157 + exceptionThrown = true;
5.158 + }
5.159 + assertEquals("It should be impossible to convert form USD with SKK->CZK convertor", true, exceptionThrown);
5.160 +
5.161 + // convert 500 CZK to USD, the API shall say this is not possible
5.162 + try {
5.163 + c.convert(500, Currency.getInstance("CZK"), Currency.getInstance("USD"));
5.164 + exceptionThrown = false;
5.165 + } catch (InvalidCurrencyException e) {
5.166 + exceptionThrown = true;
5.167 + }
5.168 + assertEquals("It should be impossible to convert to USD with SKK->CZK convertor", true, exceptionThrown);
5.169 + }
5.170 }