1.1 --- a/task4/solution11/src/org/apidesign/apifest08/currency/Convertor.java Fri Oct 17 17:33:32 2008 +0200
1.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/Convertor.java Fri Oct 17 17:34:40 2008 +0200
1.3 @@ -2,6 +2,7 @@
1.4
1.5 import java.util.ArrayList;
1.6 import java.util.Collection;
1.7 +import java.util.Date;
1.8 import java.util.HashSet;
1.9 import java.util.List;
1.10 import java.util.Set;
1.11 @@ -25,71 +26,99 @@
1.12 * as well as static exchange rates.
1.13 * No time for javadoc, again.
1.14 *
1.15 + * In Task4's version takes into account validity range of data sources,
1.16 + * can convert using an exchange rate value according to the specified instant
1.17 + * of the time and provides a method for creating a new convertor with the same
1.18 + * data sources as the old one, but with their validity ranges limited
1.19 + * to the specified range.
1.20 + * As usual, no time for javadoc.
1.21 + *
1.22 * @author ked
1.23 */
1.24 public final class Convertor<AmountType, IdentifierType> {
1.25
1.26 Computer<AmountType> computer;
1.27 - // each static exchange rate could be a special case of an exchange rate data source
1.28 - List<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates = new ArrayList<ExchangeRateValue<AmountType, IdentifierType>>();
1.29 - List<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources = new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
1.30 + // each static exchange rate is a special case of an exchange rate data source
1.31 + // historically separated
1.32 + List<ExchangeRateDataSource<AmountType, IdentifierType>> staticExchangeRateDataSources =
1.33 + new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
1.34 + List<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources =
1.35 + new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
1.36
1.37 + // ---
1.38 + // BASICS
1.39 + // ---
1.40 Convertor(Computer<AmountType> computer) {
1.41 this.computer = computer;
1.42 }
1.43 -
1.44 - void addExchangeRates(Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates) {
1.45 - for (ExchangeRateValue<AmountType, IdentifierType> exchangeRate : exchangeRates) {
1.46 - if (isExchangeRate(
1.47 - exchangeRate.getCurrencyA().getIdentifier(),
1.48 - exchangeRate.getCurrencyB().getIdentifier())) {
1.49 +
1.50 + void addExchangeRateDataSources(
1.51 + List<ExchangeRateDataSource<AmountType, IdentifierType>> target,
1.52 + Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources) {
1.53 + for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : exchangeRateDataSources) {
1.54 + if (isOverlappingExchangeRate(
1.55 + exchangeRateDataSource.getCurrencyAIdentifier(),
1.56 + exchangeRateDataSource.getCurrencyBIdentifier(),
1.57 + exchangeRateDataSource.getValidFrom(),
1.58 + exchangeRateDataSource.getValidTill())) {
1.59 throw new IllegalArgumentException("Duplicate exchange rate!");
1.60 }
1.61 - this.exchangeRates.add(exchangeRate);
1.62 + target.add(exchangeRateDataSource);
1.63 }
1.64 }
1.65 -
1.66 - void addExchangeRateDataSources(Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources) {
1.67 - for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : exchangeRateDataSources) {
1.68 - if (isExchangeRate(
1.69 - exchangeRateDataSource.getCurrencyAIdentifier(),
1.70 - exchangeRateDataSource.getCurrencyBIdentifier())) {
1.71 - throw new IllegalArgumentException("Duplicate exchange rate!");
1.72 - }
1.73 - this.exchangeRateDataSources.add(exchangeRateDataSource);
1.74 - }
1.75 - }
1.76 -
1.77 +
1.78 ExchangeRateValue<AmountType, IdentifierType> findExchangeRate(
1.79 IdentifierType currencyA,
1.80 - IdentifierType currencyB) {
1.81 - for (ExchangeRateValue<AmountType, IdentifierType> exchangeRate : exchangeRates) {
1.82 - if ((exchangeRate.getCurrencyA().getIdentifier().equals(currencyA) && exchangeRate.getCurrencyB().getIdentifier().equals(currencyB)) ||
1.83 - (exchangeRate.getCurrencyA().getIdentifier().equals(currencyB) && exchangeRate.getCurrencyB().getIdentifier().equals(currencyA))) {
1.84 - return exchangeRate;
1.85 - }
1.86 + IdentifierType currencyB,
1.87 + Date instant) {
1.88 + ExchangeRateValue<AmountType, IdentifierType> result = null;
1.89 + result = findExchangeRateInternal(staticExchangeRateDataSources, currencyA, currencyB, instant);
1.90 + if (result != null) {
1.91 + return result;
1.92 }
1.93 - for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : exchangeRateDataSources) {
1.94 - if ((exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyA) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyB)) ||
1.95 - (exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyB) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyA))) {
1.96 + result = findExchangeRateInternal(exchangeRateDataSources, currencyA, currencyB, instant);
1.97 + return result;
1.98 + }
1.99 +
1.100 + ExchangeRateValue<AmountType, IdentifierType> findExchangeRateInternal(
1.101 + List<ExchangeRateDataSource<AmountType, IdentifierType>> where,
1.102 + IdentifierType currencyA,
1.103 + IdentifierType currencyB,
1.104 + Date instant) {
1.105 + for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : where) {
1.106 + if (((exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyA) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyB)) ||
1.107 + (exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyB) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyA))) &&
1.108 + DateUtil.isInRange(instant, exchangeRateDataSource.getValidFrom(), exchangeRateDataSource.getValidTill())) {
1.109 return exchangeRateDataSource.getExchangeRate();
1.110 }
1.111 }
1.112 return null;
1.113 }
1.114 -
1.115 - boolean isExchangeRate(
1.116 +
1.117 + boolean isOverlappingExchangeRate(
1.118 IdentifierType currencyA,
1.119 - IdentifierType currencyB) {
1.120 - for (ExchangeRateValue<AmountType, IdentifierType> exchangeRate : exchangeRates) {
1.121 - if ((exchangeRate.getCurrencyA().getIdentifier().equals(currencyA) && exchangeRate.getCurrencyB().getIdentifier().equals(currencyB)) ||
1.122 - (exchangeRate.getCurrencyA().getIdentifier().equals(currencyB) && exchangeRate.getCurrencyB().getIdentifier().equals(currencyA))) {
1.123 - return true;
1.124 - }
1.125 + IdentifierType currencyB,
1.126 + Date from,
1.127 + Date to) {
1.128 + boolean result = false;
1.129 + result = isOverlappingExchangeRateInternal(staticExchangeRateDataSources, currencyA, currencyB, from, to);
1.130 + if (result == true) {
1.131 + return result;
1.132 }
1.133 - for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : exchangeRateDataSources) {
1.134 - if ((exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyA) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyB)) ||
1.135 - (exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyB) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyA))) {
1.136 + result = isOverlappingExchangeRateInternal(exchangeRateDataSources, currencyA, currencyB, from, to);
1.137 + return result;
1.138 + }
1.139 +
1.140 + boolean isOverlappingExchangeRateInternal(
1.141 + List<ExchangeRateDataSource<AmountType, IdentifierType>> where,
1.142 + IdentifierType currencyA,
1.143 + IdentifierType currencyB,
1.144 + Date from,
1.145 + Date to) {
1.146 + for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : where) {
1.147 + if (((exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyA) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyB)) ||
1.148 + (exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyB) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyA))) &&
1.149 + DateUtil.isRangesOverlapping(from, to, exchangeRateDataSource.getValidFrom(), exchangeRateDataSource.getValidTill())) {
1.150 return true;
1.151 }
1.152 }
1.153 @@ -107,8 +136,15 @@
1.154 public CurrencyValue<AmountType, IdentifierType> convert(
1.155 IdentifierType targetCurrency,
1.156 CurrencyValue<AmountType, IdentifierType> currencyValue) {
1.157 + return convert(targetCurrency, currencyValue, new Date()); // System.currentTimeMillis()
1.158 + }
1.159 +
1.160 + public CurrencyValue<AmountType, IdentifierType> convert(
1.161 + IdentifierType targetCurrency,
1.162 + CurrencyValue<AmountType, IdentifierType> currencyValue,
1.163 + Date instant) {
1.164 ExchangeRateValue<AmountType, IdentifierType> exchangeRate =
1.165 - findExchangeRate(currencyValue.getIdentifier(), targetCurrency);
1.166 + findExchangeRate(currencyValue.getIdentifier(), targetCurrency, instant);
1.167 if (exchangeRate == null) {
1.168 throw new IllegalArgumentException("Inappropriate currencies to convert!");
1.169 }
1.170 @@ -136,23 +172,57 @@
1.171 }
1.172
1.173 // ---
1.174 + // LIMITING
1.175 + // ---
1.176 + Collection<ExchangeRateDataSource<AmountType, IdentifierType>> limitDataSources(
1.177 + Collection<ExchangeRateDataSource<AmountType, IdentifierType>> source,
1.178 + Date from, Date till) {
1.179 + Collection<ExchangeRateDataSource<AmountType, IdentifierType>> result =
1.180 + new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
1.181 +
1.182 + for (ExchangeRateDataSource<AmountType, IdentifierType> dataSource : source) {
1.183 + result.add(ExchangeRateDataSource.getExchangeRateDataSource(
1.184 + dataSource.getCurrencyAIdentifier(), dataSource.getCurrencyBIdentifier(),
1.185 + dataSource.getExchangeRateProvider(),
1.186 + DateUtil.getRangesIntersectionBottom(dataSource.getValidFrom(), from),
1.187 + DateUtil.getRangesIntersectionTop(dataSource.getValidTill(), till)));
1.188 + }
1.189 +
1.190 + return result;
1.191 + }
1.192 +
1.193 + public Convertor<AmountType, IdentifierType> limitConvertor(Date from, Date till) {
1.194 + Collection<ExchangeRateDataSource<AmountType, IdentifierType>> limitedStatic =
1.195 + limitDataSources(staticExchangeRateDataSources, from, till);
1.196 + Collection<ExchangeRateDataSource<AmountType, IdentifierType>> limited =
1.197 + limitDataSources(exchangeRateDataSources, from, till);
1.198 +
1.199 + Convertor<AmountType, IdentifierType> c = new Convertor<AmountType, IdentifierType>(computer);
1.200 + c.addExchangeRateDataSources(c.staticExchangeRateDataSources, limitedStatic);
1.201 + c.addExchangeRateDataSources(c.exchangeRateDataSources, limited);
1.202 + return c;
1.203 + }
1.204 +
1.205 + // ---
1.206 // MERGING
1.207 // ---
1.208 static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> mergeConvertors(
1.209 Computer<AmountType> computer,
1.210 Collection<Convertor<AmountType, IdentifierType>> convertors) {
1.211 - Set<ExchangeRateValue<AmountType, IdentifierType>> exchangeRatesSet = new HashSet<ExchangeRateValue<AmountType, IdentifierType>>();
1.212 - Set<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSourcesSet = new HashSet<ExchangeRateDataSource<AmountType, IdentifierType>>();
1.213 + Set<ExchangeRateDataSource<AmountType, IdentifierType>> mergedStatic =
1.214 + new HashSet<ExchangeRateDataSource<AmountType, IdentifierType>>();
1.215 + Set<ExchangeRateDataSource<AmountType, IdentifierType>> merged =
1.216 + new HashSet<ExchangeRateDataSource<AmountType, IdentifierType>>();
1.217 for (Convertor<AmountType, IdentifierType> convertor : convertors) {
1.218 - exchangeRatesSet.addAll(convertor.exchangeRates);
1.219 + mergedStatic.addAll(convertor.staticExchangeRateDataSources);
1.220 }
1.221 for (Convertor<AmountType, IdentifierType> convertor : convertors) {
1.222 - exchangeRateDataSourcesSet.addAll(convertor.exchangeRateDataSources);
1.223 + merged.addAll(convertor.exchangeRateDataSources);
1.224 }
1.225 -
1.226 +
1.227 Convertor<AmountType, IdentifierType> c = new Convertor<AmountType, IdentifierType>(computer);
1.228 - c.addExchangeRates(exchangeRatesSet);
1.229 - c.addExchangeRateDataSources(exchangeRateDataSourcesSet);
1.230 + c.addExchangeRateDataSources(c.staticExchangeRateDataSources, mergedStatic);
1.231 + c.addExchangeRateDataSources(c.exchangeRateDataSources, merged);
1.232 return c;
1.233 }
1.234
1.235 @@ -195,17 +265,25 @@
1.236 static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertor(
1.237 Computer<AmountType> computer, Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates) {
1.238 Convertor<AmountType, IdentifierType> c = new Convertor<AmountType, IdentifierType>(computer);
1.239 - c.addExchangeRates(exchangeRates);
1.240 + Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources =
1.241 + new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
1.242 + for (ExchangeRateValue<AmountType, IdentifierType> exchangeRate : exchangeRates) {
1.243 + exchangeRateDataSources.add(
1.244 + ExchangeRateDataSource.getExchangeRateDataSource(
1.245 + exchangeRate.getCurrencyA().getIdentifier(), exchangeRate.getCurrencyB().getIdentifier(),
1.246 + StaticExchangeRateProvider.getStaticExchangeRateProvider(exchangeRate)));
1.247 + }
1.248 + c.addExchangeRateDataSources(c.staticExchangeRateDataSources, exchangeRateDataSources);
1.249 return c;
1.250 }
1.251
1.252 static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertorDataSource(
1.253 Computer<AmountType> computer, Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources) {
1.254 Convertor<AmountType, IdentifierType> c = new Convertor<AmountType, IdentifierType>(computer);
1.255 - c.addExchangeRateDataSources(exchangeRateDataSources);
1.256 + c.addExchangeRateDataSources(c.exchangeRateDataSources, exchangeRateDataSources);
1.257 return c;
1.258 }
1.259 -
1.260 +
1.261 static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertor(
1.262 Computer<AmountType> computer, ExchangeRateValue<AmountType, IdentifierType> exchangeRate) {
1.263 Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates =
1.264 @@ -213,7 +291,7 @@
1.265 exchangeRates.add(exchangeRate);
1.266 return getConvertor(computer, exchangeRates);
1.267 }
1.268 -
1.269 +
1.270 static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertorDataSource(
1.271 Computer<AmountType> computer, ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource) {
1.272 Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources =
1.273 @@ -221,7 +299,7 @@
1.274 exchangeRateDataSources.add(exchangeRateDataSource);
1.275 return getConvertorDataSource(computer, exchangeRateDataSources);
1.276 }
1.277 -
1.278 +
1.279 public static Convertor<Double, String> getConvertorDoubleString(
1.280 Collection<ExchangeRateValue<Double, String>> exchangeRates) {
1.281 return getConvertor(DoubleComputer, exchangeRates);
1.282 @@ -231,7 +309,7 @@
1.283 ExchangeRateValue<Double, String> exchangeRate) {
1.284 return getConvertor(DoubleComputer, exchangeRate);
1.285 }
1.286 -
1.287 +
1.288 public static Convertor<Double, String> getConvertorDataSourceDoubleString(
1.289 Collection<ExchangeRateDataSource<Double, String>> exchangeRateDataSources) {
1.290 return getConvertorDataSource(DoubleComputer, exchangeRateDataSources);
1.291 @@ -241,7 +319,7 @@
1.292 ExchangeRateDataSource<Double, String> exchangeRateDataSource) {
1.293 return getConvertorDataSource(DoubleComputer, exchangeRateDataSource);
1.294 }
1.295 -
1.296 +
1.297 public static Convertor<Integer, String> getConvertorIntegerString(
1.298 Collection<ExchangeRateValue<Integer, String>> exchangeRates) {
1.299 return getConvertor(IntegerComputer, exchangeRates);
1.300 @@ -251,7 +329,7 @@
1.301 ExchangeRateValue<Integer, String> exchangeRate) {
1.302 return getConvertor(IntegerComputer, exchangeRate);
1.303 }
1.304 -
1.305 +
1.306 public static Convertor<Integer, String> getConvertorDataSourceIntegerString(
1.307 Collection<ExchangeRateDataSource<Integer, String>> exchangeRateDataSources) {
1.308 return getConvertorDataSource(IntegerComputer, exchangeRateDataSources);
1.309 @@ -292,7 +370,7 @@
1.310 CurrencyValue<Integer, String> secondCurrencyExchangeRate) {
1.311 return getConvertorIntegerString(ExchangeRateValue.getExchangeRate(firstCurrencyExchangeRate, secondCurrencyExchangeRate));
1.312 }
1.313 -
1.314 +
1.315 // ---
1.316 // COMPUTERS
1.317 // ---
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/DateUtil.java Fri Oct 17 17:34:40 2008 +0200
2.3 @@ -0,0 +1,68 @@
2.4 +package org.apidesign.apifest08.currency;
2.5 +
2.6 +import java.util.Date;
2.7 +
2.8 +/**
2.9 + * Date util helper class.
2.10 + * @author ked
2.11 + */
2.12 +final class DateUtil {
2.13 +
2.14 + private DateUtil() {};
2.15 +
2.16 + static boolean isInRange(Date instant, Date from, Date till) {
2.17 + if ((from == null || instant.equals(from) || instant.after(from)) &&
2.18 + (till == null || instant.before(till))) {
2.19 + return true;
2.20 + } else {
2.21 + return false;
2.22 + }
2.23 + }
2.24 +
2.25 + static boolean isRangesOverlapping(Date fromA, Date tillA, Date fromB, Date tillB) {
2.26 + if ((fromA == null && tillA == null) || (fromB == null && tillB == null)) {
2.27 + return true;
2.28 + }
2.29 + if (fromA != null && isInRange(fromA, fromB, tillB)) {
2.30 + return true;
2.31 + }
2.32 + if (tillA != null && !tillA.equals(fromB) && isInRange(tillA, fromB, tillB)) {
2.33 + return true;
2.34 + }
2.35 + if (fromB != null && isInRange(fromB, fromA, tillA)) {
2.36 + return true;
2.37 + }
2.38 + if (tillB != null && !tillB.equals(fromA) && isInRange(tillB, fromA, tillA)) {
2.39 + return true;
2.40 + }
2.41 + return false;
2.42 + }
2.43 +
2.44 + static Date getRangesIntersectionBottom(Date fromA, Date fromB) {
2.45 + if (fromA == null) {
2.46 + return fromB;
2.47 + }
2.48 + if (fromB == null) {
2.49 + return fromA;
2.50 + }
2.51 + if (fromA.after(fromB)) {
2.52 + return fromA;
2.53 + } else {
2.54 + return fromB;
2.55 + }
2.56 + }
2.57 +
2.58 + static Date getRangesIntersectionTop(Date tillA, Date tillB) {
2.59 + if (tillA == null) {
2.60 + return tillB;
2.61 + }
2.62 + if (tillB == null) {
2.63 + return tillA;
2.64 + }
2.65 + if (tillA.before(tillB)) {
2.66 + return tillA;
2.67 + } else {
2.68 + return tillB;
2.69 + }
2.70 + }
2.71 +}
3.1 --- a/task4/solution11/src/org/apidesign/apifest08/currency/ExchangeRateDataSource.java Fri Oct 17 17:33:32 2008 +0200
3.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/ExchangeRateDataSource.java Fri Oct 17 17:34:40 2008 +0200
3.3 @@ -1,5 +1,6 @@
3.4 package org.apidesign.apifest08.currency;
3.5
3.6 +import java.util.Date;
3.7 import org.apidesign.apifest08.currency.ExchangeRateProvider.ExchangeRateRequest;
3.8 import org.apidesign.apifest08.currency.ExchangeRateProvider.ExchangeRateResponse;
3.9
3.10 @@ -10,94 +11,144 @@
3.11 */
3.12 public final class ExchangeRateDataSource<AmountType, IdentifierType> {
3.13
3.14 - private final IdentifierType currencyAIdentifier;
3.15 + private final IdentifierType currencyAIdentifier;
3.16 + private final IdentifierType currencyBIdentifier;
3.17 + private final ExchangeRateProvider<AmountType, IdentifierType> exchangeRateProvider;
3.18 + private final Date validFrom;
3.19 + private final Date validTill;
3.20
3.21 - private final IdentifierType currencyBIdentifier;
3.22 + private ExchangeRateDataSource(
3.23 + IdentifierType currencyAIdentifier,
3.24 + IdentifierType currencyBIdentifier,
3.25 + ExchangeRateProvider<AmountType, IdentifierType> exchangeRateProvider,
3.26 + Date validFrom,
3.27 + Date validTill) {
3.28 + if (currencyAIdentifier == null ||
3.29 + currencyBIdentifier == null ||
3.30 + currencyAIdentifier.equals(currencyBIdentifier)) {
3.31 + throw new IllegalArgumentException("Inappropriate exchange rates' identifiers!");
3.32 + }
3.33 + if (validFrom != null &&
3.34 + validTill != null &&
3.35 + !validTill.after(validFrom)) {
3.36 + throw new IllegalArgumentException("Inappropriate exchange rate validity!");
3.37 + }
3.38
3.39 - private final ExchangeRateProvider<AmountType, IdentifierType> exchangeRateProvider;
3.40 + this.currencyAIdentifier = currencyAIdentifier;
3.41 + this.currencyBIdentifier = currencyBIdentifier;
3.42 + this.exchangeRateProvider = exchangeRateProvider;
3.43 + this.validFrom = validFrom;
3.44 + this.validTill = validTill;
3.45 + }
3.46
3.47 - private ExchangeRateDataSource(
3.48 - IdentifierType currencyAIdentifier,
3.49 - IdentifierType currencyBIdentifier,
3.50 - ExchangeRateProvider<AmountType, IdentifierType> exchangeRateProvider) {
3.51 - if (currencyAIdentifier == null ||
3.52 - currencyBIdentifier == null ||
3.53 - currencyAIdentifier.equals(currencyBIdentifier)) {
3.54 - throw new IllegalArgumentException("Inappropriate exchange rates' identifiers!");
3.55 + public IdentifierType getCurrencyAIdentifier() {
3.56 + return currencyAIdentifier;
3.57 + }
3.58 +
3.59 + public IdentifierType getCurrencyBIdentifier() {
3.60 + return currencyBIdentifier;
3.61 + }
3.62 +
3.63 + ExchangeRateProvider<AmountType, IdentifierType> getExchangeRateProvider() {
3.64 + return exchangeRateProvider;
3.65 + }
3.66 +
3.67 + public Date getValidFrom() {
3.68 + return validFrom != null ? (Date) validFrom.clone() : null;
3.69 + }
3.70 +
3.71 + public Date getValidTill() {
3.72 + return validTill != null ? (Date) validTill.clone() : null;
3.73 + }
3.74 +
3.75 + public ExchangeRateValue<AmountType, IdentifierType> getExchangeRate() {
3.76 + return getExchangeRate(new Date()); // System.currentTimeMillis()
3.77 + }
3.78 +
3.79 + public ExchangeRateValue<AmountType, IdentifierType> getExchangeRate(Date instant) {
3.80 + ExchangeRateRequest<AmountType, IdentifierType> request =
3.81 + new ExchangeRateRequest<AmountType, IdentifierType>();
3.82 + ExchangeRateResponse<AmountType, IdentifierType> response =
3.83 + new ExchangeRateResponse<AmountType, IdentifierType>();
3.84 +
3.85 + request.setCurrencyAIdentifier(currencyAIdentifier);
3.86 + request.setCurrencyBIdentifier(currencyBIdentifier);
3.87 + request.setInstant(instant);
3.88 +
3.89 + exchangeRateProvider.getExchangeRate(request, response);
3.90 +
3.91 + ExchangeRateValue<AmountType, IdentifierType> result = response.getExchangeRate();
3.92 + if (result.getCurrencyA().getIdentifier().equals(currencyAIdentifier) &&
3.93 + result.getCurrencyB().getIdentifier().equals(currencyBIdentifier)) {
3.94 + return result;
3.95 + } else {
3.96 + throw new IllegalStateException("Data source's provider returned inappropriate exchange rate!");
3.97 }
3.98 - this.currencyAIdentifier = currencyAIdentifier;
3.99 - this.currencyBIdentifier = currencyBIdentifier;
3.100 - this.exchangeRateProvider = exchangeRateProvider;
3.101 - }
3.102 + }
3.103
3.104 - public IdentifierType getCurrencyAIdentifier() {
3.105 - return currencyAIdentifier;
3.106 - }
3.107 + public static <AmountType, IdentifierType> ExchangeRateDataSource<AmountType, IdentifierType> getExchangeRateDataSource(
3.108 + IdentifierType currencyAIdentifier,
3.109 + IdentifierType currencyBIdentifier,
3.110 + ExchangeRateProvider<AmountType, IdentifierType> exchangeRateProvider) {
3.111 + return getExchangeRateDataSource(
3.112 + currencyAIdentifier,
3.113 + currencyBIdentifier,
3.114 + exchangeRateProvider,
3.115 + null,
3.116 + null);
3.117 + }
3.118
3.119 - public IdentifierType getCurrencyBIdentifier() {
3.120 - return currencyBIdentifier;
3.121 - }
3.122 + public static <AmountType, IdentifierType> ExchangeRateDataSource<AmountType, IdentifierType> getExchangeRateDataSource(
3.123 + IdentifierType currencyAIdentifier,
3.124 + IdentifierType currencyBIdentifier,
3.125 + ExchangeRateProvider<AmountType, IdentifierType> exchangeRateProvider,
3.126 + Date validFrom,
3.127 + Date validTill) {
3.128 + return new ExchangeRateDataSource<AmountType, IdentifierType>(
3.129 + currencyAIdentifier,
3.130 + currencyBIdentifier,
3.131 + exchangeRateProvider,
3.132 + validFrom,
3.133 + validTill);
3.134 + }
3.135
3.136 - public ExchangeRateValue<AmountType, IdentifierType> getExchangeRate() {
3.137 - ExchangeRateRequest<AmountType, IdentifierType> request =
3.138 - new ExchangeRateRequest<AmountType, IdentifierType>();
3.139 - ExchangeRateResponse<AmountType, IdentifierType> response =
3.140 - new ExchangeRateResponse<AmountType, IdentifierType>();
3.141 + @Override
3.142 + public boolean equals(Object obj) {
3.143 + if (obj == null) {
3.144 + return false;
3.145 + }
3.146 + if (getClass() != obj.getClass()) {
3.147 + return false;
3.148 + }
3.149 + final ExchangeRateDataSource other =
3.150 + (ExchangeRateDataSource) obj;
3.151 + if (this.currencyAIdentifier != other.currencyAIdentifier &&
3.152 + (this.currencyAIdentifier == null || !this.currencyAIdentifier.equals(other.currencyAIdentifier))) {
3.153 + return false;
3.154 + }
3.155 + if (this.currencyBIdentifier != other.currencyBIdentifier &&
3.156 + (this.currencyBIdentifier == null || !this.currencyBIdentifier.equals(other.currencyBIdentifier))) {
3.157 + return false;
3.158 + }
3.159 + if (this.exchangeRateProvider != other.exchangeRateProvider &&
3.160 + (this.exchangeRateProvider == null || !this.exchangeRateProvider.equals(other.exchangeRateProvider))) {
3.161 + return false;
3.162 + }
3.163 + if (this.validFrom != other.validFrom && (this.validFrom == null || !this.validFrom.equals(other.validFrom))) {
3.164 + return false;
3.165 + }
3.166 + if (this.validTill != other.validTill && (this.validTill == null || !this.validTill.equals(other.validTill))) {
3.167 + return false;
3.168 + }
3.169 + return true;
3.170 + }
3.171
3.172 - request.setCurrencyAIdentifier(currencyAIdentifier);
3.173 - request.setCurrencyBIdentifier(currencyBIdentifier);
3.174 -
3.175 - exchangeRateProvider.getExchangeRate(request, response);
3.176 -
3.177 - if (response.getExchangeRate().getCurrencyA().getIdentifier().equals(currencyAIdentifier) &&
3.178 - response.getExchangeRate().getCurrencyB().getIdentifier().equals(currencyBIdentifier)) {
3.179 - return response.getExchangeRate();
3.180 - } else {
3.181 - throw new IllegalStateException("Data source's provider returned inappropriate exchange rate!");
3.182 + @Override
3.183 + public int hashCode() {
3.184 + int hash = 7;
3.185 + hash = 83 * hash + (this.currencyAIdentifier != null ? this.currencyAIdentifier.hashCode() : 0);
3.186 + hash = 83 * hash + (this.currencyBIdentifier != null ? this.currencyBIdentifier.hashCode() : 0);
3.187 + hash = 83 * hash + (this.exchangeRateProvider != null ? this.exchangeRateProvider.hashCode() : 0);
3.188 + return hash;
3.189 }
3.190 - }
3.191 -
3.192 - public static <AmountType, IdentifierType> ExchangeRateDataSource<AmountType, IdentifierType> getExchangeRateDataSource(
3.193 - IdentifierType currencyAIdentifier,
3.194 - IdentifierType currencyBIdentifier,
3.195 - ExchangeRateProvider<AmountType, IdentifierType> exchangeRateProvider) {
3.196 - return new ExchangeRateDataSource<AmountType, IdentifierType>(
3.197 - currencyAIdentifier,
3.198 - currencyBIdentifier,
3.199 - exchangeRateProvider);
3.200 - }
3.201 -
3.202 - @Override
3.203 - public boolean equals(Object obj) {
3.204 - if (obj == null) {
3.205 - return false;
3.206 - }
3.207 - if (getClass() != obj.getClass()) {
3.208 - return false;
3.209 - }
3.210 - final ExchangeRateDataSource other =
3.211 - (ExchangeRateDataSource) obj;
3.212 - if (this.currencyAIdentifier != other.currencyAIdentifier &&
3.213 - (this.currencyAIdentifier == null || !this.currencyAIdentifier.equals(other.currencyAIdentifier))) {
3.214 - return false;
3.215 - }
3.216 - if (this.currencyBIdentifier != other.currencyBIdentifier &&
3.217 - (this.currencyBIdentifier == null || !this.currencyBIdentifier.equals(other.currencyBIdentifier))) {
3.218 - return false;
3.219 - }
3.220 - if (this.exchangeRateProvider != other.exchangeRateProvider &&
3.221 - (this.exchangeRateProvider == null || !this.exchangeRateProvider.equals(other.exchangeRateProvider))) {
3.222 - return false;
3.223 - }
3.224 - return true;
3.225 - }
3.226 -
3.227 - @Override
3.228 - public int hashCode() {
3.229 - int hash = 7;
3.230 - hash = 83 * hash + (this.currencyAIdentifier != null ? this.currencyAIdentifier.hashCode() : 0);
3.231 - hash = 83 * hash + (this.currencyBIdentifier != null ? this.currencyBIdentifier.hashCode() : 0);
3.232 - hash = 83 * hash + (this.exchangeRateProvider != null ? this.exchangeRateProvider.hashCode() : 0);
3.233 - return hash;
3.234 - }
3.235 }
4.1 --- a/task4/solution11/src/org/apidesign/apifest08/currency/ExchangeRateProvider.java Fri Oct 17 17:33:32 2008 +0200
4.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/ExchangeRateProvider.java Fri Oct 17 17:34:40 2008 +0200
4.3 @@ -1,5 +1,7 @@
4.4 package org.apidesign.apifest08.currency;
4.5
4.6 +import java.util.Date;
4.7 +
4.8 /**
4.9 * Exchange rate provider.
4.10 *
4.11 @@ -16,6 +18,7 @@
4.12
4.13 private IdentifierType currencyAIdentifier;
4.14 private IdentifierType currencyBIdentifier;
4.15 + private Date instant;
4.16
4.17 ExchangeRateRequest() {
4.18 }
4.19 @@ -35,6 +38,14 @@
4.20 void setCurrencyBIdentifier(IdentifierType currencyBIdentifier) {
4.21 this.currencyBIdentifier = currencyBIdentifier;
4.22 }
4.23 +
4.24 + public Date getInstant() {
4.25 + return instant;
4.26 + }
4.27 +
4.28 + void setInstant(Date instant) {
4.29 + this.instant = instant;
4.30 + }
4.31 }
4.32
4.33 public final class ExchangeRateResponse<AmountType, IdentifierType> {
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/StaticExchangeRateProvider.java Fri Oct 17 17:34:40 2008 +0200
5.3 @@ -0,0 +1,48 @@
5.4 +package org.apidesign.apifest08.currency;
5.5 +
5.6 +/**
5.7 + * Static exchange rate provider.
5.8 + *
5.9 + * @author ked
5.10 + */
5.11 +final class StaticExchangeRateProvider<AmountType, IdentifierType> implements ExchangeRateProvider<AmountType, IdentifierType> {
5.12 +
5.13 + final ExchangeRateValue<AmountType, IdentifierType> exchangeRate;
5.14 +
5.15 + private StaticExchangeRateProvider(ExchangeRateValue<AmountType, IdentifierType> exchangeRate) {
5.16 + this.exchangeRate = exchangeRate;
5.17 + }
5.18 +
5.19 + public void getExchangeRate(ExchangeRateRequest<AmountType, IdentifierType> request, ExchangeRateResponse<AmountType, IdentifierType> response) {
5.20 + response.setExchangeRate(exchangeRate);
5.21 + }
5.22 +
5.23 + static <AmountType, IdentifierType> StaticExchangeRateProvider<AmountType, IdentifierType> getStaticExchangeRateProvider(ExchangeRateValue<AmountType, IdentifierType> exchangeRate) {
5.24 + return new StaticExchangeRateProvider<AmountType, IdentifierType>(exchangeRate);
5.25 + }
5.26 +
5.27 + @Override
5.28 + public boolean equals(Object obj) {
5.29 + if (obj == null) {
5.30 + return false;
5.31 + }
5.32 +
5.33 + if (getClass() != obj.getClass()) {
5.34 + return false;
5.35 + }
5.36 +
5.37 + final StaticExchangeRateProvider other = (StaticExchangeRateProvider) obj;
5.38 + if (this.exchangeRate != other.exchangeRate && (this.exchangeRate == null || !this.exchangeRate.equals(other.exchangeRate))) {
5.39 + return false;
5.40 + }
5.41 +
5.42 + return true;
5.43 + }
5.44 +
5.45 + @Override
5.46 + public int hashCode() {
5.47 + int hash = 7;
5.48 + hash = 67 * hash + (this.exchangeRate != null ? this.exchangeRate.hashCode() : 0);
5.49 + return hash;
5.50 + }
5.51 +}
6.1 --- a/task4/solution11/test/org/apidesign/apifest08/test/Task4Test.java Fri Oct 17 17:33:32 2008 +0200
6.2 +++ b/task4/solution11/test/org/apidesign/apifest08/test/Task4Test.java Fri Oct 17 17:34:40 2008 +0200
6.3 @@ -1,8 +1,13 @@
6.4 package org.apidesign.apifest08.test;
6.5
6.6 +import java.util.Calendar;
6.7 import java.util.Date;
6.8 +import java.util.GregorianCalendar;
6.9 +import java.util.TimeZone;
6.10 import junit.framework.TestCase;
6.11 import org.apidesign.apifest08.currency.Convertor;
6.12 +import org.apidesign.apifest08.currency.CurrencyValue;
6.13 +import org.apidesign.apifest08.currency.ExchangeRateValue;
6.14
6.15 /** The exchange rates are not always the same. They are changing. However
6.16 * as in order to predict the future, one needs to understand own past. That is
6.17 @@ -44,58 +49,105 @@
6.18 * @param till final date (exclusive)
6.19 * @return new convertor
6.20 */
6.21 - public static Convertor limitTo(Convertor old, Date from, Date till) {
6.22 - return null;
6.23 + public static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> limitTo(Convertor<AmountType, IdentifierType> old, Date from, Date till) {
6.24 + return old.limitConvertor(from, till);
6.25 }
6.26
6.27
6.28 public void testCompositionOfLimitedConvertors() throws Exception {
6.29 - if (Boolean.getBoolean("ignore.failing")) {
6.30 - // implement me! then delete this if statement
6.31 - return;
6.32 - }
6.33 -
6.34 - Date d1 = null; // 2008-10-01 0:00 GMT
6.35 - Date d2 = null; // 2008-10-02 0:00 GMT
6.36 - Date d3 = null; // 2008-10-03 0:00 GMT
6.37 + Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
6.38 + cal.clear();
6.39 + cal.set(2008, 9, 1, 0, 0);
6.40 + Date d1 = cal.getTime(); // 2008-10-01 0:00 GMT
6.41 + cal.set(2008, 9, 2, 0, 0);
6.42 + Date d2 = cal.getTime(); // 2008-10-02 0:00 GMT
6.43 + cal.set(2008, 9, 3, 0, 0);
6.44 + Date d3 = cal.getTime(); // 2008-10-03 0:00 GMT
6.45
6.46 - Convertor c = Task2Test.merge(
6.47 + Convertor<Double, String> c = Task2Test.merge(
6.48 limitTo(Task1Test.createCZKtoUSD(), d1, d2),
6.49 limitTo(Task1Test.createSKKtoCZK(), d2, d3)
6.50 );
6.51
6.52 + CurrencyValue<Double, String> result;
6.53 // convert $5 to CZK using c:
6.54 // cannot convert as no rate is applicable to current date
6.55 -
6.56 + try {
6.57 + c.convert("CZK", CurrencyValue.getCurrencyValue(5d, "USD"));
6.58 + fail("Should not convert");
6.59 + } catch (Exception e) {
6.60 + }
6.61 +
6.62 // convert $8 to CZK using c:
6.63 // cannot convert as no rate is applicable to current date
6.64 + try {
6.65 + c.convert("CZK", CurrencyValue.getCurrencyValue(8d, "USD"));
6.66 + fail("Should not convert");
6.67 + } catch (Exception e) {
6.68 + }
6.69
6.70 // convert 1003CZK to USD using c:
6.71 // cannot convert as no rate is applicable to current date
6.72 + try {
6.73 + c.convert("USD", CurrencyValue.getCurrencyValue(1003d, "CZK"));
6.74 + fail("Should not convert");
6.75 + } catch (Exception e) {
6.76 + }
6.77
6.78 // convert 16CZK using c:
6.79 // cannot convert as no rate is applicable to current date
6.80 + try {
6.81 + c.convert("SKK", CurrencyValue.getCurrencyValue(16d, "CZK"));
6.82 + fail("Should not convert");
6.83 + } catch (Exception e) {
6.84 + }
6.85
6.86 // convert 500SKK to CZK using c:
6.87 // cannot convert as no rate is applicable to current date
6.88 + try {
6.89 + c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"));
6.90 + fail("Should not convert");
6.91 + } catch (Exception e) {
6.92 + }
6.93
6.94 // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
6.95 // assertEquals("Result is 85 CZK");
6.96 + cal.set(2008, 9, 1, 6, 0);
6.97 + result = c.convert("CZK", CurrencyValue.getCurrencyValue(5d, "USD"), cal.getTime());
6.98 + assertEquals(CurrencyValue.getCurrencyValue(85d, "CZK"), result);
6.99
6.100 // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
6.101 // assertEquals("Result is 136 CZK");
6.102 + cal.set(2008, 9, 1, 6, 0);
6.103 + result = c.convert("CZK", CurrencyValue.getCurrencyValue(8d, "USD"), cal.getTime());
6.104 + assertEquals(CurrencyValue.getCurrencyValue(136d, "CZK"), result);
6.105
6.106 // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
6.107 // assertEquals("Result is 59 USD");
6.108 + cal.set(2008, 9, 1, 6, 0);
6.109 + result = c.convert("USD", CurrencyValue.getCurrencyValue(1003d, "CZK"), cal.getTime());
6.110 + assertEquals(CurrencyValue.getCurrencyValue(59d, "USD"), result);
6.111
6.112 // convert 16CZK using c at 2008-10-02 9:00 GMT:
6.113 // assertEquals("Result is 20 SKK");
6.114 + cal.set(2008, 9, 2, 9, 0);
6.115 + result = c.convert("SKK", CurrencyValue.getCurrencyValue(16d, "CZK"), cal.getTime());
6.116 + assertEquals(CurrencyValue.getCurrencyValue(20d, "SKK"), result);
6.117
6.118 // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
6.119 // assertEquals("Result is 400 CZK");
6.120 + cal.set(2008, 9, 2, 9, 0);
6.121 + result = c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"), cal.getTime());
6.122 + assertEquals(CurrencyValue.getCurrencyValue(400d, "CZK"), result);
6.123
6.124 // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
6.125 // cannot convert as no rate is applicable to current date
6.126 + cal.set(2008, 9, 1, 6, 0);
6.127 + try {
6.128 + result = c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"), cal.getTime());
6.129 + fail("Should not convert");
6.130 + } catch (Exception e) {
6.131 + }
6.132 }
6.133
6.134 /** Create convertor that understands two currencies, CZK and
6.135 @@ -103,30 +155,40 @@
6.136 *
6.137 * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
6.138 */
6.139 - public static Convertor createSKKtoCZK2() {
6.140 - return null;
6.141 + public static Convertor<Double, String> createSKKtoCZK2() {
6.142 + return Convertor.getConvertorDoubleString(
6.143 + ExchangeRateValue.getExchangeRate(
6.144 + CurrencyValue.getCurrencyValue(100d, "SKK"),
6.145 + CurrencyValue.getCurrencyValue(90d, "CZK")));
6.146 }
6.147
6.148 public void testDateConvetorWithTwoDifferentRates() throws Exception {
6.149 - if (Boolean.getBoolean("ignore.failing")) {
6.150 - // implement me! then delete this if statement
6.151 - return;
6.152 - }
6.153 + Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
6.154 + cal.clear();
6.155 + cal.set(2008, 9, 1, 0, 0);
6.156 + Date d1 = cal.getTime(); // 2008-10-01 0:00 GMT
6.157 + cal.set(2008, 9, 2, 0, 0);
6.158 + Date d2 = cal.getTime(); // 2008-10-02 0:00 GMT
6.159 + cal.set(2008, 9, 3, 0, 0);
6.160 + Date d3 = cal.getTime(); // 2008-10-03 0:00 GMT
6.161
6.162 - Date d1 = null; // 2008-10-01 0:00 GMT
6.163 - Date d2 = null; // 2008-10-02 0:00 GMT
6.164 - Date d3 = null; // 2008-10-03 0:00 GMT
6.165 -
6.166 - Convertor c = Task2Test.merge(
6.167 + Convertor<Double, String> c = Task2Test.merge(
6.168 limitTo(createSKKtoCZK2(), d1, d2),
6.169 limitTo(Task1Test.createSKKtoCZK(), d2, d3)
6.170 );
6.171
6.172 + CurrencyValue<Double, String> result;
6.173 // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
6.174 // assertEquals("Result is 400 CZK");
6.175 + cal.set(2008, 9, 2, 9, 0);
6.176 + result = c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"), cal.getTime());
6.177 + assertEquals(CurrencyValue.getCurrencyValue(400d, "CZK"), result);
6.178
6.179 // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
6.180 // assertEquals("Result is 450 CZK");
6.181 + cal.set(2008, 9, 1, 6, 0);
6.182 + result = c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"), cal.getTime());
6.183 + assertEquals(CurrencyValue.getCurrencyValue(450d, "CZK"), result);
6.184 }
6.185
6.186 }