task4/solution12/src/org/apidesign/apifest08/currency/Convertor.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Fri, 17 Oct 2008 17:39:18 +0200
changeset 68 4de3a4b5445a
parent 61 58ec6da75f6f
permissions -rw-r--r--
solution 12, task4
     1 package org.apidesign.apifest08.currency;
     2 
     3 import java.util.ArrayList;
     4 import java.util.Calendar;
     5 import java.util.Currency;
     6 import java.util.Date;
     7 import java.util.Hashtable;
     8 import java.util.List;
     9 
    10 import org.apidesign.apifest08.currency.exceptions.ConvertorException;
    11 import org.apidesign.apifest08.currency.exceptions.InvalidCurrencyException;
    12 import org.apidesign.apifest08.currency.exceptions.UnknownConvertorException;
    13 
    14 /**
    15  * This is the skeleton class for your API. You need to make it public, so it is accessible to your client code
    16  * (currently in Task1Test.java) file.
    17  * <p>
    18  * Feel free to create additional classes or rename this one, just keep all the API and its implementation in this
    19  * package. Do not spread it outside to other packages.
    20  */
    21 public class Convertor {
    22 
    23   private static Hashtable<String, List<ExchangeRate>> exchangeRates;
    24 
    25   private List<ExchangeRateInstance> exchangeRateInstances;
    26 
    27   /**
    28    * Constructor. Checks if all selected currencies are not null and has defined exchange rates for both
    29    * directions.
    30    * @param currencies currencies for new instance of convertor
    31    * @throws UnknownConvertorException if there are not defined exchange rates for both directions for each
    32    * pair of currencies
    33    */
    34   private Convertor(Currency[] currencies) throws UnknownConvertorException {
    35     exchangeRateInstances = new ArrayList<ExchangeRateInstance>();
    36 
    37     for (Currency currency1 : currencies) {
    38       for (Currency currency2 : currencies) {
    39         if(currency1 == null || currency2 == null) {
    40           throw new ConvertorException("None of the currencies should be null!!!");
    41         }
    42           
    43         if(!currency1.getCurrencyCode().equals(currency2.getCurrencyCode())) {
    44           String key = currency1.getCurrencyCode() + currency2.getCurrencyCode();
    45           if (!exchangeRates.containsKey(key)) {
    46             throw new UnknownConvertorException("Selected convertor (" + currency1.getCurrencyCode() + "->"
    47                 + currency2.getCurrencyCode() + ") has not defined exchange rates!!!");
    48           }
    49           
    50           exchangeRateInstances.add(new ExchangeRateInstance(key, null, null));            
    51         }
    52       }
    53     }
    54   }
    55 
    56   /**
    57    * Sets convertor rate for selected currencies.
    58    * @param currency1
    59    *          one of the currencies we want to convert to/from
    60    * @param currency2
    61    *          the other currency
    62    * @param rate
    63    *          exchange rate from currency1 to currency2
    64    * @param unit
    65    *          unit of exchangeRate (USD->CZK - unit=1, you exchange one dollar, SKK->CZK unit=100, exchange rate is for
    66    *          100SKK)
    67    */
    68   public static void setConvertorRates(Currency currency1, Currency currency2, double rate, double unit) {
    69     if (currency1 == null || currency2 == null) {
    70       throw new ConvertorException("None of the currencies should be null!!!");
    71     }
    72 
    73     if (rate <= 0 || unit <= 0) {
    74       throw new ConvertorException("Rate(" + rate + ") and unit(" + unit + ") has to be grater then zero!!!");
    75     }
    76 
    77     if (exchangeRates == null) {
    78       exchangeRates = new Hashtable<String, List<ExchangeRate>>();
    79     }
    80 
    81     String key12 = currency1.getCurrencyCode() + currency2.getCurrencyCode();
    82     String key21 = currency2.getCurrencyCode() + currency1.getCurrencyCode();
    83     double recountedRate = (unit / rate) * unit;
    84 
    85     exchangeRates.put(key12, addNewExchangeRate(key12, currency1, currency2, rate, unit, null, null));
    86     exchangeRates.put(key21, addNewExchangeRate(key21, currency2, currency1, recountedRate, unit, null, null));
    87   }
    88   
    89   public static Convertor limitExchangeRatesValidity(Convertor convertor, Date validFrom, Date validTo) {
    90     if(convertor == null || validFrom == null || validTo == null) {
    91       throw new ConvertorException("None of parameters of method limitExchangeRatesValidity should be null!!!");
    92     }
    93     
    94     List<ExchangeRateInstance> exchangeRateInstances = convertor.getExchangeRateInstances();
    95     for (ExchangeRateInstance exchangeRateInstance : exchangeRateInstances) {
    96       // get actual convertor rates for actual validity
    97       ExchangeRate actualExchangeRate = getExchangeRate(exchangeRateInstance.getKey(), exchangeRateInstance.getValidFrom(), exchangeRateInstance.getValidTo());
    98       // set new validity for theese rates
    99       actualExchangeRate.setValidFrom(validFrom);
   100       actualExchangeRate.setValidTo(validTo);
   101       // and for selected currency convertor
   102       exchangeRateInstance.setValidFrom(validFrom);
   103       exchangeRateInstance.setValidTo(substractSecond(validTo));
   104     }
   105 
   106     return convertor;
   107   }
   108   
   109   /**
   110    * Merge exchange rates of actual convertor with exchange rates from selected 
   111    * convertor. If there are same currencies in both convertors, these from selected
   112    * convertor rewrites these in actual convertor.
   113    * @param convertor convertor to merge with actual one
   114    * @return convertor with merged exchange rates 
   115    */
   116   public Convertor merge(Convertor convertor) {
   117     if(convertor == null) {
   118       throw new ConvertorException("It's impossible to merge with null convertor!!!");
   119     }
   120     
   121     exchangeRateInstances.addAll(convertor.getExchangeRateInstances());
   122 	  return this;
   123   }
   124 
   125   /**
   126    * Creates new instance of convertor.
   127    * @param currency1
   128    *          one of the currencies we want to convert to/from
   129    * @param currency2
   130    *          the other currency
   131    * @return new instance of convertor
   132    * @throws UnknownConvertorException
   133    *           thrown if convertor for selected currencies has not been defined
   134    */
   135   public static Convertor getConvertorInstance(Currency... currencies) throws UnknownConvertorException {
   136    	if(currencies.length < 2) {
   137    		throw new ConvertorException("To get convertor instance, you have to select at least two currencies!!!");
   138    	}
   139     
   140     return new Convertor(currencies);
   141   }
   142 
   143   /**
   144    * Converts selected amout of selected currency to other currency of this convertor instance.
   145    * @param amount
   146    *          amount to convert
   147    * @param originalCurrency
   148    *          currency of this amount
   149    * @param newCurrency
   150    *          currency to which we want convert
   151    * @return converted amount
   152    * @throws InvalidCurrencyException
   153    *           while one or both currencies doesn't fit for this convertor
   154    */
   155   public double convert(double amount, Currency originalCurrency, Currency newCurrency) throws InvalidCurrencyException {
   156     ExchangeRate actualyUsedExchangeRate = null;
   157 
   158     if (originalCurrency == null) {
   159       throw new ConvertorException("Original currency is null!!!");
   160     }
   161 
   162     if (newCurrency == null) {
   163       throw new ConvertorException("Destination currency is null!!!");
   164     }
   165 
   166     actualyUsedExchangeRate = getExchangeRate(originalCurrency, newCurrency);
   167 
   168     return countResult(actualyUsedExchangeRate, amount);
   169   }
   170 
   171   private double countResult(ExchangeRate actualyUsedExchangeRate, double amount) {
   172     return amount * actualyUsedExchangeRate.getRate() / actualyUsedExchangeRate.getUnit();
   173   }
   174 
   175   /**
   176    * Decides the direction of conversion and returns actual exchange rate
   177    * for selected currencies and actual moment.
   178    * @param actualCurrency
   179    *          actual currency we want to convert
   180    * @return actual exchange rate of this convertor for selected currency
   181    */
   182   private ExchangeRate getExchangeRate(Currency originalCurrency, Currency newCurrency) throws InvalidCurrencyException {
   183     ExchangeRate actualyUsedExchangeRate = null;
   184     
   185     String key = originalCurrency.getCurrencyCode() + newCurrency.getCurrencyCode();
   186 
   187     ExchangeRateInstance exchangeRateInstance = findExchangeRateInstance(key);
   188     if(exchangeRateInstance != null) {
   189       actualyUsedExchangeRate = getExchangeRate(exchangeRateInstance.getKey(), exchangeRateInstance.getValidFrom(), exchangeRateInstance.getValidTo());
   190     } else {
   191       throw new InvalidCurrencyException("This convertor could not be used for converting selected currencies (" + originalCurrency.getCurrencyCode() + "->"
   192           + newCurrency.getCurrencyCode() + ") !!!");
   193     }
   194 
   195     return actualyUsedExchangeRate;
   196   }
   197   
   198   /**
   199    * Finds instance of exchange rate for actual instance of convertor and actual moment.
   200    * @param key exchange rate instance key
   201    * @return exchange rate instance
   202    */
   203   private ExchangeRateInstance findExchangeRateInstance(String key) {
   204     ExchangeRateInstance instance = null;
   205     
   206     Date now = new Date();
   207     for (ExchangeRateInstance item : exchangeRateInstances) {
   208       if(item.getKey().equals(key) && item.getValidFrom() == null && item.getValidTo() == null) {
   209         instance = item;
   210         break;
   211       } else if(item.getKey().equals(key) && item.getValidFrom().compareTo(now) <= 0 && item.getValidTo().compareTo(now) >= 0) {
   212         instance = item;
   213         break;
   214       }
   215     }
   216     
   217     return instance;
   218   }
   219   
   220   
   221   /**
   222    * Returns currency convertors for actual instance of convertor.
   223    * @return currency convertors for actual instance of convertor
   224    */
   225   private List<ExchangeRateInstance> getExchangeRateInstances() {
   226     return exchangeRateInstances;
   227   }
   228   
   229   private static ExchangeRate getExchangeRate(String key, Date validFrom, Date validTo) {
   230     ExchangeRate exchangeRate = null;
   231     
   232     List<ExchangeRate> currencyExchangeRates = exchangeRates.get(key);
   233     for (ExchangeRate currencyExchangeRate : currencyExchangeRates) {
   234       Date actValidFrom = currencyExchangeRate.getValidFrom();
   235       Date actValidTo = currencyExchangeRate.getValidTo();
   236       
   237       if(actValidFrom == null && validFrom == null && actValidTo == null && validTo == null) {
   238         exchangeRate = currencyExchangeRate;
   239         break;
   240       } else if(actValidFrom != null && validFrom != null && actValidTo != null && validTo != null && actValidFrom.compareTo(validFrom) <= 0 && actValidTo.compareTo(validTo) > 0) {
   241         exchangeRate = currencyExchangeRate;
   242         break;
   243       }
   244     }
   245     
   246     if(exchangeRate == null) {
   247       throw new ConvertorException("Exchange rate for actual exchange rate instance not found!!!");
   248     }
   249     
   250     return exchangeRate;
   251   }
   252   
   253   private static List<ExchangeRate> addNewExchangeRate(String key, Currency currency1, Currency currency2, double rate, double unit, Date validFrom, Date validTo) {
   254     List<ExchangeRate> actualExchangeRates = new ArrayList<ExchangeRate>();
   255     ExchangeRate newExchangeRate = null;
   256     
   257     if(exchangeRates.containsKey(key)) {
   258       List<ExchangeRate> rates = exchangeRates.get(key);
   259       for (ExchangeRate exchangeRate : rates) {
   260         if(exchangeRate.getValidFrom() == null && exchangeRate.getValidTo() == null && validFrom == null && validTo == null) {
   261           newExchangeRate = exchangeRate;
   262           break;
   263         } else if(exchangeRate.getValidFrom() != null && exchangeRate.getValidTo() != null && validFrom != null && validTo != null) {
   264           if(exchangeRate.getValidFrom().compareTo(validFrom) == 0 && exchangeRate.getValidTo().compareTo(validTo) == 0) {
   265             newExchangeRate = exchangeRate;
   266             break;
   267           }
   268         }
   269       }
   270       actualExchangeRates.addAll(rates);
   271     }
   272     
   273     if(newExchangeRate == null) {
   274       actualExchangeRates.add(new ExchangeRate(currency1, currency2, rate, unit, validFrom, validTo));
   275     } else {
   276       newExchangeRate.setRate(rate);
   277       newExchangeRate.setUnit(unit);
   278       actualExchangeRates.add(newExchangeRate);
   279     }
   280     
   281     return actualExchangeRates;
   282   }
   283   
   284   /**
   285    * Substracts one second from selected date.
   286    * @param date date
   287    * @return date -1s
   288    */
   289   private static Date substractSecond(Date date) {
   290     Calendar calendar = Calendar.getInstance();
   291     calendar.setTime(date);
   292     calendar.set(Calendar.SECOND, -1);
   293     
   294     return calendar.getTime();
   295   }
   296   
   297 }