# HG changeset patch # User Jaroslav Tulach # Date 1222850585 -7200 # Node ID f6073056b9fe33fdcaa12f9a6df4f7d327b95443 # Parent 36331f7244bd809e22809d17a729e7a68cc28935 Getting ready for task2: copying all solutions to new locations diff -r 36331f7244bd -r f6073056b9fe task1/README --- a/task1/README Tue Sep 30 16:11:32 2008 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -This directory shall contain solutions for the task1. -Each solution is NetBeans Java SE project, in its own -directory solution. The id is a number of the solution. -A README file in a solution dir can contain -detailed information on the solution and -shall somehow reflect the internals and be based on -some significant aspect used in the solution. For -example: welltested, delegating, verbose, etc. - diff -r 36331f7244bd -r f6073056b9fe task2/solution01/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution01/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution01/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=3cae0313 +nbproject/build-impl.xml.script.CRC32=3c8caa17 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution01/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution01/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 01 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/AbstractConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/AbstractConvertorFactory.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,12 @@ +package org.apidesign.apifest08.currency; + +/** + * Abstract class for future possible purposes + * @author Ladislav Vitasek + */ +abstract class AbstractConvertorFactory implements CurrencyConvertorFactory{ + + AbstractConvertorFactory() { + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/AbstractCurrencyConvertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/AbstractCurrencyConvertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,64 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; + +/** + * Abstract convertor implementation. + * This class provide necessary stuff for creating any CurrencyConvertor + * Convert value can be get by different way. Conversion should be made in its subclass. + * @author Ladislav Vitasek + */ +abstract class AbstractCurrencyConvertor implements Convertor { + protected final ConversionRatioProvider conversionRatioProvider; + protected final Currency currency1; + protected final Currency currency2; + + public AbstractCurrencyConvertor(Currency currency1, Currency currency2, ConversionRatioProvider conversionRatioProvider) { + this.currency1 = currency1; + this.currency2 = currency2; + this.conversionRatioProvider = conversionRatioProvider; + } + + + public Currency getCurrency1() { + return currency1; + } + + + public Currency getCurrency2() { + return currency2; + } + +// /** +// * Conversion value can be get by Different way. Let's decide subclass where to get actual value +// * @return current value of conversion constant +// */ +// protected abstract BigDecimal getConversionValue(); + + @SuppressWarnings({"RedundantIfStatement"}) + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AbstractCurrencyConvertor that = (AbstractCurrencyConvertor) o; + + if (!conversionRatioProvider.equals(that.conversionRatioProvider)) return false; + if (!currency1.equals(that.currency1)) return false; + if (!currency2.equals(that.currency2)) return false; + + return true; + } + + public int hashCode() { + int result; + result = conversionRatioProvider.hashCode(); + result = 31 * result + currency1.hashCode(); + result = 31 * result + currency2.hashCode(); + return result; + } + + ConversionRatioProvider getConversionRatioProvider() { + return conversionRatioProvider; + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/CannotConvertException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/CannotConvertException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,26 @@ +package org.apidesign.apifest08.currency; + +/** + * If something was wrong during converting currencies... + * Eg. when the convertor is outdated + * Should be a descendant of RuntimeException? Hmmms... + * @author Ladislav Vitasek + */ +public class CannotConvertException extends RuntimeException { + + public CannotConvertException() { + + } + + public CannotConvertException(String message) { + super(message); + } + + public CannotConvertException(Throwable e) { + super(e); + } + + public CannotConvertException(String message, Throwable cause) { + super(message, cause); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/CannotInstantiateFactoryException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/CannotInstantiateFactoryException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,23 @@ +package org.apidesign.apifest08.currency; + +/** + * Failed to instantiate class for factory + * @author Ladislav Vitasek + */ +public class CannotInstantiateFactoryException extends Exception { + public CannotInstantiateFactoryException(Throwable e) { + super(e); + } + + public CannotInstantiateFactoryException(String message) { + super(message); + } + + public CannotInstantiateFactoryException() { + super(); + } + + public CannotInstantiateFactoryException(String message, Throwable cause) { + super(message, cause); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/CannotProvideValueException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/CannotProvideValueException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,24 @@ +package org.apidesign.apifest08.currency; + +/** + * Used when value cannot be get from the source + * @author Ladislav Vitasek + */ +public class CannotProvideValueException extends RuntimeException { + + public CannotProvideValueException() { + super(); + } + + public CannotProvideValueException(String message) { + super(message); + } + + public CannotProvideValueException(String message, Throwable cause) { + super(message, cause); + } + + public CannotProvideValueException(Throwable cause) { + super(cause); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/ConversionProperties.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/ConversionProperties.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,101 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * This class encapsulates settings for currency conversion + * We can use it eg. for uptime options etc. in future versions + * + * @author Ladislav Vitasek + */ +final public class ConversionProperties { + + private final ConversionRatioProvider conversionRatioProvider; + private final RoundingMode roundingMode; + + /** + * Returns conversion constants - conversion operation is invertable + * Default + */ + private final static class FixedBidirectionalRatioConversion implements ConversionRatioProvider { + private final BigDecimal currency1ToCurrency2Constant; + private final BigDecimal currency2ToCurrency1Constant; + private static final int SCALE_MAX_DEFAULT = 20; + + /** + * Constructor + * @param ratioConstant fixed ratio constant + * @param mode math rounding mode + * @throws IllegalArgumentException if conversion value is <= 0 + */ + private FixedBidirectionalRatioConversion(BigDecimal ratioConstant, RoundingMode mode) { + if (ratioConstant.compareTo(BigDecimal.ZERO) <= 0) + throw new IllegalArgumentException("Conversion value cannot be <= 0"); + this.currency1ToCurrency2Constant = ratioConstant; + this.currency2ToCurrency1Constant = BigDecimal.ONE.setScale(SCALE_MAX_DEFAULT).divide(ratioConstant, mode); + } + + public BigDecimal getCurrency1ToCurrency2Constant() { + return currency1ToCurrency2Constant; + } + + public BigDecimal getCurrency2ToCurrency1Constant() { + return currency2ToCurrency1Constant; + } + } + + + /** + * Returns new instance Conversion properties - fixed conversion ratio - both directions + * Default max scale is set to 20 + * + * @param conversionConstant value of constant for conversion + * @param roundingMode math rounding mode + * @return new instance of ConversionProperties class + * @throws IllegalArgumentException if conversion constant is <= 0 + * @see org.apidesign.apifest08.currency.ConversionProperties.FixedBidirectionalRatioConversion + */ + public static ConversionProperties create(BigDecimal conversionConstant, RoundingMode roundingMode) { + return create(new FixedBidirectionalRatioConversion(conversionConstant, roundingMode), roundingMode); + } + + /** + * Returns new instance Conversion properties + * + * @param conversionRatioProvider provider for conversion constants + * @param roundingMode math rounding mode + * @return new instance of ConversionProperties class + */ + public static ConversionProperties create(ConversionRatioProvider conversionRatioProvider, RoundingMode roundingMode) { + if (conversionRatioProvider == null || roundingMode == null) + throw new NullPointerException(); + return new ConversionProperties(conversionRatioProvider, roundingMode); + } + + /** + * Returns new instance Conversion properties with values set to conversionConstant=BigDecimal.ONE + * and roundingMode=RoundingMode.HALF_EVEN + * + * Conversion is bidirectional. + * + * @return + */ + public static ConversionProperties createDefault() { + return create(BigDecimal.ONE, RoundingMode.HALF_EVEN); + } + + private ConversionProperties(ConversionRatioProvider conversionRatioProvider, RoundingMode roundingMode) { + this.conversionRatioProvider = conversionRatioProvider; + this.roundingMode = roundingMode; + } + + + public RoundingMode getRoundingMode() { + return roundingMode; + } + + public ConversionRatioProvider getConversionRatioProvider() { + return conversionRatioProvider; + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/ConversionRatioProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/ConversionRatioProvider.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,12 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; + +/** + * Provider for conversion constant from any source + * @author Ladislav Vitasek + */ +public interface ConversionRatioProvider { + public BigDecimal getCurrency1ToCurrency2Constant() throws CannotProvideValueException; + public BigDecimal getCurrency2ToCurrency1Constant() throws CannotProvideValueException; +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,51 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; +import java.util.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 interface Convertor { + /** + * Methods converts some pounds + * API design question - what kind of parameters? + * I decided to use BigDecimal based on experience on Java conf ;-) + * @param amountOfMoney + * @return converted amount of money + * @throws org.apidesign.apifest08.currency.CannotConvertException - if convertor is outdated or any other problem + */ + + BigDecimal convertCurrency1ToCurrency2(BigDecimal amountOfMoney) throws CannotConvertException; + + /** + * Methods converts some pounds + * API design question - what kind of parameters? + * @param amountOfMoney + * @return converted amount of money + * @throws org.apidesign.apifest08.currency.CannotConvertException - if convertor is outdated or any other problem + */ + + BigDecimal convertCurrency2ToCurrency1(BigDecimal amountOfMoney) throws CannotConvertException; + + //handy getters + + /** + * Getter - Returns Currency 1 of this convertor + * @return money code + */ + Currency getCurrency1(); + + /** + * Getter - Returns Currency 2 of this convertor + * @return money code + */ + Currency getCurrency2(); + + // For Future purposes...? - it was not as a requirement in TestCase + //BigDecimal getConversionValue(); +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/ConvertorNotAvailableException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/ConvertorNotAvailableException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,23 @@ +package org.apidesign.apifest08.currency; + +/** + * No convertor is available for current conversion settings + * @author Ladislav Vitasek + */ +public class ConvertorNotAvailableException extends Exception { + public ConvertorNotAvailableException() { + + } + + public ConvertorNotAvailableException(String message) { + super(message); + } + + public ConvertorNotAvailableException(String message, Throwable cause) { + super(message, cause); + } + + public ConvertorNotAvailableException(Throwable cause) { + super(cause); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/ConvertorsFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/ConvertorsFactory.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,43 @@ +package org.apidesign.apifest08.currency; + +/** + * Some Factory for Factories :-) + * Depends on the whole application design... + * @author Ladislav Vitasek + */ +final public class ConvertorsFactory { + private CurrencyConvertorFactory currencyConvertorFactoryInstance = null; + private final static ConvertorsFactory instance = new ConvertorsFactory(); + + + private ConvertorsFactory() { + + } + + public static CurrencyConvertorFactory getCurrencyConvertorFactoryInstance() throws CannotInstantiateFactoryException { + return getInstance().getCurrencyConvertor(); + } + + /** + * Returns instance of CurrencyConvertorFactory + * @return new instance of CurrencyConvertorFactory + * @throws CannotInstantiateFactoryException + */ + private synchronized CurrencyConvertorFactory getCurrencyConvertor() throws CannotInstantiateFactoryException { + if (currencyConvertorFactoryInstance == null) {//intern implementation +// String className = System.getProperty("currencyFactory", CurrencyConvertorFactoryImpl.class.getName()); +// try { +// currencyConvertorFactoryInstance = (CurrencyConvertorFactory) Class.forName(className).newInstance(); +// } catch (Exception e) { +// throw new CannotInstantiateFactoryException(e); +// } + //without reflection + currencyConvertorFactoryInstance = new CurrencyConvertorFactoryImpl(); + } + return currencyConvertorFactoryInstance; + } + + public static ConvertorsFactory getInstance() { + return instance; + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorFactory.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,22 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; + +/** + * @author Ladislav Vitasek + */ +public interface CurrencyConvertorFactory { + + /** + * Instantiate a new instance convertor + * @param currency1 a code currency you want to convert between + * @param currency2 a code currency you want to convert between + * @param conversionProperties settings for conversion + * @return converter for currency converting + * @throws ConvertorNotAvailableException throws if there is no available convertor for selected currencies + */ + Convertor createConvertor(Currency currency1, Currency currency2, ConversionProperties conversionProperties) throws ConvertorNotAvailableException; + + + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorFactoryImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorFactoryImpl.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,29 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; + +/** + * Convertor Factory implementation + * In the real-time world this code should be optimized - convertors caching etc. + * @author Ladislav Vitasek + */ +class CurrencyConvertorFactoryImpl extends AbstractConvertorFactory { + + + CurrencyConvertorFactoryImpl() { + super(); + } + + // Note - implementation of this method is dummy + + public Convertor createConvertor(Currency currency1, Currency currency2, ConversionProperties conversionProperties) throws ConvertorNotAvailableException { + if (currency1 == null || currency2 == null || conversionProperties == null) + throw new NullPointerException(); + + try { + return new CurrencyConvertorImpl(currency1, currency2, conversionProperties.getConversionRatioProvider(), conversionProperties.getRoundingMode());//can be cached somehow + } catch (Exception e) { + throw new ConvertorNotAvailableException(e); + } + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorImpl.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,108 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Currency; + +/** + * Intern implementation of Bidirectional Convertor - this class is only for internal purposes only. + * This implementation uses BigDecimals to count result value + * Scale is set by ISO 4217 as default + * + * @author Ladislav Vitasek + */ +class CurrencyConvertorImpl extends AbstractCurrencyConvertor { + + protected RoundingMode roundingMode; + protected int currency1Scale; + protected int currency2Scale; + + /** + * Constructor + * By default it creates convertor with conversion value 1 + * + * @param currency1 currency for conversion + * @param currency2 currency for conversion + * @param provider constants provider + * @param roundingMode math rounding mode + */ + CurrencyConvertorImpl(Currency currency1, Currency currency2, ConversionRatioProvider provider, RoundingMode roundingMode) { + this(currency1, currency2, provider, roundingMode, currency1.getDefaultFractionDigits(), currency2.getDefaultFractionDigits()); + } + + /** + * Constructor + * + * @param currency1 currency for conversion + * @param currency2 currency for conversion + * @param conversionRatioProvider conversion constants provider + * @param roundingMode math rounding mode + * @param conversionRatioProvider constants provider + * @param currency2Scale + */ + CurrencyConvertorImpl(Currency currency1, Currency currency2, ConversionRatioProvider conversionRatioProvider, RoundingMode roundingMode, int currency1Scale, int currency2Scale) { + super(currency1, currency2, conversionRatioProvider); + this.roundingMode = roundingMode; + this.currency1Scale = currency1Scale; + this.currency2Scale = currency2Scale; + } + + + public BigDecimal convertCurrency1ToCurrency2(BigDecimal amountOfMoney) throws CannotConvertException { + try { + final BigDecimal result = amountOfMoney.multiply(getConversionRatioProvider().getCurrency1ToCurrency2Constant()); + return result.setScale(getCurrency2Scale(), getRoundingMode()); + } catch (CannotProvideValueException e) { + throw new CannotConvertException(e); + } catch (ArithmeticException e) { + throw new CannotConvertException(e); + } + } + + + public BigDecimal convertCurrency2ToCurrency1(BigDecimal amountOfMoney) throws CannotConvertException { + try { + final BigDecimal result = amountOfMoney.multiply(getConversionRatioProvider().getCurrency2ToCurrency1Constant()); + return result.setScale(getCurrency1Scale(), getRoundingMode()); + } catch (CannotProvideValueException e) { + throw new CannotConvertException(e); + } catch (ArithmeticException e) { + throw new CannotConvertException(e); + } + } + + public String toString() { + return "CurrencyConvertorImpl{" + + "currency1=" + getCurrency1() + + ",currency2=" + getCurrency2() + + ", roundingMode=" + roundingMode + + ", currency1Scale=" + currency1Scale + + ", currency2Scale=" + currency2Scale + + '}'; + } + + RoundingMode getRoundingMode() { + return roundingMode; + } + + int getCurrency1Scale() { + return currency1Scale; + } + + int getCurrency2Scale() { + return currency2Scale; + } + + void setRoundingMode(RoundingMode roundingMode) { + this.roundingMode = roundingMode; + } + + void setCurrency1Scale(int currency1Scale) { + this.currency1Scale = currency1Scale; + } + + void setCurrency2Scale(int currency2Scale) { + this.currency2Scale = currency2Scale; + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution01/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution01/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,104 @@ +package org.apidesign.apifest08.test; + +import junit.framework.TestCase; +import org.apidesign.apifest08.currency.*; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Currency; + +/** + * 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 { + private static CurrencyConvertorFactory currencyConvertorFactoryInstance; + + public Task1Test(String testName) { + super(testName); + } + + @Override + protected void setUp() throws Exception { + currencyConvertorFactoryInstance = ConvertorsFactory.getCurrencyConvertorFactoryInstance(); + } + + @Override + protected void tearDown() throws Exception { + currencyConvertorFactoryInstance = null; + } + + /** + * Create convertor that understands two currencies, CZK and + * USD. Make 1 USD == 17 CZK. + *

