task4/solution11/src/org/apidesign/apifest08/currency/Convertor.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 11 Oct 2008 23:38:46 +0200
changeset 61 58ec6da75f6f
parent 53 task3/solution11/src/org/apidesign/apifest08/currency/Convertor.java@09d690bb97f6
child 66 aa3f99f845ef
permissions -rw-r--r--
Copying structure for task4
japod@6
     1
package org.apidesign.apifest08.currency;
japod@6
     2
japod@37
     3
import java.util.ArrayList;
japod@37
     4
import java.util.Collection;
japod@37
     5
import java.util.HashSet;
japod@37
     6
import java.util.List;
japod@37
     7
import java.util.Set;
japod@6
     8
import org.apidesign.apifest08.currency.Computer.ComputerRequest;
japod@6
     9
import org.apidesign.apifest08.currency.Computer.ComputerResponse;
japod@6
    10
japod@6
    11
/**
japod@6
    12
 * Convertor.
japod@6
    13
 * 
japod@6
    14
 * In Task 1's version provides conversion between currency values
japod@6
    15
 * with amount stored in integer or double, that are identified
japod@6
    16
 * with string value. Exchange rates are immutable.
japod@6
    17
 * 
japod@37
    18
 * In Task2's version provides support for multiple exchange rates
japod@37
    19
 * between different currencies & merging exchange rates from
japod@37
    20
 * existing convertors into new convertor's instance.
japod@37
    21
 * No time for javadoc these features, sorry.
japod@37
    22
 * 
japod@53
    23
 * In Task3's version supports reading of current exchange rates
japod@53
    24
 * from data sources. Data sources are merged during convertors' merging
japod@53
    25
 * as well as static exchange rates.
japod@53
    26
 * No time for javadoc, again.
japod@53
    27
 * 
japod@6
    28
 * @author ked
japod@6
    29
 */
japod@6
    30
public final class Convertor<AmountType, IdentifierType> {
japod@6
    31
japod@6
    32
    Computer<AmountType> computer;
japod@53
    33
    // each static exchange rate could be a special case of an exchange rate data source
japod@37
    34
    List<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates = new ArrayList<ExchangeRateValue<AmountType, IdentifierType>>();
japod@53
    35
    List<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources = new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
japod@6
    36
japod@53
    37
    Convertor(Computer<AmountType> computer) {
japod@37
    38
        this.computer = computer;
japod@53
    39
    }
japod@53
    40
    
japod@53
    41
    void addExchangeRates(Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates) {
japod@37
    42
        for (ExchangeRateValue<AmountType, IdentifierType> exchangeRate : exchangeRates) {
japod@53
    43
            if (isExchangeRate(
japod@37
    44
                    exchangeRate.getCurrencyA().getIdentifier(),
japod@53
    45
                    exchangeRate.getCurrencyB().getIdentifier())) {
japod@37
    46
                throw new IllegalArgumentException("Duplicate exchange rate!");
japod@37
    47
            }
japod@37
    48
            this.exchangeRates.add(exchangeRate);
japod@19
    49
        }
japod@37
    50
    }
japod@53
    51
    
japod@53
    52
    void addExchangeRateDataSources(Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources) {
japod@53
    53
        for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : exchangeRateDataSources) {
japod@53
    54
            if (isExchangeRate(
japod@53
    55
                    exchangeRateDataSource.getCurrencyAIdentifier(),
japod@53
    56
                    exchangeRateDataSource.getCurrencyBIdentifier())) {
japod@53
    57
                throw new IllegalArgumentException("Duplicate exchange rate!");
japod@53
    58
            }
japod@53
    59
            this.exchangeRateDataSources.add(exchangeRateDataSource);
japod@53
    60
        }
japod@53
    61
    }
japod@53
    62
    
