task4/solution04/src/org/apidesign/apifest08/currency/DatedCompositeConvertorImpl.java
author Jaroslav Tulach <jtulach@netbeans.org>
Sat, 25 Oct 2008 20:53:00 +0200
changeset 84 2ae6e4aa7aef
permissions -rw-r--r--
Solutions by Petr Smid
jaroslav@69
     1
package org.apidesign.apifest08.currency;
jaroslav@69
     2
jaroslav@69
     3
import java.math.BigDecimal;
jaroslav@69
     4
import java.util.Currency;
jaroslav@69
     5
import java.util.Date;
jaroslav@69
     6
import java.util.HashMap;
jaroslav@69
     7
import java.util.Map;
jaroslav@69
     8
import java.util.Set;
jaroslav@69
     9
jaroslav@69
    10
jaroslav@69
    11
final class DatedCompositeConvertorImpl
jaroslav@69
    12
    implements TimedConvertor
jaroslav@69
    13
{
jaroslav@69
    14
    /**
jaroslav@69
    15
     * The convertors that are supported.
jaroslav@69
    16
     */
jaroslav@69
    17
    private final DatedConvertor[] convertors;
jaroslav@69
    18
    
jaroslav@69
    19
    /**
jaroslav@69
    20
     * Keeps track of what convertors to use to convert between currencies.
jaroslav@69
    21
     */
jaroslav@69
    22
    private final Map<DateRange, Map<Currency, Map<Currency, Convertor>>> datedConversions;
jaroslav@69
    23
    
jaroslav@69
    24
    {
jaroslav@69
    25
        datedConversions = new HashMap<DateRange, Map<Currency, Map<Currency, Convertor>>>();
jaroslav@69
    26
    }
jaroslav@69
    27
jaroslav@69
    28
    /**
jaroslav@69
    29
     * Construct a ComositeConvertorImpl with the specified convertors.
jaroslav@69
    30
     * This will result in all possible conversions between the supplied currencies being made.
jaroslav@69
    31
     * 
jaroslav@69
    32
     * @param cs the convertors to use.
jaroslav@69
    33
     * @throws IllegalArgumentException if any of the items in cs are null.
jaroslav@69
    34
     */
jaroslav@69
    35
    DatedCompositeConvertorImpl(Convertor ... cs)
jaroslav@69
    36
    {
jaroslav@69
    37
        int i;
jaroslav@69
    38
jaroslav@69
    39
        convertors = new DatedConvertor[cs.length];
jaroslav@69
    40
        i          = 0;
jaroslav@69
    41
jaroslav@69
    42
        for(final Convertor c : cs)
jaroslav@69
    43
        {
jaroslav@69
    44
            if(!(c instanceof DatedConvertor))
jaroslav@69
    45
            {
jaroslav@69
    46
                throw new IllegalArgumentException("cs must only contain DatedConvertors");
jaroslav@69
    47
            }
jaroslav@69
    48
jaroslav@69
    49
            convertors[i] = (DatedConvertor)c;
jaroslav@69
    50
            i++;
jaroslav@69
    51
        }
jaroslav@69
    52
jaroslav@69
    53
        // track all of the known conversion
jaroslav@69
    54
        for(final DatedConvertor convertor : convertors)
jaroslav@69
    55
        {
jaroslav@69
    56
            final Set<Currency>      currencies;
jaroslav@69
    57
            Map<Currency, Convertor> possible;
jaroslav@69
    58
            DateRange                range;
jaroslav@69
    59
            
jaroslav@69
    60
            if(convertor == null)
jaroslav@69
    61
            {
jaroslav@69
    62
                throw new IllegalArgumentException("cs cannot contain null");
jaroslav@69
    63
            }
jaroslav@69
    64
            
jaroslav@69
    65
            currencies = convertor.getCurrencies();
jaroslav@69
    66
            range      = convertor.getDateRange();
jaroslav@69
    67
            
jaroslav@69
    68
            for(final Currency currency : currencies)
jaroslav@69
    69
            {
jaroslav@69
    70
                Map<Currency, Map<Currency, Convertor>> possibleConversions;
jaroslav@69
    71
                
jaroslav@69
    72
                possibleConversions = datedConversions.get(range);
jaroslav@69
    73
                
jaroslav@69
    74
                if(possibleConversions == null)
jaroslav@69
    75
                {
jaroslav@69
    76
                    possibleConversions = new HashMap<Currency, Map<Currency, Convertor>>();
jaroslav@69
    77
                    datedConversions.put(range, possibleConversions);
jaroslav@69
    78
                }
jaroslav@69
    79
jaroslav@69
    80
                possible = possibleConversions.get(currency);
jaroslav@69
    81
jaroslav@69
    82
                if(possible == null)
jaroslav@69
    83
                {
jaroslav@69
    84
                    possible = new HashMap<Currency, Convertor>();
jaroslav@69
    85
                    possibleConversions.put(currency, possible);
jaroslav@69
    86
                }
jaroslav@69
    87
jaroslav@69
    88
                for(final Currency c : currencies)
jaroslav@69
    89
                {
jaroslav@69
    90
                    possible.put(c, convertor);
jaroslav@69
    91
                }
jaroslav@69
    92
            }
jaroslav@69
    93
        }
jaroslav@69
    94
jaroslav@69
    95
jaroslav@69
    96
        /*
jaroslav@69
    97
        // make up conversions that can be derived... eg:
jaroslav@69
    98
        //   we have:
jaroslav@69
    99
        //      USD <-> CAD
jaroslav@69
   100
        //      CAD <-> CZK
jaroslav@69
   101
        //      SSK <-> GBP
jaroslav@69
   102
        //   we can derive:
jaroslav@69
   103
        //      USD <-> CZK
jaroslav@69
   104
        //   we cannot derive:
jaroslav@69
   105
        //      USD <-> GBP
jaroslav@69
   106
        //      CAD <-> GBP
jaroslav@69
   107
        //      CZK <-> GBP        
jaroslav@69
   108
        // 
jaroslav@69
   109
        // NOTE: no attempt is made to deal with dates for DatedConvertors... nothing we can do about it.
jaroslav@69
   110
        do
jaroslav@69
   111
        {
jaroslav@69
   112
            newConvertors = 0;
jaroslav@69
   113
jaroslav@69
   114
            // todo... need to loop this until all the ones that can be handled are done.
jaroslav@69
   115
            for(final Currency from : getCurrencies())
jaroslav@69
   116
            {
jaroslav@69
   117
                for(final Currency to : getCurrencies())
jaroslav@69
   118
                {
jaroslav@69
   119
                    if(!(canConvert(from, to)))
jaroslav@69
   120
                    {
jaroslav@69
   121
                        final Set<Currency> fromCurrencies;
jaroslav@69
   122
                        final Set<Currency> toCurrencies;
jaroslav@69
   123
                        final Set<Currency> common;
jaroslav@69
   124
                        Map<Currency, Map<Currency, Convertor>> possibleConversions;
jaroslav@69
   125
                
jaroslav@69
   126
                        possibleConversions.get(range);
jaroslav@69
   127
                        fromCurrencies = possibleConversions.get(from).keySet();
jaroslav@69
   128
                        toCurrencies   = possibleConversions.get(to).keySet();
jaroslav@69
   129
                        common         = new HashSet<Currency>();
jaroslav@69
   130
jaroslav@69
   131
                        for(final Currency currency : fromCurrencies)
jaroslav@69
   132
                        {
jaroslav@69
   133
                            if(toCurrencies.contains(currency))
jaroslav@69
   134
                            {
jaroslav@69
   135
                                common.add(currency);
jaroslav@69
   136
                            }
jaroslav@69
   137
                        }
jaroslav@69
   138
jaroslav@69
   139
                        for(final Currency currency : common)
jaroslav@69
   140
                        {
jaroslav@69
   141
                            final Convertor convertor;
jaroslav@69
   142
jaroslav@69
   143
                            convertor = createConvertor(from, to, currency);
jaroslav@69
   144
                            possibleConversions.get(from).put(to, convertor);
jaroslav@69
   145
                            possibleConversions.get(to).put(from, convertor);
jaroslav@69
   146
                            newConvertors++;
jaroslav@69
   147
                        }
jaroslav@69
   148
                    }
jaroslav@69
   149
                }
jaroslav@69
   150
            }
jaroslav@69
   151
        }
jaroslav@69
   152
        while(newConvertors > 0);
jaroslav@69
   153
        */
jaroslav@69
   154
    }
jaroslav@69
   155
jaroslav@69
   156
    /**
jaroslav@69
   157
     * Check to see if converting between the two currencies is possible.
jaroslav@69
   158
     *
jaroslav@69
   159
     * @param from the currency to convert from.
jaroslav@69
   160
     * @param to the currency to convert to.
jaroslav@69
   161
     * @return true if the conversion is possible.
jaroslav@69
   162
     * @throws IllegalArgumentException if either from or to are null.
jaroslav@69
   163
     */
jaroslav@69
   164
    public boolean canConvert(final Currency from, final Currency to)
jaroslav@69
   165
    {
jaroslav@69
   166
        throw new UnsupportedOperationException();
jaroslav@69
   167
    }
jaroslav@69
   168
jaroslav@69
   169
    /**
jaroslav@69
   170
     * Get the currencies that the convertor supports.  Just because a currency is
jaroslav@69
   171
     * supported does not mean that canConvert will return true.
jaroslav@69
   172
     * 
jaroslav@69
   173
     * @return the supported currencies.
jaroslav@69
   174
     */
jaroslav@69
   175
    public Set<Currency> getCurrencies()
jaroslav@69
   176
    {
jaroslav@69
   177
        throw new UnsupportedOperationException();
jaroslav@69
   178
    }
jaroslav@69
   179
    
jaroslav@69
   180
    /**
jaroslav@69
   181
     * Get the conversion rate between two currencies.
jaroslav@69
   182
     * 
jaroslav@69
   183
     * @param from the currency to convert from.
jaroslav@69
   184
     * @param to the currency to convert to.
jaroslav@69
   185
     * @return the conversion rate between the two currencies.
jaroslav@69
   186
     * @throws IllegalArgumentException if either from or to is null.
jaroslav@69
   187
     * @throws InvalidConversionException if canConvert would return false.
jaroslav@69
   188
     */
jaroslav@69
   189
    public BigDecimal getConversionRate(final Currency from, final Currency to)
jaroslav@69
   190
        throws InvalidConversionException
jaroslav@69
   191
    {
jaroslav@69
   192
        throw new UnsupportedOperationException();
jaroslav@69
   193
    }
jaroslav@69
   194
jaroslav@69
   195
    private Convertor getConvertor(final Currency from, final Currency to, final Date date)
jaroslav@69
   196
    {
jaroslav@69
   197
        Map<Currency, Map<Currency, Convertor>> possibleConversions;
jaroslav@69
   198
        final Map<Currency, Convertor> possible;
jaroslav@69
   199
        Convertor                      convertor;
jaroslav@69
   200
jaroslav@69
   201
        if(from == null)
jaroslav@69
   202
        {
jaroslav@69
   203
            throw new IllegalArgumentException("from cannot be null");
jaroslav@69
   204
        }
jaroslav@69
   205
        
jaroslav@69
   206
        if(to == null)
jaroslav@69
   207
        {
jaroslav@69
   208
            throw new IllegalArgumentException("to cannot be null");
jaroslav@69
   209
        }
jaroslav@69
   210
        
jaroslav@69
   211
        possibleConversions = null;
jaroslav@69
   212
jaroslav@69
   213
        for(final DateRange range : datedConversions.keySet())
jaroslav@69
   214
        {
jaroslav@69
   215
            if(range.isInRange(date))
jaroslav@69
   216
            {
jaroslav@69
   217
                possibleConversions = datedConversions.get(range);
jaroslav@69
   218
                break;
jaroslav@69
   219
            }
jaroslav@69
   220
        }
jaroslav@69
   221
jaroslav@69
   222
        if(possibleConversions == null)
jaroslav@69
   223
        {
jaroslav@69
   224
            return (null);
jaroslav@69
   225
        }
jaroslav@69
   226
jaroslav@69
   227
        possible  = possibleConversions.get(from);
jaroslav@69
   228
        
jaroslav@69
   229
                
jaroslav@69
   230
        if(possible == null)
jaroslav@69
   231
        {
jaroslav@69
   232
            return (null);
jaroslav@69
   233
        }
jaroslav@69
   234
jaroslav@69
   235
jaroslav@69
   236
        convertor = possible.get(to);
jaroslav@69
   237
jaroslav@69
   238
        
jaroslav@69
   239
        if(convertor == null)
jaroslav@69
   240
        {
jaroslav@69
   241
            return (null);
jaroslav@69
   242
        }
jaroslav@69
   243
jaroslav@69
   244
        return (convertor);
jaroslav@69
   245
    }
jaroslav@69
   246
jaroslav@69
   247
    /**
jaroslav@69
   248
     * Convert an amount from one currency to another.
jaroslav@69
   249
     * 
jaroslav@69
   250
     * @param from the currency to convert from.
jaroslav@69
   251
     * @param to the currency to convert to.
jaroslav@69
   252
     * @param amount the amount to convert.
jaroslav@69
   253
     * @return the converted amount.
jaroslav@69
   254
     * @throws IllegalArgumentException if any of the arguments are null.
jaroslav@69
   255
     * @throws InvalidConversionException if either from or to are not valid for the convertor.
jaroslav@69
   256
     */
jaroslav@69
   257
    public BigDecimal convert(final Currency   from,
jaroslav@69
   258
                              final Currency   to,
jaroslav@69
   259
                              final BigDecimal amount)
jaroslav@69
   260
        throws InvalidConversionException
jaroslav@69
   261
    {
jaroslav@69
   262
        throw new InvalidConversionException("No date for the conversion", from);
jaroslav@69
   263
    }
jaroslav@69
   264
jaroslav@69
   265
    public BigDecimal convert(final Currency   from, 
jaroslav@69
   266
                              final Currency   to, 
jaroslav@69
   267
                              final BigDecimal amount, 
jaroslav@69
   268
                              final Date       date) 
jaroslav@69
   269
        throws InvalidConversionException 
jaroslav@69
   270
    {
jaroslav@69
   271
        final Convertor  convertor;
jaroslav@69
   272
        final BigDecimal total;
jaroslav@69
   273
        
jaroslav@69
   274
        convertor = getConvertor(from, to, date);
jaroslav@69
   275
jaroslav@69
   276
        if(convertor == null)
jaroslav@69
   277
        {
jaroslav@69
   278
            throw new InvalidConversionException("cannot convert", from);
jaroslav@69
   279
        }
jaroslav@69
   280
        
jaroslav@69
   281
        if(canConvert(from, to, date))
jaroslav@69
   282
        {
jaroslav@69
   283
            final TimedConvertor timeConvertor;
jaroslav@69
   284
            
jaroslav@69
   285
            timeConvertor = (TimedConvertor)convertor;
jaroslav@69
   286
            total         = timeConvertor.convert(from, to, amount, date);
jaroslav@69
   287
        }
jaroslav@69
   288
        else
jaroslav@69
   289
        {
jaroslav@69
   290
            throw new InvalidConversionException("cannot convert", from);
jaroslav@69
   291
        }
jaroslav@69
   292
        
jaroslav@69
   293
        return (total);
jaroslav@69
   294
    }
jaroslav@69
   295
jaroslav@69
   296
    public boolean canConvert(final Currency from,
jaroslav@69
   297
                              final Currency to,
jaroslav@69
   298
                              final Date     date)
jaroslav@69
   299
    {
jaroslav@69
   300
        Convertor     convertor;
jaroslav@69
   301
        final boolean retVal;
jaroslav@69
   302
jaroslav@69
   303
        convertor = getConvertor(from, to, date);
jaroslav@69
   304
jaroslav@69
   305
        if(convertor != null)
jaroslav@69
   306
        {
jaroslav@69
   307
            final TimedConvertor timeConvertor;
jaroslav@69
   308
jaroslav@69
   309
            timeConvertor = (TimedConvertor)convertor;
jaroslav@69
   310
            retVal        = timeConvertor.canConvert(from, to, date);
jaroslav@69
   311
        }
jaroslav@69
   312
        else
jaroslav@69
   313
        {
jaroslav@69
   314
            retVal = false;
jaroslav@69
   315
        }
jaroslav@69
   316
jaroslav@69
   317
        return (retVal);
jaroslav@69
   318
    }
jaroslav@69
   319
}