task4/solution11/src/org/apidesign/apifest08/currency/Convertor.java
changeset 66 aa3f99f845ef
parent 61 58ec6da75f6f
     1.1 --- a/task4/solution11/src/org/apidesign/apifest08/currency/Convertor.java	Sat Oct 11 23:38:46 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      // ---