adding solution14 for task2
authorjapod@localhost
Wed, 08 Oct 2008 12:51:52 +0200
changeset 49de033c457bed
parent 48 79a576394dd7
child 50 03c5c5dc94e7
adding solution14 for task2
task2/solution14/src/org/apidesign/apifest08/currency/Convertor.java
task2/solution14/src/org/apidesign/apifest08/currency/ConvertorFactory.java
task2/solution14/src/org/apidesign/apifest08/currency/CurrencyRate.java
task2/solution14/src/org/apidesign/apifest08/currency/CurrencyRateFactory.java
task2/solution14/src/org/apidesign/apifest08/currency/CurrencyRateImpl.java
task2/solution14/src/org/apidesign/apifest08/currency/Pair.java
task2/solution14/test/org/apidesign/apifest08/test/Task2Test.java
     1.1 --- a/task2/solution14/src/org/apidesign/apifest08/currency/Convertor.java	Tue Oct 07 11:19:36 2008 +0200
     1.2 +++ b/task2/solution14/src/org/apidesign/apifest08/currency/Convertor.java	Wed Oct 08 12:51:52 2008 +0200
     1.3 @@ -1,5 +1,12 @@
     1.4  package org.apidesign.apifest08.currency;
     1.5  
     1.6 +import java.util.ArrayList;
     1.7 +import java.util.Collection;
     1.8 +import java.util.Collections;
     1.9 +import java.util.HashSet;
    1.10 +import java.util.List;
    1.11 +import java.util.Set;
    1.12 +
    1.13  /** This is the skeleton class for your API. You need to make it public, so
    1.14   * it is accessible to your client code (currently in Task1Test.java) file.
    1.15   * <p>
    1.16 @@ -8,12 +15,21 @@
    1.17   * to other packages.
    1.18   */
    1.19  public final class Convertor {
    1.20 -    private String currency1;
    1.21 -    private String currency2;
    1.22 -    private Rate rate;
    1.23 -    
    1.24 +    //version 1 fields
    1.25 +
    1.26 +    private String currency1 = null;
    1.27 +    private String currency2 = null;
    1.28 +    private Rate rate = null;
    1.29 +
    1.30 +    //version 2 field
    1.31 +    private List<CurrencyRate> currencyRates = null;
    1.32 +
    1.33 +    //version - for compatible mode
    1.34 +    private int instanceVersion = 0; //compatible mode because of problem with empty currency and CZE -> CZE (1:2) rate
    1.35 +
    1.36      Convertor(String currency1, String currency2, Rate rate) {
    1.37 -        if ((currency1 == null)||(currency2 == null)||(rate == null)) {
    1.38 +        instanceVersion = 1;
    1.39 +        if ((currency1 == null) || (currency2 == null) || (rate == null)) {
    1.40              throw new IllegalArgumentException("All arguments have to be non-null.");
    1.41          }
    1.42          this.currency1 = currency1;
    1.43 @@ -21,68 +37,165 @@
    1.44          this.rate = rate;
    1.45      }
    1.46  
    1.47 +    Convertor(final CurrencyRate ... currencyRate) {
    1.48 +        instanceVersion = 2;
    1.49 +
    1.50 +        if (currencyRate == null) {
    1.51 +            throw new IllegalArgumentException("Parameter cannot be null.");
    1.52 +        }
    1.53 +        if (currencyRate.length == 0) {
    1.54 +            throw new IllegalArgumentException("CurrencyRates cannot be empty.");
    1.55 +        }
    1.56 +        Set<Pair<String,String>> currencies = new HashSet<Pair<String,String>>();
    1.57 +        List<CurrencyRate> curRates = new ArrayList<CurrencyRate>();
    1.58 +        for (int i = 0; i < currencyRate.length; i++) {
    1.59 +            CurrencyRate curRat = currencyRate[i];
    1.60 +            if (curRat == null) {
    1.61 +                throw new IllegalArgumentException("Parameter cannot be null.");
    1.62 +            }
    1.63 +            //check that currencyRate is not defined twice
    1.64 +            Pair<String, String> curPair= new Pair<String, String>(curRat.getCurrency1(), curRat.getCurrency2());
    1.65 +            if (currencies.contains(curPair)) {
    1.66 +                throw new IllegalArgumentException("Pair of currencies in a currency rate cannot be defined twice");
    1.67 +            }
    1.68 +            currencies.add(curPair);
    1.69 +            
    1.70 +            curRates.add(curRat);
    1.71 +        }
    1.72 +        this.currencyRates = Collections.unmodifiableList(curRates);
    1.73 +    }
    1.74 +
    1.75      public double convert(String fromCurrency, String toCurrency, int amount) {
    1.76 -        if ((fromCurrency == null)||(toCurrency == null)) {
    1.77 -            throw new IllegalArgumentException("All arguments have to be non-null.");            
    1.78 -        }
    1.79 -        
    1.80 -        if (currency1.equals(fromCurrency) && currency2.equals(toCurrency)) {
    1.81 -            return rate.convertAtoB(amount);
    1.82 -        } else if (currency2.equals(fromCurrency) && currency1.equals(toCurrency)) {
    1.83 -            return rate.convertBtoA(amount);
    1.84 -        } else {
    1.85 -            throw new IllegalArgumentException("Convertor " + this.toString() + 
    1.86 -                    " cannot work with currencies " + fromCurrency + " and " + toCurrency + ".");
    1.87 +        if (instanceVersion == 1) {
    1.88 +            if ((fromCurrency == null) || (toCurrency == null)) {
    1.89 +                throw new IllegalArgumentException("All arguments have to be non-null.");
    1.90 +            }
    1.91 +
    1.92 +            if (currency1.equals(fromCurrency) && currency2.equals(toCurrency)) {
    1.93 +                return rate.convertAtoB(amount);
    1.94 +            } else if (currency2.equals(fromCurrency) && currency1.equals(toCurrency)) {
    1.95 +                return rate.convertBtoA(amount);
    1.96 +            } else {
    1.97 +                throw new IllegalArgumentException("Convertor " + this.toString() +
    1.98 +                        " cannot work with currencies " + fromCurrency + " and " + toCurrency + ".");
    1.99 +            }
   1.100 +        } else { //instanceVersion == 2
   1.101 +            //find suitable convertor
   1.102 +            for (CurrencyRate curRate : currencyRates) {
   1.103 +                if ((curRate.getCurrency1().equals(fromCurrency))&& 
   1.104 +                        (curRate.getCurrency2().equals(toCurrency))) 
   1.105 +                {
   1.106 +                    return curRate.getRate().convertAtoB(amount);
   1.107 +                }
   1.108 +            }
   1.109 +            //suitable convertor not found, try to find inverse convertor
   1.110 +            for (CurrencyRate curRate : currencyRates) {
   1.111 +                if ((curRate.getCurrency2().equals(fromCurrency))&&
   1.112 +                        (curRate.getCurrency1().equals(toCurrency)))
   1.113 +                {
   1.114 +                    return curRate.getRate().convertBtoA(amount);
   1.115 +                }
   1.116 +            }            
   1.117 +            //even inverse convertor not found
   1.118 +            throw new IllegalArgumentException("Cannot work with selected currencies.");
   1.119          }
   1.120      }
   1.121  
   1.122      public double convert(String fromCurrency, String toCurrency, double amount) {
   1.123 -        if ((fromCurrency == null)||(toCurrency == null)) {
   1.124 -            throw new IllegalArgumentException("All arguments have to be non-null.");            
   1.125 -        }
   1.126 -        
   1.127 -        if (currency1.equals(fromCurrency) && currency2.equals(toCurrency)) {
   1.128 -            return rate.convertAtoB(amount);
   1.129 -        } else if (currency2.equals(fromCurrency) && currency1.equals(toCurrency)) {
   1.130 -            return rate.convertBtoA(amount);
   1.131 -        } else {
   1.132 -            throw new IllegalArgumentException("Convertor " + this.toString() + 
   1.133 -                    " cannot work with currencies " + fromCurrency + " and " + toCurrency + ".");
   1.134 +        if (instanceVersion == 1) {
   1.135 +            if ((fromCurrency == null) || (toCurrency == null)) {
   1.136 +                throw new IllegalArgumentException("All arguments have to be non-null.");
   1.137 +            }
   1.138 +
   1.139 +            if (currency1.equals(fromCurrency) && currency2.equals(toCurrency)) {
   1.140 +                return rate.convertAtoB(amount);
   1.141 +            } else if (currency2.equals(fromCurrency) && currency1.equals(toCurrency)) {
   1.142 +                return rate.convertBtoA(amount);
   1.143 +            } else {
   1.144 +                throw new IllegalArgumentException("Convertor " + this.toString() +
   1.145 +                        " cannot work with currencies " + fromCurrency + " and " + toCurrency + ".");
   1.146 +            }
   1.147 +        } else { //instanceVersion == 2
   1.148 +            //find suitable convertor
   1.149 +            for (CurrencyRate curRate : currencyRates) {
   1.150 +                if ((curRate.getCurrency1().equals(fromCurrency))&& 
   1.151 +                        (curRate.getCurrency2().equals(toCurrency))) 
   1.152 +                {
   1.153 +                    return curRate.getRate().convertAtoB(amount);
   1.154 +                }
   1.155 +            }
   1.156 +            //suitable convertor not found, try to find inverse convertor
   1.157 +            for (CurrencyRate curRate : currencyRates) {
   1.158 +                if ((curRate.getCurrency2().equals(fromCurrency))&&
   1.159 +                        (curRate.getCurrency1().equals(toCurrency)))
   1.160 +                {
   1.161 +                    return curRate.getRate().convertBtoA(amount);
   1.162 +                }
   1.163 +            }            
   1.164 +            //even inverse convertor not found
   1.165 +            throw new IllegalArgumentException("Cannot work with selected currencies.");
   1.166          }
   1.167      }
   1.168 -        
   1.169 +
   1.170 +    /**
   1.171 +     * Returns currency rates. If instantiated with constructor from vesion 1
   1.172 +     * it creates new collection with one CurrencyRate.
   1.173 +     * Note, it can cause exception because of empty currencies or same currencies.
   1.174 +     */
   1.175 +    public Collection<CurrencyRate> getCurrencyRates() {
   1.176 +        if (instanceVersion == 1) {
   1.177 +            List<CurrencyRate> ret = new ArrayList<CurrencyRate>();
   1.178 +            ret.add(new CurrencyRateImpl(currency1, currency2, rate));
   1.179 +            return Collections.unmodifiableCollection(ret);
   1.180 +        } else { //instanceVersion == 2
   1.181 +            return currencyRates;
   1.182 +        }
   1.183 +    }
   1.184 +
   1.185      @Override
   1.186      public String toString() {
   1.187 -        return currency1+currency2;
   1.188 +        if (instanceVersion == 1) {
   1.189 +            return currency1 + currency2;
   1.190 +        } else { //instanceVersion == 2
   1.191 +            return super.toString(); //better be compatible in future :-)
   1.192 +        }
   1.193      }
   1.194 -    
   1.195 +
   1.196      @Override
   1.197      public boolean equals(Object obj) {
   1.198 -        if (obj == null) {
   1.199 -            return false;
   1.200 +        if (instanceVersion == 1) {
   1.201 +            if (obj == null) {
   1.202 +                return false;
   1.203 +            }
   1.204 +            if (getClass() != obj.getClass()) {
   1.205 +                return false;
   1.206 +            }
   1.207 +            final Convertor other = (Convertor) obj;
   1.208 +            if (this.currency1 != other.currency1 && (this.currency1 == null || !this.currency1.equals(other.currency1))) {
   1.209 +                return false;
   1.210 +            }
   1.211 +            if (this.currency2 != other.currency2 && (this.currency2 == null || !this.currency2.equals(other.currency2))) {
   1.212 +                return false;
   1.213 +            }
   1.214 +            if (this.rate != other.rate && (this.rate == null || !this.rate.equals(other.rate))) {
   1.215 +                return false;
   1.216 +            }
   1.217 +            return true;
   1.218 +        } else { //instanceVersion == 2
   1.219 +            return super.equals(obj); //better be compatible in future :-)
   1.220          }
   1.221 -        if (getClass() != obj.getClass()) {
   1.222 -            return false;
   1.223 -        }
   1.224 -        final Convertor other = (Convertor) obj;
   1.225 -        if (this.currency1 != other.currency1 && (this.currency1 == null || !this.currency1.equals(other.currency1))) {
   1.226 -            return false;
   1.227 -        }
   1.228 -        if (this.currency2 != other.currency2 && (this.currency2 == null || !this.currency2.equals(other.currency2))) {
   1.229 -            return false;
   1.230 -        }
   1.231 -        if (this.rate != other.rate && (this.rate == null || !this.rate.equals(other.rate))) {
   1.232 -            return false;
   1.233 -        }
   1.234 -        return true;
   1.235      }
   1.236  
   1.237      @Override
   1.238      public int hashCode() {
   1.239 -        int hash = 5;
   1.240 -        hash = 67 * hash + (this.currency1 != null ? this.currency1.hashCode() : 0);
   1.241 -        hash = 67 * hash + (this.currency2 != null ? this.currency2.hashCode() : 0);
   1.242 -        hash = 67 * hash + (this.rate != null ? this.rate.hashCode() : 0);
   1.243 +        if (instanceVersion == 1) {
   1.244 +            int hash = 5;
   1.245 +            hash = 67 * hash + (this.currency1 != null ? this.currency1.hashCode() : 0);
   1.246 +            hash = 67 * hash + (this.currency2 != null ? this.currency2.hashCode() : 0);
   1.247 +            hash = 67 * hash + (this.rate != null ? this.rate.hashCode() : 0);
   1.248          return hash;
   1.249 +        } else { //instanceVersion == 2
   1.250 +            return super.hashCode(); //better be compatible in future :-)
   1.251 +        }
   1.252      }
   1.253  }
     2.1 --- a/task2/solution14/src/org/apidesign/apifest08/currency/ConvertorFactory.java	Tue Oct 07 11:19:36 2008 +0200
     2.2 +++ b/task2/solution14/src/org/apidesign/apifest08/currency/ConvertorFactory.java	Wed Oct 08 12:51:52 2008 +0200
     2.3 @@ -1,12 +1,15 @@
     2.4  
     2.5  package org.apidesign.apifest08.currency;
     2.6  
     2.7 +import java.util.ArrayList;
     2.8 +import java.util.List;
     2.9 +
    2.10  public final class ConvertorFactory {
    2.11  
    2.12      //Singleton
    2.13      private static ConvertorFactory thisFactory = new ConvertorFactory();    
    2.14      private ConvertorFactory() {};    
    2.15 -    public static ConvertorFactory newInstance() {
    2.16 +    public static ConvertorFactory newInstance() { //ehm, mistake - it should be named getInstance
    2.17          return thisFactory;
    2.18      }        
    2.19      
    2.20 @@ -25,4 +28,39 @@
    2.21      public Convertor createConvertor(String currency1, String currency2, double rate) {
    2.22          return new Convertor(currency1, currency2, new Rate(rate));
    2.23      }
    2.24 +
    2.25 +    public Convertor createConvertor(CurrencyRate currencyRate) {
    2.26 +        return new Convertor(currencyRate);
    2.27 +    }
    2.28 +
    2.29 +    public Convertor createConvertor(CurrencyRate ... currencyRates) {
    2.30 +        return new Convertor(currencyRates);
    2.31 +    }
    2.32 +
    2.33 +    public Convertor mergeConvertors(Convertor ... convertors) {
    2.34 +        if (convertors == null) {
    2.35 +            throw new IllegalArgumentException("Parameter cannot be null.");
    2.36 +        }
    2.37 +        if (convertors.length == 0) {
    2.38 +            throw new IllegalArgumentException("Convertors cannot be empty.");
    2.39 +        }
    2.40 +        List<CurrencyRate> currRates = new ArrayList<CurrencyRate>();
    2.41 +        List<Pair<String,String>> currPairs = new ArrayList<Pair<String,String>>();
    2.42 +        for (Convertor convertor : convertors) {
    2.43 +            if (convertor == null) {
    2.44 +                throw new IllegalArgumentException("Parameter cannot be null.");                
    2.45 +            }
    2.46 +            for (CurrencyRate currRate : convertor.getCurrencyRates()) {
    2.47 +                Pair<String,String> currPair = new Pair<String,String>(currRate.getCurrency1(), currRate.getCurrency2());
    2.48 +                if (currPairs.contains(currPair)) {
    2.49 +                    throw new IllegalArgumentException("Cannot merge - convertors contain same currency rates.");
    2.50 +                }
    2.51 +                currPairs.add(currPair);
    2.52 +                currRates.add(currRate);
    2.53 +            }
    2.54 +        }
    2.55 +        
    2.56 +        return new Convertor(currRates.toArray(new CurrencyRate[0]));
    2.57 +    }
    2.58 +
    2.59  }
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/task2/solution14/src/org/apidesign/apifest08/currency/CurrencyRate.java	Wed Oct 08 12:51:52 2008 +0200
     3.3 @@ -0,0 +1,8 @@
     3.4 +package org.apidesign.apifest08.currency;
     3.5 +
     3.6 +
     3.7 +public interface CurrencyRate {
     3.8 +    public String getCurrency1();
     3.9 +    public String getCurrency2();
    3.10 +    public Rate getRate();
    3.11 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/task2/solution14/src/org/apidesign/apifest08/currency/CurrencyRateFactory.java	Wed Oct 08 12:51:52 2008 +0200
     4.3 @@ -0,0 +1,25 @@
     4.4 +package org.apidesign.apifest08.currency;
     4.5 +
     4.6 +
     4.7 +public final class CurrencyRateFactory {
     4.8 +
     4.9 +    //Singleton
    4.10 +    private static CurrencyRateFactory thisFactory = new CurrencyRateFactory();
    4.11 +    private CurrencyRateFactory() {};
    4.12 +    public static CurrencyRateFactory getInstance() {
    4.13 +        return thisFactory;
    4.14 +    }
    4.15 +
    4.16 +    public CurrencyRate createCurrencyRate(final String currency1, final String currency2, final Rate rate) {
    4.17 +        return new CurrencyRateImpl(currency1, currency2, rate);
    4.18 +    }
    4.19 +    
    4.20 +    public CurrencyRate createCurrencyRate(final String currency1, final String currency2, int amount1, int amount2) {
    4.21 +        return new CurrencyRateImpl(currency1, currency2, new Rate(amount1, amount2));
    4.22 +    }
    4.23 +
    4.24 +    public CurrencyRate createCurrencyRate(final String currency1, final String currency2, double amount1, double amount2) {
    4.25 +        return new CurrencyRateImpl(currency1, currency2, new Rate(amount1, amount2));
    4.26 +    }
    4.27 +    
    4.28 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/task2/solution14/src/org/apidesign/apifest08/currency/CurrencyRateImpl.java	Wed Oct 08 12:51:52 2008 +0200
     5.3 @@ -0,0 +1,37 @@
     5.4 +
     5.5 +package org.apidesign.apifest08.currency;
     5.6 +
     5.7 +public final class CurrencyRateImpl implements CurrencyRate {
     5.8 +    private String currency1;
     5.9 +    private String currency2;
    5.10 +    private Rate rate;
    5.11 +    
    5.12 +    CurrencyRateImpl(final String currency1, final String currency2, final Rate rate) {
    5.13 +        if ((currency1 == null)||(currency2 == null) || (rate == null)) {
    5.14 +            throw new IllegalArgumentException("Argument cannot be null.");
    5.15 +        }
    5.16 +        if ("".equals(currency1) || "".equals(currency2)) {
    5.17 +            throw new IllegalArgumentException("Name of currency cannot be empty string");
    5.18 +        }
    5.19 +        if (currency1.equals(currency2)) {
    5.20 +            throw new IllegalArgumentException("Currencies in rate cannot be the same");
    5.21 +        }
    5.22 +       
    5.23 +        this.currency1 = currency1;
    5.24 +        this.currency2 = currency2;
    5.25 +        this.rate = rate;
    5.26 +    }
    5.27 +
    5.28 +    public String getCurrency1() {
    5.29 +        return currency1;
    5.30 +    }
    5.31 +            
    5.32 +    public String getCurrency2() {
    5.33 +        return currency2;
    5.34 +    }
    5.35 +
    5.36 +    public Rate getRate(){
    5.37 +        return rate;
    5.38 +    }
    5.39 +
    5.40 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/task2/solution14/src/org/apidesign/apifest08/currency/Pair.java	Wed Oct 08 12:51:52 2008 +0200
     6.3 @@ -0,0 +1,53 @@
     6.4 +
     6.5 +package org.apidesign.apifest08.currency;
     6.6 +
     6.7 +
     6.8 +public final class Pair<A,B> {
     6.9 +
    6.10 +    private final A first;
    6.11 +    private final B second;
    6.12 +
    6.13 +    public Pair(A first, B second) {
    6.14 +	this.first = first;
    6.15 +	this.second = second;
    6.16 +    }
    6.17 +
    6.18 +    public A getFirst() { return first; }
    6.19 +    public B getSecond() { return second; }
    6.20 +
    6.21 +    @Override
    6.22 +    public String toString() {
    6.23 +        return "(" + first + ", " + second + ")";
    6.24 +    }
    6.25 +
    6.26 +    private static boolean equals(Object x, Object y) {
    6.27 +	return (x == null && y == null) || (x != null && x.equals(y));
    6.28 +    }
    6.29 +
    6.30 +    @Override
    6.31 +    public int hashCode() {
    6.32 +        int hash = 5;
    6.33 +        hash = 59 * hash + (this.first != null ? this.first.hashCode() : 0);
    6.34 +        hash = 59 * hash + (this.second != null ? this.second.hashCode() : 0);
    6.35 +        return hash;
    6.36 +    }
    6.37 +
    6.38 +    @Override
    6.39 +    public boolean equals(Object obj) {
    6.40 +        if (obj == null) {
    6.41 +            return false;
    6.42 +        }
    6.43 +        if (getClass() != obj.getClass()) {
    6.44 +            return false;
    6.45 +        }
    6.46 +        final Pair<A, B> other = (Pair<A, B>) obj;
    6.47 +        if (this.first != other.first && (this.first == null || !this.first.equals(other.first))) {
    6.48 +            return false;
    6.49 +        }
    6.50 +        if (this.second != other.second && (this.second == null || !this.second.equals(other.second))) {
    6.51 +            return false;
    6.52 +        }
    6.53 +        return true;
    6.54 +    }
    6.55 +
    6.56 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/task2/solution14/test/org/apidesign/apifest08/test/Task2Test.java	Wed Oct 08 12:51:52 2008 +0200
     7.3 @@ -0,0 +1,169 @@
     7.4 +package org.apidesign.apifest08.test;
     7.5 +
     7.6 +import junit.framework.TestCase;
     7.7 +import org.apidesign.apifest08.currency.Convertor;
     7.8 +import org.apidesign.apifest08.currency.ConvertorFactory;
     7.9 +import org.apidesign.apifest08.currency.CurrencyRate;
    7.10 +import org.apidesign.apifest08.currency.CurrencyRateFactory;
    7.11 +
    7.12 +/** There are many currencies around the world and many banks manipulate
    7.13 + * with more than one or two at the same time. As banks are usually the
    7.14 + * best paying clients, which is true even in case of your Convertor API,
    7.15 + * it is reasonable to listen to their requests.
    7.16 + * <p>
    7.17 + * The quest for today is to enhance your existing convertor API to hold
    7.18 + * information about many currencies and allow conversions between any of them.
    7.19 + * Also, as conversion rates for diferent currencies usually arise from various
    7.20 + * bank departments, there is another important need. There is a need to
    7.21 + * compose two convertors into one by merging all the information about
    7.22 + * currencies they know about.
    7.23 + */
    7.24 +public class Task2Test extends TestCase {
    7.25 +    public Task2Test(String testName) {
    7.26 +        super(testName);
    7.27 +    }
    7.28 +
    7.29 +    @Override
    7.30 +    protected void setUp() throws Exception {
    7.31 +    }
    7.32 +
    7.33 +    @Override
    7.34 +    protected void tearDown() throws Exception {
    7.35 +    }
    7.36 +
    7.37 +    // As in Task1Test, keep in mind, that there are three parts
    7.38 +    // of the whole system:
    7.39 +    // 1. there is someone who knows the current exchange rate
    7.40 +    // 2. there is someone who wants to do the conversion
    7.41 +    // 3. there is the API between 1. and 2. which allows them to communicate
    7.42 +    // 
    7.43 +    // Please backward compatibly enhance your existing API to support following
    7.44 +    // usecases:
    7.45 +    //
    7.46 +    
    7.47 +    /** Create convertor that understands two currencies, CZK and
    7.48 +     *  SKK. Make 100 SKK == 75 CZK. This is method for the group of users that
    7.49 +     *  knows the exchange rate, and needs to use the API to create objects
    7.50 +     *  with the exchange rate. Anyone shall be ready to call this method without
    7.51 +     *  any other method being called previously. The API itself shall know
    7.52 +     *  nothing about any rates, before this method is called.
    7.53 +     */
    7.54 +    public static Convertor createTripleConvertor() {
    7.55 +        // Rates: 1USD = 15CZK
    7.56 +        // Rates: 1USD = 20SKK
    7.57 +        // Rates: 75CZK = 100SKK
    7.58 +        CurrencyRate usdCzk = CurrencyRateFactory.getInstance().createCurrencyRate("USD", "CZK", 1, 15);
    7.59 +        CurrencyRate usdSkk = CurrencyRateFactory.getInstance().createCurrencyRate("USD", "SKK", 1, 20);
    7.60 +        CurrencyRate czkSkk = CurrencyRateFactory.getInstance().createCurrencyRate("CZK", "SKK", 75, 100);
    7.61 +        return ConvertorFactory.newInstance().createConvertor(usdCzk, usdSkk, czkSkk);        
    7.62 +    }
    7.63 +
    7.64 +    /** Define convertor that understands three currencies. Use it.
    7.65 +     */
    7.66 +    public void testConvertorForUSDandCZKandSKK() throws Exception {
    7.67 +        Convertor c = createTripleConvertor();
    7.68 +
    7.69 +        // convert $5 to CZK using c:
    7.70 +        // assertEquals("Result is 75 CZK");
    7.71 +        assertEquals("Result is 75 CZK", 75.0, c.convert("USD", "CZK", 5));
    7.72 +        
    7.73 +        // convert $5 to SKK using c:
    7.74 +        // assertEquals("Result is 100 SKK");
    7.75 +        assertEquals("Result is 100 SKK", 100.0, c.convert("USD", "SKK", 5));
    7.76 +
    7.77 +        // convert 200SKK to CZK using c:
    7.78 +        // assertEquals("Result is 150 CZK");
    7.79 +        assertEquals("Result is 150 CZK", 150.0, c.convert("SKK", "CZK", 200));
    7.80 +
    7.81 +        // convert 200SKK to USD using c:
    7.82 +        // assertEquals("Result is 10 USD");
    7.83 +        assertEquals("Result is 10 USD", 10.0, c.convert("SKK", "USD", 200));
    7.84 +    }
    7.85 +
    7.86 +    /** Merge all currency rates of convertor 1 with convertor 2.
    7.87 +     * Implement this using your API, preferably this method just delegates
    7.88 +     * into some API method which does the actual work, without requiring
    7.89 +     * API clients to code anything complex.
    7.90 +     */
    7.91 +    public static Convertor merge(Convertor one, Convertor two) {
    7.92 +        return ConvertorFactory.newInstance().mergeConvertors(one,two);
    7.93 +    }
    7.94 +
    7.95 +    /** Join the convertors from previous task, Task1Test and show that it
    7.96 +     * can be used to do reasonable conversions.
    7.97 +     */
    7.98 +    public void testConvertorComposition() throws Exception {
    7.99 +        Convertor c = merge(
   7.100 +            Task1Test.createCZKtoUSD(),
   7.101 +            Task1Test.createSKKtoCZK()
   7.102 +        );
   7.103 +
   7.104 +        // convert $5 to CZK using c:
   7.105 +        // assertEquals("Result is 85 CZK");
   7.106 +        assertEquals("Result is 85 CZK", 85.0, c.convert("USD", "CZK", 5));
   7.107 +
   7.108 +        // convert $8 to CZK using c:
   7.109 +        // assertEquals("Result is 136 CZK");
   7.110 +        assertEquals("Result is 136 CZK", 136.0, c.convert("USD", "CZK", 8));
   7.111 +
   7.112 +        // convert 1003CZK to USD using c:
   7.113 +        // assertEquals("Result is 59 USD");
   7.114 +        assertEquals("Result is 59 USD", 59.0, c.convert("CZK", "USD", 1003));
   7.115 +
   7.116 +        // convert 16CZK using c:
   7.117 +        // assertEquals("Result is 20 SKK");
   7.118 +        assertEquals("Result is 20 SKK", 20.0, c.convert("CZK", "SKK", 16));
   7.119 +
   7.120 +        // convert 500SKK to CZK using c:
   7.121 +        // assertEquals("Result is 400 CZK");
   7.122 +        assertEquals("Result is 400 CZK", 400.0, c.convert("SKK", "CZK", 500));
   7.123 +
   7.124 +        //test exceptions
   7.125 +        Convertor one = Task1Test.createCZKtoUSD();
   7.126 +        Convertor two = Task1Test.createSKKtoCZK();
   7.127 +        Convertor three = Task1Test.createSKKtoCZK();
   7.128 +        try {
   7.129 +            ConvertorFactory.newInstance().mergeConvertors(one,two,three);
   7.130 +            fail();
   7.131 +        } catch (IllegalArgumentException e) {
   7.132 +            //ok
   7.133 +        }
   7.134 +
   7.135 +        //test exceptions
   7.136 +        try {
   7.137 +            ConvertorFactory.newInstance().mergeConvertors(c, two);
   7.138 +            fail();
   7.139 +        } catch (IllegalArgumentException e) {
   7.140 +            //ok
   7.141 +        }
   7.142 +        
   7.143 +        //try convertors from version 1
   7.144 +        Convertor v1one = ConvertorFactory.newInstance().createConvertor("CZE", "CZE", 1, 2);        
   7.145 +        assertEquals("CZE->CZE 1:2 10 expects 20", 20.0, v1one.convert("CZE", "CZE", 10));
   7.146 +        try {
   7.147 +            ConvertorFactory.newInstance().mergeConvertors(v1one, two);
   7.148 +            fail();
   7.149 +        } catch (IllegalArgumentException e) {
   7.150 +            //ok
   7.151 +        }
   7.152 +
   7.153 +        Convertor v1two = ConvertorFactory.newInstance().createConvertor("EUR", "", 1, 2);
   7.154 +        assertEquals("EUR->'' 1:2 10 expects 20", 20.0, v1two.convert("EUR", "", 10));
   7.155 +        try {
   7.156 +            ConvertorFactory.newInstance().mergeConvertors(v1two, two);
   7.157 +            fail();
   7.158 +        } catch (IllegalArgumentException e) {
   7.159 +            //ok
   7.160 +        }
   7.161 +
   7.162 +        Convertor v1three = ConvertorFactory.newInstance().createConvertor("EUR", "", 1, 2);
   7.163 +        assertEquals("''->EUR 1:2 10 expects 5", 5.0, v1three.convert("", "EUR", 10));
   7.164 +        try {
   7.165 +            ConvertorFactory.newInstance().mergeConvertors(v1three, two);
   7.166 +            fail();
   7.167 +        } catch (IllegalArgumentException e) {
   7.168 +            //ok
   7.169 +        }
   7.170 +        
   7.171 +    }
   7.172 +}