task3/solution04/src/org/apidesign/apifest08/currency/ConverterImpl.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 07 Oct 2008 11:05:34 +0200
changeset 45 251d0ed461fb
parent 35 task2/solution04/src/org/apidesign/apifest08/currency/ConverterImpl.java@8898c620fe96
child 55 14e78f48ac2b
permissions -rw-r--r--
Copying all solution that advanced into round #3 into task3 directory
japod@17
     1
package org.apidesign.apifest08.currency;
japod@17
     2
japod@17
     3
japod@17
     4
import java.math.BigDecimal;
japod@17
     5
import java.math.MathContext;
japod@17
     6
import java.math.RoundingMode;
japod@35
     7
import java.util.Collections;
japod@17
     8
import java.util.Currency;
japod@35
     9
import java.util.HashSet;
japod@35
    10
import java.util.Set;
japod@17
    11
japod@17
    12
japod@17
    13
/**
japod@17
    14
 * Convert between two currencies.
japod@17
    15
 *
japod@17
    16
 * @author D'Arcy Smith
japod@17
    17
 * @version 1.0
japod@17
    18
 */
japod@17
    19
final class ConvertorImpl
japod@17
    20
    implements Convertor
japod@17
    21
{
japod@17
    22
    /**
japod@17
    23
     * The currency to cvonvert from.
japod@17
    24
     */
japod@17
    25
    private final Currency currencyA;
japod@17
    26
japod@17
    27
    /**
japod@17
    28
     * The currency to cvonvert from.
japod@17
    29
     */
japod@17
    30
    private final Currency currencyB;
japod@17
    31
japod@17
    32
    /**
japod@17
    33
     * The echange rate between a and b.
japod@17
    34
     */
japod@17
    35
    private final BigDecimal currencyARate;
japod@17
    36
japod@17
    37
    /**
japod@17
    38
     * The echange rate between b and a.
japod@17
    39
     */
japod@17
    40
    private final BigDecimal currencyBRate;
japod@17
    41
    
japod@17
    42
    /**
japod@17
    43
     * Constructs a convertor with the specified currencies.
japod@17
    44
     * 
japod@17
    45
     * @param a the currency to convert from.
japod@17
    46
     * @param aRate the exchage rage between from and to.
japod@17
    47
     * @param b the currency to convert to.
japod@17
    48
     * @param bRate the exchage rage between to and from.
japod@17
    49
     * @throws IllegalArgumentException if either any of the arguments are null or if either rate <= 0.
japod@17
    50
     */
japod@17
    51
    public ConvertorImpl(final Currency   a,
japod@17
    52
                         final BigDecimal aRate,
japod@17
    53
                         final Currency   b,
japod@17
    54
                         final BigDecimal bRate)
japod@17
    55
    {
japod@17
    56
        if(a == null)
japod@17
    57
        {
japod@17
    58
            throw new IllegalArgumentException("a cannot be null");
japod@17
    59
        }
japod@17
    60
japod@17
    61
        if(b == null)
japod@17
    62
        {
japod@17
    63
            throw new IllegalArgumentException("b cannot be null");
japod@17
    64
        }
japod@17
    65
japod@17
    66
        if(aRate == null)
japod@17
    67
        {
japod@17
    68
            throw new IllegalArgumentException("aRate cannot be null");
japod@17
    69
        }
japod@17
    70
japod@17
    71
        if(bRate == null)
japod@17
    72
        {
japod@17
    73
            throw new IllegalArgumentException("bRate cannot be null");
japod@17
    74
        }
japod@17
    75
                
japod@17
    76
        if(aRate.compareTo(BigDecimal.ZERO) <= 0)
japod@17
    77
        {
japod@17
    78
            throw new IllegalArgumentException("aRate must be > 0, was: " + aRate);
japod@17
    79
        }
japod@17
    80
                
japod@17
    81
        if(bRate.compareTo(BigDecimal.ZERO) <= 0)
japod@17
    82
        {
japod@17
    83
            throw new IllegalArgumentException("bRate must be > 0, was: " + bRate);
japod@17
    84
        }
japod@17
    85
        
japod@17
    86
        currencyA     = a;
japod@17
    87
        currencyB     = b;
japod@17
    88
        currencyARate = aRate;
japod@17
    89
        currencyBRate = bRate;
japod@17
    90
    }
japod@17
    91
    
japod@17
    92
    /**
japod@17
    93
     * Convert an amount from one currency to another.
japod@17
    94
     * 
japod@17
    95
     * @param from the currency to convert from.
japod@17
    96
     * @param to the currency to convert to.
japod@17
    97
     * @param amount the amount to convert.
japod@17
    98
     * @return the converted amount.
japod@17
    99
     * @throws IllegalArgumentException if any of the arguments are null.
japod@17
   100
     * @throws InvalidConversionException if either from or to are not equal to the currencies passed to the constructor.
japod@17
   101
     */
japod@17
   102
    public BigDecimal convert(final Currency   from,
japod@17
   103
                              final Currency   to,
japod@17
   104
                              final BigDecimal amount)
japod@17
   105
        throws InvalidConversionException
japod@17
   106
    {
japod@17
   107
        final BigDecimal result;
japod@17
   108
        
japod@17
   109
        if(amount == null)
japod@17
   110
        {
japod@17
   111
            throw new IllegalArgumentException("amount cannot be null");
japod@17
   112
        }
japod@17
   113
        
japod@17
   114
        if(from == null)
japod@17
   115
        {
japod@17
   116
            throw new IllegalArgumentException("from cannot be null");
japod@17
   117
        }
japod@17
   118
        
japod@17
   119
        if(to == null)
japod@17
   120
        {
japod@17
   121
            throw new IllegalArgumentException("to cannot be null");
japod@17
   122
        }
japod@17
   123
        
japod@17
   124
        if(!(from.equals(currencyA)) && (!(from.equals(currencyB))))
japod@17
   125
        {
japod@17
   126
            throw new InvalidConversionException("cannot convert from: " + from.getCurrencyCode(), from, currencyA, currencyB);
japod@17
   127
        }
japod@17
   128
        
japod@17
   129
        if(!(to.equals(currencyA)) && (!(to.equals(currencyB))))
japod@17
   130
        {
japod@17
   131
            throw new InvalidConversionException("cannot convert to: " + to.getCurrencyCode(), to, currencyA, currencyB);
japod@17
   132
        }
japod@17
   133
japod@35
   134
        result = amount.multiply(getConversionRate(from, to));
japod@35
   135
japod@35
   136
        return (result.setScale(2, RoundingMode.HALF_DOWN));
japod@35
   137
    }
japod@35
   138
japod@35
   139
    /**
japod@35
   140
     * Check to see if converting between the two currencies is possible.
japod@35
   141
     * 
japod@35
   142
     * @param from the currency to convert from.
japod@35
   143
     * @param to the currency to convert to.
japod@35
   144
     * @return true if the conversion is possible.
japod@35
   145
     */
japod@35
   146
    public boolean canConvert(final Currency from, final Currency to)
japod@35
   147
    {
japod@35
   148
        return ((from.equals(currencyA) || from.equals(currencyB)) &&
japod@35
   149
                (to.equals(currencyA)   || to.equals(currencyB)));
japod@35
   150
    }
japod@35
   151
japod@35
   152
    /**
japod@35
   153
     * Get the currencies that the convertor supports.
japod@35
   154
     * 
japod@35
   155
     * @return the supported currencies.
japod@35
   156
     */
japod@35
   157
    public Set<Currency> getCurrencies()
japod@35
   158
    {
japod@35
   159
        final Set<Currency> currencies;
japod@35
   160
        
japod@35
   161
        currencies = new HashSet<Currency>();
japod@35
   162
        currencies.add(currencyA);
japod@35
   163
        currencies.add(currencyB);
japod@35
   164
japod@35
   165
        return (Collections.unmodifiableSet(currencies));
japod@35
   166
    }
japod@35
   167
japod@35
   168
    /**
japod@35
   169
     * Get the conversion rate between two currencies.
japod@35
   170
     * 
japod@35
   171
     * @param from the currency to convert from.
japod@35
   172
     * @param to the currency to convert to.
japod@35
   173
     * @return the conversion rate between the two currencies.
japod@35
   174
     * @throws InvalidConversionException if canConvert would return false.
japod@35
   175
     */
japod@35
   176
    public BigDecimal getConversionRate(final Currency from, 
japod@35
   177
                                        final Currency to)
japod@35
   178
        throws InvalidConversionException
japod@35
   179
    {
japod@35
   180
        final BigDecimal rate;
japod@35
   181
japod@17
   182
        if(from.equals(to))
japod@17
   183
        {
japod@35
   184
            rate = BigDecimal.ONE;
japod@17
   185
        }
japod@35
   186
        else 
japod@17
   187
        {
japod@17
   188
            final BigDecimal rateX;
japod@17
   189
            final BigDecimal rateY;
japod@17
   190
            final BigDecimal temp;
japod@17
   191
            
japod@17
   192
            if(from.equals(currencyA))
japod@17
   193
            {
japod@17
   194
                rateX = currencyARate;
japod@17
   195
                rateY = currencyBRate;
japod@17
   196
            }
japod@17
   197
            else
japod@17
   198
            {
japod@17
   199
                rateX = currencyBRate;
japod@17
   200
                rateY = currencyARate;
japod@17
   201
            }
japod@17
   202
japod@35
   203
            temp = BigDecimal.ONE.divide(rateX, MathContext.DECIMAL64);
japod@35
   204
            rate = temp.multiply(rateY);
japod@35
   205
        }
japod@35
   206
        
japod@35
   207
        return (rate.setScale(20, RoundingMode.HALF_EVEN));
japod@35
   208
    }
japod@35
   209
japod@35
   210
    /**
japod@35
   211
     * Check to see if two ConvertorImpls are equal.
japod@35
   212
     *
japod@35
   213
     * @param obj the object to check
japod@35
   214
     * @return if the ConvertorImpls are not the same (cuyrrencies and rates).
japod@35
   215
     */
japod@35
   216
    @Override
japod@35
   217
    public boolean equals(Object obj)
japod@35
   218
    {
japod@35
   219
        if (obj == null)
japod@35
   220
        {
japod@35
   221
            return false;
japod@35
   222
        }
japod@35
   223
        
japod@35
   224
        if (getClass() != obj.getClass())
japod@35
   225
        {
japod@35
   226
            return false;
japod@17
   227
        }
japod@17
   228
japod@35
   229
        final ConvertorImpl other = (ConvertorImpl) obj;
japod@35
   230
japod@35
   231
        // it would be nice if NetBeans could chck to see if the variable is final and guaranteed not to be null... but that
japod@35
   232
        // would likely be tricky... but in a NetBeans engineer reads that see if you can do it :-)
japod@35
   233
        if (this.currencyA != other.currencyA && (this.currencyA == null || !this.currencyA.equals(other.currencyA)))
japod@35
   234
        {
japod@35
   235
            return false;
japod@35
   236
        }
japod@35
   237
        
japod@35
   238
        if (this.currencyB != other.currencyB && (this.currencyB == null || !this.currencyB.equals(other.currencyB)))
japod@35
   239
        {
japod@35
   240
            return false;
japod@35
   241
        }
japod@35
   242
        
japod@35
   243
        if (this.currencyARate != other.currencyARate && (this.currencyARate == null || !this.currencyARate.equals(other.currencyARate)))
japod@35
   244
        {
japod@35
   245
            return false;
japod@35
   246
        }
japod@35
   247
        
japod@35
   248
        if (this.currencyBRate != other.currencyBRate && (this.currencyBRate == null || !this.currencyBRate.equals(other.currencyBRate)))
japod@35
   249
        {
japod@35
   250
            return false;
japod@35
   251
        }
japod@35
   252
japod@35
   253
        return true;
japod@35
   254
    }
japod@35
   255
japod@35
   256
    /**
japod@35
   257
     * Get the hashCode of the Convertor.
japod@35
   258
     *
japod@35
   259
     * @return the hashCode of the convertor.
japod@35
   260
     */
japod@35
   261
    @Override
japod@35
   262
    public int hashCode()
japod@35
   263
    {
japod@35
   264
        int hash = 7;
japod@35
   265
        hash = 37 * hash + (this.currencyA != null ? this.currencyA.hashCode() : 0);
japod@35
   266
        hash = 37 * hash + (this.currencyB != null ? this.currencyB.hashCode() : 0);
japod@35
   267
        hash = 37 * hash + (this.currencyARate != null ? this.currencyARate.hashCode() : 0);
japod@35
   268
        hash = 37 * hash + (this.currencyBRate != null ? this.currencyBRate.hashCode() : 0);
japod@35
   269
        return hash;
japod@35
   270
    }
japod@35
   271
japod@35
   272
    /**
japod@35
   273
     * Get the currencyCode of both currencies.
japod@35
   274
     *
japod@35
   275
     * @return the currency codes of both currencies.
japod@35
   276
     */
japod@35
   277
    @Override
japod@35
   278
    public String toString()
japod@35
   279
    {
japod@35
   280
        return (currencyA.getCurrencyCode() + " to " + currencyB.getCurrencyCode());
japod@17
   281
    }
japod@17
   282
}