1.1 --- a/task2/solution11/nbproject/build-impl.xml Tue Oct 07 00:24:22 2008 +0200
1.2 +++ b/task2/solution11/nbproject/build-impl.xml Tue Oct 07 00:25:53 2008 +0200
1.3 @@ -218,13 +218,13 @@
1.4 </sequential>
1.5 </macrodef>
1.6 </target>
1.7 - <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
1.8 + <target name="-init-macrodef-nbjpda">
1.9 <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
1.10 <attribute default="${main.class}" name="name"/>
1.11 <attribute default="${debug.classpath}" name="classpath"/>
1.12 <attribute default="" name="stopclassname"/>
1.13 <sequential>
1.14 - <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
1.15 + <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="dt_socket">
1.16 <classpath>
1.17 <path path="@{classpath}"/>
1.18 </classpath>
1.19 @@ -255,12 +255,6 @@
1.20 <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
1.21 <istrue value="${have-jdk-older-than-1.4}"/>
1.22 </condition>
1.23 - <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
1.24 - <os family="windows"/>
1.25 - </condition>
1.26 - <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
1.27 - <isset property="debug.transport"/>
1.28 - </condition>
1.29 </target>
1.30 <target depends="-init-debug-args" name="-init-macrodef-debug">
1.31 <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
1.32 @@ -270,7 +264,7 @@
1.33 <sequential>
1.34 <java classname="@{classname}" dir="${work.dir}" fork="true">
1.35 <jvmarg line="${debug-args-line}"/>
1.36 - <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
1.37 + <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
1.38 <jvmarg line="${run.jvmargs}"/>
1.39 <classpath>
1.40 <path path="@{classpath}"/>
1.41 @@ -317,13 +311,6 @@
1.42 ===================
1.43 -->
1.44 <target depends="init" name="deps-jar" unless="no.deps"/>
1.45 - <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
1.46 - <target depends="init" name="-check-automatic-build">
1.47 - <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
1.48 - </target>
1.49 - <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
1.50 - <antcall target="clean"/>
1.51 - </target>
1.52 <target depends="init,deps-jar" name="-pre-pre-compile">
1.53 <mkdir dir="${build.classes.dir}"/>
1.54 </target>
1.55 @@ -344,7 +331,7 @@
1.56 <!-- Empty placeholder for easier customization. -->
1.57 <!-- You can override this target in the ../build.xml file. -->
1.58 </target>
1.59 - <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
1.60 + <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
1.61 <target name="-pre-compile-single">
1.62 <!-- Empty placeholder for easier customization. -->
1.63 <!-- You can override this target in the ../build.xml file. -->
1.64 @@ -358,7 +345,7 @@
1.65 <!-- Empty placeholder for easier customization. -->
1.66 <!-- You can override this target in the ../build.xml file. -->
1.67 </target>
1.68 - <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
1.69 + <target depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
1.70 <!--
1.71 ====================
1.72 JAR BUILDING SECTION
2.1 --- a/task2/solution11/nbproject/genfiles.properties Tue Oct 07 00:24:22 2008 +0200
2.2 +++ b/task2/solution11/nbproject/genfiles.properties Tue Oct 07 00:25:53 2008 +0200
2.3 @@ -4,5 +4,5 @@
2.4 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
2.5 # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
2.6 nbproject/build-impl.xml.data.CRC32=0e1e702f
2.7 -nbproject/build-impl.xml.script.CRC32=6cbb076a
2.8 -nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5
2.9 +nbproject/build-impl.xml.script.CRC32=c899f2cf
2.10 +nbproject/build-impl.xml.stylesheet.CRC32=487672f9
3.1 --- a/task2/solution11/src/org/apidesign/apifest08/currency/Convertor.java Tue Oct 07 00:24:22 2008 +0200
3.2 +++ b/task2/solution11/src/org/apidesign/apifest08/currency/Convertor.java Tue Oct 07 00:25:53 2008 +0200
3.3 @@ -1,5 +1,10 @@
3.4 package org.apidesign.apifest08.currency;
3.5
3.6 +import java.util.ArrayList;
3.7 +import java.util.Collection;
3.8 +import java.util.HashSet;
3.9 +import java.util.List;
3.10 +import java.util.Set;
3.11 import org.apidesign.apifest08.currency.Computer.ComputerRequest;
3.12 import org.apidesign.apifest08.currency.Computer.ComputerResponse;
3.13
3.14 @@ -10,31 +15,50 @@
3.15 * with amount stored in integer or double, that are identified
3.16 * with string value. Exchange rates are immutable.
3.17 *
3.18 + * In Task2's version provides support for multiple exchange rates
3.19 + * between different currencies & merging exchange rates from
3.20 + * existing convertors into new convertor's instance.
3.21 + * No time for javadoc these features, sorry.
3.22 + *
3.23 * @author ked
3.24 */
3.25 public final class Convertor<AmountType, IdentifierType> {
3.26
3.27 Computer<AmountType> computer;
3.28 - CurrencyValue<AmountType, IdentifierType> firstCurrencyExchangeRate;
3.29 - CurrencyValue<AmountType, IdentifierType> secondCurrencyExchangeRate;
3.30 + List<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates = new ArrayList<ExchangeRateValue<AmountType, IdentifierType>>();
3.31
3.32 Convertor(
3.33 Computer<AmountType> computer,
3.34 - CurrencyValue<AmountType, IdentifierType> firstCurrencyExchangeRate,
3.35 - CurrencyValue<AmountType, IdentifierType> secondCurrencyExchangeRate) {
3.36 - if (firstCurrencyExchangeRate.getIdentifier() == null ||
3.37 - secondCurrencyExchangeRate.getIdentifier() == null ||
3.38 - firstCurrencyExchangeRate.getIdentifier().equals(secondCurrencyExchangeRate.getIdentifier())) {
3.39 - throw new IllegalArgumentException("Inappropriate exchange rates' identifiers!");
3.40 + Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates) {
3.41 + this.computer = computer;
3.42 +
3.43 + for (ExchangeRateValue<AmountType, IdentifierType> exchangeRate : exchangeRates) {
3.44 + if (findExchangeRate(
3.45 + this.exchangeRates,
3.46 + exchangeRate.getCurrencyA().getIdentifier(),
3.47 + exchangeRate.getCurrencyB().getIdentifier()) != null) {
3.48 + throw new IllegalArgumentException("Duplicate exchange rate!");
3.49 + }
3.50 + this.exchangeRates.add(exchangeRate);
3.51 }
3.52 - this.computer = computer;
3.53 - this.firstCurrencyExchangeRate = firstCurrencyExchangeRate;
3.54 - this.secondCurrencyExchangeRate = secondCurrencyExchangeRate;
3.55 + }
3.56 +
3.57 + private ExchangeRateValue<AmountType, IdentifierType> findExchangeRate(
3.58 + Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates,
3.59 + IdentifierType currencyA,
3.60 + IdentifierType currencyB) {
3.61 + for (ExchangeRateValue<AmountType, IdentifierType> exchangeRate : exchangeRates) {
3.62 + if ((exchangeRate.getCurrencyA().getIdentifier().equals(currencyA) && exchangeRate.getCurrencyB().getIdentifier().equals(currencyB)) ||
3.63 + (exchangeRate.getCurrencyA().getIdentifier().equals(currencyB) && exchangeRate.getCurrencyB().getIdentifier().equals(currencyA))) {
3.64 + return exchangeRate;
3.65 + }
3.66 + }
3.67 + return null;
3.68 }
3.69
3.70 /**
3.71 * Convert an amount of the one currency to an amount of the another one currency
3.72 - * with respect to previously specified exchange rate.
3.73 + * with respect to previously specified exchange rates.
3.74 *
3.75 * @param targetCurrency an identifier of the requested currency
3.76 * @param currencyValue an amount of the another one currency
3.77 @@ -43,52 +67,117 @@
3.78 public CurrencyValue<AmountType, IdentifierType> convert(
3.79 IdentifierType targetCurrency,
3.80 CurrencyValue<AmountType, IdentifierType> currencyValue) {
3.81 - if (firstCurrencyExchangeRate.getIdentifier().equals(targetCurrency) &&
3.82 - secondCurrencyExchangeRate.getIdentifier().equals(currencyValue.getIdentifier())) {
3.83 - ComputerRequest<AmountType> computerRequest = new ComputerRequest<AmountType>();
3.84 - computerRequest.setInput(currencyValue.getAmount());
3.85 - computerRequest.setInputCurrencyRatio(secondCurrencyExchangeRate.getAmount());
3.86 - computerRequest.setOutputCurrencyRatio(firstCurrencyExchangeRate.getAmount());
3.87 - ComputerResponse<AmountType> computerResponse = computer.compute(computerRequest);
3.88 -
3.89 - return CurrencyValue.getCurrencyValue(
3.90 - computerResponse.getResult(),
3.91 - firstCurrencyExchangeRate.getIdentifier());
3.92 - } else if (secondCurrencyExchangeRate.getIdentifier().equals(targetCurrency) &&
3.93 - firstCurrencyExchangeRate.getIdentifier().equals(currencyValue.getIdentifier())) {
3.94 - ComputerRequest<AmountType> computerRequest = new ComputerRequest<AmountType>();
3.95 - computerRequest.setInput(currencyValue.getAmount());
3.96 - computerRequest.setInputCurrencyRatio(firstCurrencyExchangeRate.getAmount());
3.97 - computerRequest.setOutputCurrencyRatio(secondCurrencyExchangeRate.getAmount());
3.98 - ComputerResponse<AmountType> computerResponse = computer.compute(computerRequest);
3.99 -
3.100 - return CurrencyValue.getCurrencyValue(
3.101 - computerResponse.getResult(),
3.102 - secondCurrencyExchangeRate.getIdentifier());
3.103 - } else {
3.104 + ExchangeRateValue<AmountType, IdentifierType> exchangeRate =
3.105 + findExchangeRate(exchangeRates, currencyValue.getIdentifier(), targetCurrency);
3.106 + if (exchangeRate == null) {
3.107 throw new IllegalArgumentException("Inappropriate currencies to convert!");
3.108 }
3.109 +
3.110 + ComputerRequest<AmountType> computerRequest = new ComputerRequest<AmountType>();
3.111 + computerRequest.setInput(currencyValue.getAmount());
3.112 +
3.113 + IdentifierType targetCurrencyRef; // just for backward compatibility :-(
3.114 + if (exchangeRate.getCurrencyA().getIdentifier().equals(targetCurrency)) {
3.115 + computerRequest.setInputCurrencyRatio(exchangeRate.getCurrencyB().getAmount());
3.116 + computerRequest.setOutputCurrencyRatio(exchangeRate.getCurrencyA().getAmount());
3.117 + targetCurrencyRef = exchangeRate.getCurrencyA().getIdentifier();
3.118 + } else {
3.119 + computerRequest.setInputCurrencyRatio(exchangeRate.getCurrencyA().getAmount());
3.120 + computerRequest.setOutputCurrencyRatio(exchangeRate.getCurrencyB().getAmount());
3.121 + targetCurrencyRef = exchangeRate.getCurrencyB().getIdentifier();
3.122 + }
3.123 +
3.124 + ComputerResponse<AmountType> computerResponse = computer.compute(computerRequest);
3.125 + return CurrencyValue.getCurrencyValue(
3.126 + computerResponse.getResult(),
3.127 + targetCurrencyRef);
3.128 + }
3.129 +
3.130 + // ---
3.131 + // MERGING
3.132 + // ---
3.133 + static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> mergeConvertors(
3.134 + Computer<AmountType> computer,
3.135 + Collection<Convertor<AmountType, IdentifierType>> convertors) {
3.136 + Set<ExchangeRateValue<AmountType, IdentifierType>> exchangeRatesSet = new HashSet<ExchangeRateValue<AmountType, IdentifierType>>();
3.137 + for (Convertor<AmountType, IdentifierType> convertor : convertors) {
3.138 + exchangeRatesSet.addAll(convertor.exchangeRates);
3.139 + }
3.140 + return getConvertor(computer, exchangeRatesSet);
3.141 + }
3.142 +
3.143 + static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> mergeConvertors(
3.144 + Computer<AmountType> computer,
3.145 + Convertor<AmountType, IdentifierType> convertorA,
3.146 + Convertor<AmountType, IdentifierType> convertorB) {
3.147 + Collection<Convertor<AmountType, IdentifierType>> convertors =
3.148 + new ArrayList<Convertor<AmountType, IdentifierType>>();
3.149 + convertors.add(convertorA);
3.150 + convertors.add(convertorB);
3.151 + return mergeConvertors(computer, convertors);
3.152 + }
3.153 +
3.154 + public static Convertor<Double, String> mergeConvertorsDoubleString(
3.155 + Collection<Convertor<Double, String>> convertors) {
3.156 + return mergeConvertors(DoubleComputer, convertors);
3.157 + }
3.158 +
3.159 + public static Convertor<Double, String> mergeConvertorsDoubleString(
3.160 + Convertor<Double, String> convertorA,
3.161 + Convertor<Double, String> convertorB) {
3.162 + return mergeConvertors(DoubleComputer, convertorA, convertorB);
3.163 + }
3.164 +
3.165 + public static Convertor<Integer, String> mergeConvertorsIntegerString(
3.166 + Collection<Convertor<Integer, String>> convertors) {
3.167 + return mergeConvertors(IntegerComputer, convertors);
3.168 + }
3.169 +
3.170 + public static Convertor<Integer, String> mergeConvertorsIntegerString(
3.171 + Convertor<Integer, String> convertorA,
3.172 + Convertor<Integer, String> convertorB) {
3.173 + return mergeConvertors(IntegerComputer, convertorA, convertorB);
3.174 + }
3.175 +
3.176 + // ---
3.177 + // CREATION
3.178 + // ---
3.179 + static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertor(
3.180 + Computer<AmountType> computer, Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates) {
3.181 + return new Convertor<AmountType, IdentifierType>(computer, exchangeRates);
3.182 }
3.183
3.184 static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertor(
3.185 - Computer<AmountType> computer,
3.186 - CurrencyValue<AmountType, IdentifierType> firstCurrencyExchangeRate,
3.187 - CurrencyValue<AmountType, IdentifierType> secondCurrencyExchangeRate) {
3.188 - return new Convertor<AmountType, IdentifierType>(
3.189 - computer,
3.190 - firstCurrencyExchangeRate,
3.191 - secondCurrencyExchangeRate);
3.192 + Computer<AmountType> computer, ExchangeRateValue<AmountType, IdentifierType> exchangeRate) {
3.193 + Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates =
3.194 + new ArrayList<ExchangeRateValue<AmountType, IdentifierType>>();
3.195 + exchangeRates.add(exchangeRate);
3.196 + return getConvertor(computer, exchangeRates);
3.197 }
3.198
3.199 - static final Computer<Double> DoubleComputer = new Computer<Double>() {
3.200 + public static Convertor<Double, String> getConvertorDoubleString(
3.201 + Collection<ExchangeRateValue<Double, String>> exchangeRates) {
3.202 + return getConvertor(DoubleComputer, exchangeRates);
3.203 + }
3.204
3.205 - public ComputerResponse<Double> compute(ComputerRequest<Double> request) {
3.206 - ComputerResponse<Double> response = new ComputerResponse<Double>();
3.207 - response.setResult(request.getInput() * request.getOutputCurrencyRatio() / request.getInputCurrencyRatio());
3.208 - return response;
3.209 - }
3.210 - };
3.211 + public static Convertor<Double, String> getConvertorDoubleString(
3.212 + ExchangeRateValue<Double, String> exchangeRate) {
3.213 + return getConvertor(DoubleComputer, exchangeRate);
3.214 + }
3.215
3.216 + public static Convertor<Integer, String> getConvertorIntegerString(
3.217 + Collection<ExchangeRateValue<Integer, String>> exchangeRates) {
3.218 + return getConvertor(IntegerComputer, exchangeRates);
3.219 + }
3.220 +
3.221 + public static Convertor<Integer, String> getConvertorIntegerString(
3.222 + ExchangeRateValue<Integer, String> exchangeRate) {
3.223 + return getConvertor(IntegerComputer, exchangeRate);
3.224 + }
3.225 +
3.226 + // ---
3.227 + // BACKWARD COMPATIBILITY - CREATION
3.228 + // ---
3.229 /**
3.230 * Creates convertor for Double|String values with specified exchange rate
3.231 * between two currencies.
3.232 @@ -97,21 +186,11 @@
3.233 * @param secondCurrencyExchangeRate second currency
3.234 * @return convertor
3.235 */
3.236 -
3.237 public static Convertor<Double, String> getConvertorDoubleString(
3.238 CurrencyValue<Double, String> firstCurrencyExchangeRate,
3.239 CurrencyValue<Double, String> secondCurrencyExchangeRate) {
3.240 - return getConvertor(DoubleComputer, firstCurrencyExchangeRate, secondCurrencyExchangeRate);
3.241 + return getConvertorDoubleString(ExchangeRateValue.getExchangeRate(firstCurrencyExchangeRate, secondCurrencyExchangeRate));
3.242 }
3.243 -
3.244 - static final Computer<Integer> IntegerComputer = new Computer<Integer>() {
3.245 -
3.246 - public ComputerResponse<Integer> compute(ComputerRequest<Integer> request) {
3.247 - ComputerResponse<Integer> response = new ComputerResponse<Integer>();
3.248 - response.setResult(request.getInput() * request.getOutputCurrencyRatio() / request.getInputCurrencyRatio());
3.249 - return response;
3.250 - }
3.251 - };
3.252
3.253 /**
3.254 * Creates convertor for Integer|String values with specified exchange rate
3.255 @@ -124,6 +203,26 @@
3.256 public static Convertor<Integer, String> getConvertorIntegerString(
3.257 CurrencyValue<Integer, String> firstCurrencyExchangeRate,
3.258 CurrencyValue<Integer, String> secondCurrencyExchangeRate) {
3.259 - return getConvertor(IntegerComputer, firstCurrencyExchangeRate, secondCurrencyExchangeRate);
3.260 + return getConvertorIntegerString(ExchangeRateValue.getExchangeRate(firstCurrencyExchangeRate, secondCurrencyExchangeRate));
3.261 }
3.262 +
3.263 + // ---
3.264 + // COMPUTERS
3.265 + // ---
3.266 + static final Computer<Double> DoubleComputer = new Computer<Double>() {
3.267 +
3.268 + public ComputerResponse<Double> compute(ComputerRequest<Double> request) {
3.269 + ComputerResponse<Double> response = new ComputerResponse<Double>();
3.270 + response.setResult(request.getInput() * request.getOutputCurrencyRatio() / request.getInputCurrencyRatio());
3.271 + return response;
3.272 + }
3.273 + };
3.274 + static final Computer<Integer> IntegerComputer = new Computer<Integer>() {
3.275 +
3.276 + public ComputerResponse<Integer> compute(ComputerRequest<Integer> request) {
3.277 + ComputerResponse<Integer> response = new ComputerResponse<Integer>();
3.278 + response.setResult(request.getInput() * request.getOutputCurrencyRatio() / request.getInputCurrencyRatio());
3.279 + return response;
3.280 + }
3.281 + };
3.282 }
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/task2/solution11/src/org/apidesign/apifest08/currency/ExchangeRateValue.java Tue Oct 07 00:25:53 2008 +0200
4.3 @@ -0,0 +1,78 @@
4.4 +package org.apidesign.apifest08.currency;
4.5 +
4.6 +import java.io.Serializable;
4.7 +
4.8 +/**
4.9 + * Value class, holding an exchange rate between two currencies.
4.10 + * Designed to be an immutable.
4.11 + *
4.12 + * @author ked
4.13 + */
4.14 +public final class ExchangeRateValue<AmountType, IdentifierType> implements Serializable {
4.15 +
4.16 + private final CurrencyValue<AmountType, IdentifierType> currencyA;
4.17 + private final CurrencyValue<AmountType, IdentifierType> currencyB;
4.18 +
4.19 + private ExchangeRateValue(
4.20 + CurrencyValue<AmountType, IdentifierType> currencyA,
4.21 + CurrencyValue<AmountType, IdentifierType> currencyB) {
4.22 + if (currencyA.getIdentifier() == null ||
4.23 + currencyB.getIdentifier() == null ||
4.24 + currencyA.getIdentifier().equals(currencyB)) {
4.25 + throw new IllegalArgumentException("Inappropriate exchange rates' identifiers!");
4.26 + }
4.27 +
4.28 + this.currencyA = currencyA;
4.29 + this.currencyB = currencyB;
4.30 + }
4.31 +
4.32 + public CurrencyValue<AmountType, IdentifierType> getCurrencyA() {
4.33 + return currencyA;
4.34 + }
4.35 +
4.36 + public CurrencyValue<AmountType, IdentifierType> getCurrencyB() {
4.37 + return currencyB;
4.38 + }
4.39 +
4.40 + @Override
4.41 + public boolean equals(Object obj) {
4.42 + if (obj == null) {
4.43 + return false;
4.44 + }
4.45 + if (getClass() != obj.getClass()) {
4.46 + return false;
4.47 + }
4.48 + final ExchangeRateValue other = (ExchangeRateValue) obj;
4.49 + if (this.currencyA != other.currencyA && (this.currencyA == null || !this.currencyA.equals(other.currencyA))) {
4.50 + return false;
4.51 + }
4.52 + if (this.currencyB != other.currencyB && (this.currencyB == null || !this.currencyB.equals(other.currencyB))) {
4.53 + return false;
4.54 + }
4.55 + return true;
4.56 + }
4.57 +
4.58 + @Override
4.59 + public int hashCode() {
4.60 + int hash = 3;
4.61 + hash = 71 * hash + (this.currencyA != null ? this.currencyA.hashCode() : 0);
4.62 + hash = 71 * hash + (this.currencyB != null ? this.currencyB.hashCode() : 0);
4.63 + return hash;
4.64 + }
4.65 +
4.66 + /**
4.67 + * Creates new instance.
4.68 + * Generic types of the new instance are derived from types of the parameters.
4.69 + *
4.70 + * @param <AmountType> type of the currency amount
4.71 + * @param <IdentifierType> type of the currency identifier
4.72 + * @param currencyA one currency of the exchange rate
4.73 + * @param currencyB another currency of the exchange rate
4.74 + * @return new instance
4.75 + */
4.76 + public static <AmountType, IdentifierType> ExchangeRateValue<AmountType, IdentifierType> getExchangeRate(
4.77 + CurrencyValue<AmountType, IdentifierType> currencyA,
4.78 + CurrencyValue<AmountType, IdentifierType> currencyB) {
4.79 + return new ExchangeRateValue<AmountType, IdentifierType>(currencyA, currencyB);
4.80 + }
4.81 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/task2/solution11/test/org/apidesign/apifest08/test/Task2Test.java Tue Oct 07 00:25:53 2008 +0200
5.3 @@ -0,0 +1,140 @@
5.4 +package org.apidesign.apifest08.test;
5.5 +
5.6 +import java.util.ArrayList;
5.7 +import java.util.Collection;
5.8 +import junit.framework.TestCase;
5.9 +import org.apidesign.apifest08.currency.Convertor;
5.10 +import org.apidesign.apifest08.currency.CurrencyValue;
5.11 +import org.apidesign.apifest08.currency.ExchangeRateValue;
5.12 +
5.13 +/** There are many currencies around the world and many banks manipulate
5.14 + * with more than one or two at the same time. As banks are usually the
5.15 + * best paying clients, which is true even in case of your Convertor API,
5.16 + * it is reasonable to listen to their requests.
5.17 + * <p>
5.18 + * The quest for today is to enhance your existing convertor API to hold
5.19 + * information about many currencies and allow conversions between any of them.
5.20 + * Also, as conversion rates for diferent currencies usually arise from various
5.21 + * bank departments, there is another important need. There is a need to
5.22 + * compose two convertors into one by merging all the information about
5.23 + * currencies they know about.
5.24 + */
5.25 +public class Task2Test extends TestCase {
5.26 +
5.27 + public Task2Test(String testName) {
5.28 + super(testName);
5.29 + }
5.30 +
5.31 + @Override
5.32 + protected void setUp() throws Exception {
5.33 + }
5.34 +
5.35 + @Override
5.36 + protected void tearDown() throws Exception {
5.37 + }
5.38 +
5.39 + // As in Task1Test, keep in mind, that there are three parts
5.40 + // of the whole system:
5.41 + // 1. there is someone who knows the current exchange rate
5.42 + // 2. there is someone who wants to do the conversion
5.43 + // 3. there is the API between 1. and 2. which allows them to communicate
5.44 + //
5.45 + // Please backward compatibly enhance your existing API to support following
5.46 + // usecases:
5.47 + //
5.48 + /** Create convertor that understands two currencies, CZK and
5.49 + * SKK. Make 100 SKK == 75 CZK. This is method for the group of users that
5.50 + * knows the exchange rate, and needs to use the API to create objects
5.51 + * with the exchange rate. Anyone shall be ready to call this method without
5.52 + * any other method being called previously. The API itself shall know
5.53 + * nothing about any rates, before this method is called.
5.54 + */
5.55 + public static Convertor<Integer, String> createTripleConvertor() {
5.56 + // Rates: 1USD = 15CZK
5.57 + // Rates: 1USD = 20SKK
5.58 + // Rates: 75CZK = 100SKK
5.59 + Collection<ExchangeRateValue<Integer, String>> exchangeRates =
5.60 + new ArrayList<ExchangeRateValue<Integer, String>>();
5.61 + exchangeRates.add(ExchangeRateValue.getExchangeRate(
5.62 + CurrencyValue.getCurrencyValue(1, "USD"),
5.63 + CurrencyValue.getCurrencyValue(15, "CZK")));
5.64 + exchangeRates.add(ExchangeRateValue.getExchangeRate(
5.65 + CurrencyValue.getCurrencyValue(1, "USD"),
5.66 + CurrencyValue.getCurrencyValue(20, "SKK")));
5.67 + exchangeRates.add(ExchangeRateValue.getExchangeRate(
5.68 + CurrencyValue.getCurrencyValue(75, "CZK"),
5.69 + CurrencyValue.getCurrencyValue(100, "SKK")));
5.70 + return Convertor.getConvertorIntegerString(exchangeRates);
5.71 + }
5.72 +
5.73 + /** Define convertor that understands three currencies. Use it.
5.74 + */
5.75 + public void testConvertorForUSDandCZKandSKK() throws Exception {
5.76 + Convertor<Integer, String> c = createTripleConvertor();
5.77 +
5.78 + CurrencyValue<Integer, String> result;
5.79 + // convert $5 to CZK using c:
5.80 + // assertEquals("Result is 75 CZK");
5.81 + result = c.convert("CZK", CurrencyValue.getCurrencyValue(5, "USD"));
5.82 + assertEquals(CurrencyValue.getCurrencyValue(75, "CZK"), result);
5.83 +
5.84 + // convert $5 to SKK using c:
5.85 + // assertEquals("Result is 100 SKK");
5.86 + result = c.convert("SKK", CurrencyValue.getCurrencyValue(5, "USD"));
5.87 + assertEquals(CurrencyValue.getCurrencyValue(100, "SKK"), result);
5.88 +
5.89 + // convert 200SKK to CZK using c:
5.90 + // assertEquals("Result is 150 CZK");
5.91 + result = c.convert("CZK", CurrencyValue.getCurrencyValue(200, "SKK"));
5.92 + assertEquals(CurrencyValue.getCurrencyValue(150, "CZK"), result);
5.93 +
5.94 + // convert 200SKK to USK using c:
5.95 + // assertEquals("Result is 10 USD");
5.96 + result = c.convert("USD", CurrencyValue.getCurrencyValue(200, "SKK"));
5.97 + assertEquals(CurrencyValue.getCurrencyValue(10, "USD"), result);
5.98 + }
5.99 +
5.100 + /** Merge all currency rates of convertor 1 with convertor 2.
5.101 + * Implement this using your API, preferably this method just delegates
5.102 + * into some API method which does the actual work, without requiring
5.103 + * API clients to code anything complex.
5.104 + */
5.105 + public static Convertor<Integer, String> merge(Convertor<Integer, String> one, Convertor<Integer, String> two) {
5.106 + return Convertor.mergeConvertorsIntegerString(one, two);
5.107 + }
5.108 +
5.109 + /** Join the convertors from previous task, Task1Test and show that it
5.110 + * can be used to do reasonable conversions.
5.111 + */
5.112 + public void testConvertorComposition() throws Exception {
5.113 + Convertor<Integer, String> c = merge(
5.114 + Task1Test.createCZKtoUSD(),
5.115 + Task1Test.createSKKtoCZK());
5.116 +
5.117 + CurrencyValue<Integer, String> result;
5.118 + // convert $5 to CZK using c:
5.119 + // assertEquals("Result is 85 CZK");
5.120 + result = c.convert("CZK", CurrencyValue.getCurrencyValue(5, "USD"));
5.121 + assertEquals(CurrencyValue.getCurrencyValue(85, "CZK"), result);
5.122 +
5.123 + // convert $8 to CZK using c:
5.124 + // assertEquals("Result is 136 CZK");
5.125 + result = c.convert("CZK", CurrencyValue.getCurrencyValue(8, "USD"));
5.126 + assertEquals(CurrencyValue.getCurrencyValue(136, "CZK"), result);
5.127 +
5.128 + // convert 1003CZK to USD using c:
5.129 + // assertEquals("Result is 59 USD");
5.130 + result = c.convert("USD", CurrencyValue.getCurrencyValue(1003, "CZK"));
5.131 + assertEquals(CurrencyValue.getCurrencyValue(59, "USD"), result);
5.132 +
5.133 + // convert 16CZK using c:
5.134 + // assertEquals("Result is 20 SKK");
5.135 + result = c.convert("SKK", CurrencyValue.getCurrencyValue(16, "CZK"));
5.136 + assertEquals(CurrencyValue.getCurrencyValue(20, "SKK"), result);
5.137 +
5.138 + // convert 500SKK to CZK using c:
5.139 + // assertEquals("Result is 400 CZK");
5.140 + result = c.convert("CZK", CurrencyValue.getCurrencyValue(500, "SKK"));
5.141 + assertEquals(CurrencyValue.getCurrencyValue(400, "CZK"), result);
5.142 + }
5.143 +}