japod@53
    63
    ExchangeRateValue<AmountType, IdentifierType> findExchangeRate(
japod@37
    64
            IdentifierType currencyA,
japod@37
    65
            IdentifierType currencyB) {
japod@37
    66
        for (ExchangeRateValue<AmountType, IdentifierType> exchangeRate : exchangeRates) {
japod@37
    67
            if ((exchangeRate.getCurrencyA().getIdentifier().equals(currencyA) && exchangeRate.getCurrencyB().getIdentifier().equals(currencyB)) ||
japod@37
    68
                    (exchangeRate.getCurrencyA().getIdentifier().equals(currencyB) && exchangeRate.getCurrencyB().getIdentifier().equals(currencyA))) {
japod@37
    69
                return exchangeRate;
japod@37
    70
            }
japod@37
    71
        }
japod@53
    72
        for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : exchangeRateDataSources) {
japod@53
    73
            if ((exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyA) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyB)) ||
japod@53
    74
                    (exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyB) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyA))) {
japod@53
    75
                return exchangeRateDataSource.getExchangeRate();
japod@53
    76
            }
japod@53
    77
        }
japod@37
    78
        return null;
japod@6
    79
    }
japod@53
    80
    
japod@53
    81
    boolean isExchangeRate(
japod@53
    82
            IdentifierType currencyA,
japod@53
    83
            IdentifierType currencyB) {
japod@53
    84
        for (ExchangeRateValue<AmountType, IdentifierType> exchangeRate : exchangeRates) {
japod@53
    85
            if ((exchangeRate.getCurrencyA().getIdentifier().equals(currencyA) && exchangeRate.getCurrencyB().getIdentifier().equals(currencyB)) ||
japod@53
    86
                    (exchangeRate.getCurrencyA().getIdentifier().equals(currencyB) && exchangeRate.getCurrencyB().getIdentifier().equals(currencyA))) {
japod@53
    87
                return true;
japod@53
    88
            }
japod@53
    89
        }
japod@53
    90
        for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : exchangeRateDataSources) {
japod@53
    91
            if ((exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyA) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyB)) ||
japod@53
    92
                    (exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyB) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyA))) {
japod@53
    93
                return true;
japod@53
    94
            }
japod@53
    95
        }
japod@53
    96
        return false;
japod@53
    97
    }
japod@6
    98
japod@6
    99
    /**
japod@6
   100
     * Convert an amount of the one currency to an amount of the another one currency
japod@37
   101
     * with respect to previously specified exchange rates.
japod@6
   102
     * 
japod@19
   103
     * @param targetCurrency an identifier of the requested currency
japod@19
   104
     * @param currencyValue an amount of the another one currency
japod@19
   105
     * @return an amount of the requested currency
japod@6
   106
     */
japod@19
   107
    public CurrencyValue<AmountType, IdentifierType> convert(
japod@19
   108
            IdentifierType targetCurrency,
japod@19
   109
            CurrencyValue<AmountType, IdentifierType> currencyValue) {
japod@37
   110
        ExchangeRateValue<AmountType, IdentifierType> exchangeRate =
japod@53
   111
                findExchangeRate(currencyValue.getIdentifier(), targetCurrency);
japod@37
   112
        if (exchangeRate == null) {
japod@19
   113
            throw new IllegalArgumentException("Inappropriate currencies to convert!");
japod@6
   114
        }
japod@37
   115
japod@37
   116
        ComputerRequest<AmountType> computerRequest = new ComputerRequest<AmountType>();
japod@37
   117
        computerRequest.setInput(currencyValue.getAmount());
japod@37
   118
japod@37
   119
        IdentifierType targetCurrencyRef; // just for backward compatibility :-(
japod@37
   120
        if (exchangeRate.getCurrencyA().getIdentifier().equals(targetCurrency)) {
japod@37
   121
            computerRequest.setInputCurrencyRatio(exchangeRate.getCurrencyB().getAmount());
japod@37
   122
            computerRequest.setOutputCurrencyRatio(exchangeRate.getCurrencyA().getAmount());
japod@37
   123
            targetCurrencyRef = exchangeRate.getCurrencyA().getIdentifier();
japod@37
   124
        } else {
japod@37
   125
            computerRequest.setInputCurrencyRatio(exchangeRate.getCurrencyA().getAmount());
japod@37
   126
            computerRequest.setOutputCurrencyRatio(exchangeRate.getCurrencyB().getAmount());
japod@37
   127
            targetCurrencyRef = exchangeRate.getCurrencyB().getIdentifier();
japod@37
   128
        }
japod@37
   129
japod@53
   130
        ComputerResponse<AmountType> computerResponse = new ComputerResponse<AmountType>();
japod@53
   131
        computer.compute(computerRequest, computerResponse);
japod@53
   132
japod@37
   133
        return CurrencyValue.getCurrencyValue(
japod@37
   134
                computerResponse.getResult(),
japod@37
   135
                targetCurrencyRef);
japod@37
   136
    }
