task4/solution13/src/org/apidesign/apifest08/currency/Convertor.java
changeset 63 20d332739f60
parent 61 58ec6da75f6f
     1.1 --- a/task4/solution13/src/org/apidesign/apifest08/currency/Convertor.java	Sat Oct 11 23:38:46 2008 +0200
     1.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/Convertor.java	Fri Oct 17 17:31:48 2008 +0200
     1.3 @@ -3,6 +3,7 @@
     1.4  import java.math.BigDecimal;
     1.5  import java.math.MathContext;
     1.6  import java.math.RoundingMode;
     1.7 +import java.util.Date;
     1.8  
     1.9  /** Convertor able to convert amount from one currency to other currency.
    1.10   * <p>
    1.11 @@ -20,6 +21,11 @@
    1.12   */
    1.13  public class Convertor {
    1.14      private Convertor[] convertors;
    1.15 +    private IDateProviderEngine dateProvider;
    1.16 +    boolean remainderAllowed = true; //if false, remained is not allowed (should be true ideally, but can't handle it now)
    1.17 +    private ExchangeRateProvider exchangeRateProvider;
    1.18 +    private Date fromDate;
    1.19 +    private Date toDate;
    1.20  
    1.21      /** Create new <code>Convertor</code> as merge of provided convertors. Merged convertor will use
    1.22       * provided convertors to convert between currencies.
    1.23 @@ -32,21 +38,26 @@
    1.24       * @return Returns new convertor instance.
    1.25       */
    1.26      public static Convertor createConvertorAsMerge(Convertor[] convertors) {
    1.27 +        for (int i=0;i<convertors.length;i++) {
    1.28 +            if (convertors[i]==null) {
    1.29 +                throw new NullPointerException("Convertor at index "+i+" can't be null");
    1.30 +            }
    1.31 +        }
    1.32          return new Convertor(convertors);
    1.33      }
    1.34      
    1.35 -    boolean remainderAllowed = true; //if false, remained is not allowed (should be true ideally, but can't handle it now)
    1.36 -    ExchangeRateProvider exchangeRateProvider; 
    1.37      
    1.38      /** Create simle convertor.
    1.39       */
    1.40      private Convertor() {
    1.41 +        setDateProvider(DateProvider.createCurrentDateProvider());
    1.42          this.convertors=new Convertor[0];
    1.43      }
    1.44      
    1.45      /** Create merge convertor.
    1.46       */            
    1.47      private Convertor(Convertor[] convertors) {
    1.48 +        this();
    1.49          this.convertors = convertors;       
    1.50      }
    1.51      
    1.52 @@ -75,7 +86,7 @@
    1.53       * @deprecated since version2. Use {@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal) } - explicitly specify conversion currencies.
    1.54       */
    1.55      public ConversionResult convert(BigDecimal amount) {
    1.56 -        return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, false,false);
    1.57 +        return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, false,false,null);
    1.58      }
    1.59      
    1.60      /**
    1.61 @@ -88,32 +99,36 @@
    1.62       * @deprecated since version2. Use {@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal) } - explicitly specify conversion currencies.
    1.63       */
    1.64      public ConversionResult convertBack(BigDecimal amount) {
    1.65 -        return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, true,false);
    1.66 +        return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, true,false,null);
    1.67      }
    1.68  
    1.69 -    private ConversionResult convertUsingSimpleConvertor(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, boolean reversibleExRate, BigDecimal amount, boolean convertBack) throws ConversionNotSupportedException, RuntimeException {
    1.70 -        ConversionResult result = new ConversionResult();
    1.71 +    private ConversionResult convertUsingSimpleConvertor(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, boolean reversibleExRate, BigDecimal amount, boolean convertBack,Date date) throws ConversionNotSupportedException, RuntimeException {
    1.72 +        if (date == null) {
    1.73 +            date = dateProvider.getCurrentDate();
    1.74 +        }
    1.75  
    1.76 -        //ExchangeRate rate = exchangeRateProvider.getExchangeRate();
    1.77          ExchangeRate rate;
    1.78          if (reversibleExRate) {
    1.79 -            rate = exchangeRateProvider.getReversibleExchangeRate(fromCurrency, toCurrency);
    1.80 +            rate = exchangeRateProvider.getReversibleExchangeRate(fromCurrency, toCurrency, date);
    1.81          } else {
    1.82 -            rate = exchangeRateProvider.getExchangeRate(fromCurrency, toCurrency);
    1.83 +            rate = exchangeRateProvider.getExchangeRate(fromCurrency, toCurrency, date);
    1.84          }
    1.85          if (rate == null) {
    1.86              return null;
    1.87          }
    1.88 +        ConversionResult result = new ConversionResult();
    1.89          
    1.90          int fromFranctionDigits = fromCurrency.getDefaultFractionDigits();
    1.91          int toFractionDigits = toCurrency.getDefaultFractionDigits();
    1.92 +        
    1.93 +        int usedFractionDigits = Math.max(fromFranctionDigits, toFractionDigits);
    1.94  
    1.95 -        if (toFractionDigits != 2) {
    1.96 -            throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + exchangeRateProvider.getToCurrency() + " has " + toFractionDigits + " defaultFractionDigits");
    1.97 -        }
    1.98 -        if (fromFranctionDigits != 2) {
    1.99 -            throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + exchangeRateProvider.getFromCurrency() + " has " + fromFranctionDigits + " defaultFractionDigits");
   1.100 -        }
   1.101 +        //if (toFractionDigits != 2) {
   1.102 +        //    throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + toCurrency + " has " + toFractionDigits + " defaultFractionDigits");
   1.103 +        //}
   1.104 +        //if (fromFranctionDigits != 2) {
   1.105 +        //    throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + fromCurrency + " has " + fromFranctionDigits + " defaultFractionDigits");
   1.106 +        //}
   1.107  
   1.108          if (amount.signum() == -1) {
   1.109              throw new RuntimeException("Can convert only non-negative value, current value is " + amount);
   1.110 @@ -134,7 +149,7 @@
   1.111              to = rate.getToValue();
   1.112          }
   1.113  
   1.114 -        BigDecimal amountCent = amount.movePointRight(toFractionDigits);
   1.115 +        BigDecimal amountCent = amount.movePointRight(usedFractionDigits);
   1.116  
   1.117          final BigDecimal multiplied = amountCent.multiply(to, context);
   1.118          BigDecimal[] division = multiplied.divideAndRemainder(from, context);
   1.119 @@ -142,26 +157,33 @@
   1.120          if (!remainderAllowed && !(BigDecimal.ZERO.equals(division[1]))) {
   1.121              throw new RuntimeException("Remained is not allowed - remaining amount is " + division[1] + " cents");
   1.122          } else {
   1.123 -            result.setRemainder(BigDecimal.ZERO);
   1.124 +            result.setRemainder(BigDecimal.ZERO.setScale(fromFranctionDigits));
   1.125          }
   1.126  
   1.127 -        BigDecimal converted = division[0].movePointLeft(toFractionDigits);
   1.128 -        converted = converted.setScale(toFractionDigits); //XXX ugly
   1.129 +        BigDecimal converted = division[0].movePointLeft(usedFractionDigits);
   1.130 +        converted = converted.setScale(toFractionDigits,RoundingMode.DOWN); //XXX ugly
   1.131          result.setConverted(converted);
   1.132 -        //result.setRemainder(...);
   1.133 +        BigDecimal[] convertedInBase = converted.multiply(from).divideAndRemainder(to);
   1.134 +        BigDecimal x2 = convertedInBase[0].add(convertedInBase[1]);
   1.135 +        BigDecimal remainder = amount.subtract(x2);
   1.136 +        result.setRemainder(remainder);
   1.137 +        //System.out.println("ConvertedInBase="+Arrays.asList(convertedInBase)+":"+x2 +" ("+amount+") ["+converted+"] <"+from+","+to+"> ->"+remainder);
   1.138          return result;
   1.139      }
   1.140      
   1.141  
   1.142 -    private ConversionResult convertValue(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency,BigDecimal amount, boolean convertBack,boolean reversibleExRate) throws RuntimeException {
   1.143 +    private ConversionResult convertValue(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency,BigDecimal amount, boolean convertBack,boolean reversibleExRate,Date date) throws RuntimeException {
   1.144          //result.setRemainder(...);
   1.145 +        if (!dateIsInLimit(date)) {
   1.146 +            return null;
   1.147 +        }        
   1.148          if (convertors.length==0) {
   1.149 -            return convertUsingSimpleConvertor(fromCurrency, toCurrency, reversibleExRate, amount, convertBack);
   1.150 +            return convertUsingSimpleConvertor(fromCurrency, toCurrency, reversibleExRate, amount, convertBack,date);
   1.151          } else {
   1.152              ConversionResult result = null;
   1.153              for (int i = 0;i<convertors.length;i++) {
   1.154                  Convertor subConvertor = convertors[i];
   1.155 -                result = subConvertor.convertValue(fromCurrency, toCurrency, amount, convertBack, reversibleExRate);
   1.156 +                result = subConvertor.convertValue(fromCurrency, toCurrency, amount, convertBack, reversibleExRate,date);
   1.157                  if (result!=null) {
   1.158                      break;
   1.159                  }
   1.160 @@ -175,6 +197,8 @@
   1.161       * <p>
   1.162       * Exchange rate is provided by exchange rate provider which was specified when Convertor was created.
   1.163       * This method is using only exchange rate from->to and not trying to use reverted excange rate to->from.
   1.164 +     * <p>
   1.165 +     * This method is using date from <code>IDateProviderEngine</code> to get exchange rate (see {@link #getDateProvider() }.
   1.166       * 
   1.167       * @param fromCurrency Source currency to convert from.
   1.168       * @param toCurrency Target currency to convert to.
   1.169 @@ -184,19 +208,40 @@
   1.170       * @throws ConversionNotSupportedException If conversion from <code>fromCurrency</code> to <code>toCurrency</code> is not supported.
   1.171       */
   1.172      public ConversionResult convert(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value) {
   1.173 -        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,false);
   1.174 +        Date defaultDate = dateProvider.getCurrentDate();
   1.175 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,false,defaultDate);
   1.176          if (result==null) {
   1.177              //throw new ConversionNotSupportedException("Conversion from " + fromCurrency + " to " + toCurrency + " is not supported");
   1.178              throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),false);
   1.179          }
   1.180          return result;
   1.181      }
   1.182 +    /**
   1.183 +     * Same as {@link #convert(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)} but using provided rate instead of
   1.184 +     * <code>IDateProviderEngine</code>.
   1.185 +     * 
   1.186 +     * @param fromCurrency Source currency to convert from.
   1.187 +     * @param toCurrency Target currency to convert to.
   1.188 +     * @param value Value in source currency which should be converted.
   1.189 +     * @return Return conversion result.
   1.190 +     * @param date Conversion date
   1.191 +     * @return Return conversion result.
   1.192 +     */
   1.193 +    public ConversionResult convert(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value, Date date) {
   1.194 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,false,date);
   1.195 +        if (result==null) {
   1.196 +            throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),false);
   1.197 +        }
   1.198 +        return result;
   1.199 +    }    
   1.200      
   1.201      /**
   1.202       * Convert <code>value</code> from <code>fromCurrency</code> to <code>toCurrency</code>.
   1.203       * Exchange rate is provided by exchange rate provider which was specified when Convertor was created.
   1.204       * <p>
   1.205       *  This method is using only exchange rate from->to and if not found, it is trying to use reverted excange rate to->from.
   1.206 +     * <p>
   1.207 +     * This method is using date from <code>IDateProviderEngine</code> to get exchange rate (see {@link #getDateProvider() }.
   1.208       * 
   1.209       * @param fromCurrency Source currency to convert from.
   1.210       * @param toCurrency Target currency to convert to.
   1.211 @@ -207,7 +252,8 @@
   1.212       *         is not supported and neither conversion from <code>toCurrency</code> to <code>fromCurrency</code> is not supported.
   1.213       */
   1.214      public ConversionResult convertWithReversibleRates(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value) {
   1.215 -        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,true);
   1.216 +        Date defaultDate = dateProvider.getCurrentDate();
   1.217 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,true,defaultDate);
   1.218          if (result==null) {
   1.219              //throw new ConversionNotSupportedException("Neither onversion nor reverted conversion from " + fromCurrency + " to " + toCurrency + "  is not supported,");
   1.220              throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),true);
   1.221 @@ -215,4 +261,75 @@
   1.222          return result;
   1.223      }
   1.224      
   1.225 +    /**
   1.226 +     * Same as {@link #convertWithReversibleRates(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)} but using provided rate instead of
   1.227 +     * <code>IDateProviderEngine</code>.
   1.228 +     * 
   1.229 +     * @param fromCurrency Source currency to convert from.
   1.230 +     * @param toCurrency Target currency to convert to.
   1.231 +     * @param value Value in source currency which should be converted.
   1.232 +     * @return Return conversion result.
   1.233 +     * @param date Conversion date
   1.234 +     * @return Return conversion result.
   1.235 +     */
   1.236 +    public ConversionResult convertWithReversibleRates(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value, Date date) {
   1.237 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,true,date);
   1.238 +        if (result==null) {
   1.239 +            throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),true);
   1.240 +        }
   1.241 +        return result;
   1.242 +    }
   1.243 +    
   1.244 +    private boolean dateIsInLimit(Date date) {
   1.245 +        boolean result;
   1.246 +//        if (date==null) {
   1.247 +//          result = true;  
   1.248 +//        } else 
   1.249 +        if (fromDate == null && toDate == null) {
   1.250 +            result = true;
   1.251 +        } else if (fromDate.getTime()<=date.getTime() && date.getTime()<=toDate.getTime()) {
   1.252 +            result = true;
   1.253 +        } else {
   1.254 +            result = false;
   1.255 +        }
   1.256 +        
   1.257 +        return result;
   1.258 +    }
   1.259 +    
   1.260 +    
   1.261 +    public void limitAllowedDates(Date from, Date till) {
   1.262 +        if (from==null) {
   1.263 +            throw new NullPointerException("from Date can't be null");
   1.264 +        }
   1.265 +        if (till==null) {
   1.266 +            throw new NullPointerException("till Date can't be null");
   1.267 +        }
   1.268 +        if (from.getTime()>till.getTime()) {
   1.269 +            throw new IllegalArgumentException("From date "+from+" must be before tii date "+till);
   1.270 +        }
   1.271 +        this.fromDate = from;
   1.272 +        this.toDate = till;
   1.273 +    }
   1.274 +    
   1.275 +
   1.276 +    /** Return current date provider.
   1.277 +     * @return Returns current date provider.
   1.278 +     */
   1.279 +    public IDateProviderEngine getDateProvider() {
   1.280 +        return dateProvider;
   1.281 +    }
   1.282 +
   1.283 +    
   1.284 +    /**
   1.285 +     * Set date provider. Date provider is used to get "current date". Current date 
   1.286 +     * is used by methods {@link #convert(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)} and  
   1.287 +     * {@link #convertWithReversibleRates(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)}.
   1.288 +     * 
   1.289 +     * @param dateProvider Date provider which should be used by Convertor.
   1.290 +     */
   1.291 +    public void setDateProvider(IDateProviderEngine dateProvider) {
   1.292 +        this.dateProvider = dateProvider;
   1.293 +    }
   1.294 +    
   1.295 +    
   1.296  }