task4/solution14/src/org/apidesign/apifest08/currency/Convertor.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Fri, 17 Oct 2008 17:35:52 +0200
changeset 67 bf7622ec1713
parent 61 58ec6da75f6f
permissions -rw-r--r--
Solution 14, task4
japod@25
     1
package org.apidesign.apifest08.currency;
japod@25
     2
japod@49
     3
import java.util.ArrayList;
japod@49
     4
import java.util.Collection;
japod@49
     5
import java.util.Collections;
jaroslav@67
     6
import java.util.Date;
japod@49
     7
import java.util.HashSet;
japod@49
     8
import java.util.List;
japod@49
     9
import java.util.Set;
japod@49
    10
japod@25
    11
/** This is the skeleton class for your API. You need to make it public, so
japod@25
    12
 * it is accessible to your client code (currently in Task1Test.java) file.
japod@25
    13
 * <p>
japod@25
    14
 * Feel free to create additional classes or rename this one, just keep all
japod@25
    15
 * the API and its implementation in this package. Do not spread it outside
japod@25
    16
 * to other packages.
japod@25
    17
 */
japod@25
    18
public final class Convertor {
japod@49
    19
    //version 1 fields
japod@49
    20
japod@49
    21
    private String currency1 = null;
japod@49
    22
    private String currency2 = null;
japod@49
    23
    private Rate rate = null;
japod@49
    24
japod@49
    25
    //version 2 field
jaroslav@67
    26
    private List<TimeLimitedCurrencyRate> currencyRates = null;
japod@49
    27
japod@49
    28
    //version - for compatible mode
japod@49
    29
    private int instanceVersion = 0; //compatible mode because of problem with empty currency and CZE -> CZE (1:2) rate
japod@49
    30
japod@25
    31
    Convertor(String currency1, String currency2, Rate rate) {
japod@49
    32
        instanceVersion = 1;
japod@49
    33
        if ((currency1 == null) || (currency2 == null) || (rate == null)) {
japod@25
    34
            throw new IllegalArgumentException("All arguments have to be non-null.");
japod@25
    35
        }
japod@25
    36
        this.currency1 = currency1;
japod@25
    37
        this.currency2 = currency2;
japod@25
    38
        this.rate = rate;
japod@25
    39
    }
japod@25
    40
japod@49
    41
    Convertor(final CurrencyRate ... currencyRate) {
japod@49
    42
        instanceVersion = 2;
japod@49
    43
japod@49
    44
        if (currencyRate == null) {
japod@49
    45
            throw new IllegalArgumentException("Parameter cannot be null.");
japod@49
    46
        }
japod@49
    47
        if (currencyRate.length == 0) {
japod@49
    48
            throw new IllegalArgumentException("CurrencyRates cannot be empty.");
japod@49
    49
        }
japod@49
    50
        Set<Pair<String,String>> currencies = new HashSet<Pair<String,String>>();
jaroslav@67
    51
        List<TimeLimitedCurrencyRate> curRates = new ArrayList<TimeLimitedCurrencyRate>();
japod@49
    52
        for (int i = 0; i < currencyRate.length; i++) {
jaroslav@67
    53
            final CurrencyRate curRat = currencyRate[i];
japod@49
    54
            if (curRat == null) {
japod@49
    55
                throw new IllegalArgumentException("Parameter cannot be null.");
japod@49
    56
            }
japod@49
    57
            //check that currencyRate is not defined twice
japod@49
    58
            Pair<String, String> curPair= new Pair<String, String>(curRat.getCurrency1(), curRat.getCurrency2());
japod@49
    59
            if (currencies.contains(curPair)) {
japod@49
    60
                throw new IllegalArgumentException("Pair of currencies in a currency rate cannot be defined twice");
japod@49
    61
            }
japod@49
    62
            currencies.add(curPair);
jaroslav@67
    63
jaroslav@67
    64
            if (curRat instanceof TimeLimitedCurrencyRate) {
jaroslav@67
    65
                curRates.add((TimeLimitedCurrencyRate)curRat);
jaroslav@67
    66
            } else {
jaroslav@67
    67
                curRates.add(new TimeLimitedCurrencyRate() { //create delegate which implements TimeLimitedCurrencyRate
jaroslav@67
    68
                    public long getFromTime() {
jaroslav@67
    69
                        return Long.MIN_VALUE;
jaroslav@67
    70
                    }
jaroslav@67
    71
jaroslav@67
    72
                    public long getToTime() {
jaroslav@67
    73
                        return Long.MAX_VALUE;
jaroslav@67
    74
                    }
jaroslav@67
    75
jaroslav@67
    76
                    public String getCurrency1() {
jaroslav@67
    77
                        return curRat.getCurrency1();
jaroslav@67
    78
                    }
jaroslav@67
    79
jaroslav@67
    80
                    public String getCurrency2() {
jaroslav@67
    81
                        return curRat.getCurrency2();
jaroslav@67
    82
                    }
jaroslav@67
    83
jaroslav@67
    84
                    public Rate getRate() {
jaroslav@67
    85
                        return curRat.getRate();
jaroslav@67
    86
                    }
jaroslav@67
    87
                });
jaroslav@67
    88
            }
jaroslav@67
    89
        }
jaroslav@67
    90
        this.currencyRates = Collections.unmodifiableList(curRates);
jaroslav@67
    91
    }
jaroslav@67
    92
jaroslav@67
    93
    Convertor(final int fakeParameter, final TimeLimitedCurrencyRate ... currencyRate) { //use fake parameter just to specify which constructor should be invoked
jaroslav@67
    94
        instanceVersion = 3;
jaroslav@67
    95
jaroslav@67
    96
        if (currencyRate == null) {
jaroslav@67
    97
            throw new IllegalArgumentException("Parameter cannot be null.");
jaroslav@67
    98
        }
jaroslav@67
    99
        List<TimeLimitedCurrencyRate> curRates = new ArrayList<TimeLimitedCurrencyRate>();
jaroslav@67
   100
        for (int i = 0; i < currencyRate.length; i++) {
jaroslav@67
   101
            final TimeLimitedCurrencyRate curRat = currencyRate[i];
jaroslav@67
   102
            if (curRat == null) {
jaroslav@67
   103
                throw new IllegalArgumentException("Parameter cannot be null.");
jaroslav@67
   104
            }
jaroslav@67
   105
japod@49
   106
            curRates.add(curRat);
japod@49
   107
        }
japod@49
   108
        this.currencyRates = Collections.unmodifiableList(curRates);
japod@49
   109
    }
japod@49
   110
japod@25
   111
    public double convert(String fromCurrency, String toCurrency, int amount) {
jaroslav@67
   112
        return convert(fromCurrency, toCurrency, amount, System.currentTimeMillis());
jaroslav@67
   113
    }
jaroslav@67
   114
jaroslav@67
   115
    public double convert(String fromCurrency, String toCurrency, int amount, Date date) {
jaroslav@67
   116
        if (date == null) {
jaroslav@67
   117
            throw new IllegalArgumentException("Date cannot be null");
jaroslav@67
   118
        }
jaroslav@67
   119
        return convert(fromCurrency, toCurrency, amount, date.getTime());
jaroslav@67
   120
    }
jaroslav@67
   121
jaroslav@67
   122
    public double convert(String fromCurrency, String toCurrency, int amount, long time) {
japod@49
   123
        if (instanceVersion == 1) {
japod@49
   124
            if ((fromCurrency == null) || (toCurrency == null)) {
japod@49
   125
                throw new IllegalArgumentException("All arguments have to be non-null.");
japod@49
   126
            }
japod@49
   127
japod@49
   128
            if (currency1.equals(fromCurrency) && currency2.equals(toCurrency)) {
japod@49
   129
                return rate.convertAtoB(amount);
japod@49
   130
            } else if (currency2.equals(fromCurrency) && currency1.equals(toCurrency)) {
japod@49
   131
                return rate.convertBtoA(amount);
japod@49
   132
            } else {
japod@49
   133
                throw new IllegalArgumentException("Convertor " + this.toString() +
japod@49
   134
                        " cannot work with currencies " + fromCurrency + " and " + toCurrency + ".");
japod@49
   135
            }
japod@54
   136
        } else { //instanceVersion >= 2
japod@49
   137
            //find suitable convertor
jaroslav@67
   138
           for (TimeLimitedCurrencyRate curRate : currencyRates) {
jaroslav@67
   139
                if ((curRate.getCurrency1().equals(fromCurrency))&&
jaroslav@67
   140
                        (curRate.getCurrency2().equals(toCurrency))&&
jaroslav@67
   141
                        (curRate.getFromTime() <= time) &&
jaroslav@67
   142
                        (curRate.getToTime() >= time))
japod@49
   143
                {
japod@49
   144
                    return curRate.getRate().convertAtoB(amount);
japod@49
   145
                }
japod@49
   146
            }
japod@49
   147
            //suitable convertor not found, try to find inverse convertor
jaroslav@67
   148
            for (TimeLimitedCurrencyRate curRate : currencyRates) {
japod@49
   149
                if ((curRate.getCurrency2().equals(fromCurrency))&&
jaroslav@67
   150
                        (curRate.getCurrency1().equals(toCurrency))&&
jaroslav@67
   151
                        (curRate.getFromTime() <= time) &&
jaroslav@67
   152
                        (curRate.getToTime() >= time))
japod@49
   153
                {
japod@49
   154
                    return curRate.getRate().convertBtoA(amount);
japod@49
   155
                }
jaroslav@67
   156
            }
japod@49
   157
            //even inverse convertor not found
japod@49
   158
            throw new IllegalArgumentException("Cannot work with selected currencies.");
japod@25
   159
        }
japod@25
   160
    }
japod@25
   161
japod@25
   162
    public double convert(String fromCurrency, String toCurrency, double amount) {
jaroslav@67
   163
        return convert(fromCurrency, toCurrency, amount, System.currentTimeMillis());
jaroslav@67
   164
    }
jaroslav@67
   165
jaroslav@67
   166
    public double convert(String fromCurrency, String toCurrency, double amount, Date date) {
jaroslav@67
   167
        if (date == null) {
jaroslav@67
   168
            throw new IllegalArgumentException("Date cannot be null");
jaroslav@67
   169
        }
jaroslav@67
   170
        return convert(fromCurrency, toCurrency, amount, date.getTime());
jaroslav@67
   171
    }
jaroslav@67
   172
jaroslav@67
   173
    public double convert(String fromCurrency, String toCurrency, double amount, long time) {
japod@49
   174
        if (instanceVersion == 1) {
japod@49
   175
            if ((fromCurrency == null) || (toCurrency == null)) {
japod@49
   176
                throw new IllegalArgumentException("All arguments have to be non-null.");
japod@49
   177
            }
japod@49
   178
japod@49
   179
            if (currency1.equals(fromCurrency) && currency2.equals(toCurrency)) {
japod@49
   180
                return rate.convertAtoB(amount);
japod@49
   181
            } else if (currency2.equals(fromCurrency) && currency1.equals(toCurrency)) {
japod@49
   182
                return rate.convertBtoA(amount);
japod@49
   183
            } else {
japod@49
   184
                throw new IllegalArgumentException("Convertor " + this.toString() +
japod@49
   185
                        " cannot work with currencies " + fromCurrency + " and " + toCurrency + ".");
japod@49
   186
            }
japod@54
   187
        } else { //instanceVersion >= 2
japod@49
   188
            //find suitable convertor
jaroslav@67
   189
            for (TimeLimitedCurrencyRate curRate : currencyRates) {
jaroslav@67
   190
                if ((curRate.getCurrency1().equals(fromCurrency))&&
jaroslav@67
   191
                        (curRate.getCurrency2().equals(toCurrency))&&
jaroslav@67
   192
                        (curRate.getFromTime() <= time) &&
jaroslav@67
   193
                        (curRate.getToTime() >= time))
japod@49
   194
                {
japod@49
   195
                    return curRate.getRate().convertAtoB(amount);
japod@49
   196
                }
japod@49
   197
            }
japod@49
   198
            //suitable convertor not found, try to find inverse convertor
jaroslav@67
   199
            for (TimeLimitedCurrencyRate curRate : currencyRates) {
japod@49
   200
                if ((curRate.getCurrency2().equals(fromCurrency))&&
jaroslav@67
   201
                        (curRate.getCurrency1().equals(toCurrency))&&
jaroslav@67
   202
                        (curRate.getFromTime() <= time) &&
jaroslav@67
   203
                        (curRate.getToTime() >= time))
japod@49
   204
                {
japod@49
   205
                    return curRate.getRate().convertBtoA(amount);
japod@49
   206
                }
jaroslav@67
   207
            }
japod@49
   208
            //even inverse convertor not found
japod@49
   209
            throw new IllegalArgumentException("Cannot work with selected currencies.");
japod@25
   210
        }
japod@25
   211
    }
japod@49
   212
japod@49
   213
    /**
japod@49
   214
     * Returns currency rates. If instantiated with constructor from vesion 1
japod@49
   215
     * it creates new collection with one CurrencyRate.
japod@49
   216
     * Note, it can cause exception because of empty currencies or same currencies.
japod@49
   217
     */
japod@49
   218
    public Collection<CurrencyRate> getCurrencyRates() {
japod@49
   219
        if (instanceVersion == 1) {
japod@49
   220
            List<CurrencyRate> ret = new ArrayList<CurrencyRate>();
japod@54
   221
            ret.add(new CurrencyRateImpl(currency1, currency2, rate)); //here it checks that currency rate is not nonsense
japod@49
   222
            return Collections.unmodifiableCollection(ret);
japod@54
   223
        } else { //instanceVersion >= 2
jaroslav@67
   224
            List<CurrencyRate> ret = new ArrayList<CurrencyRate>(currencyRates);
jaroslav@67
   225
            return Collections.unmodifiableCollection(ret);
jaroslav@67
   226
        }
jaroslav@67
   227
    }
jaroslav@67
   228
jaroslav@67
   229
    public Collection<TimeLimitedCurrencyRate> getTimeLimitedCurrencyRates() {
jaroslav@67
   230
        if (instanceVersion == 1) {
jaroslav@67
   231
            List<TimeLimitedCurrencyRate> ret = new ArrayList<TimeLimitedCurrencyRate>();
jaroslav@67
   232
            ret.add(new CurrencyRateImpl(currency1, currency2, rate)); //here it checks that currency rate is not nonsense
jaroslav@67
   233
            return Collections.unmodifiableCollection(ret);
jaroslav@67
   234
        } else { //instanceVersion >= 2
jaroslav@67
   235
            List<TimeLimitedCurrencyRate> ret = new ArrayList<TimeLimitedCurrencyRate>(currencyRates);
jaroslav@67
   236
            return Collections.unmodifiableCollection(ret);
japod@49
   237
        }
japod@49
   238
    }
japod@49
   239
japod@25
   240
    @Override
japod@25
   241
    public String toString() {
japod@49
   242
        if (instanceVersion == 1) {
japod@49
   243
            return currency1 + currency2;
japod@49
   244
        } else { //instanceVersion == 2
japod@49
   245
            return super.toString(); //better be compatible in future :-)
japod@49
   246
        }
japod@25
   247
    }
japod@49
   248
japod@25
   249
    @Override
japod@25
   250
    public boolean equals(Object obj) {
japod@49
   251
        if (instanceVersion == 1) {
japod@49
   252
            if (obj == null) {
japod@49
   253
                return false;
japod@49
   254
            }
japod@49
   255
            if (getClass() != obj.getClass()) {
japod@49
   256
                return false;
japod@49
   257
            }
japod@49
   258
            final Convertor other = (Convertor) obj;
japod@49
   259
            if (this.currency1 != other.currency1 && (this.currency1 == null || !this.currency1.equals(other.currency1))) {
japod@49
   260
                return false;
japod@49
   261
            }
japod@49
   262
            if (this.currency2 != other.currency2 && (this.currency2 == null || !this.currency2.equals(other.currency2))) {
japod@49
   263
                return false;
japod@49
   264
            }
japod@49
   265
            if (this.rate != other.rate && (this.rate == null || !this.rate.equals(other.rate))) {
japod@49
   266
                return false;
japod@49
   267
            }
japod@49
   268
            return true;
japod@49
   269
        } else { //instanceVersion == 2
japod@49
   270
            return super.equals(obj); //better be compatible in future :-)
japod@25
   271
        }
japod@25
   272
    }
japod@25
   273
japod@25
   274
    @Override
japod@25
   275
    public int hashCode() {
japod@49
   276
        if (instanceVersion == 1) {
japod@49
   277
            int hash = 5;
japod@49
   278
            hash = 67 * hash + (this.currency1 != null ? this.currency1.hashCode() : 0);
japod@49
   279
            hash = 67 * hash + (this.currency2 != null ? this.currency2.hashCode() : 0);
japod@49
   280
            hash = 67 * hash + (this.rate != null ? this.rate.hashCode() : 0);
japod@25
   281
        return hash;
japod@49
   282
        } else { //instanceVersion == 2
japod@49
   283
            return super.hashCode(); //better be compatible in future :-)
japod@49
   284
        }
japod@25
   285
    }
japod@25
   286
}