japod@37
   137
japod@37
   138
    // ---
japod@37
   139
    // MERGING
japod@37
   140
    // ---
japod@37
   141
    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> mergeConvertors(
japod@37
   142
            Computer<AmountType> computer,
japod@37
   143
            Collection<Convertor<AmountType, IdentifierType>> convertors) {
japod@37
   144
        Set<ExchangeRateValue<AmountType, IdentifierType>> exchangeRatesSet = new HashSet<ExchangeRateValue<AmountType, IdentifierType>>();
japod@53
   145
        Set<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSourcesSet = new HashSet<ExchangeRateDataSource<AmountType, IdentifierType>>();
japod@37
   146
        for (Convertor<AmountType, IdentifierType> convertor : convertors) {
japod@37
   147
            exchangeRatesSet.addAll(convertor.exchangeRates);
japod@37
   148
        }
japod@53
   149
        for (Convertor<AmountType, IdentifierType> convertor : convertors) {
japod@53
   150
            exchangeRateDataSourcesSet.addAll(convertor.exchangeRateDataSources);
japod@53
   151
        }
japod@53
   152
        
japod@53
   153
        Convertor<AmountType, IdentifierType> c = new Convertor<AmountType, IdentifierType>(computer);
japod@53
   154
        c.addExchangeRates(exchangeRatesSet);
japod@53
   155
        c.addExchangeRateDataSources(exchangeRateDataSourcesSet);
japod@53
   156
        return c;
japod@37
   157
    }
japod@37
   158
japod@37
   159
    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> mergeConvertors(
japod@37
   160
            Computer<AmountType> computer,
japod@37
   161
            Convertor<AmountType, IdentifierType> convertorA,
japod@37
   162
            Convertor<AmountType, IdentifierType> convertorB) {
japod@37
   163
        Collection<Convertor<AmountType, IdentifierType>> convertors =
japod@37
   164
                new ArrayList<Convertor<AmountType, IdentifierType>>();
japod@37
   165
        convertors.add(convertorA);
japod@37
   166
        convertors.add(convertorB);
japod@37
   167
        return mergeConvertors(computer, convertors);
japod@37
   168
    }
japod@37
   169
japod@37
   170
    public static Convertor<Double, String> mergeConvertorsDoubleString(
japod@37
   171
            Collection<Convertor<Double, String>> convertors) {
japod@37
   172
        return mergeConvertors(DoubleComputer, convertors);
japod@37
   173
    }
japod@37
   174
japod@37
   175
    public static Convertor<Double, String> mergeConvertorsDoubleString(
japod@37
   176
            Convertor<Double, String> convertorA,
japod@37
   177
            Convertor<Double, String> convertorB) {
japod@37
   178
        return mergeConvertors(DoubleComputer, convertorA, convertorB);
japod@37
   179
    }
japod@37
   180
japod@37
   181
    public static Convertor<Integer, String> mergeConvertorsIntegerString(
japod@37
   182
            Collection<Convertor<Integer, String>> convertors) {
japod@37
   183
        return mergeConvertors(IntegerComputer, convertors);
japod@37
   184
    }
