1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/ConverterImpl.java Sat Oct 11 23:38:46 2008 +0200
1.3 @@ -0,0 +1,304 @@
1.4 +package org.apidesign.apifest08.currency;
1.5 +
1.6 +
1.7 +import java.math.BigDecimal;
1.8 +import java.math.MathContext;
1.9 +import java.math.RoundingMode;
1.10 +import java.util.Collections;
1.11 +import java.util.Currency;
1.12 +import java.util.HashSet;
1.13 +import java.util.Set;
1.14 +
1.15 +
1.16 +/**
1.17 + * Convert between two currencies.
1.18 + *
1.19 + * @author D'Arcy Smith
1.20 + * @version 1.0
1.21 + */
1.22 +final class ConvertorImpl
1.23 + implements Convertor
1.24 +{
1.25 + /**
1.26 + * The currency to convert from.
1.27 + */
1.28 + private final Currency currencyA;
1.29 +
1.30 + /**
1.31 + * The currency to convert to.
1.32 + */
1.33 + private final Currency currencyB;
1.34 +
1.35 + /**
1.36 + * The echange rate between a and b.
1.37 + */
1.38 + private final BigDecimal currencyARate;
1.39 +
1.40 + /**
1.41 + * The echange rate between b and a.
1.42 + */
1.43 + private final BigDecimal currencyBRate;
1.44 +
1.45 + /**
1.46 + * Constructs a convertor with the specified currencies.
1.47 + *
1.48 + * @param a the currency to convert from.
1.49 + * @param aRate the exchage rage between from and to.
1.50 + * @param b the currency to convert to.
1.51 + * @param bRate the exchage rage between to and from.
1.52 + * @throws IllegalArgumentException if either any of the arguments are null or if either rate <= 0.
1.53 + */
1.54 + public ConvertorImpl(final Currency a,
1.55 + final BigDecimal aRate,
1.56 + final Currency b,
1.57 + final BigDecimal bRate)
1.58 + {
1.59 + if(a == null)
1.60 + {
1.61 + throw new IllegalArgumentException("a cannot be null");
1.62 + }
1.63 +
1.64 + if(b == null)
1.65 + {
1.66 + throw new IllegalArgumentException("b cannot be null");
1.67 + }
1.68 +
1.69 + if(aRate == null)
1.70 + {
1.71 + throw new IllegalArgumentException("aRate cannot be null");
1.72 + }
1.73 +
1.74 + if(bRate == null)
1.75 + {
1.76 + throw new IllegalArgumentException("bRate cannot be null");
1.77 + }
1.78 +
1.79 + if(aRate.compareTo(BigDecimal.ZERO) <= 0)
1.80 + {
1.81 + throw new IllegalArgumentException("aRate must be > 0, was: " + aRate);
1.82 + }
1.83 +
1.84 + if(bRate.compareTo(BigDecimal.ZERO) <= 0)
1.85 + {
1.86 + throw new IllegalArgumentException("bRate must be > 0, was: " + bRate);
1.87 + }
1.88 +
1.89 + currencyA = a;
1.90 + currencyB = b;
1.91 + currencyARate = aRate;
1.92 + currencyBRate = bRate;
1.93 + }
1.94 +
1.95 + /**
1.96 + * Convert an amount from one currency to another.
1.97 + *
1.98 + * @param from the currency to convert from.
1.99 + * @param to the currency to convert to.
1.100 + * @param amount the amount to convert.
1.101 + * @return the converted amount.
1.102 + * @throws IllegalArgumentException if any of the arguments are null.
1.103 + * @throws InvalidConversionException if either from or to are not equal to the currencies passed to the constructor.
1.104 + */
1.105 + public BigDecimal convert(final Currency from,
1.106 + final Currency to,
1.107 + final BigDecimal amount)
1.108 + throws InvalidConversionException
1.109 + {
1.110 + final BigDecimal result;
1.111 +
1.112 + if(amount == null)
1.113 + {
1.114 + throw new IllegalArgumentException("amount cannot be null");
1.115 + }
1.116 +
1.117 + if(from == null)
1.118 + {
1.119 + throw new IllegalArgumentException("from cannot be null");
1.120 + }
1.121 +
1.122 + if(to == null)
1.123 + {
1.124 + throw new IllegalArgumentException("to cannot be null");
1.125 + }
1.126 +
1.127 + if(!(from.equals(currencyA)) && (!(from.equals(currencyB))))
1.128 + {
1.129 + throw new InvalidConversionException("cannot convert from: " + from.getCurrencyCode(), from, currencyA, currencyB);
1.130 + }
1.131 +
1.132 + if(!(to.equals(currencyA)) && (!(to.equals(currencyB))))
1.133 + {
1.134 + throw new InvalidConversionException("cannot convert to: " + to.getCurrencyCode(), to, currencyA, currencyB);
1.135 + }
1.136 +
1.137 + result = amount.multiply(getConversionRate(from, to));
1.138 +
1.139 + return (result.setScale(2, RoundingMode.HALF_DOWN));
1.140 + }
1.141 +
1.142 + /**
1.143 + * Check to see if converting between the two currencies is possible.
1.144 + *
1.145 + * @param from the currency to convert from.
1.146 + * @param to the currency to convert to.
1.147 + * @return true if the conversion is possible.
1.148 + * @throws IllegalArgumentException if either from or to are null.
1.149 + */
1.150 + public boolean canConvert(final Currency from, final Currency to)
1.151 + {
1.152 + if(from == null)
1.153 + {
1.154 + throw new IllegalArgumentException("from cannot be null");
1.155 + }
1.156 +
1.157 + if(to == null)
1.158 + {
1.159 + throw new IllegalArgumentException("to cannot be null");
1.160 + }
1.161 +
1.162 + return ((from.equals(currencyA) || from.equals(currencyB)) &&
1.163 + (to.equals(currencyA) || to.equals(currencyB)));
1.164 + }
1.165 +
1.166 + /**
1.167 + * Get the currencies that the convertor supports.
1.168 + *
1.169 + * @return the supported currencies.
1.170 + */
1.171 + public Set<Currency> getCurrencies()
1.172 + {
1.173 + final Set<Currency> currencies;
1.174 +
1.175 + currencies = new HashSet<Currency>();
1.176 + currencies.add(currencyA);
1.177 + currencies.add(currencyB);
1.178 +
1.179 + return (Collections.unmodifiableSet(currencies));
1.180 + }
1.181 +
1.182 + /**
1.183 + * Get the conversion rate between two currencies.
1.184 + *
1.185 + * @param from the currency to convert from.
1.186 + * @param to the currency to convert to.
1.187 + * @return the conversion rate between the two currencies.
1.188 + * @throws InvalidConversionException if canConvert would return false.
1.189 + * @throws IllegalArgumentException if either from or to are null.
1.190 + */
1.191 + public BigDecimal getConversionRate(final Currency from,
1.192 + final Currency to)
1.193 + throws InvalidConversionException
1.194 + {
1.195 + final BigDecimal rate;
1.196 +
1.197 + if(from == null)
1.198 + {
1.199 + throw new IllegalArgumentException("from cannot be null");
1.200 + }
1.201 +
1.202 + if(to == null)
1.203 + {
1.204 + throw new IllegalArgumentException("to cannot be null");
1.205 + }
1.206 +
1.207 + if(from.equals(to))
1.208 + {
1.209 + rate = BigDecimal.ONE;
1.210 + }
1.211 + else
1.212 + {
1.213 + final BigDecimal rateX;
1.214 + final BigDecimal rateY;
1.215 + final BigDecimal temp;
1.216 +
1.217 + if(from.equals(currencyA))
1.218 + {
1.219 + rateX = currencyARate;
1.220 + rateY = currencyBRate;
1.221 + }
1.222 + else
1.223 + {
1.224 + rateX = currencyBRate;
1.225 + rateY = currencyARate;
1.226 + }
1.227 +
1.228 + temp = BigDecimal.ONE.divide(rateX, MathContext.DECIMAL64);
1.229 + rate = temp.multiply(rateY);
1.230 + }
1.231 +
1.232 + return (rate.setScale(20, RoundingMode.HALF_EVEN));
1.233 + }
1.234 +
1.235 + /**
1.236 + * Check to see if two ConvertorImpls are equal.
1.237 + *
1.238 + * @param obj the object to check
1.239 + * @return if the ConvertorImpls are not the same (cuyrrencies and rates).
1.240 + */
1.241 + @Override
1.242 + public boolean equals(Object obj)
1.243 + {
1.244 + if (obj == null)
1.245 + {
1.246 + return false;
1.247 + }
1.248 +
1.249 + if (getClass() != obj.getClass())
1.250 + {
1.251 + return false;
1.252 + }
1.253 +
1.254 + final ConvertorImpl other = (ConvertorImpl) obj;
1.255 +
1.256 + // it would be nice if NetBeans could chck to see if the variable is final and guaranteed not to be null... but that
1.257 + // would likely be tricky... but in a NetBeans engineer reads that see if you can do it :-)
1.258 + if (this.currencyA != other.currencyA && (this.currencyA == null || !this.currencyA.equals(other.currencyA)))
1.259 + {
1.260 + return false;
1.261 + }
1.262 +
1.263 + if (this.currencyB != other.currencyB && (this.currencyB == null || !this.currencyB.equals(other.currencyB)))
1.264 + {
1.265 + return false;
1.266 + }
1.267 +
1.268 + if (this.currencyARate != other.currencyARate && (this.currencyARate == null || !this.currencyARate.equals(other.currencyARate)))
1.269 + {
1.270 + return false;
1.271 + }
1.272 +
1.273 + if (this.currencyBRate != other.currencyBRate && (this.currencyBRate == null || !this.currencyBRate.equals(other.currencyBRate)))
1.274 + {
1.275 + return false;
1.276 + }
1.277 +
1.278 + return true;
1.279 + }
1.280 +
1.281 + /**
1.282 + * Get the hashCode of the Convertor.
1.283 + *
1.284 + * @return the hashCode of the convertor.
1.285 + */
1.286 + @Override
1.287 + public int hashCode()
1.288 + {
1.289 + int hash = 7;
1.290 + hash = 37 * hash + (this.currencyA != null ? this.currencyA.hashCode() : 0);
1.291 + hash = 37 * hash + (this.currencyB != null ? this.currencyB.hashCode() : 0);
1.292 + hash = 37 * hash + (this.currencyARate != null ? this.currencyARate.hashCode() : 0);
1.293 + hash = 37 * hash + (this.currencyBRate != null ? this.currencyBRate.hashCode() : 0);
1.294 + return hash;
1.295 + }
1.296 +
1.297 + /**
1.298 + * Get the currencyCode of both currencies.
1.299 + *
1.300 + * @return the currency codes of both currencies.
1.301 + */
1.302 + @Override
1.303 + public String toString()
1.304 + {
1.305 + return (currencyA.getCurrencyCode() + " to " + currencyB.getCurrencyCode());
1.306 + }
1.307 +}