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