+ * 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() throws ConvertorNotAvailableException { + final Currency czk = Currency.getInstance("CZK"); + final Currency usd = Currency.getInstance("USD"); + final BigDecimal constant = BigDecimal.ONE.divide(BigDecimal.valueOf(17), 10, RoundingMode.HALF_EVEN); + return currencyConvertorFactoryInstance.createConvertor(czk, usd, ConversionProperties.create(constant, RoundingMode.HALF_EVEN)); + } + + /** + * Create convertor that understands two currencies, CZK and + * SKK. Make 100 SKK == 80 CZK. + *

+ * 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() throws ConvertorNotAvailableException { + final Currency skk = Currency.getInstance("SKK"); + final Currency czk = Currency.getInstance("CZK"); + final BigDecimal constant = new BigDecimal("0.8"); + return currencyConvertorFactoryInstance.createConvertor(skk, czk, ConversionProperties.create(constant, RoundingMode.HALF_EVEN)); + } + + /** + * 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: + + final int czkDigitsScale = c.getCurrency1().getDefaultFractionDigits(); + final int usdDigitsScale = c.getCurrency2().getDefaultFractionDigits(); + + assertEquals("Result is 85 CZK", BigDecimal.valueOf(85).setScale(czkDigitsScale), c.convertCurrency2ToCurrency1(BigDecimal.valueOf(5))); + + // convert $8 to CZK + assertEquals("Result is 136 CZK", BigDecimal.valueOf(136).setScale(czkDigitsScale), c.convertCurrency2ToCurrency1(BigDecimal.valueOf(8))); + + // convert 1003CZK to USD + assertEquals("Result is 59 USD", BigDecimal.valueOf(59).setScale(usdDigitsScale), c.convertCurrency1ToCurrency2(BigDecimal.valueOf(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: + + final int skkDigitsScale = c.getCurrency1().getDefaultFractionDigits(); + final int czkDigitsScale = c.getCurrency2().getDefaultFractionDigits(); + + assertEquals("Result is 20 SKK", BigDecimal.valueOf(20).setScale(skkDigitsScale), c.convertCurrency2ToCurrency1(BigDecimal.valueOf(16))); + + // convert 500SKK to CZK + assertEquals("Result is 400 CZK", BigDecimal.valueOf(400).setScale(czkDigitsScale), c.convertCurrency1ToCurrency2(BigDecimal.valueOf(500))); + } + +} + diff -r 36331f7244bd -r f6073056b9fe task2/solution02/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution02/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution02/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=848e6267 +nbproject/build-impl.xml.script.CRC32=6be86987 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution02/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution02/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 02 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution02/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,18 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; + + +/** + * Converts currencies. To create an instance call {@link ConvertorFactory#createConvertor(Money, Money)}. + */ +public interface Convertor { + /** + * Converts amount to its equivalent in the destination currency. + * @param amount + * @param destinationCurrency + * @return + * @throws IllegalArgumentException if currency of the amount is not supported or if it is not possible to convert it to the destination currency. + */ + public Money convert(Money amount, Currency destinationCurrency) throws IllegalArgumentException; +} diff -r 36331f7244bd -r f6073056b9fe task2/solution02/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/src/org/apidesign/apifest08/currency/ConvertorFactory.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,31 @@ +package org.apidesign.apifest08.currency; + + + +/** + * Creates {@link Convertor} implementations. + * @author lukas + * + */ +public class ConvertorFactory { + private ConvertorFactory() + { + //nothing + } + + /** + * Creates {@link Convertor} that converts from sourceEquivalent.currency to destinationEquivalent.currency. + * Exchange rate is set as equivalents. It means if you want to create USD to CZK convertor where USD1 = CZK17 + * call createConvertor(new MoneyImpl(1, USD), new MoneyImpl(17, CZK)). Convertor created by this method + * rounds the result to two decimal places. + * @param sourceEquivalent + * @param destinationEquivalent + * @return + */ + public static final Convertor createConvertor(Money sourceEquivalent, Money destinationEquivalent) + { + return new DefaultConvertor(sourceEquivalent.getAmount(), destinationEquivalent.getAmount(), sourceEquivalent.getCurrency(), destinationEquivalent.getCurrency()); + } + + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution02/src/org/apidesign/apifest08/currency/DefaultConvertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/src/org/apidesign/apifest08/currency/DefaultConvertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,164 @@ +package org.apidesign.apifest08.currency; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Currency; + +/** + * Default {@link Convertor} implementation. Exchange rate is stored as equivalents. It means if we have USD to CZK convertor and USD1 = CZK17 + * we store 1 in sourceEquivalent and 17 in destinationEquivalent. This class is immutable. + * @author lukas + * + */ +class DefaultConvertor implements Convertor, Serializable { + + private static final long serialVersionUID = -1754789142402148099L; + + /** + * Equivalent in source currency. + */ + private final BigDecimal sourceEquivalent; + + /** + * Equivalent in destination currency. + */ + private final BigDecimal destinationEquivalent; + + private final Currency sourceCurrency; + + private final Currency destinationCurrency; + + public DefaultConvertor(BigDecimal sourceEquivalent, BigDecimal destinationEquivalent, Currency sourceCurrency, Currency destinationCurrency) { + super(); + if (BigDecimal.ZERO.compareTo(sourceEquivalent)==0) + { + throw new IllegalArgumentException("Source equivalent amount can not be 0."); + } + if (BigDecimal.ZERO.compareTo(destinationEquivalent)==0) + { + throw new IllegalArgumentException("Destination equivalent amount can not be 0."); + } + this.sourceEquivalent = sourceEquivalent; + this.destinationEquivalent = destinationEquivalent; + this.sourceCurrency = sourceCurrency; + this.destinationCurrency = destinationCurrency; + } + + public Money convert(Money amount, Currency destinationCurrency) { + if (amount==null) + { + throw new NullPointerException("Money is null"); + } + if (destinationCurrency==null) + { + throw new NullPointerException("destionationCurrency is null"); + } + if (isConversionInOpositeDirection(amount, destinationCurrency)) + { + return revert().convert(amount, destinationCurrency); + } + if (!amount.getCurrency().equals(getSourceCurrency())) + { + throw new IllegalArgumentException("Can not convert from "+amount.getCurrency()+". Converts between "+getSourceCurrency()+" and "+getDestinationCurrency()); + } + if (!getDestinationCurrency().equals(destinationCurrency)) + { + throw new IllegalArgumentException("Can not convert to "+destinationCurrency+". Converts between "+getSourceCurrency()+" and "+getDestinationCurrency()); + } + BigDecimal sourceAmount = amount.getAmount(); + BigDecimal destinationAmount = sourceAmount.multiply(destinationEquivalent).divide(sourceEquivalent, 2, RoundingMode.HALF_UP); + return new MoneyImpl(destinationAmount, getDestinationCurrency()); + } + + /** + * Returns true, if the conversion is in oposit direction. + * @param amount + * @param destinationCurrency + * @return + */ + private boolean isConversionInOpositeDirection(Money amount, Currency destinationCurrency) { + return amount.getCurrency().equals(getDestinationCurrency()) && destinationCurrency.equals(getSourceCurrency()); + } + + + public Convertor revert() { + return new DefaultConvertor(destinationEquivalent, sourceEquivalent, destinationCurrency, sourceCurrency); + } + + public BigDecimal getSourceEquivalent() { + return sourceEquivalent; + } + + public BigDecimal getDestinationEquivalent() { + return destinationEquivalent; + } + + public Currency getSourceCurrency() { + return sourceCurrency; + } + + public Currency getDestinationCurrency() { + return destinationCurrency; + } + + @Override + public String toString() { + return getClass().getName()+" converts "+getSourceCurrency()+" to "+getDestinationCurrency()+" " + +getSourceCurrency()+getSourceEquivalent()+"="+getDestinationCurrency()+getDestinationEquivalent(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime + * result + + ((destinationCurrency == null) ? 0 : destinationCurrency + .hashCode()); + result = prime + * result + + ((destinationEquivalent == null) ? 0 : destinationEquivalent + .hashCode()); + result = prime * result + + ((sourceCurrency == null) ? 0 : sourceCurrency.hashCode()); + result = prime + * result + + ((sourceEquivalent == null) ? 0 : sourceEquivalent.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof DefaultConvertor)) + return false; + DefaultConvertor other = (DefaultConvertor) obj; + if (destinationCurrency == null) { + if (other.destinationCurrency != null) + return false; + } else if (!destinationCurrency.equals(other.destinationCurrency)) + return false; + if (destinationEquivalent == null) { + if (other.destinationEquivalent != null) + return false; + } else if (!destinationEquivalent.equals(other.destinationEquivalent)) + return false; + if (sourceCurrency == null) { + if (other.sourceCurrency != null) + return false; + } else if (!sourceCurrency.equals(other.sourceCurrency)) + return false; + if (sourceEquivalent == null) { + if (other.sourceEquivalent != null) + return false; + } else if (!sourceEquivalent.equals(other.sourceEquivalent)) + return false; + return true; + } + + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution02/src/org/apidesign/apifest08/currency/Money.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/src/org/apidesign/apifest08/currency/Money.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,29 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; +import java.util.Currency; + +/** + * Money representation. Default implementation {@link MoneyImpl} is provided. This interface can + * be implemented by a DTO used in client application. + * @author lukas + * + */ +/* + * Whether we need such interface depends on the context. I can imagine than in a desktop application this interface + * would be useless, Money could be a class. In J2EE environment it can be useful. + */ +public interface Money { + + /** + * Returns amount. + * @return + */ + public BigDecimal getAmount(); + + /** + * Returns currency. + */ + public Currency getCurrency(); + +} \ No newline at end of file diff -r 36331f7244bd -r f6073056b9fe task2/solution02/src/org/apidesign/apifest08/currency/MoneyImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/src/org/apidesign/apifest08/currency/MoneyImpl.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,88 @@ +package org.apidesign.apifest08.currency; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Currency; + +/** + * Default implementation of {@link Money} interface. This class is immutable. + * @author lukas + * + */ +public final class MoneyImpl implements Serializable, Money{ + private static final long serialVersionUID = -6091808475616516136L; + + private final BigDecimal amount; + + private final Currency currency; + + public MoneyImpl(BigDecimal amount, Currency currency) { + if (amount==null) throw new NullPointerException("Amount is null"); + if (currency==null) throw new NullPointerException("Currency is null"+currency); + this.amount = amount; + this.currency = currency; + } + + public MoneyImpl(long amount, Currency currency) { + this(BigDecimal.valueOf(amount), currency); + } + + public MoneyImpl(double amount, Currency currency) { + this(BigDecimal.valueOf(amount), currency); + } + + /** + * Returns amount. + * @return + */ + public BigDecimal getAmount() { + return amount; + } + + /** + * Returns currency. + */ + public Currency getCurrency() { + return currency; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((amount == null) ? 0 : amount.hashCode()); + result = prime * result + + ((currency == null) ? 0 : currency.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof MoneyImpl)) + return false; + MoneyImpl other = (MoneyImpl) obj; + if (amount == null) { + if (other.amount != null) + return false; + } else if (amount.compareTo(other.amount)!=0) + return false; + if (currency == null) { + if (other.currency != null) + return false; + } else if (!currency.equals(other.currency)) + return false; + return true; + } + + @Override + public String toString() { + return getClass().getName()+"["+currency+amount+"]"; + } + + + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution02/test/org/apidesign/apifest08/test/ConvertorFactoryTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/test/org/apidesign/apifest08/test/ConvertorFactoryTest.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,52 @@ +package org.apidesign.apifest08.test; + +import static junit.framework.Assert.assertNotNull; +import static org.apidesign.apifest08.test.Task1Test.CZK; +import static org.apidesign.apifest08.test.Task1Test.USD; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.ConvertorFactory; +import org.apidesign.apifest08.currency.MoneyImpl; +import org.junit.Test; + + +public class ConvertorFactoryTest { + @Test(expected=NullPointerException.class) + public void testNullSource() + { + ConvertorFactory.createConvertor(null, new MoneyImpl(1,USD)); + } + @Test(expected=NullPointerException.class) + public void testNullDestination() + { + ConvertorFactory.createConvertor(new MoneyImpl(17,CZK), null); + } + @Test + public void testOk() + { + assertNotNull(ConvertorFactory.createConvertor(new MoneyImpl(17,CZK), new MoneyImpl(1,USD))); + } + @Test + public void testOkDecimalRate() + { + Convertor c = ConvertorFactory.createConvertor(new MoneyImpl(1,CZK), new MoneyImpl(1d/17d,USD)); + assertNotNull(c); + assertEquals(new MoneyImpl(17,CZK),c.convert(new MoneyImpl(1,USD), CZK)); + } + @Test + public void testZeroEquivalentRate() + { + try + { + ConvertorFactory.createConvertor(new MoneyImpl(1,CZK), new MoneyImpl(0,USD)); + fail("Exception expected"); + } + catch(IllegalArgumentException e) + { + assertTrue("OK",true); + } + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution02/test/org/apidesign/apifest08/test/ConvertorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/test/org/apidesign/apifest08/test/ConvertorTest.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,32 @@ +package org.apidesign.apifest08.test; + +import static org.apidesign.apifest08.test.Task1Test.CZK; +import static org.apidesign.apifest08.test.Task1Test.USD; +import static org.junit.Assert.assertEquals; + +import java.math.BigDecimal; + +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.ConvertorFactory; +import org.apidesign.apifest08.currency.Money; +import org.apidesign.apifest08.currency.MoneyImpl; +import org.junit.Test; + + +public class ConvertorTest { + + private static final Convertor CZK_TO_USD_CONVERTOR = ConvertorFactory.createConvertor(new MoneyImpl(17,CZK), new MoneyImpl(1,USD)); + @Test + public void testConvertSmall() + { + Money converted = CZK_TO_USD_CONVERTOR.convert(new MoneyImpl(0.17,CZK),USD); + assertEquals(new MoneyImpl(new BigDecimal("0.01"),USD),converted); + assertEquals(USD,converted.getCurrency()); + } + @Test + public void testConvertSmallReverse() + { + Money converted = CZK_TO_USD_CONVERTOR.convert(new MoneyImpl(0.01,USD),CZK); + assertEquals(new MoneyImpl(new BigDecimal("0.17"),CZK),converted); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution02/test/org/apidesign/apifest08/test/MoneyTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/test/org/apidesign/apifest08/test/MoneyTest.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,24 @@ +package org.apidesign.apifest08.test; + +import static junit.framework.Assert.assertEquals; +import static org.apidesign.apifest08.test.Task1Test.CZK; + +import java.math.BigDecimal; + +import org.apidesign.apifest08.currency.MoneyImpl; +import org.junit.Test; + +public class MoneyTest { + @Test(expected=NullPointerException.class) + public void testNullAmount(){ + new MoneyImpl(null,CZK); + } + @Test(expected=NullPointerException.class) + public void testNullCurrency(){ + new MoneyImpl(1,null); + } + @Test + public void testOk(){ + assertEquals(0,new MoneyImpl(123,CZK).getAmount().compareTo(new BigDecimal("123"))); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution02/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution02/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,160 @@ +package org.apidesign.apifest08.test; + +import java.util.Currency; + +import junit.framework.TestCase; + +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.ConvertorFactory; +import org.apidesign.apifest08.currency.MoneyImpl; + +/** 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 static final Currency USD = Currency.getInstance("USD"); + public static final Currency CZK = Currency.getInstance("CZK"); + public static final Currency SKK = Currency.getInstance("SKK"); + + 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.createConvertor(new MoneyImpl(17,CZK), new MoneyImpl(1,USD)); + } + + /** 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.createConvertor(new MoneyImpl(100,SKK), new MoneyImpl(80,CZK)); + } + + // + // 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",new MoneyImpl(85,CZK), c.convert(new MoneyImpl(5,USD),CZK)); + + // convert $8 to CZK + assertEquals("Result is 136 CZK",new MoneyImpl(136,CZK), c.convert(new MoneyImpl(8,USD),CZK)); + + // convert 1003CZK to USD + assertEquals("Result is 59 USD", new MoneyImpl(59,USD), c.convert(new MoneyImpl(1003,CZK),USD)); + + } + + /** 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", new MoneyImpl(20,SKK), c.convert(new MoneyImpl(16,CZK),SKK)); + + // convert 500SKK to CZK + assertEquals("Result is 400 CZK", new MoneyImpl(400,CZK), c.convert(new MoneyImpl(500,SKK),CZK)); + } + + /** 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(new MoneyImpl(5, USD), SKK); + fail("Exception expected"); + } + catch(IllegalArgumentException e) + { + assertTrue("Ok",true); + } + // convert 500 SKK to CZK, the API shall say this is not possible + try + { + c.convert(new MoneyImpl(500, SKK), CZK); + fail("Exception expected"); + } + catch(IllegalArgumentException e) + { + assertTrue("Ok",true); + } + } + + /** 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(new MoneyImpl(5, USD), SKK); + fail("Exception expected"); + } + catch(IllegalArgumentException e) + { + assertTrue("Ok",true); + } + // convert 500 CZK to USD, the API shall say this is not possible + try + { + c.convert(new MoneyImpl(500, CZK), USD); + fail("Exception expected"); + } + catch(IllegalArgumentException e) + { + assertTrue("Ok",true); + } + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution03/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution03/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution03/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution03/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution03/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution03/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=ec91bd4b +nbproject/build-impl.xml.script.CRC32=593428f7 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution03/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution03/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution03/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution03/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 03 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution03/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution03/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,41 @@ +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 class Convertor { + + public static final int FIRST_TO_SECOND = 1; + + public static final int SECOND_TO_FIRST = 2; + + private double first; + + private double second; + + public Convertor(double first, double second) { + this.first = first; + this.second = second; + } + + public double convertFirstToSecond(double value) { + return (second / first) * value; + } + + public double convertSecondToFirst(double value) { + return (first / second) * value; + } + + public double convert(double value, int typeOfConvert) { + if (FIRST_TO_SECOND == typeOfConvert) { + return convertFirstToSecond(value); + } else if (SECOND_TO_FIRST == typeOfConvert) { + return convertSecondToFirst(value); + } + throw new IllegalArgumentException("Unkown type of convert."); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution03/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution03/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,82 @@ +package org.apidesign.apifest08.test; + +import junit.framework.TestCase; +import org.apidesign.apifest08.currency.Convertor; + +/** 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 { + } + + /** Create convertor that understands two currencies, CZK and + * USD. Make 1 USD == 17 CZK. + * + * 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 new Convertor(17, 1); + } + + /** Create convertor that understands two currencies, CZK and + * SKK. Make 100 SKK == 80 CZK. + * + * 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 new Convertor(100, 80); + } + + /** 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"); + assertEquals(c.convert(5, Convertor.SECOND_TO_FIRST), (double) 85); + + // convert $8 to CZK + // assertEquals("Result is 136 CZK"); + assertEquals(c.convert(8, Convertor.SECOND_TO_FIRST), (double) 136); + + // convert 1003CZK to USD + // assertEquals("Result is 59 USD"); + assertEquals(c.convert(1003, Convertor.FIRST_TO_SECOND), (double) 59); + } + + /** 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(c.convert(16, Convertor.SECOND_TO_FIRST), (double) 20); + + // convert 500SKK to CZK + // assertEquals("Result is 400 CZK"); + assertEquals(c.convert(500, Convertor.FIRST_TO_SECOND), (double) 400); + } +} + diff -r 36331f7244bd -r f6073056b9fe task2/solution04/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution04/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution04/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution04/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution04/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution04/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=2fbfa6ce +nbproject/build-impl.xml.script.CRC32=c521eea7 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution04/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution04/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution04/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution04/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 04 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution04/src/org/apidesign/apifest08/currency/ConverterImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution04/src/org/apidesign/apifest08/currency/ConverterImpl.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,159 @@ +package org.apidesign.apifest08.currency; + + +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.Currency; + + +/** + * Convert between two currencies. + * + * @author D'Arcy Smith + * @version 1.0 + */ +final class ConvertorImpl + implements Convertor +{ + /** + * The currency to cvonvert from. + */ + private final Currency currencyA; + + /** + * The currency to cvonvert from. + */ + private final Currency currencyB; + + /** + * The echange rate between a and b. + */ + private final BigDecimal currencyARate; + + /** + * The echange rate between b and a. + */ + private final BigDecimal currencyBRate; + + /** + * Constructs a convertor with the specified currencies. + * + * @param a the currency to convert from. + * @param aRate the exchage rage between from and to. + * @param b the currency to convert to. + * @param bRate the exchage rage between to and from. + * @throws IllegalArgumentException if either any of the arguments are null or if either rate <= 0. + */ + public ConvertorImpl(final Currency a, + final BigDecimal aRate, + final Currency b, + final BigDecimal bRate) + { + if(a == null) + { + throw new IllegalArgumentException("a cannot be null"); + } + + if(b == null) + { + throw new IllegalArgumentException("b cannot be null"); + } + + if(aRate == null) + { + throw new IllegalArgumentException("aRate cannot be null"); + } + + if(bRate == null) + { + throw new IllegalArgumentException("bRate cannot be null"); + } + + if(aRate.compareTo(BigDecimal.ZERO) <= 0) + { + throw new IllegalArgumentException("aRate must be > 0, was: " + aRate); + } + + if(bRate.compareTo(BigDecimal.ZERO) <= 0) + { + throw new IllegalArgumentException("bRate must be > 0, was: " + bRate); + } + + currencyA = a; + currencyB = b; + currencyARate = aRate; + currencyBRate = bRate; + } + + /** + * Convert an amount from one currency to another. + * + * @param from the currency to convert from. + * @param to the currency to convert to. + * @param amount the amount to convert. + * @return the converted amount. + * @throws IllegalArgumentException if any of the arguments are null. + * @throws InvalidConversionException if either from or to are not equal to the currencies passed to the constructor. + */ + public BigDecimal convert(final Currency from, + final Currency to, + final BigDecimal amount) + throws InvalidConversionException + { + final BigDecimal result; + + if(amount == null) + { + throw new IllegalArgumentException("amount cannot be null"); + } + + if(from == null) + { + throw new IllegalArgumentException("from cannot be null"); + } + + if(to == null) + { + throw new IllegalArgumentException("to cannot be null"); + } + + if(!(from.equals(currencyA)) && (!(from.equals(currencyB)))) + { + throw new InvalidConversionException("cannot convert from: " + from.getCurrencyCode(), from, currencyA, currencyB); + } + + if(!(to.equals(currencyA)) && (!(to.equals(currencyB)))) + { + throw new InvalidConversionException("cannot convert to: " + to.getCurrencyCode(), to, currencyA, currencyB); + } + + // converting between the same currency is no converstion at all. + if(from.equals(to)) + { + result = amount; + } + else + { + final BigDecimal rateX; + final BigDecimal rateY; + final BigDecimal temp; + + if(from.equals(currencyA)) + { + rateX = currencyARate; + rateY = currencyBRate; + } + else + { + rateX = currencyBRate; + rateY = currencyARate; + } + + temp = amount.divide(rateX, MathContext.DECIMAL32); + result = temp.multiply(rateY); + } + + return (result.setScale(2, RoundingMode.HALF_DOWN)); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution04/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution04/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,30 @@ +package org.apidesign.apifest08.currency; + + +import java.math.BigDecimal; +import java.util.Currency; + + +/** + * Convert between two currencies. + * + * @author D'Arcy Smith + * @version 1.0 + */ +public interface Convertor +{ + /** + * Convert an amount from one currency to another. + * + * @param from the currency to convert from. + * @param to the currency to convert to. + * @param amount the amount to convert. + * @return the converted amount. + * @throws IllegalArgumentException if any of the arguments are null. + * @throws InvalidConversionException if either from or to are not valid for the convertor. + */ + BigDecimal convert(Currency from, + Currency to, + BigDecimal amount) + throws InvalidConversionException; +} diff -r 36331f7244bd -r f6073056b9fe task2/solution04/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution04/src/org/apidesign/apifest08/currency/ConvertorFactory.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,116 @@ +package org.apidesign.apifest08.currency; + +import java.lang.ref.WeakReference; +import java.math.BigDecimal; +import java.util.Currency; +import java.util.Map; +import java.util.WeakHashMap; + + +/** + * Create convertors using a flyweight to reduce the number of repetative creations of the same convertor. + * + * @author D'Arcy Smith + * @version 1.0 + */ +public final class ConvertorFactory +{ + /** + * flyweight so that only one vestion of each converter is created at a time. + */ + private final static Map> convertors; + + static + { + convertors = new WeakHashMap>(); + } + + /** + * Prevent accidental construction. + */ + private ConvertorFactory() + { + } + + /** + * Get the convertor for the specified currencies. The currency name format + * must be acceptable to java.util.Currency.getInstance(String) + * + * @param a the currency to convert from. + * @param aRate the exchange rate for a to b. + * @param b the currency to convert to. + * @param bRate the echante rate for b to a. + * @return the convertor for the specified currencies. + * @throws IllegalArgumentException if any of the arguments are null. + */ + public static Convertor getConvertor(final String a, + final BigDecimal aRate, + final String b, + final BigDecimal bRate) + { + final Currency currencyA; + final Currency currencyB; + final Convertor convertor; + + currencyA = Currency.getInstance(a); + currencyB = Currency.getInstance(b); + convertor = getConvertor(currencyA, aRate, currencyB, bRate); + + return (convertor); + } + + /** + * Get the convertor for the specified currencies. + * + * @param a the currency to convert from. + * @param aRate the exchange rate for a to b. + * @param b the currency to convert to. + * @param bRate the echante rate for b to a. + * @return the convertor for the specified currencies. + * @throws IllegalArgumentException if either any of the arguments are null or if either rate <= 0. + */ + public static Convertor getConvertor(final Currency a, + final BigDecimal aRate, + final Currency b, + final BigDecimal bRate) + { + final String key; + Convertor convertor; + + if(a == null) + { + throw new IllegalArgumentException("a cannot be null"); + } + + if(b == null) + { + throw new IllegalArgumentException("b cannot be null"); + } + + if(aRate == null) + { + throw new IllegalArgumentException("aRate cannot be null"); + } + + if(bRate == null) + { + throw new IllegalArgumentException("bRate cannot be null"); + } + + key = a.getCurrencyCode() + aRate + b.getCurrencyCode() + bRate; + + // make sure that we don't try to overwrite one + synchronized(convertors) + { + if(!(convertors.containsKey(key))) + { + convertor = new ConvertorImpl(a, aRate, b, bRate); + convertors.put(key, new WeakReference(convertor)); + } + } + + convertor = convertors.get(key).get(); + + return (convertor); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution04/src/org/apidesign/apifest08/currency/InvalidConversionException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution04/src/org/apidesign/apifest08/currency/InvalidConversionException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,82 @@ +package org.apidesign.apifest08.currency; + + +import java.util.Currency; + + +/** + * Thrown when a currency is invalid for a given Convertor. + * + * @author D'Arcy Smith + * @version 1.0 + */ +public class InvalidConversionException + extends Exception +{ + /** + * The currency that was tried. + */ + private final Currency badCurrency; + + /** + * A currency that is valid for the Convertor. + */ + private final Currency currencyA; + + /** + * A currency that is valid for the Convertor. + */ + private final Currency currencyB; + + /** + * Construct a new InvalidConversionException wit the specified message. + * + * @param msg the message for getMessage. + * @param bad the currency that is not valid. + * @param a a valid currency. + * @param b a valid currency. + */ + public InvalidConversionException(final String msg, + final Currency bad, + final Currency a, + final Currency b) + { + super(msg); + + badCurrency = bad; + currencyA = a; + currencyB = b; + } + + /** + * Get the currency that is not valid. + * + * @return the badCurrency + */ + public Currency getBadCurrency() + { + return (badCurrency); + } + + /** + * Get a currency that is valid. + * + * @return the currencyA passed to the constructor. + */ + public Currency getCurrencyA() + { + return (currencyA); + } + + /** + * Get a currency that is valid. + * + * @return the currencyB passed to the constructor. + */ + public Currency getCurrencyB() + { + return (currencyB); + } + + +} \ No newline at end of file diff -r 36331f7244bd -r f6073056b9fe task2/solution04/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution04/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,184 @@ +package org.apidesign.apifest08.test; + + +import java.math.BigDecimal; +import java.util.Currency; +import junit.framework.TestCase; +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.ConvertorFactory; +import org.apidesign.apifest08.currency.InvalidConversionException; + + +/** 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 { + + private final static Currency CZK; + private final static Currency SKK; + private final static Currency USD; + + static + { + CZK = Currency.getInstance("CZK"); + SKK = Currency.getInstance("SKK"); + USD = Currency.getInstance("USD"); + } + + public Task1Test(String testName) { + super(testName); + } + + @Override + protected void setUp() throws Exception { + } + + @Override + protected void tearDown() throws Exception { + } + + /** Create convertor that understands two currencies, CZK and + * USD. Make 1 USD == 17 CZK. + * + * 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.getConvertor("CZK", BigDecimal.valueOf(17.0), "USD", BigDecimal.valueOf(1))); + } + + /** Create convertor that understands two currencies, CZK and + * SKK. Make 100 SKK == 80 CZK. + * + * 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.getConvertor(Currency.getInstance("SKK"), BigDecimal.valueOf(100), Currency.getInstance("CZK"), BigDecimal.valueOf(80))); + } + + /** Use the convertor from createCZKtoUSD method and do few conversions + * with it. + */ + public void testCurrencyCZKUSD() throws Exception { + Convertor c = createCZKtoUSD(); + BigDecimal result; + + // convert $5 to CZK using c: + // assertEquals("Result is 85 CZK"); + result = c.convert(USD, CZK, BigDecimal.valueOf(5)); + assertEquals(new BigDecimal("85.00"), result); + + // convert $8 to CZK + // assertEquals("Result is 136 CZK"); + result = c.convert(USD, CZK, BigDecimal.valueOf(8)); + assertEquals(new BigDecimal("136.00"), result); + + // convert 1003CZK to USD + // assertEquals("Result is 59 USD"); + result = c.convert(CZK, USD, BigDecimal.valueOf(1003)); + assertEquals(new BigDecimal("59.00"), result); + } + + /** Use the convertor from createSKKtoCZK method and do few conversions + * with it. + */ + public void testCurrencySKKCZK() throws Exception { + Convertor c = createSKKtoCZK(); + BigDecimal result; + + // convert 16CZK using c: + // assertEquals("Result is 20 SKK"); + result = c.convert(CZK, SKK, BigDecimal.valueOf(16)); + assertEquals(new BigDecimal("20.00"), result); + + // convert 500SKK to CZK + // assertEquals("Result is 400 CZK"); + result = c.convert(SKK, CZK, BigDecimal.valueOf(500)); + assertEquals(new BigDecimal("400.00"), result); + } + + /** + * Verify that the CZK to USD convertor knows nothing about SKK. + */ + public void testCannotConvertToSKKwithCZKUSDConvertor() + throws Exception + { + Convertor c = createCZKtoUSD(); + + try + { + // convert $5 to SKK, the API shall say this is not possible + c.convert(USD, SKK, BigDecimal.valueOf(5)); + fail("cannot use the CZKtoUSD converter to convert to SKK"); + } + catch(InvalidConversionException ex) + { + assertEquals("cannot convert to: SKK", ex.getMessage()); + assertEquals(SKK, ex.getBadCurrency()); + assertEquals(CZK, ex.getCurrencyA()); + assertEquals(USD, ex.getCurrencyB()); + } + + try + { + // convert 500 SKK to CZK, the API shall say this is not possible + c.convert(SKK, CZK, BigDecimal.valueOf(5)); + fail("cannot use the CZKtoUSD converter to convert from SKK"); + } + catch(InvalidConversionException ex) + { + assertEquals("cannot convert from: SKK", ex.getMessage()); + assertEquals(SKK, ex.getBadCurrency()); + assertEquals(CZK, ex.getCurrencyA()); + assertEquals(USD, ex.getCurrencyB()); + } + } + + /** + * Verify that the CZK to SKK convertor knows nothing about USD. + */ + public void testCannotConvertToUSDwithSKKCZKConvertor() + throws Exception + { + Convertor c = createSKKtoCZK(); + + try + { + // convert $5 to SKK, the API shall say this is not possible + c.convert(USD, SKK, BigDecimal.valueOf(5)); + fail("cannot use the CZKtoUSD converter to convert to SKK"); + } + catch(InvalidConversionException ex) + { + assertEquals("cannot convert from: USD", ex.getMessage()); + assertEquals(USD, ex.getBadCurrency()); + assertEquals(SKK, ex.getCurrencyA()); + assertEquals(CZK, ex.getCurrencyB()); + } + + try + { + // convert 500 CZK to USD, the API shall say this is not possible + c.convert(CZK, USD, BigDecimal.valueOf(500)); + fail("cannot use the CZKtoUSD converter to convert from SKK"); + } + catch(InvalidConversionException ex) + { + assertEquals("cannot convert to: USD", ex.getMessage()); + assertEquals(USD, ex.getBadCurrency()); + assertEquals(SKK, ex.getCurrencyA()); + assertEquals(CZK, ex.getCurrencyB()); + } + } +} + diff -r 36331f7244bd -r f6073056b9fe task2/solution05/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution05/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution05/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution05/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution05/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution05/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=47a079e2 +nbproject/build-impl.xml.script.CRC32=f7fdafd7 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution05/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution05/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution05/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution05/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 05 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution05/src/org/apidesign/apifest08/currency/Amount.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution05/src/org/apidesign/apifest08/currency/Amount.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,92 @@ +package org.apidesign.apifest08.currency; + +/** + * Amount is a class reprezenting an amount of many. It consist of + * whole currency amount and of pence amount. Both items are long values + * and it's not defined that the 100 pences = 1 amount. It's up to the converter + * to verify such invariants. + * + * @author jindra + */ +public final class Amount { + + private long amount; + private long pence; + + /** + * Construct Amount with no pences. + * + * @param amount the amount in some currency + * + */ + public Amount(long amount) { + this.amount = amount; + this.pence = 0; + } + + /** + * Construct Amount with the pences. + * + * @param amount the amount in some currency + * @param pence the pence count + */ + public Amount(long amount, long pence) { + this.amount = amount; + this.pence = pence; + } + + /** + * @return the amount + */ + public long getAmount() { + return amount; + } + + /** + * @param amount the amount to set + */ + public void setAmount(long amount) { + this.amount = amount; + } + + /** + * @return the pence + */ + public long getPence() { + return pence; + } + + /** + * @param pence the pence to set + */ + public void setPence(long pence) { + this.pence = pence; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof Amount)) { + return false; + } + Amount other = (Amount) obj; + return (amount == other.amount) && (pence == other.pence); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 79 * hash + (int) (this.amount ^ (this.amount >>> 32)); + hash = 79 * hash + (int) (this.pence ^ (this.pence >>> 32)); + return hash; + } + + @Override + public String toString() { + return amount + "." + pence; + } + + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution05/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution05/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,25 @@ +package org.apidesign.apifest08.currency; + +/** + * Convertor is an interface reprezenting a convertor between currencies. + * It's able to convert one currency to the second one and back from the second one + * to the primary. + */ +public interface Convertor { + + /** + * Convert amount of primary currency into secondary currency + * + * @param amount the amount in the primary currency + * @return an amount in the secondary currency + */ + Amount convert(Amount primaryAmount); + + /** + * Convert amount of secondary currency back into primary currency + * + * @param amount the amount in the secondary currency + * @return an amount in the primary currency + */ + Amount convertBack(Amount secondaryAmount); +} diff -r 36331f7244bd -r f6073056b9fe task2/solution05/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution05/src/org/apidesign/apifest08/currency/ConvertorFactory.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,30 @@ +package org.apidesign.apifest08.currency; + +/** + * Convertor factory is a factory class for creating {@link Convertor Convertor} + * instances. + * + * @author jindra + */ +public final class ConvertorFactory { + + // this class needs no instances + private ConvertorFactory() { + } + + + /** + * Create a {@link Convertor Convertor} with given exchange rate + * + * @param exchangeRate double reprezenting the exchange rate from primary currency into + * the secundary currecny + * @return {@link Convertor Convertor} instance with given exchange rate + */ + public static Convertor createConvertor(double exchangeRate) { + if (exchangeRate == 0) { + throw new IllegalArgumentException("Zero exchange rate is not allowed."); + } + return new ConvertorImpl(exchangeRate); + + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution05/src/org/apidesign/apifest08/currency/ConvertorImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution05/src/org/apidesign/apifest08/currency/ConvertorImpl.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,56 @@ +package org.apidesign.apifest08.currency; + +/** + * ConvetorImpl it the basic implementaion of Convertor interface. + * @see Convertor Convertor for more details. + * The 100 pences makes 1 amount of the currency. + * + * @author jindra + */ +final class ConvertorImpl implements Convertor { + + private static final double P_TO_AM = 100; + private double exchangeRate; + + ConvertorImpl(double exchangeRate) { + this.exchangeRate = exchangeRate; + } + + public Amount convert(Amount amount) { + verifyInput(amount); + double result = convertToDouble(amount) * exchangeRate; + return convertToAmount(result); + } + + public Amount convertBack(Amount amount) { + if (amount == null) { + throw new IllegalArgumentException("Amount must be not null"); + } + double result = convertToDouble(amount) / exchangeRate; + return convertToAmount(result); + } + + private double convertToDouble(Amount amount) { + double am = amount.getAmount(); + double pc = amount.getPence(); + return am + (pc / P_TO_AM); + } + + private Amount convertToAmount(double result) { + long resultAm = Math.round(Math.floor(result)); + long resultPc = Math.round(Math.floor((result * P_TO_AM - resultAm * P_TO_AM))); + return new Amount(resultAm, resultPc); + } + + private void verifyInput(Amount amount) { + if (amount == null) { + throw new IllegalArgumentException("Amount must be not null"); + } + if (amount.getPence() < 0) { + throw new IllegalArgumentException("Pences must not be negative"); + } + if (amount.getPence() > P_TO_AM) { + throw new IllegalArgumentException("Pences must not be over P_TO_AM"); + } + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution05/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution05/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,104 @@ +package org.apidesign.apifest08.test; + +import junit.framework.TestCase; +import org.apidesign.apifest08.currency.Amount; +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 { + } + + /** Create convertor that understands two currencies, CZK and + * USD. Make 1 USD == 17 CZK. + * + * 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.createConvertor(17); + } + + /** Create convertor that understands two currencies, CZK and + * SKK. Make 100 SKK == 80 CZK. + * + * 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.createConvertor(0.8); + } + + /** 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", 85l, c.convert(new Amount(5)).getAmount()); + + // convert $8 to CZK + assertEquals("Result is 136 CZK", 136l, c.convert(new Amount(8)).getAmount()); + + // convert 1003CZK to USD + assertEquals("Result is 136 CZK", 59l, c.convertBack(new Amount(1003)).getAmount()); + // assertEquals("Result is 59 USD"); + } + + /** 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", 20l, c.convertBack(new Amount(16)).getAmount()); + + // convert 500SKK to CZK + assertEquals("Result is 400 CZK", 400l, c.convert(new Amount(500)).getAmount()); + } + + /** Use the convertor from createSKKtoCZK method and do few conversions + * with it. + */ + public void testCurrencySKKCZKwithPences() throws Exception { + Convertor c = createSKKtoCZK(); + // convert 16CZK 16h using c: + assertEquals("Result is 20 SKK 20h", new Amount(20, 20), c.convertBack(new Amount(16, 16))); + + // convert 500SKK 80h to CZK + assertEquals("Result is 400 CZK 64h", new Amount(400, 64), c.convert(new Amount(500, 80))); + + // convert 400CZK 80h to SKK + assertEquals("Result is 501 CZK", new Amount(501), c.convertBack(new Amount(400, 80))); + } + + public void testNegativeSKKCZ() throws Exception{ + Convertor c = createSKKtoCZK(); + // convert -16CZK using c: + assertEquals("Result is -20", new Amount(-20), c.convertBack(new Amount(-16))); + + // convert -500SKK 80h to CZK + assertEquals("Result is -400CZK 64h", new Amount(-400, 64), c.convert(new Amount(-500, 80))); + + } +} + diff -r 36331f7244bd -r f6073056b9fe task2/solution06/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution06/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution06/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=ff801896 +nbproject/build-impl.xml.script.CRC32=a0996c47 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution06/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution06/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 06 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution06/src/org/apidesign/apifest08/currency/Amount.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/src/org/apidesign/apifest08/currency/Amount.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,81 @@ +package org.apidesign.apifest08.currency; + +import static org.apidesign.apifest08.currency.Assert.notNull; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Currency; + +/** + * An amount representation. Amount is represented as composition of a value and + * a currency. + */ +public final class Amount { + + private final BigDecimal value; + private final Currency currency; + private final int scale; + private final RoundingMode roundingMode; + + public static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_EVEN; + + public Amount(final BigDecimal value, final Currency currency) { + notNull(value, "value"); + notNull(currency, "currency"); + this.value = value; + this.currency = currency; + this.scale = currency.getDefaultFractionDigits(); + this.roundingMode = DEFAULT_ROUNDING; + } + + public Amount(final BigDecimal value, final Currency currency, final RoundingMode roundingMode) { + notNull(value, "value"); + notNull(currency, "currency"); + notNull(roundingMode, "roundingMode"); + + this.value = value; + this.currency = currency; + this.scale = currency.getDefaultFractionDigits(); + this.roundingMode = roundingMode; + } + + public Amount(final long value, final Currency currency) { + this(BigDecimal.valueOf(value), currency); + } + + public Amount(final String value, final Currency currency) { + this(new BigDecimal(value), currency); + } + + /** + * @return the value with scale of the associated currency and rounded by + * the rounding mode. + */ + public BigDecimal getValue() { + return value.setScale(scale, roundingMode); + } + + /** + * @return the raw (no explicit scale, no explicit rounding) value + */ + public BigDecimal getRawValue() { + return value; + } + + public Currency getCurrency() { + return currency; + } + + public int getScale() { + return scale; + } + + public RoundingMode getRoundingMode() { + return roundingMode; + } + + @Override + public String toString() { + return value + ",- " + currency.toString(); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution06/src/org/apidesign/apifest08/currency/Assert.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/src/org/apidesign/apifest08/currency/Assert.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,11 @@ +package org.apidesign.apifest08.currency; + +public final class Assert { + static void notNull(Object value, String argumentName) { + if(value == null) { + throw new IllegalArgumentException("The argument '" + argumentName + "' connot not be null"); + } + } +} + + diff -r 36331f7244bd -r f6073056b9fe task2/solution06/src/org/apidesign/apifest08/currency/ConversionException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/src/org/apidesign/apifest08/currency/ConversionException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,26 @@ +package org.apidesign.apifest08.currency; + +/** + * Indicates that a desired conversion cannot be performed. + */ +public class ConversionException extends CurrencyException { + + private static final long serialVersionUID = 1L; + + public ConversionException() { + super(); + } + + public ConversionException(String message, Throwable cause) { + super(message, cause); + } + + public ConversionException(String message) { + super(message); + } + + public ConversionException(Throwable cause) { + super(cause); + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution06/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,64 @@ +package org.apidesign.apifest08.currency; + +import static org.apidesign.apifest08.currency.Assert.notNull; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Currency; + +public final class Convertor { + + private final Currency first; + private final Currency second; + private final BigDecimal rateValue; // a rate between the first currency and the second currency + public static final BigDecimal one = new BigDecimal(1); + + public Convertor(BigDecimal rateValue, Currency currencyFirst, Currency currencySecond) { + notNull(currencyFirst, "currencyFirst"); + notNull(currencySecond, "currencySecond"); + notNull(rateValue, "rateValue"); + + this.rateValue = rateValue; + this.first = currencyFirst; + this.second = currencySecond; + } + + /** + * Converts an amount value between the two currencies of this converter. + * + * @param amount an amount + * @param fromCurrency an amount currency + * @param toCurrency to a target currency + * @return a converted amount value + * + * @throws ConversionException if the conversion fails + * @throws UnsupportedConversionException if the conversion between a given currencies is not supported. + */ + public Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency) throws ConversionException { + notNull(amount, "amount"); + notNull(fromCurrency, "fromCurrency"); + notNull(toCurrency, "toCurrency"); + + if((fromCurrency != first && fromCurrency != second) || (toCurrency != first && toCurrency != second)) { + throw new UnsupportedConversionException(fromCurrency, toCurrency); + } + + BigDecimal rateValue = getRateValue(fromCurrency, toCurrency); + BigDecimal result = rateValue.multiply(amount); + return new Amount(result, toCurrency); + } + + private BigDecimal getRateValue(Currency fromCurrency, Currency toCurrency) { + + BigDecimal retVal; + + if(first == fromCurrency) { + retVal = rateValue; + } else { + //reverse rate + retVal = one.divide(rateValue, 10 ,RoundingMode.HALF_UP); + } + + return retVal; + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution06/src/org/apidesign/apifest08/currency/CurrencyException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/src/org/apidesign/apifest08/currency/CurrencyException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,25 @@ +package org.apidesign.apifest08.currency; + +/** + * Top level runtime exception for 'currency' API. + */ +public class CurrencyException extends RuntimeException{ + + private static final long serialVersionUID = 1L; + + public CurrencyException() { + super(); + } + + public CurrencyException(String message, Throwable cause) { + super(message, cause); + } + + public CurrencyException(String message) { + super(message); + } + + public CurrencyException(Throwable cause) { + super(cause); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution06/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,27 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; + +public final class UnsupportedConversionException extends ConversionException{ + + private static final long serialVersionUID = 1L; + + private Currency from; + private Currency to; + + public UnsupportedConversionException(Currency from, Currency to) { + super("Conversion from the currency " + from + " to the currency " + to + " or vice versa in not supported."); + this.from = from; + this.to = to; + } + + public Currency getFrom() { + return from; + } + + public Currency getTo() { + return to; + } + + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution06/test/org/apidesign/apifest08/test/Currencies.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/test/org/apidesign/apifest08/test/Currencies.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,9 @@ +package org.apidesign.apifest08.test; + +import java.util.Currency; + +public class Currencies { + public static final Currency CZK = Currency.getInstance("CZK"); + public static final Currency SKK = Currency.getInstance("SKK"); + public static final Currency USD = Currency.getInstance("USD"); +} diff -r 36331f7244bd -r f6073056b9fe task2/solution06/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution06/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,136 @@ +package org.apidesign.apifest08.test; + +import static org.apidesign.apifest08.test.Currencies.CZK; +import static org.apidesign.apifest08.test.Currencies.SKK; +import static org.apidesign.apifest08.test.Currencies.USD; + +import java.math.BigDecimal; + +import junit.framework.TestCase; + +import org.apidesign.apifest08.currency.Amount; +import org.apidesign.apifest08.currency.ConversionException; +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.UnsupportedConversionException; + +/** 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 { + } + + /** Create convertor that understands two currencies, CZK and + * USD. Make 1 USD == 17 CZK. + * + * 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 new Convertor(new BigDecimal(17), USD, CZK); + } + + /** Create convertor that understands two currencies, CZK and + * SKK. Make 100 SKK == 80 CZK. + * + * 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 new Convertor(new BigDecimal("0.8"), SKK, CZK); + } + + /** 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: + Amount result = c.convert(new BigDecimal(5), USD, CZK); + assertEquals("Result is 85 CZK", 85, result.getValue().intValue()); + + // convert $8 to CZK + result = c.convert(new BigDecimal(8), USD, CZK); + assertEquals("Result is 136 CZK", 136, result.getValue().intValue()); + + // convert 1003CZK to USD + result = c.convert(new BigDecimal(1003), CZK, USD); + assertEquals("Result is 59 USD", 59, result.getValue().intValue()); + } + + /** Use the convertor from createSKKtoCZK method and do few conversions + * with it. + */ + public void testCurrencySKKCZK() throws Exception { + Convertor c = createSKKtoCZK(); + // convert 16CZK using c: + Amount result = c.convert(new BigDecimal(16), CZK, SKK); + assertEquals("Result is 20 SKK", 20, result.getValue().intValue()); + + // convert 500SKK to CZK + result = c.convert(new BigDecimal(500), SKK, CZK); + assertEquals("Result is 400 CZK", 400, result.getValue().intValue()); + } + + + /** + * 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(new BigDecimal(5), USD, SKK); + fail("convert $5 to SKK, the API shall say this is not possible"); + } catch (ConversionException e) { + //expected + } + + // convert 500 SKK to CZK, the API shall say this is not possible + + try { + c.convert(new BigDecimal("500"), SKK, CZK); + fail("convert 500 SKK to CZK, the API shall say this is not possible"); + } catch (ConversionException e) { + //expected + } + } + + /** + * Verify that the CZK to SKK convertor knows nothing about USD. + */ + public void testCannotConvertToSKKwithCZKSKKConvertor() throws Exception { + Convertor c = createSKKtoCZK(); + // convert $5 to SKK, the API shall say this is not possible + try { + c.convert(new BigDecimal(5), USD, SKK); + fail("convert $5 to SKK, the API shall say this is not possible"); + } catch(ConversionException e) { + //expected + } + + try { + c.convert(new BigDecimal(500), CZK, USD); + fail("convert 500 CZK to USD, the API shall say this is not possible"); + } catch(ConversionException e) { + //expected + } + } +} + diff -r 36331f7244bd -r f6073056b9fe task2/solution07/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution07/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution07/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=979fc7ba +nbproject/build-impl.xml.script.CRC32=92452d37 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution07/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution07/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 07 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution07/src/org/apidesign/apifest08/currency/ConversionRate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/src/org/apidesign/apifest08/currency/ConversionRate.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,91 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * A rate of conversion from one currency to another. + * @author jdvorak + */ +public class ConversionRate { + + private final MonetaryAmount srcUnitAmount; + private final MonetaryAmount tgtUnitAmount; + private final int tgtScale; + private final RoundingMode roundingMode; + + /** + * A new conversion rate that gives tgtUnitAmount per every srcUnitAmount. + * @param srcUnitAmount the amount of source currency + * @param tgtUnitAmount the corresponding amount of target currency + * @param tgtScale the scale of the target amounts + * @param roundingMode the rounding mode to use when producing the target amounts + */ + public ConversionRate( final MonetaryAmount srcUnitAmount, final MonetaryAmount tgtUnitAmount, final int targetScale, final RoundingMode roundingMode ) { + this.srcUnitAmount = srcUnitAmount; + this.tgtUnitAmount = tgtUnitAmount; + this.tgtScale = targetScale; + this.roundingMode = roundingMode; + } + + /** + * A new conversion rate that gives tgtUnitAmount per every srcUnitAmount, default number of fraction digits and the given rounding mode. + * @param srcUnitAmount the amount of source currency + * @param tgtUnitAmount the corresponding amount of target currency + * @param roundingMode the rounding mode to use + */ + public ConversionRate( final MonetaryAmount srcUnitAmount, final MonetaryAmount tgtUnitAmount, final RoundingMode roundingMode ) { + this( srcUnitAmount, tgtUnitAmount, tgtUnitAmount.getCurrency().getDefaultFractionDigits(), roundingMode ); + } + + /** + * A new conversion rate that gives tgtUnitAmount per every srcUnitAmount, default number of fraction digits and {@link RoundingMode#HALF_EVEN}. + * @param srcUnitAmount the amount of source currency + * @param tgtUnitAmount the corresponding amount of target currency + */ + public ConversionRate( final MonetaryAmount srcUnitAmount, final MonetaryAmount tgtUnitAmount ) { + this( srcUnitAmount, tgtUnitAmount, RoundingMode.HALF_EVEN ); + } + + public RoundingMode getRoundingMode() { + return roundingMode; + } + + public MonetaryAmount getSrcUnitAmount() { + return srcUnitAmount; + } + + public int getTgtScale() { + return tgtScale; + } + + public MonetaryAmount getTgtUnitAmount() { + return tgtUnitAmount; + } + + /** + * Multiplies the given amount with the given rate. + * @param srcAmount + * @return + */ + public BigDecimal convert( final BigDecimal srcAmount ) { + return srcAmount + .multiply( tgtUnitAmount.getAmount() ) + .divide( srcUnitAmount.getAmount(), tgtScale, roundingMode ); + } + + /** + * Creates a monetary amount that corresponds to the given source amount multiplied by the rate. + * @param srcAmount the source amount + * @return the monetary amount in the target currency + * @throws IllegalArgumentException if the currency of srcAmount is not equal to the source currency of this rate + */ + public MonetaryAmount convert( final MonetaryAmount srcAmount ) { + if ( srcUnitAmount.getCurrency().equals( srcAmount.getCurrency() ) ) { + return new MonetaryAmount( convert( srcAmount.getAmount() ), tgtUnitAmount.getCurrency() ); + } else { + throw new IllegalArgumentException( "This rate converts from " + srcUnitAmount.getCurrency() + ", but a conversion from " + srcAmount.getCurrency() + " is attempted" ); + } + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution07/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,109 @@ +package org.apidesign.apifest08.currency; + +import java.util.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 interface Convertor { + + /** + * Converts by taking a request and producing a response. + * If a convertor finds it cannot perform the requested conversion, + * it should return a non-null {@link ConversionResult} that has null {@link ConversionResult#getNetAmount()}. + * A convertor must not convert to a different currency than the one specified in the request. + *

+ * When the need comes to extend the semantics, one subclasses the ConversionRequest and/or ConversionResult classes. + *

+ * This method can be called as many times as you like. + * A {@link Convertor} shall be considered immutable wrt calls to {@link #convert(org.apidesign.apifest08.currency.Convertor.ConversionRequest). + * This method of a single {@link Convertor} can be called from many threads concurrently. + * @param req the conversion request; mustn't be null + * @return the result of carrying out the conversion request; never null + * @throws IllegalRequestSubtypeException when the particular implementation cannot handle a specific ConversionRequest type + */ + public ConversionResult convert( final ConversionRequest req ) throws IllegalRequestSubtypeException; + + /** + * The request for converting a monetary amout into another currency. + * Immutable. + */ + public class ConversionRequest { + + private final MonetaryAmount srcAmount; + private final Currency tgtCurrency; + + /** + * A request to convert srcAmount into tgtCurrency. + * @param srcAmount the source amount; must not be null + * @param tgtCurrency the currency we want it in afterwards; must not be null + */ + public ConversionRequest( final MonetaryAmount srcAmount, final Currency tgtCurrency ) { + this.srcAmount = srcAmount; + this.tgtCurrency = tgtCurrency; + if ( srcAmount == null ) { + throw new NullPointerException( "The source amount" ); + } + if ( tgtCurrency == null ) { + throw new NullPointerException( "The target currency" ); + } + if ( srcAmount.getCurrency().equals( tgtCurrency ) ) { + throw new IllegalArgumentException( "Cannot request conversion from " + srcAmount.getCurrency() + " to " + tgtCurrency ); + } + } + + /** + * The source amount. + */ + public MonetaryAmount getSrcAmount() { + return srcAmount; + } + + /** + * The target currency. + */ + public Currency getTgtCurrency() { + return tgtCurrency; + } + + } + + /** + * The result of converting a monetary amount into another currency. + * For now it records just the net amount one recieves from the conversion. + * Immutable. + *

+ * Extension note: + * Other items can be added further down the road, as the need for them arises. + * These items might provide info on other aspects of the conversion, + * such as the fee or a reason why the conversion might not be admissible. + */ + public class ConversionResult { + + private final MonetaryAmount netAmount; + + /** + * A new conversion result. + * @param netAmount the amount one recieves from the conversion; + * null means the conversion was not admissible + */ + public ConversionResult( final MonetaryAmount netAmount ) { + this.netAmount = netAmount; + } + + /** + * The amount one recieves from the conversion. + * If null, the conversion is not admissible. + * @return the amount + */ + public MonetaryAmount getNetAmount() { + return netAmount; + } + + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution07/src/org/apidesign/apifest08/currency/DelegatingConvertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/src/org/apidesign/apifest08/currency/DelegatingConvertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,28 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.apidesign.apifest08.currency; + +/** + * + * @author jdvorak + */ +public class DelegatingConvertor implements Convertor { + + private final Convertor underlyingConvertor; + + public DelegatingConvertor( final Convertor underlyingConvertor ) { + this.underlyingConvertor = underlyingConvertor; + } + + protected Convertor getUnderlyingConvertor() { + return underlyingConvertor; + } + + public ConversionResult convert( final ConversionRequest req ) { + return underlyingConvertor.convert( req ); + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution07/src/org/apidesign/apifest08/currency/IllegalRequestSubtypeException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/src/org/apidesign/apifest08/currency/IllegalRequestSubtypeException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.apidesign.apifest08.currency; + +/** + * Rised when a {@link Convertor} implementation cannot handle a particular subtype of {@link Convertor.ConversionRequest}. + * @author jdvorak + */ +public class IllegalRequestSubtypeException extends IllegalArgumentException { + + public IllegalRequestSubtypeException() { + super(); + } + + public IllegalRequestSubtypeException( final String msg ) { + super( msg ); + } + + public IllegalRequestSubtypeException( final Throwable cause ) { + super( cause ); + } + + public IllegalRequestSubtypeException( final String msg, final Throwable cause ) { + super( msg, cause ); + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution07/src/org/apidesign/apifest08/currency/MonetaryAmount.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/src/org/apidesign/apifest08/currency/MonetaryAmount.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,89 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; +import java.util.Currency; + +/** + * An amount of a currency. + * Immutable. + * @author jdvorak + */ +public class MonetaryAmount { + + private final BigDecimal amount; + private final Currency currency; + + /** + * A new amount. + * @param amount the quantity of the currency; must not be null + * @param currency the currency; must not be null + */ + public MonetaryAmount( final BigDecimal amount, final Currency currency ) { + this.amount = amount; + this.currency = currency; + if ( amount == null ) { + throw new NullPointerException( "The amount" ); + } + if ( currency == null ) { + throw new NullPointerException( "The currency" ); + } + } + + /** + * A new amount. + * @param amount the quantity of the currency; must not be null + * @param currency the currency; must not be null + */ + public MonetaryAmount( final double amount, final Currency currency ) { + this( new BigDecimal( amount ), currency ); + } + + /** + * The amount. + * @return the amount + */ + public BigDecimal getAmount() { + return amount; + } + + /** + * The currency. + * @return the currency + */ + public Currency getCurrency() { + return currency; + } + + /** + * The string representation of the monetary amount. + * @return the amount, a non-breakable space, the currency + */ + @Override + public String toString() { + return amount.toPlainString() + "\u00a0" + currency.toString(); + } + + /** + * Two monetary amounts are equal to each other iff they have equal amounts of equal currencies. + * @param other the other object + * @return equality + */ + @Override + public boolean equals( final Object other ) { + if ( other instanceof MonetaryAmount ) { + final MonetaryAmount otherMonetaryAmount = (MonetaryAmount) other; + return getAmount().equals( otherMonetaryAmount.getAmount() ) && getCurrency().equals( otherMonetaryAmount.getCurrency() ); + } + return false; + } + + /** + * The hash code combines the hash codes of the amount and of the currency. + * @return hash code + */ + @Override + public int hashCode() { + return amount.hashCode() * 37 + currency.hashCode(); + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution07/src/org/apidesign/apifest08/currency/TableConvertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/src/org/apidesign/apifest08/currency/TableConvertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,71 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; +import java.util.HashMap; +import java.util.Map; + +/** + * A {@link Convertor} that works from a pre-set conversion table. + * First use {@link #putIntoTable(org.apidesign.apifest08.currency.ConversionRate)} to set the conversion table. + * Then invoke the {@link #convert(org.apidesign.apifest08.currency.Convertor.ConversionRequest)} method as many times as you wish. + * @author jdvorak + */ +public class TableConvertor implements Convertor { + + private final Map> conversionTable = new HashMap>(); + + public TableConvertor() { + } + + /** + * Puts a rate into the table. + * @param rate + */ + public void putIntoTable( final ConversionRate rate ) { + final Currency srcCurrency = rate.getSrcUnitAmount().getCurrency(); + final Currency tgtCurrency = rate.getTgtUnitAmount().getCurrency(); + synchronized ( conversionTable ) { + Map targetTable = conversionTable.get( srcCurrency ); + if ( targetTable == null ) { + targetTable = new HashMap(); + conversionTable.put( srcCurrency, targetTable ); + } + targetTable.put( tgtCurrency, rate ); + } + } + + /** + * Carries out the conversion. + * If the table does not contain a conversion from the source currency to the target one, + * a {@link ConversionResult} is returned that has null netAmount. + * This implementation works with any {@link ConversionRequest}, it won't throw {@link IllegalRequestSubtypeException}. + * @param req the conversion request + * @return the conversion result + */ + public ConversionResult convert( final ConversionRequest req ) { + final Currency srcCurrency = req.getSrcAmount().getCurrency(); + final Currency tgtCurrency = req.getTgtCurrency(); + final ConversionRate rate = findConversionRate( srcCurrency, tgtCurrency ); + if ( rate != null ) { + final MonetaryAmount tgtAmount = rate.convert( req.getSrcAmount() ); + return new ConversionResult( tgtAmount ); + } else { + return new ConversionResult( null ); // did not find the pair of currencies in the table + } + } + + /** + * Looks up the conversion between the given currencies in the table. + * @param srcCurrency the source currency + * @param tgtCurrency the target currency + * @return the conversion rate; null means no conversion between the currencies was found in the table + */ + protected ConversionRate findConversionRate( final Currency srcCurrency, final Currency tgtCurrency ) { + synchronized ( conversionTable ) { + final Map targetTable = conversionTable.get(srcCurrency); + final ConversionRate rate = (targetTable != null) ? targetTable.get(tgtCurrency) : null; + return rate; + } + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution07/test/org/apidesign/apifest08/test/ContractImposingDelegatingConvertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/test/org/apidesign/apifest08/test/ContractImposingDelegatingConvertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,53 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.apidesign.apifest08.test; + +import java.math.BigDecimal; +import java.util.Currency; +import junit.framework.Assert; +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.DelegatingConvertor; +import org.apidesign.apifest08.currency.MonetaryAmount; + +/** + * A delegating convertor that checks preconditions and postconditions. + * Useful for testing. + * @author jdvorak + */ +public class ContractImposingDelegatingConvertor extends DelegatingConvertor { + + public ContractImposingDelegatingConvertor( final Convertor underlyingConvertor ) { + super( underlyingConvertor ); + } + + @Override + public ConversionResult convert( final ConversionRequest req ) { + Assert.assertNotNull( "The request", req ); + final ConversionResult result = super.convert( req ); + Assert.assertNotNull( "Result of the convert() call", result ); + final MonetaryAmount netAmount = result.getNetAmount(); + if ( netAmount != null ) { + Assert.assertEquals( "Converted to a different currency than specified in the request", req.getTgtCurrency(), netAmount.getCurrency() ); + } + return result; + } + + /** + * Do some tests on our own. + * @return this + */ + public Convertor test() { + try { + final Currency aCurrency = Currency.getInstance( "EUR" ); + new ConversionRequest( new MonetaryAmount( BigDecimal.ONE, aCurrency ), aCurrency ); + Assert.fail( "Should have thrown an IllegalArgumentException" ); + } catch ( final IllegalArgumentException e ) { + Assert.assertEquals( "Cannot request conversion from EUR to EUR", e.getMessage() ); + } + return this; + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution07/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution07/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,177 @@ +package org.apidesign.apifest08.test; + +import java.util.Currency; +import junit.framework.TestCase; +import org.apidesign.apifest08.currency.ConversionRate; +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.MonetaryAmount; +import org.apidesign.apifest08.currency.TableConvertor; + +/** 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 + // + + protected static final Currency CZK = Currency.getInstance( "CZK" ); + protected static final Currency SKK = Currency.getInstance( "SKK" ); + protected static final Currency USD = Currency.getInstance( "USD" ); + + /** Create convertor that understands two currencies, CZK and + * USD. Make 1 USD == 17 CZK. + * 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() { + final TableConvertor convertor = new TableConvertor(); + final MonetaryAmount amountInCZK = new MonetaryAmount( 17, CZK ); + final MonetaryAmount amountInUSD = new MonetaryAmount( 1, USD ); + convertor.putIntoTable( new ConversionRate( amountInCZK, amountInUSD ) ); + convertor.putIntoTable( new ConversionRate( amountInUSD, amountInCZK ) ); + return new ContractImposingDelegatingConvertor( convertor ).test(); + } + + /** 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() { + final TableConvertor convertor = new TableConvertor(); + final MonetaryAmount amountInSKK = new MonetaryAmount( 100, SKK ); + final MonetaryAmount amountInCZK = new MonetaryAmount( 80, CZK ); + convertor.putIntoTable( new ConversionRate( amountInSKK, amountInCZK ) ); + convertor.putIntoTable( new ConversionRate( amountInCZK, amountInSKK ) ); + return new ContractImposingDelegatingConvertor( convertor ).test(); + } + + // + // 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 { + final Convertor c = createCZKtoUSD(); + + // convert $5 to CZK using c: + final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), CZK ) ); + final MonetaryAmount a1 = r1.getNetAmount(); + // assertEquals("Result is 85 CZK"); + assertNotNull( a1 ); + assertEquals( 85.0, a1.getAmount().doubleValue() ); + assertEquals( CZK, a1.getCurrency() ); + + // convert $8 to CZK + final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 8, USD ), CZK ) ); + final MonetaryAmount a2 = r2.getNetAmount(); + // assertEquals("Result is 136 CZK"); + assertNotNull( a2 ); + assertEquals( 136.0, a2.getAmount().doubleValue() ); + assertEquals( CZK, a2.getCurrency() ); + + // convert 1003CZK to USD + final Convertor.ConversionResult r3 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 1003, CZK ), USD ) ); + final MonetaryAmount a3 = r3.getNetAmount(); + // assertEquals("Result is 59 USD"); + assertNotNull( a3 ); + assertEquals( 59.0, a3.getAmount().doubleValue() ); + assertEquals( USD, a3.getCurrency() ); + } + + /** Use the convertor from createSKKtoCZK method and do few conversions + * with it. + */ + public void testCurrencySKKCZK() throws Exception { + final Convertor c = createSKKtoCZK(); + + // convert 16CZK using c: + final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 16, CZK ), SKK ) ); + final MonetaryAmount a1 = r1.getNetAmount(); + // assertEquals("Result is 20 SKK"); + assertNotNull( a1 ); + assertEquals( 20.0, a1.getAmount().doubleValue() ); + assertEquals( SKK, a1.getCurrency() ); + + // convert 500SKK to CZK + final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 500, SKK ), CZK ) ); + final MonetaryAmount a2 = r2.getNetAmount(); + // assertEquals("Result is 400 CZK"); + assertNotNull( a2 ); + assertEquals( 400.0, a2.getAmount().doubleValue() ); + assertEquals( CZK, a2.getCurrency() ); + } + + /** Verify that the CZK to USD convertor knows nothing about SKK. + */ + public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception { + final Convertor c = createCZKtoUSD(); + + // convert $5 to SKK, the API shall say this is not possible + final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), SKK ) ); + final MonetaryAmount a1 = r1.getNetAmount(); + assertNull( a1 ); + + // convert 500 SKK to CZK, the API shall say this is not possible + final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, SKK ), CZK ) ); + final MonetaryAmount a2 = r2.getNetAmount(); + assertNull( a2 ); + } + + /** Verify that the CZK to SKK convertor knows nothing about USD. + */ + public void testCannotConvertToUSDwithCZKSKKConvertor() throws Exception { + final Convertor c = createSKKtoCZK(); + + // convert $5 to SKK, the API shall say this is not possible + final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), SKK ) ); + final MonetaryAmount a1 = r1.getNetAmount(); + assertNull( a1 ); + + // convert 500 CZK to USD, the API shall say this is not possible + final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, CZK ), USD ) ); + final MonetaryAmount a2 = r2.getNetAmount(); + assertNull( a2 ); + } + +} + diff -r 36331f7244bd -r f6073056b9fe task2/solution08/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution08/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution08/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution08/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution08/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution08/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=a2ad29dd +nbproject/build-impl.xml.script.CRC32=43c3e6a6 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution08/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution08/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution08/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution08/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 08 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution08/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution08/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,55 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; +import java.util.Hashtable; +import java.util.Map; + +/** 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 class Convertor { + + private static final Map EXCHANGE_RATES = new Hashtable() { { + put("CZKUSD", 1/17F); + put("USDCZK", 17F); + put("SKKCZK", 100/80F); + put("CZKSKK", 80/100F); + } + }; + + private Currency currencyFirst; + private Currency currencySecond; + + private Convertor(Currency currencyFirst, Currency currencySecond) { + this.currencyFirst = currencyFirst; + this.currencySecond = currencySecond; + } + + public static Convertor getInstanceFor(Currency currencyFirst, Currency currencySecond) { + return new Convertor(currencyFirst, currencySecond); + } + + public float convert(float value, Currency toCurrency) { + if (!toCurrency.equals(currencyFirst) && !toCurrency.equals(currencySecond)) { + throw new IllegalArgumentException("Unsupported currency for this convertor!: " + toCurrency.getCurrencyCode()); + } + + Float rate = null; + if (toCurrency.equals(currencyFirst)) { + rate = EXCHANGE_RATES.get(currencyFirst.getCurrencyCode() + currencySecond.getCurrencyCode()); + } + if (toCurrency.equals(currencySecond)) { + rate = EXCHANGE_RATES.get(currencySecond.getCurrencyCode() + currencyFirst.getCurrencyCode()); + } + + if (rate == null) { + throw new IllegalStateException("Undefinied conversion!"); + } + + return rate*value; + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution08/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution08/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,86 @@ +package org.apidesign.apifest08.test; + +import java.util.Currency; +import junit.framework.TestCase; +import org.apidesign.apifest08.currency.Convertor; + +/** 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 { + } + + /** Create convertor that understands two currencies, CZK and + * USD. Make 1 USD == 17 CZK. + * + * 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 Convertor.getInstanceFor(Currency.getInstance("CZK"), Currency.getInstance("USD")); + } + + /** Create convertor that understands two currencies, CZK and + * SKK. Make 100 SKK == 80 CZK. + * + * 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 Convertor.getInstanceFor(Currency.getInstance("SKK"), Currency.getInstance("CZK")); + } + + /** Use the convertor from createCZKtoUSD method and do few conversions + * with it. + */ + public void testCurrencyCZKUSD() throws Exception { + Convertor c = createCZKtoUSD(); + Currency usd = Currency.getInstance("USD"); + Currency czk = Currency.getInstance("CZK"); + // convert $5 to CZK using c: + // assertEquals("Result is 85 CZK"); + assertEquals(85F, c.convert(5, usd)); + + // convert $8 to CZK + // assertEquals("Result is 136 CZK"); + assertEquals(136F, c.convert(8, usd)); + + // convert 1003CZK to USD + // assertEquals("Result is 59 USD"); + assertEquals(59F, c.convert(1003, czk)); + } + + /** Use the convertor from createSKKtoCZK method and do few conversions + * with it. + */ + public void testCurrencySKKCZK() throws Exception { + Convertor c = createSKKtoCZK(); + Currency skk = Currency.getInstance("SKK"); + Currency czk = Currency.getInstance("CZK"); + // convert 16CZK using c: + // assertEquals("Result is 20 SKK"); + assertEquals(20F, c.convert(16, skk)); + + // convert 500SKK to CZK + // assertEquals("Result is 400 CZK"); + assertEquals(400F, c.convert(500, czk)); + } +} + diff -r 36331f7244bd -r f6073056b9fe task2/solution09/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution09/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution09/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution09/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution09/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution09/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=cab2f6f1 +nbproject/build-impl.xml.script.CRC32=711fa7d6 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution09/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution09/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution09/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution09/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 09 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution09/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution09/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,26 @@ +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 interface Convertor { + + /** + * converts amount in first currency to amount second currency. + * @param amountInCents the amount of first currency in cents (or equivalent) + * @return the amount in the second currency in cents (or equivalent) + */ + public long convertTo(long amountInCents); + + + /** + * converts from second currency amount to first currency amount. + * @param amountInCents the amount of second currency in cents (or equivalent) + * @return the amount in the first currency in cents (or equivalent) + */ + public long convertFrom(long amountInCents); +} diff -r 36331f7244bd -r f6073056b9fe task2/solution09/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution09/src/org/apidesign/apifest08/currency/ConvertorFactory.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,38 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; + + +public class ConvertorFactory { + + public static Convertor getConvertor(CurrencyType from, CurrencyType to) { + if (from == CurrencyType.CZK && to == CurrencyType.USD) { + return new BasicConvertor(new BigDecimal(17)); + } else if (from == CurrencyType.SKK && to == CurrencyType.CZK) { + double rate = 0.8d; + return new BasicConvertor(new BigDecimal(rate)); + } + + + throw new UnsupportedOperationException("Conversion not supported now"); + } + + private static class BasicConvertor implements Convertor { + + private final BigDecimal conversionRate; + + BasicConvertor(BigDecimal conversionRate) { + this.conversionRate = conversionRate; + } + + @Override + public long convertTo(long amount) { + return (long) (conversionRate.doubleValue() * amount); + } + + @Override + public long convertFrom(long amount) { + return (long) (amount / conversionRate.doubleValue()); + } + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution09/src/org/apidesign/apifest08/currency/CurrencyType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution09/src/org/apidesign/apifest08/currency/CurrencyType.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,7 @@ +package org.apidesign.apifest08.currency; + +public enum CurrencyType { + + CZK, SKK, USD; + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution09/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution09/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,77 @@ +package org.apidesign.apifest08.test; + +import junit.framework.TestCase; +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.ConvertorFactory; +import org.apidesign.apifest08.currency.CurrencyType; + +/** 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); + } + + + /** Create convertor that understands two currencies, CZK and + * USD. Make 1 USD == 17 CZK. + * + * 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.getConvertor(CurrencyType.CZK, CurrencyType.USD); + } + + /** Create convertor that understands two currencies, CZK and + * SKK. Make 100 SKK == 80 CZK. + * + * 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.getConvertor(CurrencyType.SKK, CurrencyType.CZK); + } + + /** 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"); + assertEquals(85, c.convertTo(5)); + + // convert $8 to CZK + // assertEquals("Result is 136 CZK"); + assertEquals(136, c.convertTo(8)); + + // convert 1003CZK to USD + // assertEquals("Result is 59 USD"); + assertEquals(59, c.convertFrom(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(20, c.convertFrom(16)); + + // convert 500SKK to CZK + // assertEquals("Result is 400 CZK"); + assertEquals(400, c.convertTo(500)); + } +} + diff -r 36331f7244bd -r f6073056b9fe task2/solution10/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project Currency Convertor Solution 10. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution10/nbproject/.DS_Store Binary file task2/solution10/nbproject/.DS_Store has changed diff -r 36331f7244bd -r f6073056b9fe task2/solution10/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution10/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/nbproject/genfiles.properties Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,8 @@ +build.xml.data.CRC32=6601af03 +build.xml.script.CRC32=2c84d7b2 +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=6601af03 +nbproject/build-impl.xml.script.CRC32=5e67461a +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution10/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/nbproject/project.properties Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,59 @@ +application.title=basic +application.vendor=vvessan +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_Convertor_Solution_10.jar +dist.javadoc.dir=${dist.dir}/javadoc +excludes= +file.reference.junit-4.4.jar=../../libs/junit-4.4.jar +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=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +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} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff -r 36331f7244bd -r f6073056b9fe task2/solution10/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 10 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution10/src/org/apidesign/apifest08/currency/ConstantRateConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/src/org/apidesign/apifest08/currency/ConstantRateConverter.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,44 @@ +package org.apidesign.apifest08.currency; + +import java.util.*; + +final class ConstantRateConverter implements CurrencyConverter { + + private final Currency from; + private final Currency to; + + private final double rate; + + public ConstantRateConverter(Currency from, Currency to, double rate) throws IllegalArgumentException { + if (from == null || to == null) + throw new NullPointerException("None of the currencies can be null"); + if (from.equals(to)) + throw new IllegalArgumentException("Cannot create converter with two equal currencies"); + this.from = from; + this.to = to; + this.rate = rate; + } + + @Override + public double convert(double value, String from, String to) + throws CurrencyConversionException, NullPointerException { + return convert(value, Currency.getInstance(from), Currency.getInstance(to)); + } + + @Override + public double convert(double value, Currency from, Currency to) + throws NullPointerException, CurrencyConversionException { + + if (this.from.equals(from)) { + if (!this.to.equals(to)) + throw new CurrencyConversionException(from, to, "Unsupported currency"); + return value * rate; + } + if (this.from.equals(to)) { + if (!this.to.equals(from)) + throw new CurrencyConversionException(from, to, "Unsupported currency"); + return value / rate; + } + throw new CurrencyConversionException(from, to, "Unsupported currency"); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution10/src/org/apidesign/apifest08/currency/CurrencyConversionException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/src/org/apidesign/apifest08/currency/CurrencyConversionException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,42 @@ +package org.apidesign.apifest08.currency; + +import java.util.*; + +/** + * Exception thrown in cases that a CurrencyConverter is unable ensure requested accuracy of conversion. + * Such situation may occur in cases that the client is not on-line, or the exchange rates are older than + * requested etc. This exception is defined as RuntimeException to enable simple usage in simple applications, + * but enable other applications to be informed about possible problems and prevent them from using + * inaccurate data. + */ +public final class CurrencyConversionException extends RuntimeException { + + private final Currency from; + private final Currency to; + + CurrencyConversionException(Currency from, Currency to) { + this(from, to, (Throwable) null); + } + + CurrencyConversionException(Currency from, Currency to, Throwable throwable) { + this(from, to, String.format("Failed to convert curency from %1$s to %2$s", from, to), throwable); + } + + CurrencyConversionException(Currency from, Currency to, String message) { + this(from, to, message, null); + } + + CurrencyConversionException(Currency from, Currency to, String message, Throwable throwable) { + super(message, throwable); + this.from = from; + this.to = to; + } + + public Currency getFromCurrency() { + return from; + } + + public Currency getToCurrency() { + return to; + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution10/src/org/apidesign/apifest08/currency/CurrencyConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/src/org/apidesign/apifest08/currency/CurrencyConverter.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,48 @@ +package org.apidesign.apifest08.currency; + +import java.util.*; + +/** 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. + * + * The converter will usually work internally with Currency class, to be more type-safe as String can be used + * for almost anything. But methods taking strings as parameters are provided for convenience. + */ +public interface CurrencyConverter { + + /** + * This is convenience method for convert(Currency.getInstance(from), Currency.getInstance(to)). + * + * @param value that should be converted form one currency to the other + * @param from ISO-4217 code of the currency of the value provided + * @param to ISO-4212 code of the currency to which the value should be converted + * @return value expressed in the target value + * @throws IllegalArgumentException if any of the arguments is not a valid ISO code + * @throws CurrencyConversionException if the conversion cannot be performed with desired parameters, + * for example the exchange rates are not current, connection to exchange rates provider is not available + * @throws NullPointerException if any of the specified currency ISO codes is null + */ + // this method is provided to ensure future compatibility for converters supporting more than 2 currencies + // - simpler methods with fewer arguments would make using such converters less intuitive + double convert(double value, /*@NotNull*/ String from, /*@NotNull*/ String to) + throws CurrencyConversionException, NullPointerException, IllegalArgumentException; + + /** + * Converts the specified value from one currency (from) to target currency (to). + * + * @param value that should be converted form one currency to the other + * @param from ISO-4217 code of the currency of the value provided + * @param to ISO-4212 code of the currency to which the value should be converted + * @return value expressed in the target value + * @throws IllegalArgumentException if any of the arguments is not a valid ISO code + * @throws CurrencyConversionException if the conversion cannot be performed with desired parameters, + * for example the exchange rates are not current, connection to exchange rates provider is not available + * @throws NullPointerException if any of the specified currency ISO codes is null + */ + double convert(double value, /*@NotNull*/ Currency from, /*@NotNull*/ Currency to) + throws CurrencyConversionException, NullPointerException; +} diff -r 36331f7244bd -r f6073056b9fe task2/solution10/src/org/apidesign/apifest08/currency/CurrencyConverterProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/src/org/apidesign/apifest08/currency/CurrencyConverterProvider.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,68 @@ +package org.apidesign.apifest08.currency; + +import java.util.*; + +/** + * CurrencyConversionService provides methods necessary for construction all necessary currency + * conversion-related classes. + */ +public interface CurrencyConverterProvider { + + /** + * Convenience method for getConverter(amount1, Currency.getInstance(currency1)); + * + * @param amount1 amount of the money in the currency1 + * @param currency1 one of the supported currencies + * @param amount2 amount of the money in the currency2 + * @param currency2 one of the supported currencies + * @return converter able to convert between the two specified currencies + * @throws IllegalArgumentException if any of the amount values is not positive + * @throws CurrencyNotAvailableException thrown when one of the currencies is not available + */ + CurrencyConverter getConverter(double amount1, /*@NotNull*/ String currency1, + double amount2, /*@NotNull*/ String currency2) + throws IllegalArgumentException, CurrencyNotAvailableException; + + /** + * Retrieves converter that is capable of converting values between currency1 and currency2. + * The exchange is specified in easy to understand way. By specifying values in two currencies that + * are equal. For example CurrencyConverter.getConverter(25, "CZK", 1, "EUR"); means 25CKZ is equal to 1EUR. + * This enables user to use this method without having to calculate anything. In general this can be + * expressed by formula amount1[currency1] = amount2[currency2]. + * + * @param amount1 amount of the money in the currency1 + * @param currency1 one of the supported currencies + * @param amount2 amount of the money in the currency2 + * @param currency2 one of the supported currencies + * @return converter able to convert between the two specified currencies + * @throws IllegalArgumentException if any of the amount values is not positive + * @throws CurrencyNotAvailableException thrown when one of the currencies is not available + */ + CurrencyConverter getConverter(double amount1, /*@NotNull*/ Currency currency1, + double amount2, /*@NotNull*/ Currency currency2) + throws IllegalArgumentException, CurrencyNotAvailableException; + + /** + * Creates a new converter that is able to convert between all specified currencies. The converter + * may optionally support additional currencies that were not specified. + * + * @param currencies that the converter should be created for + * @return converter able to convert between all specified currencies + * @throws CurrencyNotAvailableException thrown when one of the currencies is not available + * @throws NullPointerException if any of the specified currencies is null of the array is null + */ + CurrencyConverter getConverter(/*@NotNull*/ Currency... currencies) + throws CurrencyNotAvailableException, NullPointerException; + + /** + * Convenient method for getConverter(Currency...) + * + * @param currencies that the converter should be created for + * @return converter able to convert between all specified currencies + * @throws CurrencyNotAvailableException thrown when one of the currencies is not available + * @throws NullPointerException if any of the specified currencies is null, or the array is null + * @throws IllegalArgumentException if any of the specified currencies is not a valid ISO code + */ + CurrencyConverter getConverter(/*@NotNull*/ String... currencies) + throws CurrencyNotAvailableException, IllegalArgumentException, NullPointerException; +} \ No newline at end of file diff -r 36331f7244bd -r f6073056b9fe task2/solution10/src/org/apidesign/apifest08/currency/CurrencyNotAvailableException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/src/org/apidesign/apifest08/currency/CurrencyNotAvailableException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,21 @@ +package org.apidesign.apifest08.currency; + +import java.util.*; + +public final class CurrencyNotAvailableException extends RuntimeException { + + private final Currency currency; + + CurrencyNotAvailableException(Currency currency) { + this(currency, String.format("Currency %1$s not available", currency)); + } + + CurrencyNotAvailableException(Currency currency, String message) { + super(message); + this.currency = currency; + } + + public Currency getCurrency() { + return currency; + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution10/src/org/apidesign/apifest08/currency/MultiCurrencyConstantRateConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/src/org/apidesign/apifest08/currency/MultiCurrencyConstantRateConverter.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,71 @@ +package org.apidesign.apifest08.currency; + +import java.util.*; + +final class MultiCurrencyConstantRateConverter implements CurrencyConverter { + + private final Map rates; + + public MultiCurrencyConstantRateConverter(Map rates) { + this.rates = rates; + } + + /** + * This is convenience method for convert(Currency.getInstance(from), Currency.getInstance(to)). + * + * @param value that should be converted form one currency to the other + * @param from ISO-4217 code of the currency of the value provided + * @param to ISO-4212 code of the currency to which the value should be converted + * + * @return value expressed in the target value + * + * @throws IllegalArgumentException if any of the arguments is not a valid ISO code + * @throws CurrencyConversionException + * if the conversion cannot be performed with desired parameters, for + * example the exchange rates are not current, connection to exchange rates + * provider is not available + * @throws NullPointerException if any of the specified currency ISO codes is null + */ + // this method is provided to ensure future compatibility for converters supporting more than 2 currencies + // - simpler methods with fewer arguments would make using such converters less intuitive + @Override + public double convert(double value, /*@NotNull*/ String from, /*@NotNull*/ String to) + throws CurrencyConversionException, NullPointerException, IllegalArgumentException { + return convert(value, Currency.getInstance(from), Currency.getInstance(to)); + } + + /** + * Converts the specified value from one currency (from) to target currency (to). + * + * @param value that should be converted form one currency to the other + * @param from ISO-4217 code of the currency of the value provided + * @param to ISO-4212 code of the currency to which the value should be converted + * + * @return value expressed in the target value + * + * @throws IllegalArgumentException if any of the arguments is not a valid ISO code + * @throws CurrencyConversionException + * if the conversion cannot be performed with desired parameters, for + * example the exchange rates are not current, connection to exchange rates + * provider is not available + * @throws NullPointerException if any of the specified currency ISO codes is null + */ + @Override + public double convert(double value, /*@NotNull*/ Currency from, /*@NotNull*/ Currency to) + throws CurrencyConversionException, NullPointerException { + + // this is not necessary, but we let users know that nulls are not allowed here - should be handled by annotations + if (from == null || to == null) + throw new NullPointerException("One of the specified currencies in null"); + + Double fromRate = rates.get(from); + Double toRate = rates.get(to); + + if (fromRate == null) + throw new CurrencyConversionException(from, to, String.format("Currency %1$s not supported", from)); + if (toRate == null) + throw new CurrencyConversionException(from, to, String.format("Currency %1$s not supported", to)); + + return (value / fromRate) * toRate; + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution10/src/org/apidesign/apifest08/currency/OfflineConverterProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/src/org/apidesign/apifest08/currency/OfflineConverterProvider.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,120 @@ +package org.apidesign.apifest08.currency; + +import java.util.*; + +/** + * ConvertorProvider class is introduced to handle + */ +public final class OfflineConverterProvider implements CurrencyConverterProvider { + + + // we can't synchronize on DEFAULT as it's not final and can even be null in synchronized section + private static final Object LOCK = new Object(); + private static CurrencyConverterProvider DEFAULT; + + // this is not nice and could be static, but future usage is uncertain and it's pretty easy to create + private final Map rates; + + private OfflineConverterProvider() { + rates = new HashMap(); + // simple initialization just for Task1Test + rates.put(Currency.getInstance("USD"), 100.0); + rates.put(Currency.getInstance("CZK"), 1700.0); + rates.put(Currency.getInstance("SKK"), 2125.0); + } + + /** + * Provides default implementation of ConvertorProvider. This Converter does nos not ensure accuracy + * of exchange rates, but should be functional at any circumstances including being offline. + * + * @return + */ + public static CurrencyConverterProvider getInstance() { + // should be necessary in current implementation as creating CurrencyConverterProvider is cheap, but for future + if (DEFAULT == null) + synchronized (LOCK) { + if (DEFAULT == null) + DEFAULT = new OfflineConverterProvider(); + } + return DEFAULT; + } + + @Override + public CurrencyConverter getConverter(double amount1, /*@NotNull*/ String currency1, + double amount2, /*@NotNull*/ String currency2) + throws IllegalArgumentException { + + return getConverter(amount1, Currency.getInstance(currency2), amount2, Currency.getInstance(currency1)); + } + + /** + * Retrieves converter that is capable of converting values between currency1 and currency2. The exchange is + * specified in easy to understand way. By specifying values in two currencies that are equal. For example + * CurrencyConverter.getConverter(25, "CZK", 1, "EUR"); means 25CKZ is equal to 1EUR. This enables user to + * use this method without having to calculate anything. In general this can be expressed by formula + * amount1[currency1] = amount2[currency2]. + * + * @param amount1 + * @param currency1 + * @param amount2 + * @param currency2 + * + * @return + * + * @throws IllegalArgumentException + * @throws CurrencyNotAvailableException + * + */ + @Override + public CurrencyConverter getConverter(double amount1, /*@NotNull*/ Currency currency1, + double amount2, /*@NotNull*/ Currency currency2) + throws IllegalArgumentException, CurrencyNotAvailableException { + if (amount1 <= 0.0 || amount2 <= 0.0) + throw new IllegalArgumentException( + String.format("The specified currency values (%1$s, %2$s)", amount1, amount2)); + return new ConstantRateConverter(currency2, currency1, amount2 / amount1); + } + + /** + * Creates a new converter that is able to convert between all specified currencies. + * + * @param currencies that the converter should be created for + * + * @return converter able to convert between all specified currencies + * + * @throws CurrencyNotAvailableException + * thrown when one of the currencies is not available + */ + @Override + public CurrencyConverter getConverter(/*@NotNull*/ Currency... currencies) throws CurrencyNotAvailableException { + for (Currency c : currencies) { + if (c == null) + throw new NullPointerException("One of the specified currencies is null"); + if (!rates.containsKey(c)) + throw new CurrencyNotAvailableException(c); + } + return new MultiCurrencyConstantRateConverter(rates); + } + + /** + * Creates a new converter that is able to convert between all specified currencies. + * + * @param currencies that the converter should be created for + * + * @return converter able to convert between all specified currencies + * + * @throws CurrencyNotAvailableException + * thrown when one of the currencies is not available + * @throws NullPointerException if any of the specified currencies is null + * @throws IllegalArgumentException if any of the specified currencies is not a valid ISO code + */ + @Override + public CurrencyConverter getConverter(/*@NotNull*/ String... currencies) + throws CurrencyNotAvailableException, IllegalArgumentException, NullPointerException { + + Currency[] c2 = new Currency[currencies.length]; + for (int i = 0; i < c2.length; i++) + c2[i] = Currency.getInstance(currencies[i]); + return getConverter(c2); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution10/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution10/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,134 @@ +package org.apidesign.apifest08.test; + +import junit.framework.*; +import org.apidesign.apifest08.currency.*; + +import java.util.*; + +/** 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 { + + private static final String + USD = "USD", + CZK = "CZK", + SKK = "SKK"; + + private static final Currency + USD2 = Currency.getInstance(USD), + CZK2 = Currency.getInstance(CZK), + SKK2 = Currency.getInstance(SKK); + + public Task1Test(String testName) { + super(testName); + } + + @Override + protected void setUp() throws Exception { + } + + @Override + protected void tearDown() throws Exception { + } + + /** Create convertor that understands two currencies, CZK and + * USD. Make 1 USD == 17 CZK. + * + * 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 CurrencyConverter createCZKtoUSD() { + return OfflineConverterProvider.getInstance().getConverter(17, CZK, 1, USD); + } + + public static CurrencyConverter createCZKtoUSD2() { + return OfflineConverterProvider.getInstance().getConverter(CZK, USD); + } + + /** Create convertor that understands two currencies, CZK and + * SKK. Make 100 SKK == 80 CZK. + * + * 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 CurrencyConverter createSKKtoCZK() { + return OfflineConverterProvider.getInstance().getConverter(100, SKK, 80, CZK); + } + + public static CurrencyConverter createSKKtoCZK2() { + return OfflineConverterProvider.getInstance().getConverter(SKK, CZK); + } + + /** Use the convertor from createCZKtoUSD method and do few conversions + * with it. + */ + public void testCurrencyCZKUSD() throws Exception { + CurrencyConverter c = createCZKtoUSD(); + testCZKUSD(c); + + // test without specifying rates + c = createCZKtoUSD2(); + testCZKUSD(c); + } + + private void testCZKUSD(CurrencyConverter c) { + double czk, usd; + // convert $5 to CZK using c: + // assertEquals("Result is 85 CZK"); + czk = c.convert(5, USD, CZK); + assertEquals(85.0, czk, 0.0); + czk = c.convert(5, USD2, CZK2); + assertEquals(85.0, czk, 0.0); + + // convert $8 to CZK + // assertEquals("Result is 136 CZK"); + czk = c.convert(8, USD, CZK); + assertEquals(136.0, czk, 0.0); + czk = c.convert(8, USD2, CZK2); + assertEquals(136.0, czk, 0.0); + + // convert 1003CZK to USD + // assertEquals("Result is 59 USD"); + usd = c.convert(1003, CZK, USD); + assertEquals(59.0, usd, 0.0); + usd = c.convert(1003, CZK2, USD2); + assertEquals(59.0, usd, 0.0); + } + + /** Use the convertor from createSKKtoCZK method and do few conversions + * with it. + */ + public void testCurrencySKKCZK() throws Exception { + CurrencyConverter c = createSKKtoCZK(); + testCZKSKK(c); + + // test without specifying rates + c = createSKKtoCZK2(); + testCZKSKK(c); + } + + private void testCZKSKK(CurrencyConverter c) { + double czk, skk; + // convert 16CZK using c: + // assertEquals("Result is 20 SKK"); + skk = c.convert(16, CZK, SKK); + assertEquals(20.0, skk, 0.0); + skk = c.convert(16, CZK2, SKK2); + assertEquals(20.0, skk, 0.0); + + // convert 500SKK to CZK + // assertEquals("Result is 400 CZK"); + czk = c.convert(500, SKK, CZK); + assertEquals(400.0, czk, 0.0); + czk = c.convert(500, SKK2, CZK2); + assertEquals(400.0, czk, 0.0); + } +} \ No newline at end of file diff -r 36331f7244bd -r f6073056b9fe task2/solution11/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution11/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution11/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution11/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution11/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution11/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=0e1e702f +nbproject/build-impl.xml.script.CRC32=6cbb076a +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution11/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution11/nbproject/project.properties Wed Oct 01 10:43:05 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=-Xlint:unchecked +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 36331f7244bd -r f6073056b9fe task2/solution11/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution11/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 11 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution11/src/org/apidesign/apifest08/currency/Computer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution11/src/org/apidesign/apifest08/currency/Computer.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,65 @@ +package org.apidesign.apifest08.currency; + +/** + * Interface declaring method for computing conversion. + * + * Because of a vague definition of currency amount's type, + * the interface has a generic type. + * + * @author ked + * @see http://wiki.apidesign.org/wiki/APIDesignPatterns:ResponseReply + */ +interface Computer { + + ComputerResponse compute(ComputerRequest request); + + /** + * + * @param + */ + final class ComputerRequest { + + private AmountType input; + private AmountType inputCurrencyRatio; + private AmountType outputCurrencyRatio; + + AmountType getInput() { + return input; + } + + void setInput(AmountType input) { + this.input = input; + } + + AmountType getInputCurrencyRatio() { + return inputCurrencyRatio; + } + + void setInputCurrencyRatio(AmountType inputCurrencyRatio) { + this.inputCurrencyRatio = inputCurrencyRatio; + } + + AmountType getOutputCurrencyRatio() { + return outputCurrencyRatio; + } + + void setOutputCurrencyRatio(AmountType outputCurrencyRatio) { + this.outputCurrencyRatio = outputCurrencyRatio; + } + } + + final class ComputerResponse { + + private AmountType result; + + AmountType getResult() { + return result; + } + + void setResult(AmountType result) { + this.result = result; + } + } + + +} \ No newline at end of file diff -r 36331f7244bd -r f6073056b9fe task2/solution11/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution11/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,129 @@ +package org.apidesign.apifest08.currency; + +import org.apidesign.apifest08.currency.Computer.ComputerRequest; +import org.apidesign.apifest08.currency.Computer.ComputerResponse; + +/** + * Convertor. + * + * In Task 1's version provides conversion between currency values + * with amount stored in integer or double, that are identified + * with string value. Exchange rates are immutable. + * + * @author ked + */ +public final class Convertor { + + Computer computer; + CurrencyValue firstCurrencyExchangeRate; + CurrencyValue secondCurrencyExchangeRate; + + Convertor( + Computer computer, + CurrencyValue firstCurrencyExchangeRate, + CurrencyValue secondCurrencyExchangeRate) { + if (firstCurrencyExchangeRate.getIdentifier() == null || + secondCurrencyExchangeRate.getIdentifier() == null || + firstCurrencyExchangeRate.getIdentifier().equals(secondCurrencyExchangeRate.getIdentifier())) { + throw new IllegalArgumentException("Inappropriate exchange rates' identifiers!"); + } + this.computer = computer; + this.firstCurrencyExchangeRate = firstCurrencyExchangeRate; + this.secondCurrencyExchangeRate = secondCurrencyExchangeRate; + } + + /** + * Convert an amount of the one currency to an amount of the another one currency + * with respect to previously specified exchange rate. + * + * @param targetCurrency an identifier of the requested currency + * @param currencyValue an amount of the another one currency + * @return an amount of the requested currency + */ + public CurrencyValue convert( + IdentifierType targetCurrency, + CurrencyValue currencyValue) { + if (firstCurrencyExchangeRate.getIdentifier().equals(targetCurrency) && + secondCurrencyExchangeRate.getIdentifier().equals(currencyValue.getIdentifier())) { + ComputerRequest computerRequest = new ComputerRequest(); + computerRequest.setInput(currencyValue.getAmount()); + computerRequest.setInputCurrencyRatio(secondCurrencyExchangeRate.getAmount()); + computerRequest.setOutputCurrencyRatio(firstCurrencyExchangeRate.getAmount()); + ComputerResponse computerResponse = computer.compute(computerRequest); + + return CurrencyValue.getCurrencyValue( + computerResponse.getResult(), + firstCurrencyExchangeRate.getIdentifier()); + } else if (secondCurrencyExchangeRate.getIdentifier().equals(targetCurrency) && + firstCurrencyExchangeRate.getIdentifier().equals(currencyValue.getIdentifier())) { + ComputerRequest computerRequest = new ComputerRequest(); + computerRequest.setInput(currencyValue.getAmount()); + computerRequest.setInputCurrencyRatio(firstCurrencyExchangeRate.getAmount()); + computerRequest.setOutputCurrencyRatio(secondCurrencyExchangeRate.getAmount()); + ComputerResponse computerResponse = computer.compute(computerRequest); + + return CurrencyValue.getCurrencyValue( + computerResponse.getResult(), + secondCurrencyExchangeRate.getIdentifier()); + } else { + throw new IllegalArgumentException("Inappropriate currencies to convert!"); + } + } + + static Convertor getConvertor( + Computer computer, + CurrencyValue firstCurrencyExchangeRate, + CurrencyValue secondCurrencyExchangeRate) { + return new Convertor( + computer, + firstCurrencyExchangeRate, + secondCurrencyExchangeRate); + } + + static final Computer DoubleComputer = new Computer() { + + public ComputerResponse compute(ComputerRequest request) { + ComputerResponse response = new ComputerResponse(); + response.setResult(request.getInput() * request.getOutputCurrencyRatio() / request.getInputCurrencyRatio()); + return response; + } + }; + + /** + * Creates convertor for Double|String values with specified exchange rate + * between two currencies. + * + * @param firstCurrencyExchangeRate first currency + * @param secondCurrencyExchangeRate second currency + * @return convertor + */ + + public static Convertor getConvertorDoubleString( + CurrencyValue firstCurrencyExchangeRate, + CurrencyValue secondCurrencyExchangeRate) { + return getConvertor(DoubleComputer, firstCurrencyExchangeRate, secondCurrencyExchangeRate); + } + + static final Computer IntegerComputer = new Computer() { + + public ComputerResponse compute(ComputerRequest request) { + ComputerResponse response = new ComputerResponse(); + response.setResult(request.getInput() * request.getOutputCurrencyRatio() / request.getInputCurrencyRatio()); + return response; + } + }; + + /** + * Creates convertor for Integer|String values with specified exchange rate + * between two currencies. + * + * @param firstCurrencyExchangeRate first currency + * @param secondCurrencyExchangeRate second currency + * @return convertor + */ + public static Convertor getConvertorIntegerString( + CurrencyValue firstCurrencyExchangeRate, + CurrencyValue secondCurrencyExchangeRate) { + return getConvertor(IntegerComputer, firstCurrencyExchangeRate, secondCurrencyExchangeRate); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution11/src/org/apidesign/apifest08/currency/CurrencyValue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution11/src/org/apidesign/apifest08/currency/CurrencyValue.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,72 @@ +package org.apidesign.apifest08.currency; + +import java.io.Serializable; + +/** + * Value class, holding an amount of the currency & an identifier of the currency. + * Designed to be an immutable. + * + * Because of a vague definition of types of the both fields, + * the class has generic types, used as types of the fields. + * These types should be immutable classes, too. + * + * @author ked + */ +public final class CurrencyValue implements Serializable { + + private final AmountType amount; + private final IdentifierType identifier; + + private CurrencyValue(AmountType amount, IdentifierType identifier) { + this.amount = amount; + this.identifier = identifier; + } + + public AmountType getAmount() { + return amount; + } + + public IdentifierType getIdentifier() { + return identifier; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CurrencyValue other = (CurrencyValue) obj; + if (this.amount != other.amount && (this.amount == null || !this.amount.equals(other.amount))) { + return false; + } + if (this.identifier != other.identifier && (this.identifier == null || !this.identifier.equals(other.identifier))) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 97 * hash + (this.amount != null ? this.amount.hashCode() : 0); + hash = 97 * hash + (this.identifier != null ? this.identifier.hashCode() : 0); + return hash; + } + + /** + * Creates new instance. + * Generic types of the new instance are derived from types of the parameters. + * + * @param type of the currency amount + * @param type of the currency identifier + * @param amount currency amount + * @param identifier currency identifier + * @return new instance + */ + public static CurrencyValue getCurrencyValue(AmountType amount, IdentifierType identifier) { + return new CurrencyValue(amount, identifier); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution11/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution11/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,161 @@ +package org.apidesign.apifest08.test; + +import junit.framework.TestCase; +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.CurrencyValue; + +/** 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 Convertor.getConvertorIntegerString( + CurrencyValue.getCurrencyValue(1, "USD"), + CurrencyValue.getCurrencyValue(17, "CZK") + ); + } + + /** 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 Convertor.getConvertorIntegerString( + CurrencyValue.getCurrencyValue(100, "SKK"), + CurrencyValue.getCurrencyValue(80, "CZK") + ); + } + + // + // 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(); + + CurrencyValue result; + + // convert $5 to CZK using c: + // assertEquals("Result is 85 CZK"); + result = c.convert("CZK", CurrencyValue.getCurrencyValue(5, "USD")); + assertEquals(CurrencyValue.getCurrencyValue(85, "CZK"), result); + + // convert $8 to CZK + // assertEquals("Result is 136 CZK"); + result = c.convert("CZK", CurrencyValue.getCurrencyValue(8, "USD")); + assertEquals(CurrencyValue.getCurrencyValue(136, "CZK"), result); + + // convert 1003CZK to USD + // assertEquals("Result is 59 USD"); + result = c.convert("USD", CurrencyValue.getCurrencyValue(1003, "CZK")); + assertEquals(CurrencyValue.getCurrencyValue(59, "USD"), result); + } + + /** Use the convertor from createSKKtoCZK method and do few conversions + * with it. + */ + public void testCurrencySKKCZK() throws Exception { + Convertor c = createSKKtoCZK(); + + CurrencyValue result; + + // convert 16CZK using c: + // assertEquals("Result is 20 SKK"); + result = c.convert("SKK", CurrencyValue.getCurrencyValue(16, "CZK")); + assertEquals(CurrencyValue.getCurrencyValue(20, "SKK"), result); + + // convert 500SKK to CZK + // assertEquals("Result is 400 CZK"); + result = c.convert("CZK", CurrencyValue.getCurrencyValue(500, "SKK")); + assertEquals(CurrencyValue.getCurrencyValue(400, "CZK"), result); + } + + /** Verify that the CZK to USD convertor knows nothing about SKK. + */ + public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception { + Convertor c = createCZKtoUSD(); + try { + // convert $5 to SKK, the API shall say this is not possible + c.convert("SKK", CurrencyValue.getCurrencyValue(16, "CZK")); + assertTrue("Should not convert", false); + } catch (Exception e) { + assertTrue(true); + } + try { + // convert 500 SKK to CZK, the API shall say this is not possible + c.convert("CZK", CurrencyValue.getCurrencyValue(500, "SKK")); + assertTrue("Should not convert", false); + } catch (Exception e) { + assertTrue(true); + } + + } + + /** Verify that the CZK to SKK convertor knows nothing about USD. + */ + public void testCannotConvertToUSDwithSKKCZKConvertor() throws Exception { + Convertor c = createSKKtoCZK(); + try { + // convert $5 to SKK, the API shall say this is not possible + c.convert("SKK", CurrencyValue.getCurrencyValue(5, "USD")); + assertTrue("Should not convert", false); + } catch (Exception e) { + assertTrue(true); + } + try { + // convert 500 CZK to USD, the API shall say this is not possible + c.convert("USD", CurrencyValue.getCurrencyValue(500, "CZK")); + assertTrue("Should not convert", false); + } catch (Exception e) { + assertTrue(true); + } + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution12/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution12/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution12/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution12/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution12/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution12/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=b63e115b +nbproject/build-impl.xml.script.CRC32=3bdfc4fa +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution12/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution12/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution12/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution12/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 12 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution12/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution12/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,149 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; +import java.util.Hashtable; + +import org.apidesign.apifest08.currency.exceptions.ConvertorException; +import org.apidesign.apifest08.currency.exceptions.InvalidCurrencyException; +import org.apidesign.apifest08.currency.exceptions.UnknownConvertorException; + +/** + * 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 class Convertor { + + private static Hashtable exchangeRates; + + private ExchangeRate exchangeRate12; + private ExchangeRate exchangeRate21; + + private Convertor(Currency currency1, Currency currency2) throws UnknownConvertorException { + String key12 = currency1.getCurrencyCode() + currency2.getCurrencyCode(); + String key21 = currency2.getCurrencyCode() + currency1.getCurrencyCode(); + + if (!(exchangeRates.containsKey(key12) && exchangeRates.containsKey(key21))) { + throw new UnknownConvertorException("Selected convertor (" + currency1.getCurrencyCode() + "->" + + currency2.getCurrencyCode() + ") has not defined any rates!!!"); + } + + this.exchangeRate12 = exchangeRates.get(key12); + this.exchangeRate21 = exchangeRates.get(key21); + } + + /** + * Sets convertor rate for selected currencies. + * @param currency1 + * one of the currencies we want to convert to/from + * @param currency2 + * the other currency + * @param rate + * exchange rate from currency1 to currency2 + * @param unit + * unit of exchangeRate (USD->CZK - unit=1, you exchange one dollar, SKK->CZK unit=100, exchange rate is for + * 100SKK) + */ + public static void setConvertorRates(Currency currency1, Currency currency2, double rate, double unit) { + if (currency1 == null || currency2 == null) { + throw new ConvertorException("None of the currencies should be null!!!"); + } + + if (rate <= 0 || unit <= 0) { + throw new ConvertorException("Rate(" + rate + ") and unit(" + unit + ") has to be grater then zero!!!"); + } + + if (exchangeRates == null) { + exchangeRates = new Hashtable(); + } + + String key12 = currency1.getCurrencyCode() + currency2.getCurrencyCode(); + String key21 = currency2.getCurrencyCode() + currency1.getCurrencyCode(); + double recountedRate = (unit / rate) * unit; + + exchangeRates.put(key12, new ExchangeRate(currency1, currency2, rate, unit)); + exchangeRates.put(key21, new ExchangeRate(currency2, currency1, recountedRate, unit)); + + } + + /** + * Creates new instance of convertor. + * @param currency1 + * one of the currencies we want to convert to/from + * @param currency2 + * the other currency + * @return new instance of convertor + * @throws UnknownConvertorException + * thrown if convertor for selected currencies has not been defined + */ + public static Convertor getConvertorInstance(Currency currency1, Currency currency2) throws UnknownConvertorException { + if (currency1 == null || currency2 == null) { + throw new ConvertorException("None of the currencies should be null!!!"); + } + return new Convertor(currency1, currency2); + } + + /** + * Converts selected amout of selected currency to other currency of this convertor instance. + * @param amount + * amount to convert + * @param originalCurrency + * currency of this amount + * @param newCurrency + * currency to which we want convert + * @return converted amount + * @throws InvalidCurrencyException + * while one or both currencies doesn't fit for this convertor + */ + public double convert(double amount, Currency originalCurrency, Currency newCurrency) throws InvalidCurrencyException { + ExchangeRate actualyUsedExchangeRate = null; + + if (originalCurrency == null) { + throw new ConvertorException("Original currency is null!!!"); + } + + if (newCurrency == null) { + throw new ConvertorException("Destination currency is null!!!"); + } + + actualyUsedExchangeRate = getExchangeRate(originalCurrency, newCurrency); + + return countResult(actualyUsedExchangeRate, amount); + } + + private double countResult(ExchangeRate actualyUsedExchangeRate, double amount) { + return amount * actualyUsedExchangeRate.getRate() / actualyUsedExchangeRate.getUnit(); + } + + /** + * Decides the direction of conversion and returns instance of actual exchange rate. + * @param actualCurrency + * actual currency we want to convert + * @return actual exchange rate of this convertor for selected currency + */ + private ExchangeRate getExchangeRate(Currency originalCurrency, Currency newCurrency) throws InvalidCurrencyException { + ExchangeRate actualyUsedExchangeRate = null; + + if (originalCurrency.getCurrencyCode().equals(exchangeRate12.getOriginalCurrency().getCurrencyCode()) + && newCurrency.getCurrencyCode().equals(exchangeRate12.getNewCurrency().getCurrencyCode())) { + actualyUsedExchangeRate = exchangeRate12; + } else if (originalCurrency.getCurrencyCode().equals(exchangeRate21.getOriginalCurrency().getCurrencyCode()) + && newCurrency.getCurrencyCode().equals(exchangeRate21.getNewCurrency().getCurrencyCode())) { + actualyUsedExchangeRate = exchangeRate21; + } else { + throw new InvalidCurrencyException("This convertor " + this + + " could not be used for converting selected currencies (" + originalCurrency.getCurrencyCode() + "->" + + newCurrency.getCurrencyCode() + ") !!!"); + } + + return actualyUsedExchangeRate; + } + + public String toString() { + String currency1Code = exchangeRate12.getOriginalCurrency().getCurrencyCode(); + String currency2Code = exchangeRate12.getNewCurrency().getCurrencyCode(); + return "Converter [" + currency1Code + "->" + currency2Code + ", " + currency2Code + "->" + currency1Code + "]"; + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution12/src/org/apidesign/apifest08/currency/ExchangeRate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution12/src/org/apidesign/apifest08/currency/ExchangeRate.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,35 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; + +public class ExchangeRate { + + private Currency originalCurrency; + private Currency newCurrency; + private double unit; + private double rate; + + public ExchangeRate(Currency originalCurrency, Currency newCurrency, double rate, double unit) { + this.newCurrency = newCurrency; + this.originalCurrency = originalCurrency; + this.rate = rate; + this.unit = unit; + } + + public Currency getOriginalCurrency() { + return originalCurrency; + } + + public Currency getNewCurrency() { + return newCurrency; + } + + public double getUnit() { + return unit; + } + + public double getRate() { + return rate; + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution12/src/org/apidesign/apifest08/currency/exceptions/ConvertorException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution12/src/org/apidesign/apifest08/currency/exceptions/ConvertorException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,20 @@ +package org.apidesign.apifest08.currency.exceptions; + +public class ConvertorException extends RuntimeException { + + public ConvertorException() { + } + + public ConvertorException(String message) { + super(message); + } + + public ConvertorException(Throwable cause) { + super(cause); + } + + public ConvertorException(String message, Throwable cause) { + super(message, cause); + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution12/src/org/apidesign/apifest08/currency/exceptions/InvalidCurrencyException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution12/src/org/apidesign/apifest08/currency/exceptions/InvalidCurrencyException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,20 @@ +package org.apidesign.apifest08.currency.exceptions; + +public class InvalidCurrencyException extends Exception { + + public InvalidCurrencyException() { + } + + public InvalidCurrencyException(String message) { + super(message); + } + + public InvalidCurrencyException(Throwable cause) { + super(cause); + } + + public InvalidCurrencyException(String message, Throwable cause) { + super(message, cause); + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution12/src/org/apidesign/apifest08/currency/exceptions/UnknownConvertorException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution12/src/org/apidesign/apifest08/currency/exceptions/UnknownConvertorException.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,20 @@ +package org.apidesign.apifest08.currency.exceptions; + +public class UnknownConvertorException extends Exception { + + public UnknownConvertorException() { + } + + public UnknownConvertorException(String message) { + super(message); + } + + public UnknownConvertorException(Throwable cause) { + super(cause); + } + + public UnknownConvertorException(String message, Throwable cause) { + super(message, cause); + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution12/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution12/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,174 @@ +package org.apidesign.apifest08.test; + +import java.util.Currency; + +import junit.framework.TestCase; + +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.exceptions.InvalidCurrencyException; +import org.apidesign.apifest08.currency.exceptions.UnknownConvertorException; + +/** + * 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() { + // set exchange rates + Convertor.setConvertorRates(Currency.getInstance("USD"), Currency.getInstance("CZK"), 17d, 1d); + + // create new instance + Convertor convertor = null; + try { + convertor = Convertor.getConvertorInstance(Currency.getInstance("USD"), Currency.getInstance("CZK")); + } catch (UnknownConvertorException e) { + e.printStackTrace(); + } + + return convertor; + } + + /** + * 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() { + // set exchange rates + Convertor.setConvertorRates(Currency.getInstance("SKK"), Currency.getInstance("CZK"), 80d, 100d); + + // create new instance + Convertor convertor = null; + try { + convertor = Convertor.getConvertorInstance(Currency.getInstance("SKK"), Currency.getInstance("CZK")); + } catch (UnknownConvertorException e) { + e.printStackTrace(); + } + + return convertor; + } + + // + // 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: + double result = c.convert(5, Currency.getInstance("USD"), Currency.getInstance("CZK")); + assertEquals("Result is not 85 CZK", 85.0, result); + + // convert $8 to CZK + result = c.convert(8, Currency.getInstance("USD"), Currency.getInstance("CZK")); + assertEquals("Result is not 136 CZK", 136.0, result); + + // convert 1003CZK to USD + result = c.convert(1003, Currency.getInstance("CZK"), Currency.getInstance("USD")); + assertEquals("Result is not 59 USD", 59.0, result); + } + + /** + * Use the convertor from createSKKtoCZK method and do few conversions with it. + */ + public void testCurrencySKKCZK() throws Exception { + Convertor c = createSKKtoCZK(); + + // convert 16CZK using c: + double result = c.convert(16, Currency.getInstance("CZK"), Currency.getInstance("SKK")); + assertEquals("Result is not 20 SKK", 20.0, result); + + // convert 500SKK to CZK + result = c.convert(500, Currency.getInstance("SKK"), Currency.getInstance("CZK")); + assertEquals("Result is not 400 CZK", 400.0, result); + } + + /** + * Verify that the CZK to USD convertor knows nothing about SKK. + */ + public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception { + Convertor c = createCZKtoUSD(); + boolean exceptionThrown = false; + + // convert $5 to SKK, the API shall say this is not possible + try { + c.convert(5, Currency.getInstance("USD"), Currency.getInstance("SKK")); + exceptionThrown = false; + } catch (InvalidCurrencyException e) { + exceptionThrown = true; + } + assertEquals("It should be impossible to convert to SKK with USD->CZK convertor", true, exceptionThrown); + + // convert 500 SKK to CZK, the API shall say this is not possible + try { + c.convert(500, Currency.getInstance("SKK"), Currency.getInstance("CZK")); + exceptionThrown = false; + } catch (InvalidCurrencyException e) { + exceptionThrown = true; + } + assertEquals("It should be impossible to convert from SKK with USD->CZK convertor", true, exceptionThrown); + + } + + /** + * Verify that the CZK to SKK convertor knows nothing about USD. + */ + public void testCannotConvertToSKKwithSKKCZKConvertor() throws Exception { + Convertor c = createSKKtoCZK(); + boolean exceptionThrown = false; + + // convert $5 to SKK, the API shall say this is not possible + try { + c.convert(5, Currency.getInstance("USD"), Currency.getInstance("SKK")); + exceptionThrown = false; + } catch (InvalidCurrencyException e) { + exceptionThrown = true; + } + assertEquals("It should be impossible to convert form USD with SKK->CZK convertor", true, exceptionThrown); + + // convert 500 CZK to USD, the API shall say this is not possible + try { + c.convert(500, Currency.getInstance("CZK"), Currency.getInstance("USD")); + exceptionThrown = false; + } catch (InvalidCurrencyException e) { + exceptionThrown = true; + } + assertEquals("It should be impossible to convert to USD with SKK->CZK convertor", true, exceptionThrown); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution13/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution13/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution13/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution13/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution13/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution13/nbproject/genfiles.properties Wed Oct 01 10:43:05 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=de21ce77 +nbproject/build-impl.xml.script.CRC32=0903858a +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r 36331f7244bd -r f6073056b9fe task2/solution13/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution13/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution13/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution13/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 13 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution13/src/org/apidesign/apifest08/currency/ConversionResult.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/ConversionResult.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,49 @@ + +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; + +/** + * Result of currency conversion. Holds converted value and remainder. + *

+ * Converter describes value converted to target value. Remainder describes + * how much from original amount was not possible to convert. Converted never loses any (small) money + * in conversion error (rounding), but instead of rounding is converts only as much as possible and keeps rest as remainder. + * + * @author arnostvalicek + */ +public class ConversionResult { + private BigDecimal converted; + private BigDecimal remainder; + + /** + * Get converted value. + * @return Returns converted value. + */ + public BigDecimal getConverted() { + return converted; + } + + void setConverted(BigDecimal converted) { + this.converted = converted; + } + + + /** + * Get remainder of conversion. Remainder is set if part of converted amount which can't be converted + * because this target currency precision can't handle small numbers. Remainder value is in from currency + *

+ * Converter never loses any precision in conversion. Remainer describes how much of amount can't be converted. + * If we substract remainder from amount we will be able to get exact conversion. + * + * @return Returns remainder of conversion. + */ + public BigDecimal getRemainder() { + return remainder; + } + + void setRemainder(BigDecimal remainder) { + this.remainder = remainder; + } + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution13/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,105 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; + +/** Convertor able to convert amount from one currency to other currency. + * + * Exchange rate is provided by {@link ExchangeRateProvider}. + */ +public class Convertor { + boolean remainderAllowed = false; //if false, remained is not allowed (should be true ideally, but can't handle it now) + ExchangeRateProvider exchangeRateProvider; + + private Convertor() { + + } + + /** + * Static method used to create convertor. + * + * @param exchangeRateProvider {@link ExchangeRateProvider} used to get exchange rate. + * + * @return Returns Convertor which can be used to convert money. + */ + public static Convertor createConvertor(ExchangeRateProvider exchangeRateProvider) { + Convertor c = new Convertor(); + + c.exchangeRateProvider = exchangeRateProvider; + return c; + } + + /** + * Convert amount from fromCurrency to toCurrency as specified + * in ExchangeRateProvider. + * + * @param amount Amount which should be converted. Can't be negative value (can be zero or positive). + * @return Return ConversionResult which holds conversion result. + */ + public ConversionResult convert(BigDecimal amount) { + return convertValue(amount, false); + } + + /** + * Convert amount from toCurrency to fromCurrency as specified + * in ExchangeRateProvider. This is reverted order than suggested by names of currency fields in ExchangeRate. + * + * @param amount Amount which should be converted. Can't be negative value (can be zero or positive). + * @return Return ConversionResult which holds conversion result. + */ + public ConversionResult convertBack(BigDecimal amount) { + return convertValue(amount, true); + } + + private ConversionResult convertValue(BigDecimal amount, boolean convertBack) throws RuntimeException { + ConversionResult result = new ConversionResult(); + + ExchangeRate rate = exchangeRateProvider.getExchangeRate(); + int fromFranctionDigits = exchangeRateProvider.getFromCurrency().getDefaultFractionDigits(); + int toFractionDigits = exchangeRateProvider.getToCurrency().getDefaultFractionDigits(); + + if (toFractionDigits!=2) { + throw new RuntimeException("Can't process currency with defaultFractionDigits!=2, "+exchangeRateProvider.getToCurrency()+" has "+toFractionDigits+" defaultFractionDigits"); + } + if (fromFranctionDigits!=2) { + throw new RuntimeException("Can't process currency with defaultFractionDigits!=2, "+exchangeRateProvider.getFromCurrency()+" has "+fromFranctionDigits+" defaultFractionDigits"); + } + + if (amount.signum()==-1) { + throw new RuntimeException("Can convert only non-negative value, current value is "+amount); + } + + + MathContext context = new MathContext(0, RoundingMode.DOWN); + + BigDecimal from; + BigDecimal to; + if (convertBack) { + //converting in reverted way + to = rate.getFromValue(); + from = rate.getToValue(); + } else { + //converting in mornak way + from = rate.getFromValue(); + to = rate.getToValue(); + } + + BigDecimal amountCent = amount.movePointRight(2); + + final BigDecimal multiplied = amountCent.multiply(to,context); + BigDecimal[] division = multiplied.divideAndRemainder(from,context); + + if (!remainderAllowed && !(BigDecimal.ZERO.equals(division[1]))) { + throw new RuntimeException("Remained is not allowed - remaining amount is " + division[1]+ " cents"); + } else { + result.setRemainder(BigDecimal.ZERO); + } + + final BigDecimal converted = division[0].movePointLeft(2); + result.setConverted(converted); + //result.setRemainder(...); + + return result; + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution13/src/org/apidesign/apifest08/currency/ConvertorCurrency.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/ConvertorCurrency.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,46 @@ + +package org.apidesign.apifest08.currency; + +import java.util.Currency; + +/** + * Desription of currency. + * + * Java has similar class {@link java.util.Currency}, but original class is not flexible + * enough, we use our own implementation of currency. + * + * @author arnostvalicek + */ +public class ConvertorCurrency { + + private Currency currency; + + private void setJavaCurrency(Currency javaCurrency) { + this.currency = javaCurrency; + } + + /** + * Static method providing instance of ConvertorCurrency base of currency code. + * + * @param currencyCode Code of required currency. + * @return Returns required ConvertorCurrency + */ + public static ConvertorCurrency getInstance(String currencyCode) { + ConvertorCurrency convertorCurrency = new ConvertorCurrency(); + convertorCurrency.setJavaCurrency(Currency.getInstance(currencyCode)); + return convertorCurrency; + } + + /** + * Gets the default number of fraction digits used with this currency. For example, the default number of fraction digits for the Euro is 2, while for the Japanese Yen it's 0. + * @return Returns the default number of fraction digits used with this currency. + */ + public int getDefaultFractionDigits() { + return currency.getDefaultFractionDigits(); + } + + @Override + public String toString() { + return getClass() + " based on " + (currency != null ? currency.toString() : "NO-BASE-CURRENCY"); + } +} diff -r 36331f7244bd -r f6073056b9fe task2/solution13/src/org/apidesign/apifest08/currency/ExchangeRate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/ExchangeRate.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,41 @@ + +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; + +/** + * Exchange rate value. Contains from and to value. + * + * @author arnostvalicek + */ +public class ExchangeRate { + private BigDecimal numberFor; + private BigDecimal numberGet; + + public ExchangeRate(BigDecimal fromValue, BigDecimal toValue) { + this.numberFor = fromValue; + this.numberGet = toValue; + } + + @Override + public String toString() { + return "for "+numberFor+" recieve "+numberGet+" @"+getClass().getName(); + } + + public BigDecimal getFromValue() { + return numberFor; + } + + public BigDecimal getToValue() { + return numberGet; + } + + +// public ExchangeRate createExchangeRate(BigDecimal forValue, BigDecimal getValue) { +// ExchangeRate rate = new ExchangeRate(forValue, getValue); +// return rate; +// } + + + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution13/src/org/apidesign/apifest08/currency/ExchangeRateProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution13/src/org/apidesign/apifest08/currency/ExchangeRateProvider.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,62 @@ + +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * Exchange rate provider - can provide fixed exchange rate which does not depend + * on date (method {@link #getExchangeRate()} ) or exchange rate based on date (method {@link #getExchangeRate(java.util.Date) }). + * + * + * @author arnostvalicek + */ +public class ExchangeRateProvider { + BigDecimal fromValue, toValue; + ConvertorCurrency fromCurrency, toCurrency; + + /** + * Simple constructor for ExchangeRateProviderM which can provide fixed exchange rate. + * + * Describes conversion from to to currency. + * + * @param fromValue From value. BigDecimal value, precision should be set to currency precision. + * @param fromCurrency From currency. + * @param toValue To value. BigDecimal value, precision should be set to currency precision. + * @param toCurrency To currency. + */ + public ExchangeRateProvider(BigDecimal fromValue, ConvertorCurrency fromCurrency, BigDecimal toValue, ConvertorCurrency toCurrency) { + this.fromValue = fromValue; + this.toValue = toValue; + this.fromCurrency = fromCurrency; + this.toCurrency = toCurrency; + } + + /** + * Get fixed exange rate for currencies (from->to). + * @return Returns exchange rate. + */ + public ExchangeRate getExchangeRate() { + return new ExchangeRate(fromValue, toValue); + } + + /** + * Get exchange rate for currencies (from->to) based on provided date. + * @param date Date for which exchange rate should be provided. + * @return Returns exchange rate + */ + public ExchangeRate getExchangeRate(Date date) { + return new ExchangeRate(fromValue, toValue); + } + + + ConvertorCurrency getFromCurrency() { + return fromCurrency; + } + + ConvertorCurrency getToCurrency() { + return toCurrency; + } + + +} diff -r 36331f7244bd -r f6073056b9fe task2/solution13/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution13/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,209 @@ +package org.apidesign.apifest08.test; + +import java.math.BigDecimal; +import junit.framework.TestCase; +import org.apidesign.apifest08.currency.ConversionResult; +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.ConvertorCurrency; +import org.apidesign.apifest08.currency.ExchangeRateProvider; + +/** 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 { + } + + /** Create convertor that understands two currencies, CZK and + * USD. Make 1 USD == 17 CZK. + * + * 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 Convertor createCZKtoUSD() { + ConvertorCurrency fromCurrency = ConvertorCurrency.getInstance("CZK"); + ConvertorCurrency toCurrency = ConvertorCurrency.getInstance("USD"); + ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(17), fromCurrency, new BigDecimal(1), toCurrency); + + return Convertor.createConvertor(exchangeRateProvider); + } + + /** Create convertor that understands two currencies, CZK and + * SKK. Make 100 SKK == 80 CZK. + * + * 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 Convertor createSKKtoCZK() { + ConvertorCurrency fromCurrency = ConvertorCurrency.getInstance("SKK"); + ConvertorCurrency toCurrency = ConvertorCurrency.getInstance("CZK"); + ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(100), fromCurrency, new BigDecimal(80), toCurrency); + + return Convertor.createConvertor(exchangeRateProvider); + } + + + public Convertor createCZKtoYEN() { + ConvertorCurrency fromCurrency = ConvertorCurrency.getInstance("CZK"); + ConvertorCurrency toCurrency = ConvertorCurrency.getInstance("JPY"); + ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(1), fromCurrency, new BigDecimal(1), toCurrency); + + return Convertor.createConvertor(exchangeRateProvider); + } + + /** Use the convertor from createCZKtoUSD method and do few conversions + * with it. + */ + public void testCurrencyCZKUSD() throws Exception { + Convertor convertCzkUsd = createCZKtoUSD(); + + { + // convert $1 to CZK using c: + ConversionResult result = convertCzkUsd.convertBack(new BigDecimal(1)); + assertEquals("Result is 17 CZK", new BigDecimal("17.00"), result.getConverted()); + assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder()); + } + + { + // convert 17CKZ to $ using c: + ConversionResult result = convertCzkUsd.convert(new BigDecimal(17)); + assertEquals("Result is 1 $", new BigDecimal("1.00"), result.getConverted()); + assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder()); + } + + { + // convert $5 to CZK using c: + ConversionResult result = convertCzkUsd.convertBack(new BigDecimal(5)); + assertEquals("Result is 85 CZK", new BigDecimal("85.00"), result.getConverted()); + assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder()); + } + + { + // convert $8 to CZK + ConversionResult result = convertCzkUsd.convertBack(new BigDecimal(8)); + assertEquals("Result is 136 CZK", new BigDecimal("136.00"), result.getConverted()); + assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder()); + } + + { + // convert 1003CZK to USD + ConversionResult result = convertCzkUsd.convert(new BigDecimal(1003)); + assertEquals("Result is 59 USD", new BigDecimal("59.00"), result.getConverted()); + assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder()); + } + } + + /** Use the convertor from createSKKtoCZK method and do few conversions + * with it. + */ + public void testCurrencySKKCZK() throws Exception { + Convertor convertSkkCzk = createSKKtoCZK(); + { + // convert 100SKK using c: + ConversionResult result = convertSkkCzk.convert(new BigDecimal(100)); + assertEquals("Result is 80 CZK", new BigDecimal("80.00"), result.getConverted()); + } + { + // convert 80CZK using c: + ConversionResult result = convertSkkCzk.convertBack(new BigDecimal(80)); + assertEquals("Result is 100 SKK", new BigDecimal("100.00"), result.getConverted()); + } + + { + // convert 16CZK using c: + ConversionResult result = convertSkkCzk.convertBack(new BigDecimal(16)); + assertEquals("Result is 20 SKK", new BigDecimal("20.00"), result.getConverted()); + } + + { + // convert 500SKK to CZK + ConversionResult result = convertSkkCzk.convert(new BigDecimal(500)); + assertEquals("Result is 400 CZK", new BigDecimal("400.00"), result.getConverted()); + assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder()); + } + + { + // convert 501SKK to CZK + ConversionResult result = convertSkkCzk.convert(new BigDecimal(501)); + assertEquals("Result is 400 CZK", new BigDecimal("400.80"), result.getConverted()); + assertEquals("No Remainer", BigDecimal.ZERO, result.getRemainder()); + + } + } + + /** + * Convert SKK to CZK. Convertor can't convert whole amout (can't convert one SKK cent to CZK). Remaining + * amount is stored in remainder result. + * + * Test is currently failing, because implementation can't handle this case. + */ +// public void testConvertSmallUnits_failing() { +// Convertor convertSkkCzk = createSKKtoCZK(); +// { +// // convert 501SKK to CZK +// ConversionResult result = convertSkkCzk.convert(new BigDecimal("501.01")); +// assertEquals("Result is 400 CZK", new BigDecimal("400.80"), result.getConverted()); +// assertEquals("No Remainer", new BigDecimal("0.01"), result.getRemainder()); +// +// } +// +// } + + /** + * Test converting from CZK to JPY. Remained has scale of CZK. + * + * This test is currently failing, because converter implementation currently can't handle conversion from "cent" to "no-cent" currency. + */ +// public void testConvertCzkToJpy_failing() { +// Convertor convertSkkCzk = createCZKtoYEN(); +// { +// // convert 501SKK to CZK +// ConversionResult result = convertSkkCzk.convert(new BigDecimal("120.00")); +// assertEquals("Result is 120 YEN", new BigDecimal("120"), result.getConverted()); +// assertEquals("No Remainer", new BigDecimal("0.00"), result.getRemainder()); +// +// } +// } + + /** + * Test converting from JPY to CZK. Remained has scale of JPY. + * + * This test is currently failing, because converter implementation currently can't handle conversion from "cent" to "no-cent" currency. + */ +// public void testConvertJpyToCzk_failing() { +// Convertor convertSkkCzk = createCZKtoYEN(); +// { +// // convert 501SKK to CZK +// ConversionResult result = convertSkkCzk.convert(new BigDecimal("120.00")); +// assertEquals("Result is 120 YEN", new BigDecimal("120"), result.getConverted()); +// assertEquals("No Remainer", new BigDecimal("0"), result.getRemainder()); +// +// } +// } + + public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception { + Convertor c = createCZKtoUSD(); + // convert $5 to SKK, the API shall say this is not possible + // convert 500 SKK to CZK, the API shall say this is not possible + // ... no api for required call, should be here? + } + + +} + diff -r 36331f7244bd -r f6073056b9fe task2/solution14/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution14/build.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution14/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution14/nbproject/build-impl.xml Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution14/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution14/nbproject/genfiles.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution14/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution14/nbproject/project.properties Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution14/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution14/nbproject/project.xml Wed Oct 01 10:43:05 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 14 + 1.6.5 + + + + + + + + + diff -r 36331f7244bd -r f6073056b9fe task2/solution14/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution14/src/org/apidesign/apifest08/currency/Convertor.java Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution14/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution14/src/org/apidesign/apifest08/currency/ConvertorFactory.java Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution14/src/org/apidesign/apifest08/currency/Rate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution14/src/org/apidesign/apifest08/currency/Rate.java Wed Oct 01 10:43:05 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 36331f7244bd -r f6073056b9fe task2/solution14/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task2/solution14/test/org/apidesign/apifest08/test/Task1Test.java Wed Oct 01 10:43:05 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 + } + + } +}