# HG changeset patch # User japod@localhost # Date 1222775478 -7200 # Node ID a022dd2a5d3071d70e5304bea7c76ec2601ee51a # Parent b4c6b18eaf3fd6f4751e84286ebe091b72a0efc6 adding solution 14 diff -r b4c6b18eaf3f -r a022dd2a5d30 task1/solution14/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution14/build.xml Tue Sep 30 13:51:18 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r b4c6b18eaf3f -r a022dd2a5d30 task1/solution14/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution14/nbproject/build-impl.xml Tue Sep 30 13:51:18 2008 +0200 @@ -0,0 +1,642 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + diff -r b4c6b18eaf3f -r a022dd2a5d30 task1/solution14/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution14/nbproject/genfiles.properties Tue Sep 30 13:51:18 2008 +0200 @@ -0,0 +1,8 @@ +build.xml.data.CRC32=2ab820eb +build.xml.script.CRC32=58a52595 +build.xml.stylesheet.CRC32=be360661 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=1d0fd5f2 +nbproject/build-impl.xml.script.CRC32=951643da +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r b4c6b18eaf3f -r a022dd2a5d30 task1/solution14/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution14/nbproject/project.properties Tue Sep 30 13:51:18 2008 +0200 @@ -0,0 +1,68 @@ +application.title=currency +application.vendor=apidesign.org +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=default +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/currency.jar +dist.javadoc.dir=${dist.dir}/javadoc +excludes= +file.reference.junit-4.4.jar=../../libs/junit-4.4.jar +file.reference.src-apifest08=.. +includes=** +jar.compress=false +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.source=1.5 +javac.target=1.5 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${file.reference.junit-4.4.jar} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding= +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=local +jnlp.codebase.url=file:/home/jarda/src/apifest08/currency/dist +jnlp.descriptor=application +jnlp.enabled=false +jnlp.offline-allowed=false +jnlp.signed=false +meta.inf.dir=${src.dir}/META-INF +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +src.dir=src +test.src.dir=test diff -r b4c6b18eaf3f -r a022dd2a5d30 task1/solution14/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution14/nbproject/project.xml Tue Sep 30 13:51:18 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 14 + 1.6.5 + + + + + + + + + diff -r b4c6b18eaf3f -r a022dd2a5d30 task1/solution14/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution14/src/org/apidesign/apifest08/currency/Convertor.java Tue Sep 30 13:51:18 2008 +0200 @@ -0,0 +1,88 @@ +package org.apidesign.apifest08.currency; + +/** This is the skeleton class for your API. You need to make it public, so + * it is accessible to your client code (currently in Task1Test.java) file. + *

