# HG changeset patch # User japod@localhost # Date 1222603958 -7200 # Node ID 97662396c0fd68cbb4ae296f38e21c2937af6c50 # Parent d283f44013341c4ba8e580b1d7d1c2530bad3b88 Adding solutions received for task1 diff -r d283f4401334 -r 97662396c0fd task1/README --- a/task1/README Fri Sep 26 06:21:08 2008 +0200 +++ b/task1/README Sun Sep 28 14:12:38 2008 +0200 @@ -1,16 +1,9 @@ This directory shall contain solutions for the task1. Each solution is NetBeans Java SE project, in its own -directory with name being a codename. The codename +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. -At the end the structure shall be: -task1/xyz/build.xml -task1/xyz/nbproject/ -task1/xyz/... - -task1/abc/build.xml -task1/abc/nbproject/ -task1/abc/... - diff -r d283f4401334 -r 97662396c0fd task1/solution01/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution01/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/nbproject/build-impl.xml Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/nbproject/genfiles.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/nbproject/project.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 01 + 1.6.5 + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/AbstractConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/AbstractConvertorFactory.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/AbstractCurrencyConvertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/AbstractCurrencyConvertor.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/CannotConvertException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/CannotConvertException.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/CannotInstantiateFactoryException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/CannotInstantiateFactoryException.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/CannotProvideValueException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/CannotProvideValueException.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/ConversionProperties.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/ConversionProperties.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/ConversionRatioProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/ConversionRatioProvider.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/Convertor.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/ConvertorNotAvailableException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/ConvertorNotAvailableException.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/ConvertorsFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/ConvertorsFactory.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorFactory.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorFactoryImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorFactoryImpl.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/src/org/apidesign/apifest08/currency/CurrencyConvertorImpl.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution01/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution01/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution02/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution02/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/nbproject/build-impl.xml Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution02/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/nbproject/genfiles.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution02/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/nbproject/project.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution02/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 02 + 1.6.5 + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution02/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/src/org/apidesign/apifest08/currency/Convertor.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,38 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; + + +/** + * Converts one currency to other. The conversion is unidirectional. + * For example you can have convertor that converts USD (sourceCurrency) to CZK (destination currency). You can call the {@link Convertor#convert(Money)} method + * with amount in USD to get the equivalent in CZK. If you need convert CZK to USD you can call {@link Convertor#revert()} method to get CZK to USD + * convertor. To create a convertor instance call {@link ConvertorFactory#createConvertor(Currency, Currency)}. + */ +public interface Convertor { + /** + * Converts amount in source currency to amount in destination currency. The result is rounded to two decimal places. + * @param money + * @return + * @throws IllegalArgumentException if money.getCurrency is not equal to sourceCurrency. + */ + public Money convert(Money money); + + /** + * Returns convertor that converts from destination currency to source currency with the same exchange rate. + * @return + */ + public Convertor revert(); + + /** + * Returns source currency. + * @return + */ + public Currency getSourceCurrency(); + + /** + * Returns destination currency. + * @return + */ + public Currency getDestinationCurrency(); +} diff -r d283f4401334 -r 97662396c0fd task1/solution02/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/src/org/apidesign/apifest08/currency/ConvertorFactory.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,50 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; + + +/** + * Creates default {@link Convertor} implementations. + * @author lukas + * + */ +public class ConvertorFactory { + private static final DefaultConvertorFactory DEFAULT_FACTORY = new DefaultConvertorFactory(); + + private ConvertorFactory() + { + //nothing + } + + /** + * Creates {@link Convertor} that converts from sourceCurrency to destinationCurrency with stored exchange rate. + * @param sourceCurrency + * @param destinationCurrency + * @return + * @throws UnsupportedConversionException when exchange rate between currencies is not known. + */ + /* + * Only one of the createConveror methods is needed. The assignment is not explicit where the exchange rate should be set. + */ + public static final Convertor createConvertor(Currency sourceCurrency, Currency destinationCurrency) throws UnsupportedConversionException + { + return DEFAULT_FACTORY.getConvertor(sourceCurrency, destinationCurrency); + } + /** + * 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)). + * @param sourceEquivalent + * @param destinationEquivalent + * @return + */ + /* + * Only one of the createConveror methods is needed. The assignment is not explicit where the exchange rate should be set. + */ + public static final Convertor createConvertor(Money sourceEquivalent, Money destinationEquivalent) + { + return new DefaultConvertor(sourceEquivalent.getAmount(), destinationEquivalent.getAmount(), sourceEquivalent.getCurrency(), destinationEquivalent.getCurrency()); + } + + +} diff -r d283f4401334 -r 97662396c0fd task1/solution02/src/org/apidesign/apifest08/currency/DefaultConvertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/src/org/apidesign/apifest08/currency/DefaultConvertor.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,134 @@ +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(); + this.sourceEquivalent = sourceEquivalent; + this.destinationEquivalent = destinationEquivalent; + this.sourceCurrency = sourceCurrency; + this.destinationCurrency = destinationCurrency; + } + + public Money convert(Money money) { + if (money==null) + { + throw new NullPointerException("Money is null"); + } + if (!money.getCurrency().equals(getSourceCurrency())) + { + throw new IllegalArgumentException("Can not convert from "+money.getCurrency()+". Converts "+getSourceCurrency()+" to "+getDestinationCurrency()); + } + BigDecimal sourceAmount = money.getAmount(); + BigDecimal destinationAmount = sourceAmount.multiply(destinationEquivalent).divide(sourceEquivalent, 2, RoundingMode.HALF_DOWN); + return new MoneyImpl(destinationAmount, getDestinationCurrency()); + } + + + 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 d283f4401334 -r 97662396c0fd task1/solution02/src/org/apidesign/apifest08/currency/DefaultConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/src/org/apidesign/apifest08/currency/DefaultConvertorFactory.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,49 @@ +package org.apidesign.apifest08.currency; + + +import java.math.BigDecimal; +import java.util.Currency; +import java.util.HashMap; +import java.util.Map; + +/** + * Default factory for convertors. Basically it just keeps exchange rates for given currency combinations. + * @author lukas + * + */ +class DefaultConvertorFactory { + private static final Currency SKK = Currency.getInstance("SKK"); + private static final Currency USD = Currency.getInstance("USD"); + private static final Currency CZK = Currency.getInstance("CZK"); + private Map convertorMap = new HashMap(); + + public DefaultConvertorFactory() + { + addConvertor(CZK,USD,BigDecimal.valueOf(17),BigDecimal.valueOf(1)); + addConvertor(CZK,SKK,BigDecimal.valueOf(80),BigDecimal.valueOf(100)); + } + + private void addConvertor(Currency sourceCurrency, Currency destinationCurrency, BigDecimal sourceEquivalent, BigDecimal destinationEquivalent) { + DefaultConvertor convertor = new DefaultConvertor(sourceEquivalent, destinationEquivalent, sourceCurrency, destinationCurrency); + convertorMap.put(getConvertorKey(sourceCurrency, destinationCurrency), convertor); + convertorMap.put(getConvertorKey(destinationCurrency, sourceCurrency), convertor.revert()); + } + + public Convertor getConvertor(Currency sourceCurrency, Currency destinationCurrency) throws UnsupportedConversionException + { + String convertorKey = getConvertorKey(sourceCurrency, destinationCurrency); + Convertor result = convertorMap.get(convertorKey); + if (result!=null) + { + return result; + } + else + { + throw new UnsupportedConversionException("Conversion from "+sourceCurrency+" to "+destinationCurrency+" is not supported"); + } + } + + private String getConvertorKey(Currency sourceCurrency, Currency destinationCurrency) { + return sourceCurrency.getCurrencyCode()+destinationCurrency.getCurrencyCode(); + } +} diff -r d283f4401334 -r 97662396c0fd task1/solution02/src/org/apidesign/apifest08/currency/Money.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/src/org/apidesign/apifest08/currency/Money.java Sun Sep 28 14:12:38 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 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 d283f4401334 -r 97662396c0fd task1/solution02/src/org/apidesign/apifest08/currency/MoneyImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/src/org/apidesign/apifest08/currency/MoneyImpl.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,87 @@ +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.setScale(2); + 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); + } + + /* (non-Javadoc) + * @see org.apidesign.apifest08.currency.Money#getAmount() + */ + public BigDecimal getAmount() { + return amount; + } + + /* (non-Javadoc) + * @see org.apidesign.apifest08.currency.Money#getCurrency() + */ + 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.equals(other.amount)) + 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 d283f4401334 -r 97662396c0fd task1/solution02/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,28 @@ +package org.apidesign.apifest08.currency; + +/** + * Exception thrown when conversion is not supported. + * @author lukas + * + */ +public class UnsupportedConversionException extends IllegalArgumentException { + private static final long serialVersionUID = 4412475695345865196L; + + public UnsupportedConversionException() { + super(); + } + + public UnsupportedConversionException(String message, Throwable cause) { + super(message, cause); + } + + public UnsupportedConversionException(String s) { + super(s); + } + + public UnsupportedConversionException(Throwable cause) { + super(cause); + } + + +} diff -r d283f4401334 -r 97662396c0fd task1/solution02/test/org/apidesign/apifest08/test/ConvertorFactoryTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/test/org/apidesign/apifest08/test/ConvertorFactoryTest.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,46 @@ +package org.apidesign.apifest08.test; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static org.apidesign.apifest08.test.Task1Test.CZK; +import static org.apidesign.apifest08.test.Task1Test.USD; + +import java.util.Currency; + +import org.apidesign.apifest08.currency.ConvertorFactory; +import org.apidesign.apifest08.currency.UnsupportedConversionException; +import org.junit.Test; + + +public class ConvertorFactoryTest { + @Test(expected=NullPointerException.class) + public void testNullSource() + { + ConvertorFactory.createConvertor(null, USD); + } + @Test(expected=NullPointerException.class) + public void testNullDestination() + { + ConvertorFactory.createConvertor(CZK, null); + } + @Test(expected=IllegalArgumentException.class) + public void testShortSource() + { + ConvertorFactory.createConvertor(Currency.getInstance("CZ"), USD); + } + @Test(expected=UnsupportedConversionException.class) + public void testUnknownCombination() + { + ConvertorFactory.createConvertor(CZK, Currency.getInstance("ZAR")); + } + @Test + public void testOk() + { + assertNotNull(ConvertorFactory.createConvertor(CZK, USD)); + } + @Test + public void testReverted() + { + assertEquals(ConvertorFactory.createConvertor(CZK, USD).revert(), ConvertorFactory.createConvertor(USD, CZK)); + } +} diff -r d283f4401334 -r 97662396c0fd task1/solution02/test/org/apidesign/apifest08/test/ConvertorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/test/org/apidesign/apifest08/test/ConvertorTest.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,30 @@ +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.ConvertorFactory; +import org.apidesign.apifest08.currency.Money; +import org.apidesign.apifest08.currency.MoneyImpl; +import org.junit.Test; + + +public class ConvertorTest { + + @Test + public void testConvertSmall() + { + Money converted = ConvertorFactory.createConvertor(CZK, USD).convert(new MoneyImpl(0.17,CZK)); + assertEquals(new MoneyImpl(new BigDecimal("0.01"),USD),converted); + assertEquals(USD,converted.getCurrency()); + } + @Test + public void testConvertSmallReverse() + { + Money converted = ConvertorFactory.createConvertor(USD, CZK).convert(new MoneyImpl(0.01,USD)); + assertEquals(new MoneyImpl(new BigDecimal("0.17"),CZK),converted); + } +} diff -r d283f4401334 -r 97662396c0fd task1/solution02/test/org/apidesign/apifest08/test/MoneyTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/test/org/apidesign/apifest08/test/MoneyTest.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,24 @@ +package org.apidesign.apifest08.test; + +import static junit.framework.Assert.assertEquals; + +import java.math.BigDecimal; + +import org.apidesign.apifest08.currency.MoneyImpl; +import org.junit.Test; +import static org.apidesign.apifest08.test.Task1Test.*; + +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(new BigDecimal("123.00"),new MoneyImpl(123,CZK).getAmount()); + } +} diff -r d283f4401334 -r 97662396c0fd task1/solution02/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution02/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,91 @@ +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 { + } + + /** 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() { + //You can use both variants. I wrote the first one but than I realized that maybe + //you want me to write the second one. So I have written both. + return ConvertorFactory.createConvertor(CZK, USD); + //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. + * + * 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() { + //You can use both variants. I wrote the first one but than I realized that maybe + //you want me to write the second one. So I have written both. + //return ConvertorFactory.createConvertor(SKK, CZK); + return ConvertorFactory.createConvertor(new MoneyImpl(100,SKK), new MoneyImpl(80,CZK)); + } + + /** Use the convertor from createCZKtoUSD method and do few conversions + * with it. + */ + public void testCurrencyCZKUSD() throws Exception { + Convertor czkToUsdConvertor = createCZKtoUSD(); + // convert $5 to CZK using c: + Convertor usdToCzkConvertor = czkToUsdConvertor.revert(); + assertEquals("Result is 85 CZK",new MoneyImpl(85,CZK), usdToCzkConvertor.convert(new MoneyImpl(5,USD))); + + // convert $8 to CZK + assertEquals("Result is 136 CZK",new MoneyImpl(136,CZK), usdToCzkConvertor.convert(new MoneyImpl(8,USD))); + + // convert 1003CZK to USD + assertEquals("Result is 59 USD", new MoneyImpl(59,USD), czkToUsdConvertor.convert(new MoneyImpl(1003,CZK))); + } + + /** Use the convertor from createSKKtoCZK method and do few conversions + * with it. + */ + public void testCurrencySKKCZK() throws Exception { + Convertor skkToCzkConvertor = createSKKtoCZK(); + // convert 16CZK using c: + assertEquals("Result is 20 SKK", new MoneyImpl(20,SKK), skkToCzkConvertor.revert().convert(new MoneyImpl(16,CZK))); + + // convert 500SKK to CZK + assertEquals("Result is 400 CZK", new MoneyImpl(400,CZK), skkToCzkConvertor.convert(new MoneyImpl(500,SKK))); + } +} + diff -r d283f4401334 -r 97662396c0fd task1/solution03/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution03/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution03/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution03/nbproject/build-impl.xml Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution03/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution03/nbproject/genfiles.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution03/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution03/nbproject/project.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution03/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution03/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 03 + 1.6.5 + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution03/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution03/src/org/apidesign/apifest08/currency/Convertor.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution03/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution03/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution04/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution04/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution04/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution04/nbproject/build-impl.xml Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution04/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution04/nbproject/genfiles.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution04/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution04/nbproject/project.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution04/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution04/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 04 + 1.6.5 + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution04/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution04/src/org/apidesign/apifest08/currency/Convertor.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,105 @@ +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 + */ +public final class Convertor +{ + /** + * The currency to cvonvert from. + */ + private final Currency currencyA; + + /** + * The currency to cvonvert from. + */ + private final Currency currencyB; + + /** + * Constructs a convertor with the specified currencies. + * + * @param a the currency to convert from. + * @param b the currency to convert to. + * @throws IllegalArgumentException if either a or b are null. + */ + public Convertor(final Currency a, + final Currency b) + { + if(a == null) + { + throw new IllegalArgumentException("a cannot be null"); + } + + if(b == null) + { + throw new IllegalArgumentException("a cannot be null"); + } + + currencyA = a; + currencyB = b; + } + + /** + * Convert from currency "b" to currency "a". + * + * @param amount the amount to convert. + * @return the converted amount. + * @throws IllegalArgumentException if amount is null. + */ + public BigDecimal convertFrom(final BigDecimal amount) + { + final BigDecimal aInUSD; + final BigDecimal bInUSD; + final BigDecimal temp; + final BigDecimal result; + + if(amount == null) + { + throw new IllegalArgumentException("amount cannot be null"); + } + + aInUSD = CurrencyValues.getValue(currencyA); + bInUSD = CurrencyValues.getValue(currencyB); + temp = amount.divide(bInUSD, MathContext.DECIMAL32); + result = temp.multiply(aInUSD); + + return (result.setScale(2, RoundingMode.HALF_DOWN)); + } + + /** + * Convert from currency "a" to currency "b". + * + * @param amount the amount to convert. + * @return the converted amount. + * @throws IllegalArgumentException if amount is null. + */ + public BigDecimal convertTo(final BigDecimal amount) + { + final BigDecimal aInUSD; + final BigDecimal bInUSD; + final BigDecimal temp; + final BigDecimal result; + + if(amount == null) + { + throw new IllegalArgumentException("amount cannot be null"); + } + + aInUSD = CurrencyValues.getValue(currencyA); + bInUSD = CurrencyValues.getValue(currencyB); + temp = amount.divide(aInUSD, MathContext.DECIMAL32); + result = temp.multiply(bInUSD); + + return (result.setScale(2, RoundingMode.HALF_DOWN)); + } +} diff -r d283f4401334 -r 97662396c0fd task1/solution04/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution04/src/org/apidesign/apifest08/currency/ConvertorFactory.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,95 @@ +package org.apidesign.apifest08.currency; + +import java.lang.ref.WeakReference; +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 b the currency to convert to. + * @return the convertor for the specified currencies. + */ + public static Convertor getConvertor(final String a, + final String b) + { + final Currency currencyA; + final Currency currencyB; + final Convertor convertor; + + currencyA = Currency.getInstance(a); + currencyB = Currency.getInstance(b); + convertor = getConvertor(currencyA, currencyB); + + return (convertor); + } + + /** + * Get the convertor for the specified currencies. + * + * @param a the currency to convert from. + * @param b the currency to convert to. + * @return the convertor for the specified currencies. + */ + public static Convertor getConvertor(final Currency a, + final Currency b) + { + 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"); + } + + key = a.getCurrencyCode() + b.getCurrencyCode(); + + // make sure that we don't try to overwrite one + synchronized(convertors) + { + if(!(convertors.containsKey(key))) + { + convertor = new Convertor(a, b); + convertors.put(key, new WeakReference(convertor)); + } + } + + convertor = convertors.get(key).get(); + + return (convertor); + } +} diff -r d283f4401334 -r 97662396c0fd task1/solution04/src/org/apidesign/apifest08/currency/CurrencyValues.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution04/src/org/apidesign/apifest08/currency/CurrencyValues.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,122 @@ +package org.apidesign.apifest08.currency; + + +import java.math.BigDecimal; +import java.util.Currency; +import java.util.HashMap; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + + +/** + * Keeps track of the current value for each currency as USD. + * + * @author D'Arcy Smith + * @version 1.0 + */ +class CurrencyValues +{ + /** + * The values expressed in USD. + */ + private static Map values; + + /** + * Update the values periodically + */ + private static final Timer refresher; + + static + { + final Refresher refresherTask; + final long delay; + + // load the map NOW! (don't use the scheduler to do it just because we want + // to be 100% certain it is loaded before anything else can be called. + refresh(); + + refresherTask = new Refresher(); + refresher = new Timer("CurrencyValues Refresher", true); + + // update once an hour + delay = 1000 * 60 * 60; + refresher.scheduleAtFixedRate(refresherTask, delay, delay); + } + + /** + * Prevent accidental creation. + */ + private CurrencyValues() + { + } + + /** + * Refresh the currency values. + */ + static void refresh() + { + Map newValues; + Currency currency; + + newValues = new HashMap(); + + // these would update from a data source, database, web service, something... + currency = Currency.getInstance("USD"); + newValues.put(currency, BigDecimal.valueOf(1.0).setScale(2)); + + currency = Currency.getInstance("CZK"); + newValues.put(currency, BigDecimal.valueOf(17.0)); + + currency = Currency.getInstance("SKK"); + newValues.put(currency, BigDecimal.valueOf(21.25)); + + // don't sycnhronize all of it because clients can use slightly out of + // date information. + synchronized(CurrencyValues.class) + { + values = newValues; + } + } + + /** + * Get the value of the specified currency in USD. + * + * @param currency the corrency to get. + * @return the value of the currency in USD. + * @throws IllegalArgumentException if currency is null. + */ + static BigDecimal getValue(final Currency currency) + { + final BigDecimal value; + + if(currency == null) + { + throw new IllegalArgumentException("currencyName cannot be null"); + } + + // make sure we are not updating the map right now + synchronized(CurrencyValues.class) + { + value = values.get(currency); + } + + return (value); + } + + /** + * Used to update the currency map periodically. + */ + private static class Refresher + extends TimerTask + { + /** + * call the refresh method. + */ + @Override + public void run() + { + refresh(); + } + } +} diff -r d283f4401334 -r 97662396c0fd task1/solution04/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution04/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,118 @@ +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; + +/** 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.getConvertor("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 Convertor createSKKtoCZK() + { + return (ConvertorFactory.getConvertor(Currency.getInstance("SKK"), Currency.getInstance("CZK"))); + } + + public static Convertor createUSDtoUSD() + { + return (ConvertorFactory.getConvertor(Currency.getInstance("USD"), Currency.getInstance("USD"))); + } + + /** 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.convertFrom(BigDecimal.valueOf(5)); + assertEquals(new BigDecimal("85.00"), result); + + // convert $8 to CZK + // assertEquals("Result is 136 CZK"); + result = c.convertFrom(BigDecimal.valueOf(8)); + assertEquals(new BigDecimal("136.00"), result); + + // convert 1003CZK to USD + // assertEquals("Result is 59 USD"); + result = c.convertTo(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.convertFrom(BigDecimal.valueOf(16)); + assertEquals(new BigDecimal("20.00"), result); + + // convert 500SKK to CZK + // assertEquals("Result is 400 CZK"); + result = c.convertTo(BigDecimal.valueOf(500)); + assertEquals(new BigDecimal("400.00"), result); + } + + /** Use the convertor from createSKKtoCZK method and do few conversions + * with it. + */ + public void testCurrencyUSDUSD() throws Exception { + Convertor c = createUSDtoUSD(); + BigDecimal result; + + // convert 1USD using c: + // assertEquals("Result is 1 USD"); + result = c.convertFrom(BigDecimal.valueOf(1)); + assertEquals(new BigDecimal("1.00"), result); + + // convert 500USD to USD + // assertEquals("Result is 500 USD"); + result = c.convertTo(BigDecimal.valueOf(500)); + assertEquals(new BigDecimal("500.00"), result); + } +} + diff -r d283f4401334 -r 97662396c0fd task1/solution05/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution05/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution05/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution05/nbproject/build-impl.xml Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution05/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution05/nbproject/genfiles.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution05/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution05/nbproject/project.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution05/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution05/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 05 + 1.6.5 + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution05/src/org/apidesign/apifest08/currency/Amount.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution05/src/org/apidesign/apifest08/currency/Amount.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution05/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution05/src/org/apidesign/apifest08/currency/Convertor.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution05/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution05/src/org/apidesign/apifest08/currency/ConvertorFactory.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution05/src/org/apidesign/apifest08/currency/ConvertorImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution05/src/org/apidesign/apifest08/currency/ConvertorImpl.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution05/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution05/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution06/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution06/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/nbproject/build-impl.xml Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution06/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/nbproject/genfiles.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution06/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/nbproject/project.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution06/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 06 + 1.6.5 + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution06/src/org/apidesign/apifest08/currency/Amount.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/src/org/apidesign/apifest08/currency/Amount.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,80 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Currency; +import static org.apidesign.apifest08.currency.Assert.*; + +/** + * 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 d283f4401334 -r 97662396c0fd task1/solution06/src/org/apidesign/apifest08/currency/Assert.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/src/org/apidesign/apifest08/currency/Assert.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,9 @@ +package org.apidesign.apifest08.currency; + +public class Assert { + static void notNull(Object value, String argumentName) { + if(value == null) { + throw new IllegalArgumentException("The argument '" + argumentName + "' connot not be null"); + } + } +} diff -r d283f4401334 -r 97662396c0fd task1/solution06/src/org/apidesign/apifest08/currency/Bid.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/src/org/apidesign/apifest08/currency/Bid.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,74 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Currency; +import static org.apidesign.apifest08.currency.Assert.*; + + +/** + * A bid representation. A did is defined as two currenncies and its value. + */ +public final class Bid { + private final Currency first; + private final Currency second; + private final BigDecimal bidValue; // a bid between the first currency and the second currency + public static final BigDecimal one = new BigDecimal(1); + + Bid(Currency first, Currency second, BigDecimal bid) { + notNull(first, "first"); + notNull(second, "second"); + this.first = first; + this.bidValue = bid; + this.second = second; + } + + Bid(Currency first, Currency second) { + notNull(first, "first"); + notNull(second, "second"); + this.first = first; + this.second = second; + this.bidValue = null; + } + + + BigDecimal getBidValue(Currency from, Currency to) { + if((from != first || from != second) || (to != first || to != second)) { + new IllegalArgumentException("This bid can be used only for: " + first + " " + second); + } + + notNull(bidValue, "bidValue"); + + BigDecimal retVal; + + if(first == from) { + retVal = bidValue; + } else { + //reverse bid + retVal = one.divide(bidValue, 10 ,RoundingMode.HALF_UP); + } + + return retVal; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + first.hashCode() + second.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Bid other = (Bid) obj; + + return (this.first == other.first || this.first == other.second) && (this.second == other.second || this.second == other.first) ; + } +} \ No newline at end of file diff -r d283f4401334 -r 97662396c0fd task1/solution06/src/org/apidesign/apifest08/currency/ConversionException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/src/org/apidesign/apifest08/currency/ConversionException.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution06/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/src/org/apidesign/apifest08/currency/Convertor.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,32 @@ +package org.apidesign.apifest08.currency; + +import java.math.BigDecimal; +import java.util.Currency; + +public abstract class Convertor { + + /** + * Converts an amount to another amount according to a given currency. + * + * @param from a source + * @param toCurrency a target currency + * @return a converted amount + * @throws ConversionException if the conversion fails + * @throws UnsupportedConversionException if the conversion between a given currencies is not supported. + */ + public abstract Amount convert(Amount from, Currency toCurrency) throws ConversionException, UnsupportedConversionException; + + /** + * Converts an amount value between two currencies. + * + * @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 abstract Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency) throws ConversionException, UnsupportedConversionException; + +} diff -r d283f4401334 -r 97662396c0fd task1/solution06/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/src/org/apidesign/apifest08/currency/ConvertorFactory.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,65 @@ +package org.apidesign.apifest08.currency; + +import static org.apidesign.apifest08.currency.Assert.notNull; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Currency; +import java.util.List; + +/** + * The factory for {@link Convertor}. + * + * @see #newInstance() + */ +public final class ConvertorFactory { + //do not expose constructor + private ConvertorFactory(){} + + /** + * @return a convertor instance. + * + * @throws CurrencyException in a convertor cannot be instantiated + */ + public static Convertor newInstance() throws CurrencyException{ + return new DefaultConvertor(); + } + + private static final class DefaultConvertor extends Convertor { + private List bids; + + private DefaultConvertor() { + bids = new ArrayList(); + Bid usdCzk = new Bid(Currencies.USD, Currencies.CZK, new BigDecimal("17")); + bids.add(usdCzk); + Bid skCzk = new Bid(Currencies.SKK, Currencies.CZK, new BigDecimal("0.8")); + bids.add(skCzk); + } + + @Override + public Amount convert(Amount from, Currency targetCurrency) + throws CurrencyException { + return convert(from.getValue(), from.getCurrency(), targetCurrency); + + } + + @Override + public Amount convert(BigDecimal amount, Currency from, + Currency targetCurrency) throws ConversionException, + UnsupportedConversionException { + notNull(from, "from"); + notNull(targetCurrency, "targetCurrency"); + + int index = bids.indexOf(new Bid(from, targetCurrency)); + if(index == -1) { + throw new UnsupportedConversionException(from, targetCurrency); + } + Bid bid = bids.get(index); + BigDecimal bidValue = bid.getBidValue(from, targetCurrency); + BigDecimal result = bidValue.multiply(amount); + return new Amount(result, targetCurrency); + } + + } +} diff -r d283f4401334 -r 97662396c0fd task1/solution06/src/org/apidesign/apifest08/currency/Currencies.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/src/org/apidesign/apifest08/currency/Currencies.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,9 @@ +package org.apidesign.apifest08.currency; + +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 d283f4401334 -r 97662396c0fd task1/solution06/src/org/apidesign/apifest08/currency/CurrencyException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/src/org/apidesign/apifest08/currency/CurrencyException.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution06/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,27 @@ +package org.apidesign.apifest08.currency; + +import java.util.Currency; + +public 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 yet. Missing bid."); + this.from = from; + this.to = to; + } + + public Currency getFrom() { + return from; + } + + public Currency getTo() { + return to; + } + + +} diff -r d283f4401334 -r 97662396c0fd task1/solution06/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution06/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,101 @@ +package org.apidesign.apifest08.test; + +import static org.apidesign.apifest08.currency.Currencies.CZK; +import static org.apidesign.apifest08.currency.Currencies.SKK; +import static org.apidesign.apifest08.currency.Currencies.USD; + +import java.math.BigDecimal; + +import junit.framework.TestCase; + +import org.apidesign.apifest08.currency.Amount; +import org.apidesign.apifest08.currency.Convertor; +import org.apidesign.apifest08.currency.ConvertorFactory; +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 ConvertorFactory.newInstance(); + } + + /** 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.newInstance(); + } + + /** 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()); + } + + public void testUnssuportedConversion(){ + Convertor c = ConvertorFactory.newInstance(); + try { + c.convert(new BigDecimal(5), USD, SKK); + fail(); + } catch(UnsupportedConversionException e) { + //expected + } + } +} + diff -r d283f4401334 -r 97662396c0fd task1/solution07/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution07/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/nbproject/build-impl.xml Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution07/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/nbproject/genfiles.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution07/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/nbproject/project.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution07/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 07 + 1.6.5 + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution07/src/org/apidesign/apifest08/currency/ConversionRate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/src/org/apidesign/apifest08/currency/ConversionRate.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution07/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/src/org/apidesign/apifest08/currency/Convertor.java Sun Sep 28 14:12:38 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. + * 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 d283f4401334 -r 97662396c0fd task1/solution07/src/org/apidesign/apifest08/currency/DelegatingConvertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/src/org/apidesign/apifest08/currency/DelegatingConvertor.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution07/src/org/apidesign/apifest08/currency/IllegalRequestSubtypeException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/src/org/apidesign/apifest08/currency/IllegalRequestSubtypeException.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution07/src/org/apidesign/apifest08/currency/MonetaryAmount.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/src/org/apidesign/apifest08/currency/MonetaryAmount.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution07/src/org/apidesign/apifest08/currency/TableConvertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/src/org/apidesign/apifest08/currency/TableConvertor.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution07/test/org/apidesign/apifest08/test/ContractImposingDelegatingConvertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/test/org/apidesign/apifest08/test/ContractImposingDelegatingConvertor.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution07/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution07/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,122 @@ +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 { + } + + 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. + * + * 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. + * + * 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(); + } + + /** 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() ); + } + +} + diff -r d283f4401334 -r 97662396c0fd task1/solution08/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution08/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution08/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution08/nbproject/build-impl.xml Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution08/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution08/nbproject/genfiles.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution08/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution08/nbproject/project.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution08/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution08/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 08 + 1.6.5 + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution08/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution08/src/org/apidesign/apifest08/currency/Convertor.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution08/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution08/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution09/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution09/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution09/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution09/nbproject/build-impl.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,684 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set platform.home + Must set platform.bootcp + Must set platform.java + Must set platform.javac + + The J2SE Platform is not correctly set up. + Your active platform is: ${platform.active}, but the corresponding property "platforms.${platform.active}.home" is not found in the project's properties files. + Either open the project in the IDE and setup the Platform with the same name or add it manually. + For example like this: + ant -Duser.properties.file=<path_to_property_file> jar (where you put the property "platforms.${platform.active}.home" in a .properties file) + or ant -Dplatforms.${platform.active}.home=<path_to_JDK_home> jar (where no properties file is used) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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: + + + + + + + ${platform.java} -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + ${platform.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 d283f4401334 -r 97662396c0fd task1/solution09/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution09/nbproject/genfiles.properties Sun Sep 28 14:12:38 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=e8d85d23 +nbproject/build-impl.xml.script.CRC32=f0008fa7 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r d283f4401334 -r 97662396c0fd task1/solution09/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution09/nbproject/project.properties Sun Sep 28 14:12:38 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=JDK_1.6 +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 d283f4401334 -r 97662396c0fd task1/solution09/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution09/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,17 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 09 + 1.6.5 + + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution09/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution09/src/org/apidesign/apifest08/currency/Convertor.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution09/src/org/apidesign/apifest08/currency/ConvertorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution09/src/org/apidesign/apifest08/currency/ConvertorFactory.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution09/src/org/apidesign/apifest08/currency/CurrencyType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution09/src/org/apidesign/apifest08/currency/CurrencyType.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,7 @@ +package org.apidesign.apifest08.currency; + +public enum CurrencyType { + + CZK, SKK, USD; + +} diff -r d283f4401334 -r 97662396c0fd task1/solution09/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution09/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution10/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project Currency Convertor Solution 10. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution10/nbproject/.DS_Store Binary file task1/solution10/nbproject/.DS_Store has changed diff -r d283f4401334 -r 97662396c0fd task1/solution10/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/nbproject/build-impl.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,684 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set platform.home + Must set platform.bootcp + Must set platform.java + Must set platform.javac + + The J2SE Platform is not correctly set up. + Your active platform is: ${platform.active}, but the corresponding property "platforms.${platform.active}.home" is not found in the project's properties files. + Either open the project in the IDE and setup the Platform with the same name or add it manually. + For example like this: + ant -Duser.properties.file=<path_to_property_file> jar (where you put the property "platforms.${platform.active}.home" in a .properties file) + or ant -Dplatforms.${platform.active}.home=<path_to_JDK_home> jar (where no properties file is used) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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: + + + + + + + ${platform.java} -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + ${platform.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 d283f4401334 -r 97662396c0fd task1/solution10/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/nbproject/genfiles.properties Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,8 @@ +build.xml.data.CRC32=75e20e37 +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=75e20e37 +nbproject/build-impl.xml.script.CRC32=e2acc04f +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 diff -r d283f4401334 -r 97662396c0fd task1/solution10/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/nbproject/project.properties Sun Sep 28 14:12:38 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=JDK_1.6 +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 d283f4401334 -r 97662396c0fd task1/solution10/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,17 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 10 + 1.6.5 + + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution10/src/org/apidesign/apifest08/currency/ConstantRateConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/src/org/apidesign/apifest08/currency/ConstantRateConverter.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution10/src/org/apidesign/apifest08/currency/CurrencyConversionException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/src/org/apidesign/apifest08/currency/CurrencyConversionException.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution10/src/org/apidesign/apifest08/currency/CurrencyConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/src/org/apidesign/apifest08/currency/CurrencyConverter.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution10/src/org/apidesign/apifest08/currency/CurrencyConverterProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/src/org/apidesign/apifest08/currency/CurrencyConverterProvider.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution10/src/org/apidesign/apifest08/currency/CurrencyNotAvailableException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/src/org/apidesign/apifest08/currency/CurrencyNotAvailableException.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution10/src/org/apidesign/apifest08/currency/MultiCurrencyConstantRateConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/src/org/apidesign/apifest08/currency/MultiCurrencyConstantRateConverter.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution10/src/org/apidesign/apifest08/currency/OfflineConverterProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/src/org/apidesign/apifest08/currency/OfflineConverterProvider.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution10/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution10/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution11/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution11/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution11/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution11/nbproject/build-impl.xml Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution11/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution11/nbproject/genfiles.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution11/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution11/nbproject/project.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution11/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution11/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 11 + 1.6.5 + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution11/src/org/apidesign/apifest08/currency/Computer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution11/src/org/apidesign/apifest08/currency/Computer.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution11/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution11/src/org/apidesign/apifest08/currency/Convertor.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,119 @@ +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) { + 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 currencyValue an amount of the one currency + * @return an amount of the another one currency + */ + public CurrencyValue convert(CurrencyValue currencyValue) { + if (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 if (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 { + throw new IllegalArgumentException("Inappropriate currency 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 d283f4401334 -r 97662396c0fd task1/solution11/src/org/apidesign/apifest08/currency/CurrencyValue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution11/src/org/apidesign/apifest08/currency/CurrencyValue.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution11/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution11/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,99 @@ +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 { + } + + /** 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.getConvertorIntegerString( + CurrencyValue.getCurrencyValue(1, "USD"), + CurrencyValue.getCurrencyValue(17, "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 Convertor.getConvertorIntegerString( + CurrencyValue.getCurrencyValue(100, "SKK"), + CurrencyValue.getCurrencyValue(80, "CZK") + ); + } + + /** 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(CurrencyValue.getCurrencyValue(5, "USD")); + assertEquals(CurrencyValue.getCurrencyValue(85, "CZK"), result); + + // convert $8 to CZK + // assertEquals("Result is 136 CZK"); + result = c.convert(CurrencyValue.getCurrencyValue(8, "USD")); + assertEquals(CurrencyValue.getCurrencyValue(136, "CZK"), result); + + // convert 1003CZK to USD + // assertEquals("Result is 59 USD"); + result = c.convert(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(CurrencyValue.getCurrencyValue(16, "CZK")); + assertEquals(CurrencyValue.getCurrencyValue(20, "SKK"), result); + + // convert 500SKK to CZK + // assertEquals("Result is 400 CZK"); + result = c.convert(CurrencyValue.getCurrencyValue(500, "SKK")); + assertEquals(CurrencyValue.getCurrencyValue(400, "CZK"), result); + } +} + diff -r d283f4401334 -r 97662396c0fd task1/solution12/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution12/build.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project. + + + diff -r d283f4401334 -r 97662396c0fd task1/solution12/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution12/nbproject/build-impl.xml Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution12/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution12/nbproject/genfiles.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution12/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution12/nbproject/project.properties Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution12/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution12/nbproject/project.xml Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Currency Convertor Solution 12 + 1.6.5 + + + + + + + + + diff -r d283f4401334 -r 97662396c0fd task1/solution12/src/org/apidesign/apifest08/currency/Convertor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution12/src/org/apidesign/apifest08/currency/Convertor.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,110 @@ +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 class Convertor { + + private Currency currency1; + private Currency currency2; + + private Convertor(Currency currency1, Currency currency2) { + this.currency1 = currency1; + this.currency2 = currency2; + } + + /** + * 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 + */ + public static Convertor getConvertorInstance(Currency currency1, Currency currency2) { + 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 actualCurrency + * currency of this amount + * @return converted amount + */ + public double convert(double amount, Currency actualCurrency) { + ExchangeRate exchangeRate = null; + + if (actualCurrency == null) { + throw new ConvertorException("Selected currency is null!!!"); + } + + if (currency1.getSymbol().equals(actualCurrency.getSymbol()) + || currency2.getSymbol().equals(actualCurrency.getSymbol())) { + exchangeRate = getExchangeRate(actualCurrency); + } else { + throw new ConvertorException("This convertor could not be used for selected currency(" + + actualCurrency.getCurrencyCode() + ") " + this + "!!!"); + } + + return countResult(exchangeRate, amount); + } + + private double countResult(ExchangeRate exchangeRate, double amount) { + return amount * exchangeRate.getRate() / exchangeRate.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 actualCurrency) { + ExchangeRate exchangeRate = null; + + if (actualCurrency.getCurrencyCode().equals(currency1.getCurrencyCode())) { + exchangeRate = getExchangeRateInstance(actualCurrency, currency2); + } else if (actualCurrency.getCurrencyCode().equals(currency2.getCurrencyCode())) { + exchangeRate = getExchangeRateInstance(actualCurrency, currency1); + } + + return exchangeRate; + } + + /** + * Returns actual exchange rate between original and destination currency. + * @param originalCurrency + * original currency, from which we want to convert + * @param newCurrency + * destination currency, to which we want to convert + * @return exchange rate between this two currencies + */ + private ExchangeRate getExchangeRateInstance(Currency originalCurrency, Currency newCurrency) { + if ("USD".equals(originalCurrency.getCurrencyCode()) && "CZK".equals(newCurrency.getCurrencyCode())) { + return new ExchangeRate(originalCurrency, newCurrency, 17d, 1d); + } else if ("CZK".equals(originalCurrency.getCurrencyCode()) && "USD".equals(newCurrency.getCurrencyCode())) { + return new ExchangeRate(originalCurrency, newCurrency, 1d / 17d, 1d); + } else if ("SKK".equals(originalCurrency.getCurrencyCode()) && "CZK".equals(newCurrency.getCurrencyCode())) { + return new ExchangeRate(originalCurrency, newCurrency, 80d, 100d); + } else if ("CZK".equals(originalCurrency.getCurrencyCode()) && "SKK".equals(newCurrency.getCurrencyCode())) { + return new ExchangeRate(originalCurrency, newCurrency, 100d / (80d / 100d), 100d); + } else { + throw new ConvertorException("Defined currencies don't have secified rates [" + + originalCurrency.getCurrencyCode() + ", " + newCurrency.getCurrencyCode() + "]!!!"); + } + } + + public String toString() { + return "Converter [" + currency1.getCurrencyCode() + ", " + currency2.getCurrencyCode() + "]"; + } +} diff -r d283f4401334 -r 97662396c0fd task1/solution12/src/org/apidesign/apifest08/currency/ConvertorException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution12/src/org/apidesign/apifest08/currency/ConvertorException.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,20 @@ +package org.apidesign.apifest08.currency; + +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 d283f4401334 -r 97662396c0fd task1/solution12/src/org/apidesign/apifest08/currency/ExchangeRate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution12/src/org/apidesign/apifest08/currency/ExchangeRate.java Sun Sep 28 14:12:38 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 d283f4401334 -r 97662396c0fd task1/solution12/test/org/apidesign/apifest08/test/Task1Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/task1/solution12/test/org/apidesign/apifest08/test/Task1Test.java Sun Sep 28 14:12:38 2008 +0200 @@ -0,0 +1,78 @@ +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.getConvertorInstance(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.getConvertorInstance(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(); + + // convert $5 to CZK using c: + double result = c.convert(5, Currency.getInstance("USD")); + assertEquals("Result is not 85 CZK", 85.0, result); + + // convert $8 to CZK + result = c.convert(8, Currency.getInstance("USD")); + assertEquals("Result is not 136 CZK", 136.0, result); + + // convert 1003CZK to USD + result = c.convert(1003, Currency.getInstance("CZK")); + 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")); + assertEquals("Result is not 20 SKK", 20.0, result); + + // convert 500SKK to CZK + result = c.convert(500, Currency.getInstance("SKK")); + assertEquals("Result is not 400 CZK", 400.0, result); + } +}