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