japod@37
   185
japod@37
   186
    public static Convertor<Integer, String> mergeConvertorsIntegerString(
japod@37
   187
            Convertor<Integer, String> convertorA,
japod@37
   188
            Convertor<Integer, String> convertorB) {
japod@37
   189
        return mergeConvertors(IntegerComputer, convertorA, convertorB);
japod@37
   190
    }
japod@37
   191
japod@37
   192
    // ---
japod@37
   193
    // CREATION
japod@37
   194
    // ---
japod@37
   195
    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertor(
japod@37
   196
            Computer<AmountType> computer, Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates) {
japod@53
   197
        Convertor<AmountType, IdentifierType> c = new Convertor<AmountType, IdentifierType>(computer);
japod@53
   198
        c.addExchangeRates(exchangeRates);
japod@53
   199
        return c;
japod@6
   200
    }
japod@6
   201
japod@53
   202
    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertorDataSource(
japod@53
   203
            Computer<AmountType> computer, Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources) {
japod@53
   204
        Convertor<AmountType, IdentifierType> c = new Convertor<AmountType, IdentifierType>(computer);
japod@53
   205
        c.addExchangeRateDataSources(exchangeRateDataSources);
japod@53
   206
        return c;
japod@53
   207
    }
japod@53
   208
    
japod@6
   209
    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertor(
japod@37
   210
            Computer<AmountType> computer, ExchangeRateValue<AmountType, IdentifierType> exchangeRate) {
japod@37
   211
        Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates =
japod@37
   212
                new ArrayList<ExchangeRateValue<AmountType, IdentifierType>>();
japod@37
   213
        exchangeRates.add(exchangeRate);
japod@37
   214
        return getConvertor(computer, exchangeRates);
japod@6
   215
    }
japod@6
   216
    
japod@53
   217
    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertorDataSource(
japod@53
   218
            Computer<AmountType> computer, ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource) {
japod@53
   219
        Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources =
japod@53
   220
                new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
japod@53
   221
        exchangeRateDataSources.add(exchangeRateDataSource);
japod@53
   222
        return getConvertorDataSource(computer, exchangeRateDataSources);
japod@53
   223
    }
japod@53
   224
    
japod@37
   225
    public static Convertor<Double, String> getConvertorDoubleString(
japod@37
   226
            Collection<ExchangeRateValue<Double, String>> exchangeRates) {
japod@37
   227
        return getConvertor(DoubleComputer, exchangeRates);
japod@37
   228
    }
japod@6
   229
japod@37
   230
    public static Convertor<Double, String> getConvertorDoubleString(
japod@37
   231
            ExchangeRateValue<Double, String> exchangeRate) {
japod@37
   232
        return getConvertor(DoubleComputer, exchangeRate);
japod@37
   233
    }
japod@53
   234
    
japod@53
   235
    public static Convertor<Double, String> getConvertorDataSourceDoubleString(
japod@53
   236
            Collection<ExchangeRateDataSource<Double, String>> exchangeRateDataSources) {
japod@53
   237
        return getConvertorDataSource(DoubleComputer, exchangeRateDataSources);
japod@53
   238
    }
japod@6
   239
japod@53
   240
    public static Convertor<Double, String> getConvertorDataSourceDoubleString(
japod@53
   241
            ExchangeRateDataSource<Double, String> exchangeRateDataSource) {
japod@53
   242
        return getConvertorDataSource(DoubleComputer, exchangeRateDataSource);
japod@53
   243
    }
japod@53
   244
    
japod@37
   245
    public static Convertor<Integer, String> getConvertorIntegerString(
japod@37
   246
            Collection<ExchangeRateValue<Integer, String>> exchangeRates) {
japod@37
   247
        return getConvertor(IntegerComputer, exchangeRates);
japod@37
   248
    }