+ * Feel free to create additional classes or rename this one, just keep all + * the API and its implementation in this package. Do not spread it outside + * to other packages. + */ +public final class Convertor { + private String currency1; + private String currency2; + private Rate rate; + + Convertor(String currency1, String currency2, Rate rate) { + if ((currency1 == null)||(currency2 == null)||(rate == null)) { + throw new IllegalArgumentException("All arguments have to be non-null."); + } + this.currency1 = currency1; + this.currency2 = currency2; + this.rate = rate; + } + + public double convert(String fromCurrency, String toCurrency, int amount) { + if ((fromCurrency == null)||(toCurrency == null)) { + throw new IllegalArgumentException("All arguments have to be non-null."); + } + + if (currency1.equals(fromCurrency) && currency2.equals(toCurrency)) { + return rate.convertAtoB(amount); + } else if (currency2.equals(fromCurrency) && currency1.equals(toCurrency)) { + return rate.convertBtoA(amount); + } else { + throw new IllegalArgumentException("Convertor " + this.toString() + + " cannot work with currencies " + fromCurrency + " and " + toCurrency + "."); + } + } + + public double convert(String fromCurrency, String toCurrency, double amount) { + if ((fromCurrency == null)||(toCurrency == null)) { + throw new IllegalArgumentException("All arguments have to be non-null."); + } + + if (currency1.equals(fromCurrency) && currency2.equals(toCurrency)) { + return rate.convertAtoB(amount); + } else if (currency2.equals(fromCurrency) && currency1.equals(toCurrency)) { + return rate.convertBtoA(amount); + } else { + throw new IllegalArgumentException("Convertor " + this.toString() + + " cannot work with currencies " + fromCurrency + " and " + toCurrency + "."); + } + } + + @Override + public String toString() { + return currency1+currency2; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Convertor other = (Convertor) obj; + if (this.currency1 != other.currency1 && (this.currency1 == null || !this.currency1.equals(other.currency1))) { + return false; + } + if (this.currency2 != other.currency2 && (this.currency2 == null || !this.currency2.equals(other.currency2))) { + return false; + } + if (this.rate != other.rate && (this.rate == null || !this.rate.equals(other.rate))) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 67 * hash + (this.currency1 != null ? this.currency1.hashCode() : 0); + hash = 67 * hash + (this.currency2 != null ? this.currency2.hashCode() : 0); + hash = 67 * hash + (this.rate != null ? this.rate.hashCode() : 0); + return hash; + } +} diff -r b4c6b18eaf3f -r a022dd2a5d30 task1/solution14/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution14/src/org/apidesign/apifest08/currency/ConvertorFactory.java Tue Sep 30 13:51:18 2008 +0200 @@ -0,0 +1,28 @@ + +package org.apidesign.apifest08.currency; + +public final class ConvertorFactory { + + //Singleton + private static ConvertorFactory thisFactory = new ConvertorFactory(); + private ConvertorFactory() {}; + public static ConvertorFactory newInstance() { + return thisFactory; + } + + public Convertor createConvertor(String currency1, String currency2, Rate rate) { + return new Convertor(currency1, currency2, rate); + } + + public Convertor createConvertor(String currency1, String currency2, int amount1, int amount2) { + return new Convertor(currency1, currency2, new Rate(amount1, amount2)); + } + + public Convertor createConvertor(String currency1, String currency2, double amount1, double amount2) { + return new Convertor(currency1, currency2, new Rate(amount1, amount2)); + } + + public Convertor createConvertor(String currency1, String currency2, double rate) { + return new Convertor(currency1, currency2, new Rate(rate)); + } +} diff -r b4c6b18eaf3f -r a022dd2a5d30 task1/solution14/src/org/apidesign/apifest08/currency/Rate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution14/src/org/apidesign/apifest08/currency/Rate.java Tue Sep 30 13:51:18 2008 +0200 @@ -0,0 +1,68 @@ + +package org.apidesign.apifest08.currency; + +public final class Rate { + + private double rate; + + public Rate(int amountA, int amountB) { + rate = amountA / (double)amountB; + if (rate <= 0) { + throw new IllegalArgumentException("Exchange rate must be positive."); + } + } + + public Rate(double amountA, double amountB) { + rate = amountA / amountB; + if (rate <= 0) { + throw new IllegalArgumentException("Exchange rate must be positive."); + } + } + + public Rate(double rate) { + this.rate = rate; + if (this.rate <= 0) { + throw new IllegalArgumentException("Exchange rate must be positive."); + } + } + + public double convertAtoB(int a) { + return a / rate; + } + + public double convertAtoB(double a) { + return a / rate; + } + + public double convertBtoA(int b) { + return b * rate; + } + + public double convertBtoA(double b) { + return b * rate; + } + + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Rate other = (Rate) obj; + return true; + } + + @Override + public int hashCode() { + int hash = 5; + return hash; + } + + @Override + public String toString() { + return ""+rate; + } +} diff -r b4c6b18eaf3f -r a022dd2a5d30 task1/solution14/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution14/test/org/apidesign/apifest08/test/Task1Test.java Tue Sep 30 13:51:18 2008 +0200 @@ -0,0 +1,144 @@ +package org.apidesign.apifest08.test; + +import junit.framework.TestCase; +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.ConvertorFactory; + +/** Finish the Convertor API, and then write bodies of methods inside + * of this class to match the given tasks. To fullfil your task, use the + * API define in the org.apidesign.apifest08.currency package. + * Do not you reflection, or other hacks as your code + * shall run without any runtime permissions. + */ +public class Task1Test extends TestCase { + public Task1Test(String testName) { + super(testName); + } + + @Override + protected void setUp() throws Exception { + } + + @Override + protected void tearDown() throws Exception { + } + + // + // Imagine that there are three parts of the whole system: + // 1. there is someone who knows the current exchange rate + // 2. there is someone who wants to do the conversion + // 3. there is the API between 1. and 2. which allows them to communicate + // Please design such API + // + + /** Create convertor that understands two currencies, CZK and + * USD. Make 1 USD == 17 CZK. This is a method provided for #1 group - + * e.g. those that know the exchange rate. They somehow need to create + * the objects from the API and tell them the exchange rate. The API itself + * knows nothing about any rates, before the createCZKtoUSD method is called. + * + * Creation of the convertor shall not require subclassing of any class + * or interface on the client side. + * + * @return prepared convertor ready for converting USD to CZK and CZK to USD + */ + public static Convertor createCZKtoUSD() { + return ConvertorFactory.newInstance().createConvertor("CZK", "USD", 17, 1); + } + + /** Create convertor that understands two currencies, CZK and + * SKK. Make 100 SKK == 80 CZK. Again this is method for the #1 group - + * it knows the exchange rate, and needs to use the API to create objects + * with the exchange rate. Anyone shall be ready to call this method without + * any other method being called previously. The API itself shall know + * nothing about any rates, before this method is called. + * + * Creation of the convertor shall not require subclassing of any class + * or interface on the client side. + * + * @return prepared convertor ready for converting SKK to CZK and CZK to SKK + */ + public static Convertor createSKKtoCZK() { + return ConvertorFactory.newInstance().createConvertor("SKK", "CZK", 100, 80); + } + + // + // now the methods for group #2 follow: + // this group knows nothing about exchange rates, but knows how to use + // the API to do conversions. It somehow (by calling one of the factory + // methods) gets objects from the API and uses them to do the conversions. + // + + /** Use the convertor from createCZKtoUSD method and do few conversions + * with it. + */ + public void testCurrencyCZKUSD() throws Exception { + Convertor c = createCZKtoUSD(); + // convert $5 to CZK using c: + assertEquals("Result is 85 CZK", 85.0, c.convert("USD", "CZK", 5)); + + // convert $8 to CZK + assertEquals("Result is 136 CZK", 136.0, c.convert("USD", "CZK", 8)); + + // convert 1003CZK to USD + assertEquals("Result is 59 CZK", 59.0, c.convert("CZK", "USD", 1003)); + } + + /** Use the convertor from createSKKtoCZK method and do few conversions + * with it. + */ + public void testCurrencySKKCZK() throws Exception { + Convertor c = createSKKtoCZK(); + // convert 16CZK using c: + // assertEquals("Result is 20 SKK"); + assertEquals("Result is 20 SKK", 20.0, c.convert("CZK", "SKK", 16)); + + // convert 500SKK to CZK + // assertEquals("Result is 400 CZK"); + assertEquals("Result is 400 SKK", 400.0, c.convert("SKK", "CZK", 500)); + } + + /** Verify that the CZK to USD convertor knows nothing about SKK. + */ + public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception { + Convertor c = createCZKtoUSD(); + // convert $5 to SKK, the API shall say this is not possible + try { + c.convert("USD", "SKK", 5); + fail("Converting SKK with CZKUSD convertor is impossible"); + } catch (IllegalArgumentException e){ + //ok + } + + // convert 500 SKK to CZK, the API shall say this is not possible + try { + c.convert("SKK", "CZK", 500); + fail("Converting SKK with CZKUSD convertor is impossible"); + } catch (IllegalArgumentException e){ + //ok + } + + } + + /** Verify that the CZK to SKK convertor knows nothing about USD. + */ + public void testCannotConvertToUSDwithCZKSKKConvertor() throws Exception { + Convertor c = createSKKtoCZK(); + // convert $5 to SKK, the API shall say this is not possible + try { + c.convert("USD", "SKK", 5); + fail("Converting SKK with SKKCZK convertor is impossible"); + } catch (IllegalArgumentException e){ + //ok + } + + // convert 500 CZK to USD, the API shall say this is not possible + try { + c.convert("CZK", "USD", 500); + fail("Converting USD with SKKCZK convertor is impossible"); + } catch (IllegalArgumentException e){ + //ok + } + + } +}