Solution 13 for task4
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Fri, 17 Oct 2008 17:31:48 +0200
changeset 6320d332739f60
parent 62 f711ecd374f3
child 64 51f7b894eba6
Solution 13 for task4
task4/solution13/src/org/apidesign/apifest08/currency/ConversionNotSupportedException.java
task4/solution13/src/org/apidesign/apifest08/currency/ConversionResult.java
task4/solution13/src/org/apidesign/apifest08/currency/Convertor.java
task4/solution13/src/org/apidesign/apifest08/currency/ConvertorCurrency.java
task4/solution13/src/org/apidesign/apifest08/currency/DateProvider.java
task4/solution13/src/org/apidesign/apifest08/currency/ExchangeRateProvider.java
task4/solution13/src/org/apidesign/apifest08/currency/IDateExchangeRateEngine.java
task4/solution13/src/org/apidesign/apifest08/currency/IDateProviderEngine.java
task4/solution13/test/org/apidesign/apifest08/test/ConvertorWithDateExchangeRateTest.java
task4/solution13/test/org/apidesign/apifest08/test/RemainderTest.java
task4/solution13/test/org/apidesign/apifest08/test/Task1Test.java
task4/solution13/test/org/apidesign/apifest08/test/Task2Test.java
task4/solution13/test/org/apidesign/apifest08/test/Task4Test.java
     1.1 --- a/task4/solution13/src/org/apidesign/apifest08/currency/ConversionNotSupportedException.java	Sat Oct 11 23:46:05 2008 +0200
     1.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/ConversionNotSupportedException.java	Fri Oct 17 17:31:48 2008 +0200
     1.3 @@ -46,9 +46,9 @@
     1.4      public String toString() {
     1.5          if (from!=null && to !=null) {
     1.6            if (reversed) {
     1.7 -             return "Neither onversion nor reverted conversion from " + from + " to " + to + "  is not supported,"; 
     1.8 +             return "Neither conversion nor reverted conversion from " + from + " to " + to + " is supported,"; 
     1.9            }  else {
    1.10 -             return "Conversion from " + from + " to " + to + "  is not supported,"; 
    1.11 +             return "Conversion from " + from + " to " + to + " is not supported,"; 
    1.12            }
    1.13          } else {
    1.14              return super.toString();
     2.1 --- a/task4/solution13/src/org/apidesign/apifest08/currency/ConversionResult.java	Sat Oct 11 23:46:05 2008 +0200
     2.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/ConversionResult.java	Fri Oct 17 17:31:48 2008 +0200
     2.3 @@ -46,4 +46,11 @@
     2.4          this.remainder = remainder;
     2.5      }
     2.6  
     2.7 +    @Override
     2.8 +    public String toString() {
     2.9 +        return getClass().getSimpleName()+": converter:"+getConverted()+", remainder:"+getRemainder();
    2.10 +    }
    2.11 +    
    2.12 +    
    2.13 +
    2.14  }
     3.1 --- a/task4/solution13/src/org/apidesign/apifest08/currency/Convertor.java	Sat Oct 11 23:46:05 2008 +0200
     3.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/Convertor.java	Fri Oct 17 17:31:48 2008 +0200
     3.3 @@ -3,6 +3,7 @@
     3.4  import java.math.BigDecimal;
     3.5  import java.math.MathContext;
     3.6  import java.math.RoundingMode;
     3.7 +import java.util.Date;
     3.8  
     3.9  /** Convertor able to convert amount from one currency to other currency.
    3.10   * <p>
    3.11 @@ -20,6 +21,11 @@
    3.12   */
    3.13  public class Convertor {
    3.14      private Convertor[] convertors;
    3.15 +    private IDateProviderEngine dateProvider;
    3.16 +    boolean remainderAllowed = true; //if false, remained is not allowed (should be true ideally, but can't handle it now)
    3.17 +    private ExchangeRateProvider exchangeRateProvider;
    3.18 +    private Date fromDate;
    3.19 +    private Date toDate;
    3.20  
    3.21      /** Create new <code>Convertor</code> as merge of provided convertors. Merged convertor will use
    3.22       * provided convertors to convert between currencies.
    3.23 @@ -32,21 +38,26 @@
    3.24       * @return Returns new convertor instance.
    3.25       */
    3.26      public static Convertor createConvertorAsMerge(Convertor[] convertors) {
    3.27 +        for (int i=0;i<convertors.length;i++) {
    3.28 +            if (convertors[i]==null) {
    3.29 +                throw new NullPointerException("Convertor at index "+i+" can't be null");
    3.30 +            }
    3.31 +        }
    3.32          return new Convertor(convertors);
    3.33      }
    3.34      
    3.35 -    boolean remainderAllowed = true; //if false, remained is not allowed (should be true ideally, but can't handle it now)
    3.36 -    ExchangeRateProvider exchangeRateProvider; 
    3.37      
    3.38      /** Create simle convertor.
    3.39       */
    3.40      private Convertor() {
    3.41 +        setDateProvider(DateProvider.createCurrentDateProvider());
    3.42          this.convertors=new Convertor[0];
    3.43      }
    3.44      
    3.45      /** Create merge convertor.
    3.46       */            
    3.47      private Convertor(Convertor[] convertors) {
    3.48 +        this();
    3.49          this.convertors = convertors;       
    3.50      }
    3.51      
    3.52 @@ -75,7 +86,7 @@
    3.53       * @deprecated since version2. Use {@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal) } - explicitly specify conversion currencies.
    3.54       */
    3.55      public ConversionResult convert(BigDecimal amount) {
    3.56 -        return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, false,false);
    3.57 +        return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, false,false,null);
    3.58      }
    3.59      
    3.60      /**
    3.61 @@ -88,32 +99,36 @@
    3.62       * @deprecated since version2. Use {@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal) } - explicitly specify conversion currencies.
    3.63       */
    3.64      public ConversionResult convertBack(BigDecimal amount) {
    3.65 -        return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, true,false);
    3.66 +        return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, true,false,null);
    3.67      }
    3.68  
    3.69 -    private ConversionResult convertUsingSimpleConvertor(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, boolean reversibleExRate, BigDecimal amount, boolean convertBack) throws ConversionNotSupportedException, RuntimeException {
    3.70 -        ConversionResult result = new ConversionResult();
    3.71 +    private ConversionResult convertUsingSimpleConvertor(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, boolean reversibleExRate, BigDecimal amount, boolean convertBack,Date date) throws ConversionNotSupportedException, RuntimeException {
    3.72 +        if (date == null) {
    3.73 +            date = dateProvider.getCurrentDate();
    3.74 +        }
    3.75  
    3.76 -        //ExchangeRate rate = exchangeRateProvider.getExchangeRate();
    3.77          ExchangeRate rate;
    3.78          if (reversibleExRate) {
    3.79 -            rate = exchangeRateProvider.getReversibleExchangeRate(fromCurrency, toCurrency);
    3.80 +            rate = exchangeRateProvider.getReversibleExchangeRate(fromCurrency, toCurrency, date);
    3.81          } else {
    3.82 -            rate = exchangeRateProvider.getExchangeRate(fromCurrency, toCurrency);
    3.83 +            rate = exchangeRateProvider.getExchangeRate(fromCurrency, toCurrency, date);
    3.84          }
    3.85          if (rate == null) {
    3.86              return null;
    3.87          }
    3.88 +        ConversionResult result = new ConversionResult();
    3.89          
    3.90          int fromFranctionDigits = fromCurrency.getDefaultFractionDigits();
    3.91          int toFractionDigits = toCurrency.getDefaultFractionDigits();
    3.92 +        
    3.93 +        int usedFractionDigits = Math.max(fromFranctionDigits, toFractionDigits);
    3.94  
    3.95 -        if (toFractionDigits != 2) {
    3.96 -            throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + exchangeRateProvider.getToCurrency() + " has " + toFractionDigits + " defaultFractionDigits");
    3.97 -        }
    3.98 -        if (fromFranctionDigits != 2) {
    3.99 -            throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + exchangeRateProvider.getFromCurrency() + " has " + fromFranctionDigits + " defaultFractionDigits");
   3.100 -        }
   3.101 +        //if (toFractionDigits != 2) {
   3.102 +        //    throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + toCurrency + " has " + toFractionDigits + " defaultFractionDigits");
   3.103 +        //}
   3.104 +        //if (fromFranctionDigits != 2) {
   3.105 +        //    throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + fromCurrency + " has " + fromFranctionDigits + " defaultFractionDigits");
   3.106 +        //}
   3.107  
   3.108          if (amount.signum() == -1) {
   3.109              throw new RuntimeException("Can convert only non-negative value, current value is " + amount);
   3.110 @@ -134,7 +149,7 @@
   3.111              to = rate.getToValue();
   3.112          }
   3.113  
   3.114 -        BigDecimal amountCent = amount.movePointRight(toFractionDigits);
   3.115 +        BigDecimal amountCent = amount.movePointRight(usedFractionDigits);
   3.116  
   3.117          final BigDecimal multiplied = amountCent.multiply(to, context);
   3.118          BigDecimal[] division = multiplied.divideAndRemainder(from, context);
   3.119 @@ -142,26 +157,33 @@
   3.120          if (!remainderAllowed && !(BigDecimal.ZERO.equals(division[1]))) {
   3.121              throw new RuntimeException("Remained is not allowed - remaining amount is " + division[1] + " cents");
   3.122          } else {
   3.123 -            result.setRemainder(BigDecimal.ZERO);
   3.124 +            result.setRemainder(BigDecimal.ZERO.setScale(fromFranctionDigits));
   3.125          }
   3.126  
   3.127 -        BigDecimal converted = division[0].movePointLeft(toFractionDigits);
   3.128 -        converted = converted.setScale(toFractionDigits); //XXX ugly
   3.129 +        BigDecimal converted = division[0].movePointLeft(usedFractionDigits);
   3.130 +        converted = converted.setScale(toFractionDigits,RoundingMode.DOWN); //XXX ugly
   3.131          result.setConverted(converted);
   3.132 -        //result.setRemainder(...);
   3.133 +        BigDecimal[] convertedInBase = converted.multiply(from).divideAndRemainder(to);
   3.134 +        BigDecimal x2 = convertedInBase[0].add(convertedInBase[1]);
   3.135 +        BigDecimal remainder = amount.subtract(x2);
   3.136 +        result.setRemainder(remainder);
   3.137 +        //System.out.println("ConvertedInBase="+Arrays.asList(convertedInBase)+":"+x2 +" ("+amount+") ["+converted+"] <"+from+","+to+"> ->"+remainder);
   3.138          return result;
   3.139      }
   3.140      
   3.141  
   3.142 -    private ConversionResult convertValue(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency,BigDecimal amount, boolean convertBack,boolean reversibleExRate) throws RuntimeException {
   3.143 +    private ConversionResult convertValue(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency,BigDecimal amount, boolean convertBack,boolean reversibleExRate,Date date) throws RuntimeException {
   3.144          //result.setRemainder(...);
   3.145 +        if (!dateIsInLimit(date)) {
   3.146 +            return null;
   3.147 +        }        
   3.148          if (convertors.length==0) {
   3.149 -            return convertUsingSimpleConvertor(fromCurrency, toCurrency, reversibleExRate, amount, convertBack);
   3.150 +            return convertUsingSimpleConvertor(fromCurrency, toCurrency, reversibleExRate, amount, convertBack,date);
   3.151          } else {
   3.152              ConversionResult result = null;
   3.153              for (int i = 0;i<convertors.length;i++) {
   3.154                  Convertor subConvertor = convertors[i];
   3.155 -                result = subConvertor.convertValue(fromCurrency, toCurrency, amount, convertBack, reversibleExRate);
   3.156 +                result = subConvertor.convertValue(fromCurrency, toCurrency, amount, convertBack, reversibleExRate,date);
   3.157                  if (result!=null) {
   3.158                      break;
   3.159                  }
   3.160 @@ -175,6 +197,8 @@
   3.161       * <p>
   3.162       * Exchange rate is provided by exchange rate provider which was specified when Convertor was created.
   3.163       * This method is using only exchange rate from->to and not trying to use reverted excange rate to->from.
   3.164 +     * <p>
   3.165 +     * This method is using date from <code>IDateProviderEngine</code> to get exchange rate (see {@link #getDateProvider() }.
   3.166       * 
   3.167       * @param fromCurrency Source currency to convert from.
   3.168       * @param toCurrency Target currency to convert to.
   3.169 @@ -184,19 +208,40 @@
   3.170       * @throws ConversionNotSupportedException If conversion from <code>fromCurrency</code> to <code>toCurrency</code> is not supported.
   3.171       */
   3.172      public ConversionResult convert(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value) {
   3.173 -        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,false);
   3.174 +        Date defaultDate = dateProvider.getCurrentDate();
   3.175 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,false,defaultDate);
   3.176          if (result==null) {
   3.177              //throw new ConversionNotSupportedException("Conversion from " + fromCurrency + " to " + toCurrency + " is not supported");
   3.178              throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),false);
   3.179          }
   3.180          return result;
   3.181      }
   3.182 +    /**
   3.183 +     * Same as {@link #convert(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)} but using provided rate instead of
   3.184 +     * <code>IDateProviderEngine</code>.
   3.185 +     * 
   3.186 +     * @param fromCurrency Source currency to convert from.
   3.187 +     * @param toCurrency Target currency to convert to.
   3.188 +     * @param value Value in source currency which should be converted.
   3.189 +     * @return Return conversion result.
   3.190 +     * @param date Conversion date
   3.191 +     * @return Return conversion result.
   3.192 +     */
   3.193 +    public ConversionResult convert(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value, Date date) {
   3.194 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,false,date);
   3.195 +        if (result==null) {
   3.196 +            throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),false);
   3.197 +        }
   3.198 +        return result;
   3.199 +    }    
   3.200      
   3.201      /**
   3.202       * Convert <code>value</code> from <code>fromCurrency</code> to <code>toCurrency</code>.
   3.203       * Exchange rate is provided by exchange rate provider which was specified when Convertor was created.
   3.204       * <p>
   3.205       *  This method is using only exchange rate from->to and if not found, it is trying to use reverted excange rate to->from.
   3.206 +     * <p>
   3.207 +     * This method is using date from <code>IDateProviderEngine</code> to get exchange rate (see {@link #getDateProvider() }.
   3.208       * 
   3.209       * @param fromCurrency Source currency to convert from.
   3.210       * @param toCurrency Target currency to convert to.
   3.211 @@ -207,7 +252,8 @@
   3.212       *         is not supported and neither conversion from <code>toCurrency</code> to <code>fromCurrency</code> is not supported.
   3.213       */
   3.214      public ConversionResult convertWithReversibleRates(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value) {
   3.215 -        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,true);
   3.216 +        Date defaultDate = dateProvider.getCurrentDate();
   3.217 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,true,defaultDate);
   3.218          if (result==null) {
   3.219              //throw new ConversionNotSupportedException("Neither onversion nor reverted conversion from " + fromCurrency + " to " + toCurrency + "  is not supported,");
   3.220              throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),true);
   3.221 @@ -215,4 +261,75 @@
   3.222          return result;
   3.223      }
   3.224      
   3.225 +    /**
   3.226 +     * Same as {@link #convertWithReversibleRates(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)} but using provided rate instead of
   3.227 +     * <code>IDateProviderEngine</code>.
   3.228 +     * 
   3.229 +     * @param fromCurrency Source currency to convert from.
   3.230 +     * @param toCurrency Target currency to convert to.
   3.231 +     * @param value Value in source currency which should be converted.
   3.232 +     * @return Return conversion result.
   3.233 +     * @param date Conversion date
   3.234 +     * @return Return conversion result.
   3.235 +     */
   3.236 +    public ConversionResult convertWithReversibleRates(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value, Date date) {
   3.237 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,true,date);
   3.238 +        if (result==null) {
   3.239 +            throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),true);
   3.240 +        }
   3.241 +        return result;
   3.242 +    }
   3.243 +    
   3.244 +    private boolean dateIsInLimit(Date date) {
   3.245 +        boolean result;
   3.246 +//        if (date==null) {
   3.247 +//          result = true;  
   3.248 +//        } else 
   3.249 +        if (fromDate == null && toDate == null) {
   3.250 +            result = true;
   3.251 +        } else if (fromDate.getTime()<=date.getTime() && date.getTime()<=toDate.getTime()) {
   3.252 +            result = true;
   3.253 +        } else {
   3.254 +            result = false;
   3.255 +        }
   3.256 +        
   3.257 +        return result;
   3.258 +    }
   3.259 +    
   3.260 +    
   3.261 +    public void limitAllowedDates(Date from, Date till) {
   3.262 +        if (from==null) {
   3.263 +            throw new NullPointerException("from Date can't be null");
   3.264 +        }
   3.265 +        if (till==null) {
   3.266 +            throw new NullPointerException("till Date can't be null");
   3.267 +        }
   3.268 +        if (from.getTime()>till.getTime()) {
   3.269 +            throw new IllegalArgumentException("From date "+from+" must be before tii date "+till);
   3.270 +        }
   3.271 +        this.fromDate = from;
   3.272 +        this.toDate = till;
   3.273 +    }
   3.274 +    
   3.275 +
   3.276 +    /** Return current date provider.
   3.277 +     * @return Returns current date provider.
   3.278 +     */
   3.279 +    public IDateProviderEngine getDateProvider() {
   3.280 +        return dateProvider;
   3.281 +    }
   3.282 +
   3.283 +    
   3.284 +    /**
   3.285 +     * Set date provider. Date provider is used to get "current date". Current date 
   3.286 +     * is used by methods {@link #convert(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)} and  
   3.287 +     * {@link #convertWithReversibleRates(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)}.
   3.288 +     * 
   3.289 +     * @param dateProvider Date provider which should be used by Convertor.
   3.290 +     */
   3.291 +    public void setDateProvider(IDateProviderEngine dateProvider) {
   3.292 +        this.dateProvider = dateProvider;
   3.293 +    }
   3.294 +    
   3.295 +    
   3.296  }
     4.1 --- a/task4/solution13/src/org/apidesign/apifest08/currency/ConvertorCurrency.java	Sat Oct 11 23:46:05 2008 +0200
     4.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/ConvertorCurrency.java	Fri Oct 17 17:31:48 2008 +0200
     4.3 @@ -12,22 +12,38 @@
     4.4   * @author arnostvalicek
     4.5   */
     4.6  public class ConvertorCurrency {
     4.7 +    private String currencyCode;
     4.8 +    private int fractionDigits;
     4.9  
    4.10 -    private Currency currency;
    4.11 -
    4.12 -    private void setJavaCurrency(Currency javaCurrency) {
    4.13 -        this.currency = javaCurrency;
    4.14 -    }
    4.15  
    4.16      /**
    4.17 -     * Static method providing instance of <code>ConvertorCurrency</code> base of currency code.
    4.18 +     * Static method providing instance of <code>ConvertorCurrency</code> based on currency code.
    4.19 +     * <p>
    4.20 +     * <code>currencyCode</code> is one of codes supported by Java class {@link java.util.Currency}.
    4.21       * 
    4.22       * @param currencyCode Code of required currency.
    4.23       * @return Returns required <code>ConvertorCurrency</code>
    4.24       */
    4.25      public static ConvertorCurrency getInstance(String currencyCode) {
    4.26          ConvertorCurrency convertorCurrency = new ConvertorCurrency();
    4.27 -        convertorCurrency.setJavaCurrency(Currency.getInstance(currencyCode));
    4.28 +        Currency currency = Currency.getInstance(currencyCode);
    4.29 +        convertorCurrency.setCurrencyCode(currency.getCurrencyCode());
    4.30 +        convertorCurrency.setDefaultFractionDigits(currency.getDefaultFractionDigits());
    4.31 +        return convertorCurrency;
    4.32 +    }
    4.33 +    
    4.34 +    /**
    4.35 +     * Static method providing instance of <code>ConvertorCurrency</code> base of currency code.
    4.36 +     * 
    4.37 +     * @param currencyCode Code of required currency.
    4.38 +     * @param fractionDigits Number of fraction digits for currency.
    4.39 +     * @return Returns required <code>ConvertorCurrency</code>
    4.40 +     * @since version4
    4.41 +     */
    4.42 +    public static ConvertorCurrency getInstance(String currencyCode, int fractionDigits) {
    4.43 +        ConvertorCurrency convertorCurrency = new ConvertorCurrency();
    4.44 +        convertorCurrency.setCurrencyCode(currencyCode);
    4.45 +        convertorCurrency.setDefaultFractionDigits(fractionDigits);
    4.46          return convertorCurrency;
    4.47      }
    4.48  
    4.49 @@ -36,7 +52,23 @@
    4.50       * @return Returns the default number of fraction digits used with this currency.
    4.51       */
    4.52      public int getDefaultFractionDigits() {
    4.53 -        return currency.getDefaultFractionDigits();
    4.54 +        return fractionDigits;
    4.55 +    }
    4.56 +    
    4.57 +    private void setDefaultFractionDigits(int value) {
    4.58 +        this.fractionDigits = value;
    4.59 +    }
    4.60 +    
    4.61 +    /**
    4.62 +     * Get currency code.
    4.63 +     * @return Returns currency code.
    4.64 +     */
    4.65 +    public String getCurrencyCode() {
    4.66 +        return currencyCode;
    4.67 +    }
    4.68 +    
    4.69 +    private void setCurrencyCode(String value) {
    4.70 +        currencyCode = value;
    4.71      }
    4.72  
    4.73      @Override
    4.74 @@ -44,7 +76,7 @@
    4.75          boolean result;
    4.76          if (obj instanceof ConvertorCurrency) {
    4.77              ConvertorCurrency that = (ConvertorCurrency) obj;
    4.78 -            result = currency.equals(that.currency);
    4.79 +            result = getCurrencyCode().equals(that.getCurrencyCode())  && getDefaultFractionDigits()==that.getDefaultFractionDigits();
    4.80          } else {
    4.81              result = false;
    4.82          }
    4.83 @@ -53,18 +85,13 @@
    4.84  
    4.85      @Override
    4.86      public int hashCode() {
    4.87 -        return currency==null ? 47/*??*/ : currency.hashCode();
    4.88 +        return currencyCode==null ? 47/*??*/ : currencyCode.hashCode();
    4.89      }
    4.90 -    
    4.91 -    
    4.92 -    
    4.93 +        
    4.94  
    4.95      @Override
    4.96      public String toString() {
    4.97 -        return  "ConvertorCurrency[" + (currency != null ? currency.toString() : "NO-BASE-CURRENCY")+"]";
    4.98 +        return  "ConvertorCurrency[" + (currencyCode != null ? currencyCode : "NO-BASE-CURRENCY")+"]";
    4.99      }
   4.100 -    
   4.101 -    String getCurrencyCode() {
   4.102 -        return currency.getCurrencyCode();
   4.103 -    }
   4.104 -}
   4.105 +   
   4.106 +}
   4.107 \ No newline at end of file
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/DateProvider.java	Fri Oct 17 17:31:48 2008 +0200
     5.3 @@ -0,0 +1,36 @@
     5.4 +package org.apidesign.apifest08.currency;
     5.5 +
     5.6 +import java.util.Date;
     5.7 +
     5.8 +/**
     5.9 + * Provider for current date.
    5.10 + * 
    5.11 + * @author arnostvalicek
    5.12 + * @since version4.
    5.13 + */
    5.14 +public class DateProvider {
    5.15 +    private DateProvider() {
    5.16 +        
    5.17 +    }
    5.18 +    
    5.19 +    /**
    5.20 +     * Create date provider based on current system time.
    5.21 +     * @see     java.lang.System#currentTimeMillis()
    5.22 +     */    
    5.23 +    public static IDateProviderEngine createCurrentDateProvider() {
    5.24 +        return new CurrentDateProvider();
    5.25 +    }
    5.26 +    
    5.27 +    /**
    5.28 +     * Date provider based on current system time.
    5.29 +     * @see     java.lang.System#currentTimeMillis()
    5.30 +     */
    5.31 +    private static class CurrentDateProvider implements IDateProviderEngine {
    5.32 +
    5.33 +        public Date getCurrentDate() {
    5.34 +            return new Date();
    5.35 +        }
    5.36 +        
    5.37 +    }
    5.38 +
    5.39 +}
     6.1 --- a/task4/solution13/src/org/apidesign/apifest08/currency/ExchangeRateProvider.java	Sat Oct 11 23:46:05 2008 +0200
     6.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/ExchangeRateProvider.java	Fri Oct 17 17:31:48 2008 +0200
     6.3 @@ -12,6 +12,7 @@
     6.4   * <ul>
     6.5   * <li>{@link #createExchangeRateProvider() } - create <em>simple</em> exchange rate provider using fixed echange rate.
     6.6   * <li>{@link #createExchangeRateProvider(IExchangeRateEngine) } - create exchange rate provider using custom {@link IExchangeRateEngine}.
     6.7 + * <li>{@link #createDateExchangeRateProvider(IDateExchangeRateEngine) } - create exchange rate provider using custom {@link IDateExchangeRateEngine}.
     6.8   * </ul>
     6.9   * <p>
    6.10   * Date dependend exchange rate to be implemented.
    6.11 @@ -21,6 +22,7 @@
    6.12  public class ExchangeRateProvider {
    6.13  
    6.14      IExchangeRateEngine exrateEngine;
    6.15 +    IDateExchangeRateEngine dateExrateEngine;
    6.16      
    6.17      /**
    6.18       * Simple constructor for <code>ExchangeRateProviderM</code> which can provide fixed exchange rate.
    6.19 @@ -62,6 +64,18 @@
    6.20          provider.exrateEngine = exchangeRateEngine;
    6.21          return provider;
    6.22      }
    6.23 +    
    6.24 +    /**
    6.25 +     * Static method to create exchange rate provider which is using provided <code>IExIDateExchangeRateEnginechangeRateEngine</code>. This exchange rate provider is using
    6.26 +     * <code>IExchangeRateEngine</code> to get actual exchange rate.
    6.27 +     * @param exchangeRateEngine <code>IDateExchangeRateEngine</code> used to get exchange rate.
    6.28 +     * @return Returns instance of <code>ExchangeRateProvider</code>
    6.29 +     */
    6.30 +    public static ExchangeRateProvider createDateExchangeRateProvider(IDateExchangeRateEngine exchangeRateEngine) {
    6.31 +        ExchangeRateProvider provider = new ExchangeRateProvider();
    6.32 +        provider.dateExrateEngine = exchangeRateEngine;
    6.33 +        return provider;
    6.34 +    }
    6.35  
    6.36      /**
    6.37       * Add new exchange rate to <code></code> to this <em>simple</em> exchange rate provider.
    6.38 @@ -103,25 +117,44 @@
    6.39  
    6.40      /**
    6.41       * Get fixed exange rate for currencies (from->to).
    6.42 +     * @param fromCurrency Source currency.
    6.43 +     * @param toCurrency Target currency.
    6.44       * @return Returns exchange rate or <code>null</code> if exchange rate not found.
    6.45       */
    6.46      public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
    6.47 -        return getExchangeRateImpl(fromCurrency, toCurrency);
    6.48 +        return getExchangeRateImpl(fromCurrency, toCurrency,null);
    6.49 +    }
    6.50 +    
    6.51 +    /**
    6.52 +     * Get exange rate for currencies (from->to) for date.
    6.53 +     * @param fromCurrency Source currency.
    6.54 +     * @param toCurrency Target currency.
    6.55 +     * @param date Conversion date.
    6.56 +     * @return Returns exchange rate or <code>null</code> if exchange rate not found.
    6.57 +     */
    6.58 +    public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, Date date) {
    6.59 +        return getExchangeRateImpl(fromCurrency, toCurrency,date);
    6.60      }
    6.61  
    6.62      /**
    6.63       * Get fixed exange rate for currencies (from->to) or reversed exchange rate (to->from).
    6.64 +     * @param fromCurrency Source currency.
    6.65 +     * @param toCurrency Target currency.
    6.66       * @return Returns exchange rate or <code>null</code> if exchange rate not found.
    6.67       */
    6.68      public ExchangeRate getReversibleExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
    6.69 -        ExchangeRate rate = getExchangeRateImpl(fromCurrency, toCurrency);
    6.70 -        if (rate == null) {
    6.71 -            ExchangeRate revertedRate = getExchangeRateImpl(toCurrency, fromCurrency);
    6.72 -            if (revertedRate != null) {
    6.73 -                rate = ExchangeRate.createRevertedRate(revertedRate);
    6.74 -            }
    6.75 -        }
    6.76 -        return rate;
    6.77 +        return getReversibleExrateImpl(fromCurrency, toCurrency,null);
    6.78 +    }
    6.79 +    
    6.80 +    /**
    6.81 +     * Get exange rate for currencies (from->to) or reversed exchange rate (to->from) for date.
    6.82 +     * @param fromCurrency Source currency.
    6.83 +     * @param toCurrency Target currency.
    6.84 +     * @param date Conversion date.
    6.85 +     * @return Returns exchange rate or <code>null</code> if exchange rate not found.
    6.86 +     */
    6.87 +    public ExchangeRate getReversibleExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, Date date) {
    6.88 +        return getReversibleExrateImpl(fromCurrency, toCurrency, date);
    6.89      }
    6.90  
    6.91      /**
    6.92 @@ -157,9 +190,28 @@
    6.93              throw new IllegalStateException("Method supported only for FixedOneExchangeRateEngine. This method is deprecated");
    6.94          }
    6.95      }
    6.96 +    
    6.97  
    6.98 -    private ExchangeRate getExchangeRateImpl(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
    6.99 -        ExchangeRate result = exrateEngine.getExchangeRate(fromCurrency, toCurrency);
   6.100 +    private ExchangeRate getReversibleExrateImpl(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency,Date date) {
   6.101 +        ExchangeRate rate = getExchangeRateImpl(fromCurrency, toCurrency, date);
   6.102 +        if (rate == null) {
   6.103 +            ExchangeRate revertedRate = getExchangeRateImpl(toCurrency, fromCurrency, date);
   6.104 +            if (revertedRate != null) {
   6.105 +                rate = ExchangeRate.createRevertedRate(revertedRate);
   6.106 +            }
   6.107 +        }
   6.108 +        return rate;
   6.109 +    }    
   6.110 +
   6.111 +    private ExchangeRate getExchangeRateImpl(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency,Date date) {
   6.112 +        ExchangeRate result;
   6.113 +        if (exrateEngine!=null) {
   6.114 +            result = exrateEngine.getExchangeRate(fromCurrency, toCurrency);
   6.115 +        } else if (dateExrateEngine!=null) {
   6.116 +            result = dateExrateEngine.getExchangeRate(fromCurrency, toCurrency, date);
   6.117 +        } else {
   6.118 +            throw new IllegalStateException("No exchange rate engine provided");
   6.119 +        }
   6.120          return result;
   6.121      }
   6.122  
   6.123 @@ -224,7 +276,7 @@
   6.124       */
   6.125      private static class FixedExchangeRateEngine implements IExchangeRateEngine {
   6.126  
   6.127 -        private Map exchangeRateMap = new HashMap();
   6.128 +        private Map<ConvertorCurrency, Map<ConvertorCurrency, ExchangeRate>> exchangeRateMap = new HashMap<ConvertorCurrency, Map<ConvertorCurrency, ExchangeRate>>();
   6.129  
   6.130          private FixedExchangeRateEngine() {
   6.131          }
   6.132 @@ -238,15 +290,14 @@
   6.133          }
   6.134  
   6.135          public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
   6.136 -            Map map2 = (Map) exchangeRateMap.get(fromCurrency);
   6.137 +            Map<ConvertorCurrency,ExchangeRate> map2 = exchangeRateMap.get(fromCurrency);
   6.138              if (map2 == null) {
   6.139                  return null;
   6.140              }
   6.141 -            ExchangeRate result = (ExchangeRate) map2.get(toCurrency);
   6.142 +            ExchangeRate result = map2.get(toCurrency);
   6.143              return result;
   6.144          }
   6.145  
   6.146 -        @SuppressWarnings("unchecked")
   6.147          public synchronized void addFixedCurencyRate(ConvertorCurrency fromCurrency, BigDecimal fromValue, ConvertorCurrency toCurrency, BigDecimal toValue) {
   6.148              if (fromValue == null) {
   6.149                  throw new NullPointerException("fromValue can't be null");
   6.150 @@ -254,9 +305,9 @@
   6.151              if (toValue == null) {
   6.152                  throw new NullPointerException("toValue can't be null");
   6.153              }
   6.154 -            Map map2 = (Map) exchangeRateMap.get(fromCurrency);
   6.155 +            Map<ConvertorCurrency,ExchangeRate> map2 = exchangeRateMap.get(fromCurrency);
   6.156              if (map2 == null) {
   6.157 -                map2 = new HashMap();
   6.158 +                map2 = new HashMap<ConvertorCurrency,ExchangeRate>();
   6.159                  exchangeRateMap.put(fromCurrency, map2);
   6.160              }
   6.161  
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/IDateExchangeRateEngine.java	Fri Oct 17 17:31:48 2008 +0200
     7.3 @@ -0,0 +1,21 @@
     7.4 +package org.apidesign.apifest08.currency;
     7.5 +
     7.6 +import java.util.Date;
     7.7 +
     7.8 +/**
     7.9 + * Interface for exchange rate engine using dates.
    7.10 + * 
    7.11 + * @author arnostvalicek
    7.12 + */
    7.13 +public interface IDateExchangeRateEngine {
    7.14 +    /**
    7.15 +     * Get exchange rate for conversion from <code>fromCurrency</code> to <code>toCurrency</code> at <code>date</code>.
    7.16 +     * 
    7.17 +     * @param fromCurrency From currency.
    7.18 +     * @param toCurrency To currency.
    7.19 +     * @param date Conversion date.
    7.20 +     * @return Returns <code>ExchangeRate</code> if exchange rate is known or <code>null</code> if exchanger rate is not known.
    7.21 +     */
    7.22 +    public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, Date date);
    7.23 +
    7.24 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/IDateProviderEngine.java	Fri Oct 17 17:31:48 2008 +0200
     8.3 @@ -0,0 +1,19 @@
     8.4 +package org.apidesign.apifest08.currency;
     8.5 +
     8.6 +import java.util.Date;
     8.7 +
     8.8 +/**
     8.9 + * Date provider - provides current date.
    8.10 + * @see DateProvider#createCurrentDateProvider() 
    8.11 + * @see Convertor#setDateProvider(IDateProviderEngine) 
    8.12 + * @author arnostvalicek
    8.13 + * @since version4
    8.14 + */
    8.15 +public interface IDateProviderEngine {
    8.16 +    /**
    8.17 +     * Get current date.
    8.18 +     * @return Return current date.
    8.19 +     */
    8.20 +    public Date getCurrentDate();
    8.21 +
    8.22 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/ConvertorWithDateExchangeRateTest.java	Fri Oct 17 17:31:48 2008 +0200
     9.3 @@ -0,0 +1,109 @@
     9.4 +/*
     9.5 + * To change this template, choose Tools | Templates
     9.6 + * and open the template in the editor.
     9.7 + */
     9.8 +package org.apidesign.apifest08.test;
     9.9 +
    9.10 +import java.math.BigDecimal;
    9.11 +import java.text.ParseException;
    9.12 +import java.text.SimpleDateFormat;
    9.13 +import java.util.Date;
    9.14 +import junit.framework.TestCase;
    9.15 +import org.apidesign.apifest08.currency.ConversionResult;
    9.16 +import org.apidesign.apifest08.currency.Convertor;
    9.17 +import org.apidesign.apifest08.currency.ConvertorCurrency;
    9.18 +import org.apidesign.apifest08.currency.ExchangeRate;
    9.19 +import org.apidesign.apifest08.currency.ExchangeRateProvider;
    9.20 +import org.apidesign.apifest08.currency.IDateExchangeRateEngine;
    9.21 +import org.apidesign.apifest08.currency.IDateProviderEngine;
    9.22 +
    9.23 +/**
    9.24 + *
    9.25 + * @author arnostvalicek
    9.26 + */
    9.27 +public class ConvertorWithDateExchangeRateTest extends TestCase {
    9.28 +
    9.29 +    private static ConvertorCurrency CZK = ConvertorCurrency.getInstance("CZK");
    9.30 +    private static ConvertorCurrency SKK = ConvertorCurrency.getInstance("SKK");
    9.31 +    private static ConvertorCurrency USD = ConvertorCurrency.getInstance("USD");
    9.32 +    private SimpleDateFormat df;
    9.33 +
    9.34 +    @Override
    9.35 +    protected void setUp() throws Exception {
    9.36 +        super.setUp();
    9.37 +        df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
    9.38 +    }
    9.39 +
    9.40 +    @Override
    9.41 +    protected void tearDown() throws Exception {
    9.42 +        super.tearDown();
    9.43 +        df = null;
    9.44 +    }
    9.45 +
    9.46 +    private IDateExchangeRateEngine createDateEngine() {
    9.47 +        IDateExchangeRateEngine engine = new IDateExchangeRateEngine() {
    9.48 +
    9.49 +            public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, Date date) {
    9.50 +                int day = date.getDay();
    9.51 +                if (day >= 1 && day <= 5) {
    9.52 +                    //weekday
    9.53 +                    return new ExchangeRate(new BigDecimal(1), new BigDecimal(2));
    9.54 +                } else {
    9.55 +                    //weekend
    9.56 +                    return new ExchangeRate(new BigDecimal(1), new BigDecimal(3));
    9.57 +                }
    9.58 +            }
    9.59 +        };
    9.60 +        return engine;
    9.61 +    }
    9.62 +
    9.63 +    private Convertor createConvertor() {
    9.64 +        ExchangeRateProvider exchangeRateProvider = ExchangeRateProvider.createDateExchangeRateProvider(createDateEngine());
    9.65 +        Convertor c = Convertor.createConvertor(exchangeRateProvider);
    9.66 +        return c;
    9.67 +    }
    9.68 +
    9.69 +    public void testConvertuUsingDifferentRatesForDays() throws ParseException {
    9.70 +        Convertor c = createConvertor();
    9.71 +        {
    9.72 +            Date d1 = df.parse("2008-10-15 9:00 GMT");  //Wednesday
    9.73 +            ConversionResult resultWeekday = c.convert(CZK, SKK, new BigDecimal("2.3"), d1);
    9.74 +            assertEquals("Weekday conversion", new BigDecimal("4.60"), resultWeekday.getConverted());
    9.75 +        }
    9.76 +        {
    9.77 +            Date d1 = df.parse("2008-10-18 9:00 GMT");  //Saturday
    9.78 +            ConversionResult resultWeekday = c.convert(CZK, SKK, new BigDecimal("2.3"), d1);
    9.79 +            assertEquals("Weekday conversion", new BigDecimal("6.90"), resultWeekday.getConverted());
    9.80 +        }
    9.81 +    }
    9.82 +
    9.83 +    public void testConvertUsingDifferenDefaultDay() throws ParseException {
    9.84 +        class MyDateProvider implements IDateProviderEngine {
    9.85 +
    9.86 +            private Date date;
    9.87 +
    9.88 +            void setDate(Date date) {
    9.89 +                this.date = date;
    9.90 +            }
    9.91 +
    9.92 +            public Date getCurrentDate() {
    9.93 +                return date;
    9.94 +            }
    9.95 +        }
    9.96 +
    9.97 +        Convertor c = createConvertor();
    9.98 +        MyDateProvider dp = new MyDateProvider();
    9.99 +        c.setDateProvider(dp);
   9.100 +
   9.101 +        {
   9.102 +            dp.setDate(df.parse("2008-10-15 9:00 GMT")); //Wednesday
   9.103 +            ConversionResult resultWeekday = c.convert(CZK, SKK, new BigDecimal("2.3"));
   9.104 +            assertEquals("Weekday conversion", new BigDecimal("4.60"), resultWeekday.getConverted());
   9.105 +        }
   9.106 +        {
   9.107 +            dp.setDate(df.parse("2008-10-18 9:00 GMT"));  //Saturday
   9.108 +            ConversionResult resultWeekday = c.convert(CZK, SKK, new BigDecimal("2.3"));
   9.109 +            assertEquals("Weekday conversion", new BigDecimal("6.90"), resultWeekday.getConverted());
   9.110 +        }
   9.111 +    }
   9.112 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/RemainderTest.java	Fri Oct 17 17:31:48 2008 +0200
    10.3 @@ -0,0 +1,80 @@
    10.4 +/*
    10.5 + * To change this template, choose Tools | Templates
    10.6 + * and open the template in the editor.
    10.7 + */
    10.8 +
    10.9 +package org.apidesign.apifest08.test;
   10.10 +
   10.11 +import java.math.BigDecimal;
   10.12 +import java.text.SimpleDateFormat;
   10.13 +import junit.framework.TestCase;
   10.14 +import org.apidesign.apifest08.currency.ConversionResult;
   10.15 +import org.apidesign.apifest08.currency.Convertor;
   10.16 +import org.apidesign.apifest08.currency.ConvertorCurrency;
   10.17 +import org.apidesign.apifest08.currency.ExchangeRateProvider;
   10.18 +
   10.19 +/**
   10.20 + *
   10.21 + * @author arnostvalicek
   10.22 + */
   10.23 +public class RemainderTest extends TestCase {
   10.24 +    private static ConvertorCurrency CZK = ConvertorCurrency.getInstance("CZK");
   10.25 +    private static ConvertorCurrency SKK = ConvertorCurrency.getInstance("SKK");
   10.26 +    private static ConvertorCurrency USD = ConvertorCurrency.getInstance("USD");
   10.27 +    private SimpleDateFormat df;
   10.28 +    
   10.29 +    @Override
   10.30 +    protected void setUp() throws Exception {
   10.31 +        super.setUp();
   10.32 +        df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
   10.33 +        
   10.34 +    }
   10.35 +
   10.36 +    @Override
   10.37 +    protected void tearDown() throws Exception {
   10.38 +        super.tearDown();
   10.39 +    }
   10.40 +    
   10.41 +    public void testRemainder1() {
   10.42 +        Convertor c = Task2Test.createUsdToSkkConvertor();
   10.43 +        {
   10.44 +            final BigDecimal convertedValue = new BigDecimal("12.34");
   10.45 +            ConversionResult result = c.convertWithReversibleRates(SKK,USD,convertedValue);
   10.46 +            //System.out.println("Result = "+result);
   10.47 +            assertEquals("Converted", new BigDecimal("0.61"),result.getConverted());
   10.48 +            assertEquals("Remainder", new BigDecimal("0.14"),result.getRemainder());
   10.49 +            
   10.50 +            ConversionResult resultBack = c.convertWithReversibleRates(USD, SKK, result.getConverted());
   10.51 +            assertEquals("Conversion back", convertedValue, resultBack.getConverted().add(result.getRemainder()));
   10.52 +            
   10.53 +        }
   10.54 +        {
   10.55 +            ConversionResult result = c.convertWithReversibleRates(SKK,USD,new BigDecimal("20.00"));
   10.56 +            //System.out.println("Result = "+result);
   10.57 +        }
   10.58 +    
   10.59 +        {
   10.60 +            ConversionResult result = c.convertWithReversibleRates(USD,SKK,new BigDecimal("1.00"));
   10.61 +            //System.out.println("Result = "+result);
   10.62 +        }
   10.63 +        
   10.64 +    }
   10.65 +    
   10.66 +    public void testRemainderStripCents() {
   10.67 +        ConvertorCurrency CZK_cents=ConvertorCurrency.getInstance("CZK", 2);
   10.68 +        ConvertorCurrency CZK_nocents=ConvertorCurrency.getInstance("CZK", 0);
   10.69 +        
   10.70 +        
   10.71 +        ExchangeRateProvider exchangeRateProvider = ExchangeRateProvider.createExchangeRateProvider();
   10.72 +        exchangeRateProvider.addFixedCurencyRate(CZK_cents, new BigDecimal(1), CZK_nocents, new BigDecimal(1));
   10.73 +        
   10.74 +        Convertor c = Convertor.createConvertor(exchangeRateProvider);
   10.75 +        
   10.76 +        ConversionResult result = c.convertWithReversibleRates(CZK_cents, CZK_nocents, new BigDecimal("2.34"));
   10.77 +        assertEquals("Converted",new BigDecimal("2"),result.getConverted());
   10.78 +        assertEquals("Remainder",new BigDecimal("0.34"),result.getRemainder());
   10.79 +    }
   10.80 +    
   10.81 +    
   10.82 +
   10.83 +}
    11.1 --- a/task4/solution13/test/org/apidesign/apifest08/test/Task1Test.java	Sat Oct 11 23:46:05 2008 +0200
    11.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/Task1Test.java	Fri Oct 17 17:31:48 2008 +0200
    11.3 @@ -78,35 +78,35 @@
    11.4              // convert $1 to CZK using c:
    11.5              ConversionResult result = convertCzkUsd.convertBack(new BigDecimal(1));
    11.6              assertEquals("Result is 17 CZK", new BigDecimal("17.00"), result.getConverted());
    11.7 -            assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder());
    11.8 +            assertEquals("No Remainer", new BigDecimal("00.00").setScale(2), result.getRemainder());
    11.9          }
   11.10  
   11.11          {
   11.12              // convert 17CKZ to $ using c:
   11.13              ConversionResult result = convertCzkUsd.convert(new BigDecimal(17));
   11.14              assertEquals("Result is 1 $", new BigDecimal("1.00"), result.getConverted());
   11.15 -            assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder());
   11.16 +            assertEquals("No Remainer", new BigDecimal("00.00").setScale(2), result.getRemainder());
   11.17          }
   11.18  
   11.19          {
   11.20              // convert $5 to CZK using c:
   11.21              ConversionResult result = convertCzkUsd.convertBack(new BigDecimal(5));
   11.22              assertEquals("Result is 85 CZK", new BigDecimal("85.00"), result.getConverted());
   11.23 -            assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder());
   11.24 +            assertEquals("No Remainer", BigDecimal.ZERO.setScale(2), result.getRemainder());
   11.25          }
   11.26          
   11.27          {
   11.28              // convert $8 to CZK
   11.29              ConversionResult result = convertCzkUsd.convertBack(new BigDecimal(8));
   11.30              assertEquals("Result is 136 CZK", new BigDecimal("136.00"), result.getConverted());
   11.31 -            assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder());
   11.32 +            assertEquals("No Remainer", BigDecimal.ZERO.setScale(2), result.getRemainder());
   11.33          }
   11.34  
   11.35          {
   11.36              // convert 1003CZK to USD
   11.37              ConversionResult result = convertCzkUsd.convert(new BigDecimal(1003));
   11.38              assertEquals("Result is 59 USD", new BigDecimal("59.00"), result.getConverted());
   11.39 -            assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder());
   11.40 +            assertEquals("No Remainer", BigDecimal.ZERO.setScale(2), result.getRemainder());
   11.41          }
   11.42      }
   11.43  
   11.44 @@ -136,14 +136,14 @@
   11.45              // convert 500SKK to CZK
   11.46              ConversionResult result = convertSkkCzk.convert(new BigDecimal(500));
   11.47              assertEquals("Result is 400 CZK", new BigDecimal("400.00"), result.getConverted());
   11.48 -            assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder());            
   11.49 +            assertEquals("No Remainer", BigDecimal.ZERO.setScale(2), result.getRemainder());            
   11.50          }
   11.51          
   11.52          {
   11.53              // convert 501SKK to CZK
   11.54              ConversionResult result = convertSkkCzk.convert(new BigDecimal(501));
   11.55              assertEquals("Result is 400 CZK", new BigDecimal("400.80"), result.getConverted());
   11.56 -            assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder());
   11.57 +            assertEquals("No Remainer", BigDecimal.ZERO.setScale(2), result.getRemainder());
   11.58              
   11.59          }
   11.60      }
    12.1 --- a/task4/solution13/test/org/apidesign/apifest08/test/Task2Test.java	Sat Oct 11 23:46:05 2008 +0200
    12.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/Task2Test.java	Fri Oct 17 17:31:48 2008 +0200
    12.3 @@ -38,7 +38,7 @@
    12.4      
    12.5      public static Convertor createUsdToSkkConvertor() {
    12.6          ConvertorCurrency fromCurrency = currencyUSD;
    12.7 -        ConvertorCurrency toCurrency = currencyUSD;
    12.8 +        ConvertorCurrency toCurrency = currencySKK;
    12.9          ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(1), fromCurrency, new BigDecimal(20), toCurrency);
   12.10          
   12.11          return Convertor.createConvertor(exchangeRateProvider);
    13.1 --- a/task4/solution13/test/org/apidesign/apifest08/test/Task4Test.java	Sat Oct 11 23:46:05 2008 +0200
    13.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/Task4Test.java	Fri Oct 17 17:31:48 2008 +0200
    13.3 @@ -1,8 +1,13 @@
    13.4  package org.apidesign.apifest08.test;
    13.5  
    13.6 +import java.math.BigDecimal;
    13.7 +import java.text.SimpleDateFormat;
    13.8  import java.util.Date;
    13.9  import junit.framework.TestCase;
   13.10 +import org.apidesign.apifest08.currency.ConversionNotSupportedException;
   13.11  import org.apidesign.apifest08.currency.Convertor;
   13.12 +import org.apidesign.apifest08.currency.ConvertorCurrency;
   13.13 +import org.apidesign.apifest08.currency.ExchangeRateProvider;
   13.14  
   13.15  /** The exchange rates are not always the same. They are changing. However
   13.16   * as in order to predict the future, one needs to understand own past. That is
   13.17 @@ -18,16 +23,26 @@
   13.18   * System.currentTimeMillis() as default date.
   13.19   */
   13.20  public class Task4Test extends TestCase {
   13.21 +    private static ConvertorCurrency CZK = ConvertorCurrency.getInstance("CZK");
   13.22 +    private static ConvertorCurrency SKK = ConvertorCurrency.getInstance("SKK");
   13.23 +    private static ConvertorCurrency USD = ConvertorCurrency.getInstance("USD");
   13.24 +    private SimpleDateFormat df;
   13.25 +    
   13.26 +    
   13.27      public Task4Test(String testName) {
   13.28          super(testName);
   13.29      }
   13.30  
   13.31      @Override
   13.32      protected void setUp() throws Exception {
   13.33 +        super.setUp();
   13.34 +        df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
   13.35      }
   13.36  
   13.37      @Override
   13.38      protected void tearDown() throws Exception {
   13.39 +        super.tearDown();
   13.40 +        df = null;
   13.41      }
   13.42  
   13.43      // Backward compatibly enhance your existing API to support following
   13.44 @@ -45,19 +60,22 @@
   13.45       * @return new convertor
   13.46       */
   13.47      public static Convertor limitTo(Convertor old, Date from, Date till) {
   13.48 -        return null;
   13.49 +        Convertor c = Convertor.createConvertorAsMerge(new Convertor[]{old});
   13.50 +        c.limitAllowedDates(from, till);
   13.51 +        return c;
   13.52      }
   13.53  
   13.54  
   13.55      public void testCompositionOfLimitedConvertors() throws Exception {
   13.56 -        if (Boolean.getBoolean("ignore.failing")) {
   13.57 -            // implement me! then delete this if statement
   13.58 -            return;
   13.59 -        }
   13.60 +//        if (Boolean.getBoolean("ignore.failing")) {
   13.61 +//            // implement me! then delete this if statement
   13.62 +//            return;
   13.63 +//        }
   13.64 +        
   13.65  
   13.66 -        Date d1 = null; // 2008-10-01 0:00 GMT
   13.67 -        Date d2 = null; // 2008-10-02 0:00 GMT
   13.68 -        Date d3 = null; // 2008-10-03 0:00 GMT
   13.69 +        Date d1 = df.parse("2008-10-01 0:00 GMT");
   13.70 +        Date d2 = df.parse("2008-10-02 0:00 GMT");
   13.71 +        Date d3 = df.parse("2008-10-03 0:00 GMT");
   13.72          
   13.73          Convertor c = Task2Test.merge(
   13.74              limitTo(Task1Test.createCZKtoUSD(), d1, d2),
   13.75 @@ -66,36 +84,81 @@
   13.76  
   13.77          // convert $5 to CZK using c:
   13.78          // cannot convert as no rate is applicable to current date
   13.79 +        try {
   13.80 +          c.convert(USD, CZK, new BigDecimal(5));
   13.81 +          fail();        
   13.82 +        } catch (ConversionNotSupportedException e) {
   13.83 +            //exception expected
   13.84 +        }
   13.85  
   13.86          // convert $8 to CZK using c:
   13.87          // cannot convert as no rate is applicable to current date
   13.88 +        try {
   13.89 +          c.convert(USD, CZK, new BigDecimal(8));
   13.90 +          fail();        
   13.91 +        } catch (ConversionNotSupportedException e) {
   13.92 +            //exception expected
   13.93 +        }
   13.94 +        
   13.95  
   13.96          // convert 1003CZK to USD using c:
   13.97          // cannot convert as no rate is applicable to current date
   13.98 +        try {
   13.99 +          c.convert(CZK, USD, new BigDecimal(1003));
  13.100 +          fail();        
  13.101 +        } catch (ConversionNotSupportedException e) {
  13.102 +            //exception expected
  13.103 +        }
  13.104  
  13.105          // convert 16CZK using c:
  13.106          // cannot convert as no rate is applicable to current date
  13.107 +        // ???
  13.108 +        try {
  13.109 +          c.convert(CZK, USD, new BigDecimal(16));
  13.110 +          fail();        
  13.111 +        } catch (ConversionNotSupportedException e) {
  13.112 +            //exception expected
  13.113 +        }
  13.114  
  13.115          // convert 500SKK to CZK using c:
  13.116          // cannot convert as no rate is applicable to current date
  13.117 +        try {
  13.118 +          c.convert(SKK, CZK, new BigDecimal(500));
  13.119 +          fail();        
  13.120 +        } catch (ConversionNotSupportedException e) {
  13.121 +            //exception expected
  13.122 +        }
  13.123 +        
  13.124  
  13.125          // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
  13.126          // assertEquals("Result is 85 CZK");
  13.127 +        assertEquals("Result is 85 CZK", new BigDecimal("85.00"), c.convertWithReversibleRates(USD, CZK , new BigDecimal("5"),df.parse("2008-10-01 6:00 GMT")).getConverted());        
  13.128  
  13.129          // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
  13.130          // assertEquals("Result is 136 CZK");
  13.131 +        assertEquals("Result is 136 CZK", new BigDecimal("136.00"), c.convertWithReversibleRates(USD, CZK , new BigDecimal("8"),df.parse("2008-10-01 6:00 GMT")).getConverted());        
  13.132  
  13.133          // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
  13.134          // assertEquals("Result is 59 USD");
  13.135 +        assertEquals("Result is 59 USD", new BigDecimal("59.00"), c.convertWithReversibleRates(CZK, USD , new BigDecimal("1003"),df.parse("2008-10-01 6:00 GMT")).getConverted());        
  13.136  
  13.137          // convert 16CZK using c at 2008-10-02 9:00 GMT:
  13.138          // assertEquals("Result is 20 SKK");
  13.139 +        assertEquals("Result is 20 SKK", new BigDecimal("20.00"), c.convertWithReversibleRates(CZK, SKK , new BigDecimal("16"),df.parse("2008-10-02 9:00 GMT")).getConverted());        
  13.140  
  13.141          // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  13.142          // assertEquals("Result is 400 CZK");
  13.143 +        assertEquals("Result is 400 SKK", new BigDecimal("400.00"), c.convertWithReversibleRates(SKK, CZK , new BigDecimal("500"),df.parse("2008-10-02 9:00 GMT")).getConverted());        
  13.144  
  13.145          // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  13.146          // cannot convert as no rate is applicable to current date
  13.147 +        try {
  13.148 +          c.convertWithReversibleRates(SKK, CZK , new BigDecimal("500"),df.parse("2008-10-01 6:00 GMT")).getConverted();        
  13.149 +          fail();        
  13.150 +        } catch (ConversionNotSupportedException e) {
  13.151 +            //exception expected
  13.152 +        }
  13.153 +
  13.154      }
  13.155  
  13.156      /** Create convertor that understands two currencies, CZK and
  13.157 @@ -104,18 +167,17 @@
  13.158       * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
  13.159       */
  13.160      public static Convertor createSKKtoCZK2() {
  13.161 -        return null;
  13.162 +        ExchangeRateProvider exchangeRateProvider = ExchangeRateProvider.createExchangeRateProvider();
  13.163 +        exchangeRateProvider.addFixedCurencyRate(SKK, new BigDecimal("100.00"), CZK, new BigDecimal("90.00"));
  13.164 +        Convertor c  = Convertor.createConvertor(exchangeRateProvider);
  13.165 +        return c;
  13.166      }
  13.167  
  13.168      public void testDateConvetorWithTwoDifferentRates() throws Exception {
  13.169 -        if (Boolean.getBoolean("ignore.failing")) {
  13.170 -            // implement me! then delete this if statement
  13.171 -            return;
  13.172 -        }
  13.173  
  13.174 -        Date d1 = null; // 2008-10-01 0:00 GMT
  13.175 -        Date d2 = null; // 2008-10-02 0:00 GMT
  13.176 -        Date d3 = null; // 2008-10-03 0:00 GMT
  13.177 +        Date d1 = df.parse("2008-10-01 0:00 GMT");
  13.178 +        Date d2 = df.parse("2008-10-02 0:00 GMT");
  13.179 +        Date d3 = df.parse("2008-10-03 0:00 GMT");
  13.180  
  13.181          Convertor c = Task2Test.merge(
  13.182              limitTo(createSKKtoCZK2(), d1, d2),
  13.183 @@ -123,10 +185,10 @@
  13.184          );
  13.185  
  13.186          // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  13.187 -        // assertEquals("Result is 400 CZK");
  13.188 +        assertEquals("Result is 400 CZK", new BigDecimal("400.00"), c.convertWithReversibleRates(SKK, CZK , new BigDecimal("500"),df.parse("2008-10-02 9:00 GMT")).getConverted());        
  13.189  
  13.190          // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  13.191 -        // assertEquals("Result is 450 CZK");
  13.192 +        assertEquals("Result is 450 CZK", new BigDecimal("450.00"), c.convertWithReversibleRates(SKK, CZK , new BigDecimal("500"),df.parse("2008-10-01 9:00 GMT")).getConverted());        
  13.193      }
  13.194  
  13.195  }