japod@37
   249
japod@37
   250
    public static Convertor<Integer, String> getConvertorIntegerString(
japod@37
   251
            ExchangeRateValue<Integer, String> exchangeRate) {
japod@37
   252
        return getConvertor(IntegerComputer, exchangeRate);
japod@37
   253
    }
japod@53
   254
    
japod@53
   255
    public static Convertor<Integer, String> getConvertorDataSourceIntegerString(
japod@53
   256
            Collection<ExchangeRateDataSource<Integer, String>> exchangeRateDataSources) {
japod@53
   257
        return getConvertorDataSource(IntegerComputer, exchangeRateDataSources);
japod@53
   258
    }
japod@53
   259
japod@53
   260
    public static Convertor<Integer, String> getConvertorDataSourceIntegerString(
japod@53
   261
            ExchangeRateDataSource<Integer, String> exchangeRateDataSource) {
japod@53
   262
        return getConvertorDataSource(IntegerComputer, exchangeRateDataSource);
japod@53
   263
    }
japod@37
   264
japod@37
   265
    // ---
japod@37
   266
    // BACKWARD COMPATIBILITY - CREATION
japod@37
   267
    // ---
japod@6
   268
    /**
japod@6
   269
     * Creates convertor for Double|String values with specified exchange rate
japod@6
   270
     * between two currencies.
japod@6
   271
     * 
japod@6
   272
     * @param firstCurrencyExchangeRate first currency
japod@6
   273
     * @param secondCurrencyExchangeRate second currency
japod@6
   274
     * @return convertor
japod@6
   275
     */
japod@6
   276
    public static Convertor<Double, String> getConvertorDoubleString(
japod@6
   277
            CurrencyValue<Double, String> firstCurrencyExchangeRate,
japod@6
   278
            CurrencyValue<Double, String> secondCurrencyExchangeRate) {
japod@37
   279
        return getConvertorDoubleString(ExchangeRateValue.getExchangeRate(firstCurrencyExchangeRate, secondCurrencyExchangeRate));
japod@6
   280
    }
japod@6
   281
japod@6
   282
    /**
japod@6
   283
     * Creates convertor for Integer|String values with specified exchange rate
japod@6
   284
     * between two currencies.
japod@6
   285
     * 
japod@6
   286
     * @param firstCurrencyExchangeRate first currency
japod@6
   287
     * @param secondCurrencyExchangeRate second currency
japod@6
   288
     * @return convertor
japod@6
   289
     */
japod@6
   290
    public static Convertor<Integer, String> getConvertorIntegerString(
japod@6
   291
            CurrencyValue<Integer, String> firstCurrencyExchangeRate,
japod@6
   292
            CurrencyValue<Integer, String> secondCurrencyExchangeRate) {
japod@37
   293
        return getConvertorIntegerString(ExchangeRateValue.getExchangeRate(firstCurrencyExchangeRate, secondCurrencyExchangeRate));
japod@6
   294
    }
japod@37
   295
    
japod@37
   296
    // ---
japod@37
   297
    // COMPUTERS
japod@37
   298
    // ---
japod@37
   299
    static final Computer<Double> DoubleComputer = new Computer<Double>() {
japod@37
   300
japod@53
   301
        public void compute(ComputerRequest<Double> request, ComputerResponse<Double> response) {
japod@37
   302
            response.setResult(request.getInput() * request.getOutputCurrencyRatio() / request.getInputCurrencyRatio());
japod@37
   303
        }
japod@37
   304
    };
japod@37
   305
    static final Computer<Integer> IntegerComputer = new Computer<Integer>() {
japod@37
   306
japod@53
   307
        public void compute(ComputerRequest<Integer> request, ComputerResponse<Integer> response) {
japod@37
   308
            response.setResult(request.getInput() * request.getOutputCurrencyRatio() / request.getInputCurrencyRatio());
japod@37
   309
        }
japod@37
   310
    };
japod@6
   311
}