Merging task4 solutions
authorJaroslav Tulach <jtulach@netbeans.org>
Sat, 18 Oct 2008 07:47:34 +0200
changeset 80067f86d76ac7
parent 79 0c910349ba67
parent 72 c6b50876b5cf
child 81 ec70f883de4a
Merging task4 solutions
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/currency/test/org/apidesign/apifest08/test/Task4Test.java	Sat Oct 18 07:47:34 2008 +0200
     1.3 @@ -0,0 +1,132 @@
     1.4 +package org.apidesign.apifest08.test;
     1.5 +
     1.6 +import java.util.Date;
     1.7 +import junit.framework.TestCase;
     1.8 +import org.apidesign.apifest08.currency.Convertor;
     1.9 +
    1.10 +/** The exchange rates are not always the same. They are changing. However
    1.11 + * as in order to predict the future, one needs to understand own past. That is
    1.12 + * why it is important to know the exchange rate as it was at any time during
    1.13 + * the past.
    1.14 + * <p>
    1.15 + * Today's quest is to enhance the convertor API to deal with dates.
    1.16 + * One shall be able to convert a currency at any date. Each currencies rate shall
    1.17 + * be associated with a range between two Date objects. In order
    1.18 + * to keep compatibility with old API that knew nothing about dates, the
    1.19 + * rates associated then are applicable "for eternity". Any use of existing
    1.20 + * convert methods that do not accept a Date argument, uses the current
    1.21 + * System.currentTimeMillis() as default date.
    1.22 + */
    1.23 +public class Task4Test extends TestCase {
    1.24 +    public Task4Test(String testName) {
    1.25 +        super(testName);
    1.26 +    }
    1.27 +
    1.28 +    @Override
    1.29 +    protected void setUp() throws Exception {
    1.30 +    }
    1.31 +
    1.32 +    @Override
    1.33 +    protected void tearDown() throws Exception {
    1.34 +    }
    1.35 +
    1.36 +    // Backward compatibly enhance your existing API to support following
    1.37 +    // usecases:
    1.38 +    //
    1.39 +
    1.40 +    /** Takes a convertor with any rates associated and creates new convertor
    1.41 +     * that returns the same values as the old one for time between from to till.
    1.42 +     * Otherwise it returns no results. This is just a helper method that
    1.43 +     * shall call some real one in the API.
    1.44 +     * 
    1.45 +     * @param old existing convertor
    1.46 +     * @param from initial date (inclusive)
    1.47 +     * @param till final date (exclusive)
    1.48 +     * @return new convertor
    1.49 +     */
    1.50 +    public static Convertor limitTo(Convertor old, Date from, Date till) {
    1.51 +        return null;
    1.52 +    }
    1.53 +
    1.54 +
    1.55 +    public void testCompositionOfLimitedConvertors() throws Exception {
    1.56 +        if (Boolean.getBoolean("ignore.failing")) {
    1.57 +            // implement me! then delete this if statement
    1.58 +            return;
    1.59 +        }
    1.60 +
    1.61 +        Date d1 = null; // 2008-10-01 0:00 GMT
    1.62 +        Date d2 = null; // 2008-10-02 0:00 GMT
    1.63 +        Date d3 = null; // 2008-10-03 0:00 GMT
    1.64 +        
    1.65 +        Convertor c = Task2Test.merge(
    1.66 +            limitTo(Task1Test.createCZKtoUSD(), d1, d2),
    1.67 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
    1.68 +        );
    1.69 +
    1.70 +        // convert $5 to CZK using c:
    1.71 +        // cannot convert as no rate is applicable to current date
    1.72 +
    1.73 +        // convert $8 to CZK using c:
    1.74 +        // cannot convert as no rate is applicable to current date
    1.75 +
    1.76 +        // convert 1003CZK to USD using c:
    1.77 +        // cannot convert as no rate is applicable to current date
    1.78 +
    1.79 +        // convert 16CZK using c:
    1.80 +        // cannot convert as no rate is applicable to current date
    1.81 +
    1.82 +        // convert 500SKK to CZK using c:
    1.83 +        // cannot convert as no rate is applicable to current date
    1.84 +
    1.85 +        // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
    1.86 +        // assertEquals("Result is 85 CZK");
    1.87 +
    1.88 +        // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
    1.89 +        // assertEquals("Result is 136 CZK");
    1.90 +
    1.91 +        // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
    1.92 +        // assertEquals("Result is 59 USD");
    1.93 +
    1.94 +        // convert 16CZK using c at 2008-10-02 9:00 GMT:
    1.95 +        // assertEquals("Result is 20 SKK");
    1.96 +
    1.97 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
    1.98 +        // assertEquals("Result is 400 CZK");
    1.99 +
   1.100 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
   1.101 +        // cannot convert as no rate is applicable to current date
   1.102 +    }
   1.103 +
   1.104 +    /** Create convertor that understands two currencies, CZK and
   1.105 +     *  SKK. Make 100 SKK == 90 CZK.
   1.106 +     *
   1.107 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
   1.108 +     */
   1.109 +    public static Convertor createSKKtoCZK2() {
   1.110 +        return null;
   1.111 +    }
   1.112 +
   1.113 +    public void testDateConvetorWithTwoDifferentRates() throws Exception {
   1.114 +        if (Boolean.getBoolean("ignore.failing")) {
   1.115 +            // implement me! then delete this if statement
   1.116 +            return;
   1.117 +        }
   1.118 +
   1.119 +        Date d1 = null; // 2008-10-01 0:00 GMT
   1.120 +        Date d2 = null; // 2008-10-02 0:00 GMT
   1.121 +        Date d3 = null; // 2008-10-03 0:00 GMT
   1.122 +
   1.123 +        Convertor c = Task2Test.merge(
   1.124 +            limitTo(createSKKtoCZK2(), d1, d2),
   1.125 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
   1.126 +        );
   1.127 +
   1.128 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
   1.129 +        // assertEquals("Result is 400 CZK");
   1.130 +
   1.131 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
   1.132 +        // assertEquals("Result is 450 CZK");
   1.133 +    }
   1.134 +
   1.135 +}
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/task4/solution04/build.xml	Sat Oct 18 07:47:34 2008 +0200
     2.3 @@ -0,0 +1,69 @@
     2.4 +<?xml version="1.0" encoding="UTF-8"?>
     2.5 +<!-- You may freely edit this file. See commented blocks below for -->
     2.6 +<!-- some examples of how to customize the build. -->
     2.7 +<!-- (If you delete it and reopen the project it will be recreated.) -->
     2.8 +<project name="currency" default="default" basedir=".">
     2.9 +    <description>Builds, tests, and runs the project.</description>
    2.10 +    <import file="nbproject/build-impl.xml"/>
    2.11 +    <!--
    2.12 +
    2.13 +    There exist several targets which are by default empty and which can be 
    2.14 +    used for execution of your tasks. These targets are usually executed 
    2.15 +    before and after some main targets. They are: 
    2.16 +
    2.17 +      -pre-init:                 called before initialization of project properties
    2.18 +      -post-init:                called after initialization of project properties
    2.19 +      -pre-compile:              called before javac compilation
    2.20 +      -post-compile:             called after javac compilation
    2.21 +      -pre-compile-single:       called before javac compilation of single file
    2.22 +      -post-compile-single:      called after javac compilation of single file
    2.23 +      -pre-compile-test:         called before javac compilation of JUnit tests
    2.24 +      -post-compile-test:        called after javac compilation of JUnit tests
    2.25 +      -pre-compile-test-single:  called before javac compilation of single JUnit test
    2.26 +      -post-compile-test-single: called after javac compilation of single JUunit test
    2.27 +      -pre-jar:                  called before JAR building
    2.28 +      -post-jar:                 called after JAR building
    2.29 +      -post-clean:               called after cleaning build products
    2.30 +
    2.31 +    (Targets beginning with '-' are not intended to be called on their own.)
    2.32 +
    2.33 +    Example of inserting an obfuscator after compilation could look like this:
    2.34 +
    2.35 +        <target name="-post-compile">
    2.36 +            <obfuscate>
    2.37 +                <fileset dir="${build.classes.dir}"/>
    2.38 +            </obfuscate>
    2.39 +        </target>
    2.40 +
    2.41 +    For list of available properties check the imported 
    2.42 +    nbproject/build-impl.xml file. 
    2.43 +
    2.44 +
    2.45 +    Another way to customize the build is by overriding existing main targets.
    2.46 +    The targets of interest are: 
    2.47 +
    2.48 +      -init-macrodef-javac:     defines macro for javac compilation
    2.49 +      -init-macrodef-junit:     defines macro for junit execution
    2.50 +      -init-macrodef-debug:     defines macro for class debugging
    2.51 +      -init-macrodef-java:      defines macro for class execution
    2.52 +      -do-jar-with-manifest:    JAR building (if you are using a manifest)
    2.53 +      -do-jar-without-manifest: JAR building (if you are not using a manifest)
    2.54 +      run:                      execution of project 
    2.55 +      -javadoc-build:           Javadoc generation
    2.56 +      test-report:              JUnit report generation
    2.57 +
    2.58 +    An example of overriding the target for project execution could look like this:
    2.59 +
    2.60 +        <target name="run" depends="currency-impl.jar">
    2.61 +            <exec dir="bin" executable="launcher.exe">
    2.62 +                <arg file="${dist.jar}"/>
    2.63 +            </exec>
    2.64 +        </target>
    2.65 +
    2.66 +    Notice that the overridden target depends on the jar target and not only on 
    2.67 +    the compile target as the regular run target does. Again, for a list of available 
    2.68 +    properties which you can use, check the target you are overriding in the
    2.69 +    nbproject/build-impl.xml file. 
    2.70 +
    2.71 +    -->
    2.72 +</project>
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/task4/solution04/nbproject/build-impl.xml	Sat Oct 18 07:47:34 2008 +0200
     3.3 @@ -0,0 +1,642 @@
     3.4 +<?xml version="1.0" encoding="UTF-8"?>
     3.5 +<!--
     3.6 +*** GENERATED FROM project.xml - DO NOT EDIT  ***
     3.7 +***         EDIT ../build.xml INSTEAD         ***
     3.8 +
     3.9 +For the purpose of easier reading the script
    3.10 +is divided into following sections:
    3.11 +
    3.12 +  - initialization
    3.13 +  - compilation
    3.14 +  - jar
    3.15 +  - execution
    3.16 +  - debugging
    3.17 +  - javadoc
    3.18 +  - junit compilation
    3.19 +  - junit execution
    3.20 +  - junit debugging
    3.21 +  - applet
    3.22 +  - cleanup
    3.23 +
    3.24 +        -->
    3.25 +<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="Currency_Convertor_Solution_04-impl">
    3.26 +    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
    3.27 +    <!-- 
    3.28 +                ======================
    3.29 +                INITIALIZATION SECTION 
    3.30 +                ======================
    3.31 +            -->
    3.32 +    <target name="-pre-init">
    3.33 +        <!-- Empty placeholder for easier customization. -->
    3.34 +        <!-- You can override this target in the ../build.xml file. -->
    3.35 +    </target>
    3.36 +    <target depends="-pre-init" name="-init-private">
    3.37 +        <property file="nbproject/private/config.properties"/>
    3.38 +        <property file="nbproject/private/configs/${config}.properties"/>
    3.39 +        <property file="nbproject/private/private.properties"/>
    3.40 +    </target>
    3.41 +    <target depends="-pre-init,-init-private" name="-init-user">
    3.42 +        <property file="${user.properties.file}"/>
    3.43 +        <!-- The two properties below are usually overridden -->
    3.44 +        <!-- by the active platform. Just a fallback. -->
    3.45 +        <property name="default.javac.source" value="1.4"/>
    3.46 +        <property name="default.javac.target" value="1.4"/>
    3.47 +    </target>
    3.48 +    <target depends="-pre-init,-init-private,-init-user" name="-init-project">
    3.49 +        <property file="nbproject/configs/${config}.properties"/>
    3.50 +        <property file="nbproject/project.properties"/>
    3.51 +    </target>
    3.52 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
    3.53 +        <available file="${manifest.file}" property="manifest.available"/>
    3.54 +        <condition property="manifest.available+main.class">
    3.55 +            <and>
    3.56 +                <isset property="manifest.available"/>
    3.57 +                <isset property="main.class"/>
    3.58 +                <not>
    3.59 +                    <equals arg1="${main.class}" arg2="" trim="true"/>
    3.60 +                </not>
    3.61 +            </and>
    3.62 +        </condition>
    3.63 +        <condition property="manifest.available+main.class+mkdist.available">
    3.64 +            <and>
    3.65 +                <istrue value="${manifest.available+main.class}"/>
    3.66 +                <isset property="libs.CopyLibs.classpath"/>
    3.67 +            </and>
    3.68 +        </condition>
    3.69 +        <condition property="have.tests">
    3.70 +            <or>
    3.71 +                <available file="${test.src.dir}"/>
    3.72 +            </or>
    3.73 +        </condition>
    3.74 +        <condition property="have.sources">
    3.75 +            <or>
    3.76 +                <available file="${src.dir}"/>
    3.77 +            </or>
    3.78 +        </condition>
    3.79 +        <condition property="netbeans.home+have.tests">
    3.80 +            <and>
    3.81 +                <isset property="netbeans.home"/>
    3.82 +                <isset property="have.tests"/>
    3.83 +            </and>
    3.84 +        </condition>
    3.85 +        <condition property="no.javadoc.preview">
    3.86 +            <and>
    3.87 +                <isset property="javadoc.preview"/>
    3.88 +                <isfalse value="${javadoc.preview}"/>
    3.89 +            </and>
    3.90 +        </condition>
    3.91 +        <property name="run.jvmargs" value=""/>
    3.92 +        <property name="javac.compilerargs" value=""/>
    3.93 +        <property name="work.dir" value="${basedir}"/>
    3.94 +        <condition property="no.deps">
    3.95 +            <and>
    3.96 +                <istrue value="${no.dependencies}"/>
    3.97 +            </and>
    3.98 +        </condition>
    3.99 +        <property name="javac.debug" value="true"/>
   3.100 +        <property name="javadoc.preview" value="true"/>
   3.101 +        <property name="application.args" value=""/>
   3.102 +        <property name="source.encoding" value="${file.encoding}"/>
   3.103 +        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
   3.104 +            <and>
   3.105 +                <isset property="javadoc.encoding"/>
   3.106 +                <not>
   3.107 +                    <equals arg1="${javadoc.encoding}" arg2=""/>
   3.108 +                </not>
   3.109 +            </and>
   3.110 +        </condition>
   3.111 +        <property name="javadoc.encoding.used" value="${source.encoding}"/>
   3.112 +        <property name="includes" value="**"/>
   3.113 +        <property name="excludes" value=""/>
   3.114 +        <property name="do.depend" value="false"/>
   3.115 +        <condition property="do.depend.true">
   3.116 +            <istrue value="${do.depend}"/>
   3.117 +        </condition>
   3.118 +        <condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
   3.119 +            <and>
   3.120 +                <isset property="jaxws.endorsed.dir"/>
   3.121 +                <available file="nbproject/jaxws-build.xml"/>
   3.122 +            </and>
   3.123 +        </condition>
   3.124 +    </target>
   3.125 +    <target name="-post-init">
   3.126 +        <!-- Empty placeholder for easier customization. -->
   3.127 +        <!-- You can override this target in the ../build.xml file. -->
   3.128 +    </target>
   3.129 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
   3.130 +        <fail unless="src.dir">Must set src.dir</fail>
   3.131 +        <fail unless="test.src.dir">Must set test.src.dir</fail>
   3.132 +        <fail unless="build.dir">Must set build.dir</fail>
   3.133 +        <fail unless="dist.dir">Must set dist.dir</fail>
   3.134 +        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
   3.135 +        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
   3.136 +        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
   3.137 +        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
   3.138 +        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
   3.139 +        <fail unless="dist.jar">Must set dist.jar</fail>
   3.140 +    </target>
   3.141 +    <target name="-init-macrodef-property">
   3.142 +        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
   3.143 +            <attribute name="name"/>
   3.144 +            <attribute name="value"/>
   3.145 +            <sequential>
   3.146 +                <property name="@{name}" value="${@{value}}"/>
   3.147 +            </sequential>
   3.148 +        </macrodef>
   3.149 +    </target>
   3.150 +    <target name="-init-macrodef-javac">
   3.151 +        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
   3.152 +            <attribute default="${src.dir}" name="srcdir"/>
   3.153 +            <attribute default="${build.classes.dir}" name="destdir"/>
   3.154 +            <attribute default="${javac.classpath}" name="classpath"/>
   3.155 +            <attribute default="${includes}" name="includes"/>
   3.156 +            <attribute default="${excludes}" name="excludes"/>
   3.157 +            <attribute default="${javac.debug}" name="debug"/>
   3.158 +            <attribute default="" name="sourcepath"/>
   3.159 +            <element name="customize" optional="true"/>
   3.160 +            <sequential>
   3.161 +                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
   3.162 +                    <classpath>
   3.163 +                        <path path="@{classpath}"/>
   3.164 +                    </classpath>
   3.165 +                    <compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
   3.166 +                    <customize/>
   3.167 +                </javac>
   3.168 +            </sequential>
   3.169 +        </macrodef>
   3.170 +        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
   3.171 +            <attribute default="${src.dir}" name="srcdir"/>
   3.172 +            <attribute default="${build.classes.dir}" name="destdir"/>
   3.173 +            <attribute default="${javac.classpath}" name="classpath"/>
   3.174 +            <sequential>
   3.175 +                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
   3.176 +                    <classpath>
   3.177 +                        <path path="@{classpath}"/>
   3.178 +                    </classpath>
   3.179 +                </depend>
   3.180 +            </sequential>
   3.181 +        </macrodef>
   3.182 +        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
   3.183 +            <attribute default="${build.classes.dir}" name="destdir"/>
   3.184 +            <sequential>
   3.185 +                <fail unless="javac.includes">Must set javac.includes</fail>
   3.186 +                <pathconvert pathsep="," property="javac.includes.binary">
   3.187 +                    <path>
   3.188 +                        <filelist dir="@{destdir}" files="${javac.includes}"/>
   3.189 +                    </path>
   3.190 +                    <globmapper from="*.java" to="*.class"/>
   3.191 +                </pathconvert>
   3.192 +                <delete>
   3.193 +                    <files includes="${javac.includes.binary}"/>
   3.194 +                </delete>
   3.195 +            </sequential>
   3.196 +        </macrodef>
   3.197 +    </target>
   3.198 +    <target name="-init-macrodef-junit">
   3.199 +        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
   3.200 +            <attribute default="${includes}" name="includes"/>
   3.201 +            <attribute default="${excludes}" name="excludes"/>
   3.202 +            <attribute default="**" name="testincludes"/>
   3.203 +            <sequential>
   3.204 +                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
   3.205 +                    <batchtest todir="${build.test.results.dir}">
   3.206 +                        <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
   3.207 +                            <filename name="@{testincludes}"/>
   3.208 +                        </fileset>
   3.209 +                    </batchtest>
   3.210 +                    <classpath>
   3.211 +                        <path path="${run.test.classpath}"/>
   3.212 +                    </classpath>
   3.213 +                    <syspropertyset>
   3.214 +                        <propertyref prefix="test-sys-prop."/>
   3.215 +                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
   3.216 +                    </syspropertyset>
   3.217 +                    <formatter type="brief" usefile="false"/>
   3.218 +                    <formatter type="xml"/>
   3.219 +                    <jvmarg line="${run.jvmargs}"/>
   3.220 +                </junit>
   3.221 +            </sequential>
   3.222 +        </macrodef>
   3.223 +    </target>
   3.224 +    <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
   3.225 +        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
   3.226 +            <attribute default="${main.class}" name="name"/>
   3.227 +            <attribute default="${debug.classpath}" name="classpath"/>
   3.228 +            <attribute default="" name="stopclassname"/>
   3.229 +            <sequential>
   3.230 +                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
   3.231 +                    <classpath>
   3.232 +                        <path path="@{classpath}"/>
   3.233 +                    </classpath>
   3.234 +                </nbjpdastart>
   3.235 +            </sequential>
   3.236 +        </macrodef>
   3.237 +        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
   3.238 +            <attribute default="${build.classes.dir}" name="dir"/>
   3.239 +            <sequential>
   3.240 +                <nbjpdareload>
   3.241 +                    <fileset dir="@{dir}" includes="${fix.classes}">
   3.242 +                        <include name="${fix.includes}*.class"/>
   3.243 +                    </fileset>
   3.244 +                </nbjpdareload>
   3.245 +            </sequential>
   3.246 +        </macrodef>
   3.247 +    </target>
   3.248 +    <target name="-init-debug-args">
   3.249 +        <property name="version-output" value="java version &quot;${ant.java.version}"/>
   3.250 +        <condition property="have-jdk-older-than-1.4">
   3.251 +            <or>
   3.252 +                <contains string="${version-output}" substring="java version &quot;1.0"/>
   3.253 +                <contains string="${version-output}" substring="java version &quot;1.1"/>
   3.254 +                <contains string="${version-output}" substring="java version &quot;1.2"/>
   3.255 +                <contains string="${version-output}" substring="java version &quot;1.3"/>
   3.256 +            </or>
   3.257 +        </condition>
   3.258 +        <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
   3.259 +            <istrue value="${have-jdk-older-than-1.4}"/>
   3.260 +        </condition>
   3.261 +        <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
   3.262 +            <os family="windows"/>
   3.263 +        </condition>
   3.264 +        <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
   3.265 +            <isset property="debug.transport"/>
   3.266 +        </condition>
   3.267 +    </target>
   3.268 +    <target depends="-init-debug-args" name="-init-macrodef-debug">
   3.269 +        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
   3.270 +            <attribute default="${main.class}" name="classname"/>
   3.271 +            <attribute default="${debug.classpath}" name="classpath"/>
   3.272 +            <element name="customize" optional="true"/>
   3.273 +            <sequential>
   3.274 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
   3.275 +                    <jvmarg line="${debug-args-line}"/>
   3.276 +                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
   3.277 +                    <jvmarg line="${run.jvmargs}"/>
   3.278 +                    <classpath>
   3.279 +                        <path path="@{classpath}"/>
   3.280 +                    </classpath>
   3.281 +                    <syspropertyset>
   3.282 +                        <propertyref prefix="run-sys-prop."/>
   3.283 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
   3.284 +                    </syspropertyset>
   3.285 +                    <customize/>
   3.286 +                </java>
   3.287 +            </sequential>
   3.288 +        </macrodef>
   3.289 +    </target>
   3.290 +    <target name="-init-macrodef-java">
   3.291 +        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
   3.292 +            <attribute default="${main.class}" name="classname"/>
   3.293 +            <element name="customize" optional="true"/>
   3.294 +            <sequential>
   3.295 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
   3.296 +                    <jvmarg line="${run.jvmargs}"/>
   3.297 +                    <classpath>
   3.298 +                        <path path="${run.classpath}"/>
   3.299 +                    </classpath>
   3.300 +                    <syspropertyset>
   3.301 +                        <propertyref prefix="run-sys-prop."/>
   3.302 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
   3.303 +                    </syspropertyset>
   3.304 +                    <customize/>
   3.305 +                </java>
   3.306 +            </sequential>
   3.307 +        </macrodef>
   3.308 +    </target>
   3.309 +    <target name="-init-presetdef-jar">
   3.310 +        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
   3.311 +            <jar compress="${jar.compress}" jarfile="${dist.jar}">
   3.312 +                <j2seproject1:fileset dir="${build.classes.dir}"/>
   3.313 +            </jar>
   3.314 +        </presetdef>
   3.315 +    </target>
   3.316 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
   3.317 +    <!--
   3.318 +                ===================
   3.319 +                COMPILATION SECTION
   3.320 +                ===================
   3.321 +            -->
   3.322 +    <target depends="init" name="deps-jar" unless="no.deps"/>
   3.323 +    <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
   3.324 +    <target depends="init" name="-check-automatic-build">
   3.325 +        <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
   3.326 +    </target>
   3.327 +    <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
   3.328 +        <antcall target="clean"/>
   3.329 +    </target>
   3.330 +    <target depends="init,deps-jar" name="-pre-pre-compile">
   3.331 +        <mkdir dir="${build.classes.dir}"/>
   3.332 +    </target>
   3.333 +    <target name="-pre-compile">
   3.334 +        <!-- Empty placeholder for easier customization. -->
   3.335 +        <!-- You can override this target in the ../build.xml file. -->
   3.336 +    </target>
   3.337 +    <target if="do.depend.true" name="-compile-depend">
   3.338 +        <j2seproject3:depend/>
   3.339 +    </target>
   3.340 +    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
   3.341 +        <j2seproject3:javac/>
   3.342 +        <copy todir="${build.classes.dir}">
   3.343 +            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
   3.344 +        </copy>
   3.345 +    </target>
   3.346 +    <target name="-post-compile">
   3.347 +        <!-- Empty placeholder for easier customization. -->
   3.348 +        <!-- You can override this target in the ../build.xml file. -->
   3.349 +    </target>
   3.350 +    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
   3.351 +    <target name="-pre-compile-single">
   3.352 +        <!-- Empty placeholder for easier customization. -->
   3.353 +        <!-- You can override this target in the ../build.xml file. -->
   3.354 +    </target>
   3.355 +    <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
   3.356 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
   3.357 +        <j2seproject3:force-recompile/>
   3.358 +        <j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
   3.359 +    </target>
   3.360 +    <target name="-post-compile-single">
   3.361 +        <!-- Empty placeholder for easier customization. -->
   3.362 +        <!-- You can override this target in the ../build.xml file. -->
   3.363 +    </target>
   3.364 +    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
   3.365 +    <!--
   3.366 +                ====================
   3.367 +                JAR BUILDING SECTION
   3.368 +                ====================
   3.369 +            -->
   3.370 +    <target depends="init" name="-pre-pre-jar">
   3.371 +        <dirname file="${dist.jar}" property="dist.jar.dir"/>
   3.372 +        <mkdir dir="${dist.jar.dir}"/>
   3.373 +    </target>
   3.374 +    <target name="-pre-jar">
   3.375 +        <!-- Empty placeholder for easier customization. -->
   3.376 +        <!-- You can override this target in the ../build.xml file. -->
   3.377 +    </target>
   3.378 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
   3.379 +        <j2seproject1:jar/>
   3.380 +    </target>
   3.381 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
   3.382 +        <j2seproject1:jar manifest="${manifest.file}"/>
   3.383 +    </target>
   3.384 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
   3.385 +        <j2seproject1:jar manifest="${manifest.file}">
   3.386 +            <j2seproject1:manifest>
   3.387 +                <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
   3.388 +            </j2seproject1:manifest>
   3.389 +        </j2seproject1:jar>
   3.390 +        <echo>To run this application from the command line without Ant, try:</echo>
   3.391 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
   3.392 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
   3.393 +        <pathconvert property="run.classpath.with.dist.jar">
   3.394 +            <path path="${run.classpath}"/>
   3.395 +            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
   3.396 +        </pathconvert>
   3.397 +        <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
   3.398 +    </target>
   3.399 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
   3.400 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
   3.401 +        <pathconvert property="run.classpath.without.build.classes.dir">
   3.402 +            <path path="${run.classpath}"/>
   3.403 +            <map from="${build.classes.dir.resolved}" to=""/>
   3.404 +        </pathconvert>
   3.405 +        <pathconvert pathsep=" " property="jar.classpath">
   3.406 +            <path path="${run.classpath.without.build.classes.dir}"/>
   3.407 +            <chainedmapper>
   3.408 +                <flattenmapper/>
   3.409 +                <globmapper from="*" to="lib/*"/>
   3.410 +            </chainedmapper>
   3.411 +        </pathconvert>
   3.412 +        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
   3.413 +        <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
   3.414 +            <fileset dir="${build.classes.dir}"/>
   3.415 +            <manifest>
   3.416 +                <attribute name="Main-Class" value="${main.class}"/>
   3.417 +                <attribute name="Class-Path" value="${jar.classpath}"/>
   3.418 +            </manifest>
   3.419 +        </copylibs>
   3.420 +        <echo>To run this application from the command line without Ant, try:</echo>
   3.421 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
   3.422 +        <echo>java -jar "${dist.jar.resolved}"</echo>
   3.423 +    </target>
   3.424 +    <target name="-post-jar">
   3.425 +        <!-- Empty placeholder for easier customization. -->
   3.426 +        <!-- You can override this target in the ../build.xml file. -->
   3.427 +    </target>
   3.428 +    <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
   3.429 +    <!--
   3.430 +                =================
   3.431 +                EXECUTION SECTION
   3.432 +                =================
   3.433 +            -->
   3.434 +    <target depends="init,compile" description="Run a main class." name="run">
   3.435 +        <j2seproject1:java>
   3.436 +            <customize>
   3.437 +                <arg line="${application.args}"/>
   3.438 +            </customize>
   3.439 +        </j2seproject1:java>
   3.440 +    </target>
   3.441 +    <target name="-do-not-recompile">
   3.442 +        <property name="javac.includes.binary" value=""/>
   3.443 +    </target>
   3.444 +    <target depends="init,-do-not-recompile,compile-single" name="run-single">
   3.445 +        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
   3.446 +        <j2seproject1:java classname="${run.class}"/>
   3.447 +    </target>
   3.448 +    <!--
   3.449 +                =================
   3.450 +                DEBUGGING SECTION
   3.451 +                =================
   3.452 +            -->
   3.453 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger">
   3.454 +        <j2seproject1:nbjpdastart name="${debug.class}"/>
   3.455 +    </target>
   3.456 +    <target depends="init,compile" name="-debug-start-debuggee">
   3.457 +        <j2seproject3:debug>
   3.458 +            <customize>
   3.459 +                <arg line="${application.args}"/>
   3.460 +            </customize>
   3.461 +        </j2seproject3:debug>
   3.462 +    </target>
   3.463 +    <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
   3.464 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
   3.465 +        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
   3.466 +    </target>
   3.467 +    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
   3.468 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
   3.469 +        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
   3.470 +        <j2seproject3:debug classname="${debug.class}"/>
   3.471 +    </target>
   3.472 +    <target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
   3.473 +    <target depends="init" name="-pre-debug-fix">
   3.474 +        <fail unless="fix.includes">Must set fix.includes</fail>
   3.475 +        <property name="javac.includes" value="${fix.includes}.java"/>
   3.476 +    </target>
   3.477 +    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
   3.478 +        <j2seproject1:nbjpdareload/>
   3.479 +    </target>
   3.480 +    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
   3.481 +    <!--
   3.482 +                ===============
   3.483 +                JAVADOC SECTION
   3.484 +                ===============
   3.485 +            -->
   3.486 +    <target depends="init" name="-javadoc-build">
   3.487 +        <mkdir dir="${dist.javadoc.dir}"/>
   3.488 +        <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
   3.489 +            <classpath>
   3.490 +                <path path="${javac.classpath}"/>
   3.491 +            </classpath>
   3.492 +            <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
   3.493 +                <filename name="**/*.java"/>
   3.494 +            </fileset>
   3.495 +        </javadoc>
   3.496 +    </target>
   3.497 +    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
   3.498 +        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
   3.499 +    </target>
   3.500 +    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
   3.501 +    <!--
   3.502 +                =========================
   3.503 +                JUNIT COMPILATION SECTION
   3.504 +                =========================
   3.505 +            -->
   3.506 +    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
   3.507 +        <mkdir dir="${build.test.classes.dir}"/>
   3.508 +    </target>
   3.509 +    <target name="-pre-compile-test">
   3.510 +        <!-- Empty placeholder for easier customization. -->
   3.511 +        <!-- You can override this target in the ../build.xml file. -->
   3.512 +    </target>
   3.513 +    <target if="do.depend.true" name="-compile-test-depend">
   3.514 +        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
   3.515 +    </target>
   3.516 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
   3.517 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
   3.518 +        <copy todir="${build.test.classes.dir}">
   3.519 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
   3.520 +        </copy>
   3.521 +    </target>
   3.522 +    <target name="-post-compile-test">
   3.523 +        <!-- Empty placeholder for easier customization. -->
   3.524 +        <!-- You can override this target in the ../build.xml file. -->
   3.525 +    </target>
   3.526 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
   3.527 +    <target name="-pre-compile-test-single">
   3.528 +        <!-- Empty placeholder for easier customization. -->
   3.529 +        <!-- You can override this target in the ../build.xml file. -->
   3.530 +    </target>
   3.531 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
   3.532 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
   3.533 +        <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
   3.534 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
   3.535 +        <copy todir="${build.test.classes.dir}">
   3.536 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
   3.537 +        </copy>
   3.538 +    </target>
   3.539 +    <target name="-post-compile-test-single">
   3.540 +        <!-- Empty placeholder for easier customization. -->
   3.541 +        <!-- You can override this target in the ../build.xml file. -->
   3.542 +    </target>
   3.543 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
   3.544 +    <!--
   3.545 +                =======================
   3.546 +                JUNIT EXECUTION SECTION
   3.547 +                =======================
   3.548 +            -->
   3.549 +    <target depends="init" if="have.tests" name="-pre-test-run">
   3.550 +        <mkdir dir="${build.test.results.dir}"/>
   3.551 +    </target>
   3.552 +    <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
   3.553 +        <j2seproject3:junit testincludes="**/*Test.java"/>
   3.554 +    </target>
   3.555 +    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
   3.556 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
   3.557 +    </target>
   3.558 +    <target depends="init" if="have.tests" name="test-report"/>
   3.559 +    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
   3.560 +    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
   3.561 +    <target depends="init" if="have.tests" name="-pre-test-run-single">
   3.562 +        <mkdir dir="${build.test.results.dir}"/>
   3.563 +    </target>
   3.564 +    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
   3.565 +        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
   3.566 +        <j2seproject3:junit excludes="" includes="${test.includes}"/>
   3.567 +    </target>
   3.568 +    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
   3.569 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
   3.570 +    </target>
   3.571 +    <target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
   3.572 +    <!--
   3.573 +                =======================
   3.574 +                JUNIT DEBUGGING SECTION
   3.575 +                =======================
   3.576 +            -->
   3.577 +    <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
   3.578 +        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
   3.579 +        <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
   3.580 +        <delete file="${test.report.file}"/>
   3.581 +        <mkdir dir="${build.test.results.dir}"/>
   3.582 +        <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
   3.583 +            <customize>
   3.584 +                <syspropertyset>
   3.585 +                    <propertyref prefix="test-sys-prop."/>
   3.586 +                    <mapper from="test-sys-prop.*" to="*" type="glob"/>
   3.587 +                </syspropertyset>
   3.588 +                <arg value="${test.class}"/>
   3.589 +                <arg value="showoutput=true"/>
   3.590 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
   3.591 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
   3.592 +            </customize>
   3.593 +        </j2seproject3:debug>
   3.594 +    </target>
   3.595 +    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
   3.596 +        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
   3.597 +    </target>
   3.598 +    <target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
   3.599 +    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
   3.600 +        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
   3.601 +    </target>
   3.602 +    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
   3.603 +    <!--
   3.604 +                =========================
   3.605 +                APPLET EXECUTION SECTION
   3.606 +                =========================
   3.607 +            -->
   3.608 +    <target depends="init,compile-single" name="run-applet">
   3.609 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
   3.610 +        <j2seproject1:java classname="sun.applet.AppletViewer">
   3.611 +            <customize>
   3.612 +                <arg value="${applet.url}"/>
   3.613 +            </customize>
   3.614 +        </j2seproject1:java>
   3.615 +    </target>
   3.616 +    <!--
   3.617 +                =========================
   3.618 +                APPLET DEBUGGING  SECTION
   3.619 +                =========================
   3.620 +            -->
   3.621 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
   3.622 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
   3.623 +        <j2seproject3:debug classname="sun.applet.AppletViewer">
   3.624 +            <customize>
   3.625 +                <arg value="${applet.url}"/>
   3.626 +            </customize>
   3.627 +        </j2seproject3:debug>
   3.628 +    </target>
   3.629 +    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
   3.630 +    <!--
   3.631 +                ===============
   3.632 +                CLEANUP SECTION
   3.633 +                ===============
   3.634 +            -->
   3.635 +    <target depends="init" name="deps-clean" unless="no.deps"/>
   3.636 +    <target depends="init" name="-do-clean">
   3.637 +        <delete dir="${build.dir}"/>
   3.638 +        <delete dir="${dist.dir}"/>
   3.639 +    </target>
   3.640 +    <target name="-post-clean">
   3.641 +        <!-- Empty placeholder for easier customization. -->
   3.642 +        <!-- You can override this target in the ../build.xml file. -->
   3.643 +    </target>
   3.644 +    <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
   3.645 +</project>
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/task4/solution04/nbproject/genfiles.properties	Sat Oct 18 07:47:34 2008 +0200
     4.3 @@ -0,0 +1,8 @@
     4.4 +build.xml.data.CRC32=2ab820eb
     4.5 +build.xml.script.CRC32=58a52595
     4.6 +build.xml.stylesheet.CRC32=be360661
     4.7 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
     4.8 +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
     4.9 +nbproject/build-impl.xml.data.CRC32=2fbfa6ce
    4.10 +nbproject/build-impl.xml.script.CRC32=c521eea7
    4.11 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/task4/solution04/nbproject/project.properties	Sat Oct 18 07:47:34 2008 +0200
     5.3 @@ -0,0 +1,68 @@
     5.4 +application.title=currency
     5.5 +application.vendor=apidesign.org
     5.6 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8
     5.7 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
     5.8 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=default
     5.9 +build.classes.dir=${build.dir}/classes
    5.10 +build.classes.excludes=**/*.java,**/*.form
    5.11 +# This directory is removed when the project is cleaned:
    5.12 +build.dir=build
    5.13 +build.generated.dir=${build.dir}/generated
    5.14 +# Only compile against the classpath explicitly listed here:
    5.15 +build.sysclasspath=ignore
    5.16 +build.test.classes.dir=${build.dir}/test/classes
    5.17 +build.test.results.dir=${build.dir}/test/results
    5.18 +debug.classpath=\
    5.19 +    ${run.classpath}
    5.20 +debug.test.classpath=\
    5.21 +    ${run.test.classpath}
    5.22 +# This directory is removed when the project is cleaned:
    5.23 +dist.dir=dist
    5.24 +dist.jar=${dist.dir}/currency.jar
    5.25 +dist.javadoc.dir=${dist.dir}/javadoc
    5.26 +excludes=
    5.27 +file.reference.junit-4.4.jar=../../libs/junit-4.4.jar
    5.28 +file.reference.src-apifest08=..
    5.29 +includes=**
    5.30 +jar.compress=false
    5.31 +javac.classpath=
    5.32 +# Space-separated list of extra javac options
    5.33 +javac.compilerargs=
    5.34 +javac.deprecation=false
    5.35 +javac.source=1.5
    5.36 +javac.target=1.5
    5.37 +javac.test.classpath=\
    5.38 +    ${javac.classpath}:\
    5.39 +    ${build.classes.dir}:\
    5.40 +    ${file.reference.junit-4.4.jar}
    5.41 +javadoc.additionalparam=
    5.42 +javadoc.author=false
    5.43 +javadoc.encoding=
    5.44 +javadoc.noindex=false
    5.45 +javadoc.nonavbar=false
    5.46 +javadoc.notree=false
    5.47 +javadoc.private=false
    5.48 +javadoc.splitindex=true
    5.49 +javadoc.use=true
    5.50 +javadoc.version=false
    5.51 +javadoc.windowtitle=
    5.52 +jnlp.codebase.type=local
    5.53 +jnlp.codebase.url=file:/home/jarda/src/apifest08/currency/dist
    5.54 +jnlp.descriptor=application
    5.55 +jnlp.enabled=false
    5.56 +jnlp.offline-allowed=false
    5.57 +jnlp.signed=false
    5.58 +meta.inf.dir=${src.dir}/META-INF
    5.59 +platform.active=default_platform
    5.60 +run.classpath=\
    5.61 +    ${javac.classpath}:\
    5.62 +    ${build.classes.dir}
    5.63 +# Space-separated list of JVM arguments used when running the project
    5.64 +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
    5.65 +# or test-sys-prop.name=value to set system properties for unit tests):
    5.66 +run.jvmargs=
    5.67 +run.test.classpath=\
    5.68 +    ${javac.test.classpath}:\
    5.69 +    ${build.test.classes.dir}
    5.70 +src.dir=src
    5.71 +test.src.dir=test
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/task4/solution04/nbproject/project.xml	Sat Oct 18 07:47:34 2008 +0200
     6.3 @@ -0,0 +1,16 @@
     6.4 +<?xml version="1.0" encoding="UTF-8"?>
     6.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
     6.6 +    <type>org.netbeans.modules.java.j2seproject</type>
     6.7 +    <configuration>
     6.8 +        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
     6.9 +            <name>Currency Convertor Solution 04</name>
    6.10 +            <minimum-ant-version>1.6.5</minimum-ant-version>
    6.11 +            <source-roots>
    6.12 +                <root id="src.dir"/>
    6.13 +            </source-roots>
    6.14 +            <test-roots>
    6.15 +                <root id="test.src.dir"/>
    6.16 +            </test-roots>
    6.17 +        </data>
    6.18 +    </configuration>
    6.19 +</project>
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/CompositeConvertorImpl.java	Sat Oct 18 07:47:34 2008 +0200
     7.3 @@ -0,0 +1,352 @@
     7.4 +package org.apidesign.apifest08.currency;
     7.5 +
     7.6 +
     7.7 +import java.math.BigDecimal;
     7.8 +import java.math.RoundingMode;
     7.9 +import java.util.Collections;
    7.10 +import java.util.Currency;
    7.11 +import java.util.HashMap;
    7.12 +import java.util.HashSet;
    7.13 +import java.util.Map;
    7.14 +import java.util.Set;
    7.15 +
    7.16 +
    7.17 +/**
    7.18 + * A composite convertor allows conversions between many currencies by forwarding conversion requests to stored convertors.
    7.19 + * A composite convertor will build all possible conversions that are allowed by the underlying set of convertors.
    7.20 + *
    7.21 + * @author D'Arcy Smith
    7.22 + * @verson 1.2
    7.23 + */
    7.24 +final class CompositeConvertorImpl
    7.25 +    implements Convertor
    7.26 +{
    7.27 +    /**
    7.28 +     * The convertors that are supported.
    7.29 +     */
    7.30 +    private final Convertor[] convertors;
    7.31 +    
    7.32 +    /**
    7.33 +     * Keeps track of what convertors to use to convert between currencies.
    7.34 +     */
    7.35 +    private final Map<Currency, Map<Currency, Convertor>> possibleConversions;
    7.36 +    
    7.37 +    {
    7.38 +        possibleConversions = new HashMap<Currency, Map<Currency, Convertor>>();
    7.39 +    }
    7.40 +
    7.41 +    /**
    7.42 +     * Construct a ComositeConvertorImpl with the specified convertors.
    7.43 +     * This will result in all possible conversions between the supplied currencies being made.
    7.44 +     * 
    7.45 +     * @param cs the convertors to use.
    7.46 +     * @throws IllegalArgumentException if any of the items in cs are null.
    7.47 +     */
    7.48 +    CompositeConvertorImpl(Convertor ... cs)
    7.49 +    {
    7.50 +        int newConvertors;
    7.51 +        
    7.52 +        convertors = cs;
    7.53 +
    7.54 +        // track all of the known conversion
    7.55 +        for(final Convertor convertor : convertors)
    7.56 +        {
    7.57 +            final Set<Currency>      currencies;
    7.58 +            Map<Currency, Convertor> possible;
    7.59 +
    7.60 +            if(convertor == null)
    7.61 +            {
    7.62 +                throw new IllegalArgumentException("cs cannot contain null");
    7.63 +            }
    7.64 +            
    7.65 +            currencies = convertor.getCurrencies();
    7.66 +
    7.67 +            for(final Currency currency : currencies)
    7.68 +            {
    7.69 +                possible = possibleConversions.get(currency);
    7.70 +
    7.71 +                if(possible == null)
    7.72 +                {
    7.73 +                    possible = new HashMap<Currency, Convertor>();
    7.74 +                    possibleConversions.put(currency, possible);
    7.75 +                }
    7.76 +
    7.77 +                for(final Currency c : currencies)
    7.78 +                {
    7.79 +                    possible.put(c, convertor);
    7.80 +                }
    7.81 +            }
    7.82 +        }
    7.83 +
    7.84 +        // make up conversions that can be derived... eg:
    7.85 +        //   we have:
    7.86 +        //      USD <-> CAD
    7.87 +        //      CAD <-> CZK
    7.88 +        //      SSK <-> GBP
    7.89 +        //   we can derive:
    7.90 +        //      USD <-> CZK
    7.91 +        //   we cannot derive:
    7.92 +        //      USD <-> GBP
    7.93 +        //      CAD <-> GBP
    7.94 +        //      CZK <-> GBP        
    7.95 +        // 
    7.96 +        // NOTE: no attempt is made to deal with dates for DatedConvertors... nothing we can do about it.
    7.97 +        do
    7.98 +        {
    7.99 +            newConvertors = 0;
   7.100 +
   7.101 +            // todo... need to loop this until all the ones that can be handled are done.
   7.102 +            for(final Currency from : getCurrencies())
   7.103 +            {
   7.104 +                for(final Currency to : getCurrencies())
   7.105 +                {
   7.106 +                    if(!(canConvert(from, to)))
   7.107 +                    {
   7.108 +                        final Set<Currency> fromCurrencies;
   7.109 +                        final Set<Currency> toCurrencies;
   7.110 +                        final Set<Currency> common;
   7.111 +
   7.112 +                        fromCurrencies = possibleConversions.get(from).keySet();
   7.113 +                        toCurrencies   = possibleConversions.get(to).keySet();
   7.114 +                        common         = new HashSet<Currency>();
   7.115 +
   7.116 +                        for(final Currency currency : fromCurrencies)
   7.117 +                        {
   7.118 +                            if(toCurrencies.contains(currency))
   7.119 +                            {
   7.120 +                                common.add(currency);
   7.121 +                            }
   7.122 +                        }
   7.123 +
   7.124 +                        for(final Currency currency : common)
   7.125 +                        {
   7.126 +                            final Convertor convertor;
   7.127 +
   7.128 +                            convertor = createConvertor(from, to, currency);
   7.129 +                            possibleConversions.get(from).put(to, convertor);
   7.130 +                            possibleConversions.get(to).put(from, convertor);
   7.131 +                            newConvertors++;
   7.132 +                        }
   7.133 +                    }
   7.134 +                }
   7.135 +            }
   7.136 +        }
   7.137 +        while(newConvertors > 0);
   7.138 +    }
   7.139 +
   7.140 +    /**
   7.141 +     * Check to see if converting between the two currencies is possible.
   7.142 +     *
   7.143 +     * @param from the currency to convert from.
   7.144 +     * @param to the currency to convert to.
   7.145 +     * @return true if the conversion is possible.
   7.146 +     * @throws IllegalArgumentException if either from or to are null.
   7.147 +     */
   7.148 +    public boolean canConvert(final Currency from, final Currency to)
   7.149 +    {
   7.150 +        final Map<Currency, Convertor> possible;
   7.151 +
   7.152 +        if(from == null)
   7.153 +        {
   7.154 +            throw new IllegalArgumentException("from cannot be null");
   7.155 +        }
   7.156 +        
   7.157 +        if(to == null)
   7.158 +        {
   7.159 +            throw new IllegalArgumentException("to cannot be null");
   7.160 +        }
   7.161 +                
   7.162 +        possible = possibleConversions.get(from);
   7.163 +
   7.164 +        if(possible.containsKey(to))
   7.165 +        {
   7.166 +            return (true);
   7.167 +        }
   7.168 +
   7.169 +        return (false);
   7.170 +    }
   7.171 +
   7.172 +    /**
   7.173 +     * Get the currencies that the convertor supports.  Just because a currency is
   7.174 +     * supported does not mean that canConvert will return true.
   7.175 +     * 
   7.176 +     * @return the supported currencies.
   7.177 +     */
   7.178 +    public Set<Currency> getCurrencies()
   7.179 +    {
   7.180 +        final Set<Currency> currencies;
   7.181 +
   7.182 +        currencies = possibleConversions.keySet();
   7.183 +
   7.184 +        return (Collections.unmodifiableSet(currencies));
   7.185 +    }
   7.186 +    
   7.187 +    /**
   7.188 +     * Get the conversion rate between two currencies.
   7.189 +     * 
   7.190 +     * @param from the currency to convert from.
   7.191 +     * @param to the currency to convert to.
   7.192 +     * @return the conversion rate between the two currencies.
   7.193 +     * @throws IllegalArgumentException if either from or to is null.
   7.194 +     * @throws InvalidConversionException if canConvert would return false.
   7.195 +     */
   7.196 +    public BigDecimal getConversionRate(final Currency from, final Currency to)
   7.197 +        throws InvalidConversionException
   7.198 +    {
   7.199 +        final Map<Currency, Convertor> possible;
   7.200 +        Convertor                      convertor;
   7.201 +
   7.202 +        if(from == null)
   7.203 +        {
   7.204 +            throw new IllegalArgumentException("from cannot be null");
   7.205 +        }
   7.206 +        
   7.207 +        if(to == null)
   7.208 +        {
   7.209 +            throw new IllegalArgumentException("to cannot be null");
   7.210 +        }
   7.211 +        
   7.212 +        if(!(canConvert(from, to)))
   7.213 +        {
   7.214 +            throw new InvalidConversionException("cannot convert", to);
   7.215 +        }
   7.216 +
   7.217 +        possible  = possibleConversions.get(from);
   7.218 +        convertor = possible.get(to);
   7.219 +
   7.220 +        if(convertor == null)
   7.221 +        {
   7.222 +            throw new Error();
   7.223 +        }
   7.224 +
   7.225 +        return (convertor.getConversionRate(from, to));
   7.226 +    }
   7.227 +
   7.228 +    private Convertor getConvertor(final Currency from, final Currency to)
   7.229 +        throws InvalidConversionException
   7.230 +    {
   7.231 +        final Map<Currency, Convertor> possible;
   7.232 +        Convertor                      convertor;
   7.233 +
   7.234 +        if(from == null)
   7.235 +        {
   7.236 +            throw new IllegalArgumentException("from cannot be null");
   7.237 +        }
   7.238 +        
   7.239 +        if(to == null)
   7.240 +        {
   7.241 +            throw new IllegalArgumentException("to cannot be null");
   7.242 +        }
   7.243 +        
   7.244 +        if(!(canConvert(from, to)))
   7.245 +        {
   7.246 +            throw new InvalidConversionException("cannot convert", to);
   7.247 +        }
   7.248 +
   7.249 +        possible  = possibleConversions.get(from);
   7.250 +        convertor = possible.get(to);
   7.251 +
   7.252 +        if(convertor == null)
   7.253 +        {
   7.254 +            throw new Error();
   7.255 +        }
   7.256 +
   7.257 +        return (convertor);
   7.258 +    }
   7.259 +
   7.260 +    /**
   7.261 +     * Convert an amount from one currency to another.
   7.262 +     * 
   7.263 +     * @param from the currency to convert from.
   7.264 +     * @param to the currency to convert to.
   7.265 +     * @param amount the amount to convert.
   7.266 +     * @return the converted amount.
   7.267 +     * @throws IllegalArgumentException if any of the arguments are null.
   7.268 +     * @throws InvalidConversionException if either from or to are not valid for the convertor.
   7.269 +     */
   7.270 +    public BigDecimal convert(final Currency   from,
   7.271 +                              final Currency   to,
   7.272 +                              final BigDecimal amount)
   7.273 +        throws InvalidConversionException
   7.274 +    {
   7.275 +        final BigDecimal result;
   7.276 +        final Convertor  convertor;
   7.277 +        
   7.278 +        if(amount == null)
   7.279 +        {
   7.280 +            throw new IllegalArgumentException("amount cannot be null");
   7.281 +        }
   7.282 +        
   7.283 +        if(from == null)
   7.284 +        {
   7.285 +            throw new IllegalArgumentException("from cannot be null");
   7.286 +        }
   7.287 +        
   7.288 +        if(to == null)
   7.289 +        {
   7.290 +            throw new IllegalArgumentException("to cannot be null");
   7.291 +        }
   7.292 +
   7.293 +        // fixed a bug from Task2 that showed up in Task3... before we did the conversion here,
   7.294 +        // but that meant that the underlying covnerter convert method never got called... which
   7.295 +        // meant that in Task3 the exchange rate never changed.
   7.296 +        convertor = getConvertor(from, to);
   7.297 +        result    = convertor.convert(from, to, amount);
   7.298 +
   7.299 +        return (result.setScale(2, RoundingMode.HALF_DOWN));
   7.300 +    }
   7.301 +
   7.302 +    /**
   7.303 +     * Create a convertor between two currencies using another currency that is able to convert between both.
   7.304 +     * 
   7.305 +     * @param from the currency to convert from.
   7.306 +     * @param to the currency to convert to.
   7.307 +     * @param intermediary the currency to use as a go-between.
   7.308 +     * @return a Convertor that is able to convert between from an to.
   7.309 +     * @throws IllegalArgumentException if any of the arguments are null.
   7.310 +     */
   7.311 +    private Convertor createConvertor(final Currency from,
   7.312 +                                      final Currency to,
   7.313 +                                      final Currency intermediary) 
   7.314 +    {
   7.315 +        final Convertor fromIntermediary;
   7.316 +        final Convertor toIntermediary;
   7.317 +
   7.318 +        if(from == null)
   7.319 +        {
   7.320 +            throw new IllegalArgumentException("from cannot be null");
   7.321 +        }
   7.322 +        
   7.323 +        if(to == null)
   7.324 +        {
   7.325 +            throw new IllegalArgumentException("to cannot be null");
   7.326 +        }
   7.327 +        
   7.328 +        if(intermediary == null)
   7.329 +        {
   7.330 +            throw new IllegalArgumentException("intermediary cannot be null");
   7.331 +        }
   7.332 +        
   7.333 +        fromIntermediary = possibleConversions.get(from).get(intermediary);
   7.334 +        toIntermediary   = possibleConversions.get(to).get(intermediary);
   7.335 +
   7.336 +        try
   7.337 +        {
   7.338 +            final BigDecimal fromRate;
   7.339 +            final BigDecimal toRate;
   7.340 +            final BigDecimal rate;
   7.341 +            final Convertor  convertor;
   7.342 +            
   7.343 +            fromRate  = fromIntermediary.getConversionRate(from, intermediary);
   7.344 +            toRate    = toIntermediary.getConversionRate(intermediary, to);
   7.345 +            rate      = fromRate.multiply(toRate);            
   7.346 +            convertor = ConvertorFactory.getConvertor(from, BigDecimal.ONE, to, rate);
   7.347 +            
   7.348 +            return (convertor);
   7.349 +        }
   7.350 +        catch (InvalidConversionException ex)
   7.351 +        {
   7.352 +            throw new Error();
   7.353 +        }
   7.354 +    }
   7.355 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/ConverterImpl.java	Sat Oct 18 07:47:34 2008 +0200
     8.3 @@ -0,0 +1,148 @@
     8.4 +package org.apidesign.apifest08.currency;
     8.5 +
     8.6 +
     8.7 +import java.math.BigDecimal;
     8.8 +import java.util.Currency;
     8.9 +import java.util.Set;
    8.10 +
    8.11 +
    8.12 +/**
    8.13 + * Convert between two currencies.
    8.14 + *
    8.15 + * @author D'Arcy Smith
    8.16 + * @version 1.1
    8.17 + */
    8.18 +class ConvertorImpl
    8.19 +    implements ExchangeRateConvertor
    8.20 +{
    8.21 +    /**
    8.22 +     */
    8.23 +    private final ExchangeRate rate;
    8.24 +
    8.25 +    /**
    8.26 +     * Constructs a convertor with the specified currencies.
    8.27 +     * 
    8.28 +     * @param a the currency to convert from.
    8.29 +     * @param aRate the exchage rage between from and to.
    8.30 +     * @param b the currency to convert to.
    8.31 +     * @param bRate the exchage rage between to and from.
    8.32 +     * @throws IllegalArgumentException if either any of the arguments are null or if either rate <= 0.
    8.33 +     */
    8.34 +    public ConvertorImpl(final ExchangeRate r)
    8.35 +    {
    8.36 +        if(r == null)
    8.37 +        {
    8.38 +            throw new IllegalArgumentException("r cannot be null");
    8.39 +        }
    8.40 +
    8.41 +        rate = r;
    8.42 +    }
    8.43 +    
    8.44 +    /**
    8.45 +     * Convert an amount from one currency to another.
    8.46 +     * 
    8.47 +     * @param from the currency to convert from.
    8.48 +     * @param to the currency to convert to.
    8.49 +     * @param amount the amount to convert.
    8.50 +     * @return the converted amount.
    8.51 +     * @throws IllegalArgumentException if any of the arguments are null.
    8.52 +     * @throws InvalidConversionException if either from or to are not equal to the currencies passed to the constructor.
    8.53 +     */
    8.54 +    public BigDecimal convert(final Currency   from,
    8.55 +                              final Currency   to,
    8.56 +                              final BigDecimal amount)
    8.57 +        throws InvalidConversionException
    8.58 +    {
    8.59 +        return (rate.convert(from, to, amount));
    8.60 +    }
    8.61 +
    8.62 +    /**
    8.63 +     * Check to see if converting between the two currencies is possible.
    8.64 +     * 
    8.65 +     * @param from the currency to convert from.
    8.66 +     * @param to the currency to convert to.
    8.67 +     * @return true if the conversion is possible.
    8.68 +     * @throws IllegalArgumentException if either from or to are null.
    8.69 +     */
    8.70 +    public boolean canConvert(final Currency from, final Currency to)
    8.71 +    {
    8.72 +        return (rate.canConvert(from, to));
    8.73 +    }
    8.74 +
    8.75 +    /**
    8.76 +     * Get the currencies that the convertor supports.
    8.77 +     * 
    8.78 +     * @return the supported currencies.
    8.79 +     */
    8.80 +    public Set<Currency> getCurrencies()
    8.81 +    {
    8.82 +        return (rate.getCurrencies());
    8.83 +    }
    8.84 +
    8.85 +    /**
    8.86 +     * Get the conversion rate between two currencies.
    8.87 +     * 
    8.88 +     * @param from the currency to convert from.
    8.89 +     * @param to the currency to convert to.
    8.90 +     * @return the conversion rate between the two currencies.
    8.91 +     * @throws InvalidConversionException if canConvert would return false.
    8.92 +     * @throws IllegalArgumentException if either from or to are null.
    8.93 +     */
    8.94 +    public BigDecimal getConversionRate(final Currency from, 
    8.95 +                                        final Currency to)
    8.96 +        throws InvalidConversionException
    8.97 +    {                
    8.98 +        return (rate.getConversionRate(from, to));
    8.99 +    }
   8.100 +
   8.101 +    /**
   8.102 +     * Check to see if two ConvertorImpls are equal.
   8.103 +     *
   8.104 +     * @param obj the object to check
   8.105 +     * @return if the ConvertorImpls are not the same (cuyrrencies and rates).
   8.106 +     */
   8.107 +    @Override
   8.108 +    public boolean equals(Object obj)
   8.109 +    {
   8.110 +        if (obj == null)
   8.111 +        {
   8.112 +            return false;
   8.113 +        }
   8.114 +        
   8.115 +        if (getClass() != obj.getClass())
   8.116 +        {
   8.117 +            return false;
   8.118 +        }
   8.119 +
   8.120 +        final ConvertorImpl other = (ConvertorImpl) obj;
   8.121 +
   8.122 +        return (rate.equals(other.rate));
   8.123 +    }
   8.124 +
   8.125 +    /**
   8.126 +     * Get the hashCode of the Convertor.
   8.127 +     *
   8.128 +     * @return the hashCode of the convertor.
   8.129 +     */
   8.130 +    @Override
   8.131 +    public int hashCode()
   8.132 +    {
   8.133 +        return (rate.hashCode());
   8.134 +    }
   8.135 +
   8.136 +    /**
   8.137 +     * Get the currencyCode of both currencies.
   8.138 +     *
   8.139 +     * @return the currency codes of both currencies.
   8.140 +     */
   8.141 +    @Override
   8.142 +    public String toString()
   8.143 +    {
   8.144 +        return (rate.getCurrencyA().getCurrencyCode() + " to " + rate.getCurrencyB().getCurrencyCode());
   8.145 +    }
   8.146 +
   8.147 +    public ExchangeRate getExchangeRate()
   8.148 +    {
   8.149 +        return (rate);
   8.150 +    }
   8.151 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/Convertor.java	Sat Oct 18 07:47:34 2008 +0200
     9.3 @@ -0,0 +1,59 @@
     9.4 +package org.apidesign.apifest08.currency;
     9.5 +
     9.6 +
     9.7 +import java.math.BigDecimal;
     9.8 +import java.util.Currency;
     9.9 +import java.util.Set;
    9.10 +
    9.11 +
    9.12 +/**
    9.13 + * Convert between two currencies.
    9.14 + *
    9.15 + * @author D'Arcy Smith
    9.16 + * @version 1.0
    9.17 + */
    9.18 +public interface Convertor
    9.19 +{
    9.20 +    /**
    9.21 +     * Convert an amount from one currency to another.
    9.22 +     * 
    9.23 +     * @param from the currency to convert from.
    9.24 +     * @param to the currency to convert to.
    9.25 +     * @param amount the amount to convert.
    9.26 +     * @return the converted amount.
    9.27 +     * @throws IllegalArgumentException if any of the arguments are null.
    9.28 +     * @throws InvalidConversionException if either from or to are not valid for the convertor.
    9.29 +     */
    9.30 +    BigDecimal convert(Currency   from, 
    9.31 +                       Currency   to, 
    9.32 +                       BigDecimal amount)
    9.33 +        throws InvalidConversionException;
    9.34 +
    9.35 +    /**
    9.36 +     * Check to see if converting between the two currencies is possible.
    9.37 +     *
    9.38 +     * @param from the currency to convert from.
    9.39 +     * @param to the currency to convert to.
    9.40 +     * @return true if the conversion is possible.
    9.41 +     */
    9.42 +    boolean canConvert(Currency from, Currency to);
    9.43 +
    9.44 +    /**
    9.45 +     * Get the currencies that the convertor supports.  Just because a currency is
    9.46 +     * supported does not mean that canConvert will return true.
    9.47 +     * 
    9.48 +     * @return the supported currencies.
    9.49 +     */
    9.50 +    Set<Currency> getCurrencies();
    9.51 +
    9.52 +    /**
    9.53 +     * Get the conversion rate between two currencies.
    9.54 +     * 
    9.55 +     * @param from the currency to convert from.
    9.56 +     * @param to the currency to convert to.
    9.57 +     * @return the conversion rate between the two currencies.
    9.58 +     * @throws InvalidConversionException if canConvert would return false.
    9.59 +     */
    9.60 +    BigDecimal getConversionRate(final Currency from, final Currency to)
    9.61 +        throws InvalidConversionException;
    9.62 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/ConvertorFactory.java	Sat Oct 18 07:47:34 2008 +0200
    10.3 @@ -0,0 +1,193 @@
    10.4 +package org.apidesign.apifest08.currency;
    10.5 +
    10.6 +import java.math.BigDecimal;
    10.7 +import java.util.Currency;
    10.8 +import java.util.Date;
    10.9 +
   10.10 +
   10.11 +/**
   10.12 + * Create convertors using a flyweight to reduce the number of repetative creations of the same convertor.
   10.13 + * 
   10.14 + * @author D'Arcy Smith
   10.15 + * @version 1.2
   10.16 + */
   10.17 +public final class ConvertorFactory
   10.18 +{
   10.19 +    /** 
   10.20 +     * Prevent accidental construction.
   10.21 +     */
   10.22 +    private ConvertorFactory()
   10.23 +    {        
   10.24 +    }
   10.25 +    
   10.26 +    /**
   10.27 +     * Get the convertor for the specified currencies.  The currency name format
   10.28 +     * must be acceptable to java.util.Currency.getInstance(String)
   10.29 +     * 
   10.30 +     * @param a the currency to convert from.
   10.31 +     * @param aRate the exchange rate for a to b.
   10.32 +     * @param b the currency to convert to.
   10.33 +     * @param bRate the echante rate for b to a.
   10.34 +     * @return the convertor for the specified currencies.
   10.35 +     * @throws IllegalArgumentException if any of the arguments are null.
   10.36 +     */
   10.37 +    public static Convertor getConvertor(final String     a,
   10.38 +                                         final BigDecimal aRate,
   10.39 +                                         final String     b,
   10.40 +                                         final BigDecimal bRate)
   10.41 +    {
   10.42 +        final Currency  currencyA;
   10.43 +        final Currency  currencyB;
   10.44 +        final Convertor convertor;
   10.45 +        
   10.46 +        currencyA = Currency.getInstance(a);
   10.47 +        currencyB = Currency.getInstance(b);        
   10.48 +        convertor = getConvertor(currencyA, aRate, currencyB, bRate);
   10.49 +        
   10.50 +        return (convertor);
   10.51 +    }
   10.52 +    
   10.53 +    /**
   10.54 +     * Get the convertor for the specified currencies.
   10.55 +     * 
   10.56 +     * @param a the currency to convert from.
   10.57 +     * @param aRate the exchange rate for a to b.
   10.58 +     * @param b the currency to convert to.
   10.59 +     * @param bRate the echante rate for b to a.
   10.60 +     * @return the convertor for the specified currencies.
   10.61 +     * @throws IllegalArgumentException if either any of the arguments are null or if either rate <= 0.
   10.62 +     */
   10.63 +    public static Convertor getConvertor(final Currency   a,
   10.64 +                                         final BigDecimal aRate,
   10.65 +                                         final Currency   b,
   10.66 +                                         final BigDecimal bRate)
   10.67 +    {
   10.68 +        Convertor          convertor;
   10.69 +        final ExchangeRate rate;
   10.70 +
   10.71 +        if(a == null)
   10.72 +        {
   10.73 +            throw new IllegalArgumentException("a cannot be null");
   10.74 +        }
   10.75 +
   10.76 +        if(b == null)
   10.77 +        {
   10.78 +            throw new IllegalArgumentException("b cannot be null");
   10.79 +        }
   10.80 +        
   10.81 +        if(aRate == null)
   10.82 +        {
   10.83 +            throw new IllegalArgumentException("aRate cannot be null");
   10.84 +        }
   10.85 +        
   10.86 +        if(bRate == null)
   10.87 +        {
   10.88 +            throw new IllegalArgumentException("bRate cannot be null");
   10.89 +        }
   10.90 +
   10.91 +        rate      = ExchangeRate.getExchangeRate(a, b, aRate, bRate);
   10.92 +        convertor = getConvertor(rate);
   10.93 +                
   10.94 +        return (convertor);
   10.95 +    }
   10.96 +    
   10.97 +    public static Convertor getConvertor(final ExchangeRate rate)
   10.98 +    {
   10.99 +        final ConvertorImpl convertor;
  10.100 +
  10.101 +        if(rate == null)
  10.102 +        {
  10.103 +            throw new IllegalArgumentException("rate cannot be null");
  10.104 +        }
  10.105 +        
  10.106 +        convertor = new ConvertorImpl(rate);
  10.107 +                
  10.108 +        return (convertor);        
  10.109 +    }
  10.110 +
  10.111 +    public static DatedConvertor getConvertor(final Date      from,
  10.112 +                                              final Date      till,
  10.113 +                                              final Convertor convertor)
  10.114 +    {
  10.115 +        final DateRange      range;
  10.116 +        final ExchangeRate   rate;
  10.117 +        final DatedConvertor datedConvertor;
  10.118 +
  10.119 +        if(from == null)
  10.120 +        {
  10.121 +            throw new IllegalArgumentException("from cannot be null");
  10.122 +        }
  10.123 +
  10.124 +        if(till == null)
  10.125 +        {
  10.126 +            throw new IllegalArgumentException("till cannot be null");
  10.127 +        }
  10.128 +
  10.129 +        if(convertor == null)
  10.130 +        {
  10.131 +            throw new IllegalArgumentException("convertor cannot be null");
  10.132 +        }
  10.133 +        
  10.134 +        if(from.after(till))
  10.135 +        {
  10.136 +            throw new IllegalArgumentException(from + " cannot be after " + till);
  10.137 +        }
  10.138 +        
  10.139 +        if(convertor instanceof ExchangeRateConvertor)
  10.140 +        {
  10.141 +            rate = ((ExchangeRateConvertor)convertor).getExchangeRate();
  10.142 +        }
  10.143 +        else
  10.144 +        {
  10.145 +            throw new Error();
  10.146 +        }
  10.147 +
  10.148 +        range          = new DateRange(from, till);
  10.149 +        datedConvertor = new DatedConvertorImpl(range, rate);
  10.150 +        
  10.151 +        return (datedConvertor);
  10.152 +    }
  10.153 +
  10.154 +    /**
  10.155 +     * 
  10.156 +     * @param cs
  10.157 +     * @return
  10.158 +     */
  10.159 +    public static Convertor mergeConvertors(final Convertor ... cs)
  10.160 +    {
  10.161 +        Convertor convertor;
  10.162 +        int       dated;
  10.163 +        int       nonDated;
  10.164 +
  10.165 +        dated    = 0;
  10.166 +        nonDated = 0;
  10.167 +
  10.168 +        for(final Convertor c : cs)
  10.169 +        {
  10.170 +            if(c instanceof DatedConvertor)
  10.171 +            {
  10.172 +                dated++;
  10.173 +            }
  10.174 +            else
  10.175 +            {
  10.176 +                nonDated++;
  10.177 +            }
  10.178 +        }
  10.179 +
  10.180 +        if(dated != 0 && nonDated != 0)
  10.181 +        {
  10.182 +            throw new IllegalArgumentException("cannot mix DatedConvertors and non-DatedConvertors");
  10.183 +        }
  10.184 +
  10.185 +        if(dated != 0)
  10.186 +        {
  10.187 +            convertor = new DatedCompositeConvertorImpl(cs);
  10.188 +        }
  10.189 +        else
  10.190 +        {
  10.191 +            convertor = new CompositeConvertorImpl(cs);
  10.192 +        }
  10.193 +
  10.194 +        return (convertor);
  10.195 +    }
  10.196 +}
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/DateRange.java	Sat Oct 18 07:47:34 2008 +0200
    11.3 @@ -0,0 +1,91 @@
    11.4 +package org.apidesign.apifest08.currency;
    11.5 +
    11.6 +
    11.7 +import java.util.Date;
    11.8 +
    11.9 +
   11.10 +public final class DateRange
   11.11 +{
   11.12 +    private final Date from;
   11.13 +    private final Date till;
   11.14 +    
   11.15 +    DateRange(final Date f,
   11.16 +              final Date t)
   11.17 +    {
   11.18 +        if(f == null && t != null)
   11.19 +        {
   11.20 +            throw new IllegalArgumentException("f was null but t was not");
   11.21 +        }
   11.22 +        
   11.23 +        if(f != null && t == null)
   11.24 +        {
   11.25 +            throw new IllegalArgumentException("f was null but t was not");
   11.26 +        }
   11.27 +        
   11.28 +        from = f;
   11.29 +        till = t;
   11.30 +    }
   11.31 +    
   11.32 +    public Date getFrom()
   11.33 +    {
   11.34 +        return (from);
   11.35 +    }
   11.36 +    
   11.37 +    public Date getTill()
   11.38 +    {
   11.39 +        return (from);
   11.40 +    }
   11.41 +    
   11.42 +    public boolean isInRange(final Date date)
   11.43 +    {
   11.44 +        final boolean retVal;
   11.45 +
   11.46 +        if(date.equals(from) || date.equals(till))
   11.47 +        {
   11.48 +            retVal = true;
   11.49 +        }
   11.50 +        else if(date.after(from) && date.before(till))
   11.51 +        {
   11.52 +            retVal = true;
   11.53 +        }
   11.54 +        else
   11.55 +        {
   11.56 +            retVal = false;
   11.57 +        }
   11.58 +
   11.59 +        return (retVal);
   11.60 +    }
   11.61 +
   11.62 +    @Override
   11.63 +    public boolean equals(Object obj) {
   11.64 +        if (obj == null) {
   11.65 +            return false;
   11.66 +        }
   11.67 +        if (getClass() != obj.getClass()) {
   11.68 +            return false;
   11.69 +        }
   11.70 +        final DateRange other = (DateRange) obj;
   11.71 +        if (this.from != other.from && (this.from == null || !this.from.equals(other.from))) {
   11.72 +            return false;
   11.73 +        }
   11.74 +        if (this.till != other.till && (this.till == null || !this.till.equals(other.till))) {
   11.75 +            return false;
   11.76 +        }
   11.77 +        return true;
   11.78 +    }
   11.79 +
   11.80 +    @Override
   11.81 +    public int hashCode() {
   11.82 +        int hash = 7;
   11.83 +        hash = 89 * hash + (this.from != null ? this.from.hashCode() : 0);
   11.84 +        hash = 89 * hash + (this.till != null ? this.till.hashCode() : 0);
   11.85 +        return hash;
   11.86 +    }
   11.87 +
   11.88 +    @Override
   11.89 +    public String toString()
   11.90 +    {
   11.91 +        return (from + " until " + till);
   11.92 +    }
   11.93 +
   11.94 +}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/DatedCompositeConvertorImpl.java	Sat Oct 18 07:47:34 2008 +0200
    12.3 @@ -0,0 +1,319 @@
    12.4 +package org.apidesign.apifest08.currency;
    12.5 +
    12.6 +import java.math.BigDecimal;
    12.7 +import java.util.Currency;
    12.8 +import java.util.Date;
    12.9 +import java.util.HashMap;
   12.10 +import java.util.Map;
   12.11 +import java.util.Set;
   12.12 +
   12.13 +
   12.14 +final class DatedCompositeConvertorImpl
   12.15 +    implements TimedConvertor
   12.16 +{
   12.17 +    /**
   12.18 +     * The convertors that are supported.
   12.19 +     */
   12.20 +    private final DatedConvertor[] convertors;
   12.21 +    
   12.22 +    /**
   12.23 +     * Keeps track of what convertors to use to convert between currencies.
   12.24 +     */
   12.25 +    private final Map<DateRange, Map<Currency, Map<Currency, Convertor>>> datedConversions;
   12.26 +    
   12.27 +    {
   12.28 +        datedConversions = new HashMap<DateRange, Map<Currency, Map<Currency, Convertor>>>();
   12.29 +    }
   12.30 +
   12.31 +    /**
   12.32 +     * Construct a ComositeConvertorImpl with the specified convertors.
   12.33 +     * This will result in all possible conversions between the supplied currencies being made.
   12.34 +     * 
   12.35 +     * @param cs the convertors to use.
   12.36 +     * @throws IllegalArgumentException if any of the items in cs are null.
   12.37 +     */
   12.38 +    DatedCompositeConvertorImpl(Convertor ... cs)
   12.39 +    {
   12.40 +        int i;
   12.41 +
   12.42 +        convertors = new DatedConvertor[cs.length];
   12.43 +        i          = 0;
   12.44 +
   12.45 +        for(final Convertor c : cs)
   12.46 +        {
   12.47 +            if(!(c instanceof DatedConvertor))
   12.48 +            {
   12.49 +                throw new IllegalArgumentException("cs must only contain DatedConvertors");
   12.50 +            }
   12.51 +
   12.52 +            convertors[i] = (DatedConvertor)c;
   12.53 +            i++;
   12.54 +        }
   12.55 +
   12.56 +        // track all of the known conversion
   12.57 +        for(final DatedConvertor convertor : convertors)
   12.58 +        {
   12.59 +            final Set<Currency>      currencies;
   12.60 +            Map<Currency, Convertor> possible;
   12.61 +            DateRange                range;
   12.62 +            
   12.63 +            if(convertor == null)
   12.64 +            {
   12.65 +                throw new IllegalArgumentException("cs cannot contain null");
   12.66 +            }
   12.67 +            
   12.68 +            currencies = convertor.getCurrencies();
   12.69 +            range      = convertor.getDateRange();
   12.70 +            
   12.71 +            for(final Currency currency : currencies)
   12.72 +            {
   12.73 +                Map<Currency, Map<Currency, Convertor>> possibleConversions;
   12.74 +                
   12.75 +                possibleConversions = datedConversions.get(range);
   12.76 +                
   12.77 +                if(possibleConversions == null)
   12.78 +                {
   12.79 +                    possibleConversions = new HashMap<Currency, Map<Currency, Convertor>>();
   12.80 +                    datedConversions.put(range, possibleConversions);
   12.81 +                }
   12.82 +
   12.83 +                possible = possibleConversions.get(currency);
   12.84 +
   12.85 +                if(possible == null)
   12.86 +                {
   12.87 +                    possible = new HashMap<Currency, Convertor>();
   12.88 +                    possibleConversions.put(currency, possible);
   12.89 +                }
   12.90 +
   12.91 +                for(final Currency c : currencies)
   12.92 +                {
   12.93 +                    possible.put(c, convertor);
   12.94 +                }
   12.95 +            }
   12.96 +        }
   12.97 +
   12.98 +
   12.99 +        /*
  12.100 +        // make up conversions that can be derived... eg:
  12.101 +        //   we have:
  12.102 +        //      USD <-> CAD
  12.103 +        //      CAD <-> CZK
  12.104 +        //      SSK <-> GBP
  12.105 +        //   we can derive:
  12.106 +        //      USD <-> CZK
  12.107 +        //   we cannot derive:
  12.108 +        //      USD <-> GBP
  12.109 +        //      CAD <-> GBP
  12.110 +        //      CZK <-> GBP        
  12.111 +        // 
  12.112 +        // NOTE: no attempt is made to deal with dates for DatedConvertors... nothing we can do about it.
  12.113 +        do
  12.114 +        {
  12.115 +            newConvertors = 0;
  12.116 +
  12.117 +            // todo... need to loop this until all the ones that can be handled are done.
  12.118 +            for(final Currency from : getCurrencies())
  12.119 +            {
  12.120 +                for(final Currency to : getCurrencies())
  12.121 +                {
  12.122 +                    if(!(canConvert(from, to)))
  12.123 +                    {
  12.124 +                        final Set<Currency> fromCurrencies;
  12.125 +                        final Set<Currency> toCurrencies;
  12.126 +                        final Set<Currency> common;
  12.127 +                        Map<Currency, Map<Currency, Convertor>> possibleConversions;
  12.128 +                
  12.129 +                        possibleConversions.get(range);
  12.130 +                        fromCurrencies = possibleConversions.get(from).keySet();
  12.131 +                        toCurrencies   = possibleConversions.get(to).keySet();
  12.132 +                        common         = new HashSet<Currency>();
  12.133 +
  12.134 +                        for(final Currency currency : fromCurrencies)
  12.135 +                        {
  12.136 +                            if(toCurrencies.contains(currency))
  12.137 +                            {
  12.138 +                                common.add(currency);
  12.139 +                            }
  12.140 +                        }
  12.141 +
  12.142 +                        for(final Currency currency : common)
  12.143 +                        {
  12.144 +                            final Convertor convertor;
  12.145 +
  12.146 +                            convertor = createConvertor(from, to, currency);
  12.147 +                            possibleConversions.get(from).put(to, convertor);
  12.148 +                            possibleConversions.get(to).put(from, convertor);
  12.149 +                            newConvertors++;
  12.150 +                        }
  12.151 +                    }
  12.152 +                }
  12.153 +            }
  12.154 +        }
  12.155 +        while(newConvertors > 0);
  12.156 +        */
  12.157 +    }
  12.158 +
  12.159 +    /**
  12.160 +     * Check to see if converting between the two currencies is possible.
  12.161 +     *
  12.162 +     * @param from the currency to convert from.
  12.163 +     * @param to the currency to convert to.
  12.164 +     * @return true if the conversion is possible.
  12.165 +     * @throws IllegalArgumentException if either from or to are null.
  12.166 +     */
  12.167 +    public boolean canConvert(final Currency from, final Currency to)
  12.168 +    {
  12.169 +        throw new UnsupportedOperationException();
  12.170 +    }
  12.171 +
  12.172 +    /**
  12.173 +     * Get the currencies that the convertor supports.  Just because a currency is
  12.174 +     * supported does not mean that canConvert will return true.
  12.175 +     * 
  12.176 +     * @return the supported currencies.
  12.177 +     */
  12.178 +    public Set<Currency> getCurrencies()
  12.179 +    {
  12.180 +        throw new UnsupportedOperationException();
  12.181 +    }
  12.182 +    
  12.183 +    /**
  12.184 +     * Get the conversion rate between two currencies.
  12.185 +     * 
  12.186 +     * @param from the currency to convert from.
  12.187 +     * @param to the currency to convert to.
  12.188 +     * @return the conversion rate between the two currencies.
  12.189 +     * @throws IllegalArgumentException if either from or to is null.
  12.190 +     * @throws InvalidConversionException if canConvert would return false.
  12.191 +     */
  12.192 +    public BigDecimal getConversionRate(final Currency from, final Currency to)
  12.193 +        throws InvalidConversionException
  12.194 +    {
  12.195 +        throw new UnsupportedOperationException();
  12.196 +    }
  12.197 +
  12.198 +    private Convertor getConvertor(final Currency from, final Currency to, final Date date)
  12.199 +    {
  12.200 +        Map<Currency, Map<Currency, Convertor>> possibleConversions;
  12.201 +        final Map<Currency, Convertor> possible;
  12.202 +        Convertor                      convertor;
  12.203 +
  12.204 +        if(from == null)
  12.205 +        {
  12.206 +            throw new IllegalArgumentException("from cannot be null");
  12.207 +        }
  12.208 +        
  12.209 +        if(to == null)
  12.210 +        {
  12.211 +            throw new IllegalArgumentException("to cannot be null");
  12.212 +        }
  12.213 +        
  12.214 +        possibleConversions = null;
  12.215 +
  12.216 +        for(final DateRange range : datedConversions.keySet())
  12.217 +        {
  12.218 +            if(range.isInRange(date))
  12.219 +            {
  12.220 +                possibleConversions = datedConversions.get(range);
  12.221 +                break;
  12.222 +            }
  12.223 +        }
  12.224 +
  12.225 +        if(possibleConversions == null)
  12.226 +        {
  12.227 +            return (null);
  12.228 +        }
  12.229 +
  12.230 +        possible  = possibleConversions.get(from);
  12.231 +        
  12.232 +                
  12.233 +        if(possible == null)
  12.234 +        {
  12.235 +            return (null);
  12.236 +        }
  12.237 +
  12.238 +
  12.239 +        convertor = possible.get(to);
  12.240 +
  12.241 +        
  12.242 +        if(convertor == null)
  12.243 +        {
  12.244 +            return (null);
  12.245 +        }
  12.246 +
  12.247 +        return (convertor);
  12.248 +    }
  12.249 +
  12.250 +    /**
  12.251 +     * Convert an amount from one currency to another.
  12.252 +     * 
  12.253 +     * @param from the currency to convert from.
  12.254 +     * @param to the currency to convert to.
  12.255 +     * @param amount the amount to convert.
  12.256 +     * @return the converted amount.
  12.257 +     * @throws IllegalArgumentException if any of the arguments are null.
  12.258 +     * @throws InvalidConversionException if either from or to are not valid for the convertor.
  12.259 +     */
  12.260 +    public BigDecimal convert(final Currency   from,
  12.261 +                              final Currency   to,
  12.262 +                              final BigDecimal amount)
  12.263 +        throws InvalidConversionException
  12.264 +    {
  12.265 +        throw new InvalidConversionException("No date for the conversion", from);
  12.266 +    }
  12.267 +
  12.268 +    public BigDecimal convert(final Currency   from, 
  12.269 +                              final Currency   to, 
  12.270 +                              final BigDecimal amount, 
  12.271 +                              final Date       date) 
  12.272 +        throws InvalidConversionException 
  12.273 +    {
  12.274 +        final Convertor  convertor;
  12.275 +        final BigDecimal total;
  12.276 +        
  12.277 +        convertor = getConvertor(from, to, date);
  12.278 +
  12.279 +        if(convertor == null)
  12.280 +        {
  12.281 +            throw new InvalidConversionException("cannot convert", from);
  12.282 +        }
  12.283 +        
  12.284 +        if(canConvert(from, to, date))
  12.285 +        {
  12.286 +            final TimedConvertor timeConvertor;
  12.287 +            
  12.288 +            timeConvertor = (TimedConvertor)convertor;
  12.289 +            total         = timeConvertor.convert(from, to, amount, date);
  12.290 +        }
  12.291 +        else
  12.292 +        {
  12.293 +            throw new InvalidConversionException("cannot convert", from);
  12.294 +        }
  12.295 +        
  12.296 +        return (total);
  12.297 +    }
  12.298 +
  12.299 +    public boolean canConvert(final Currency from,
  12.300 +                              final Currency to,
  12.301 +                              final Date     date)
  12.302 +    {
  12.303 +        Convertor     convertor;
  12.304 +        final boolean retVal;
  12.305 +
  12.306 +        convertor = getConvertor(from, to, date);
  12.307 +
  12.308 +        if(convertor != null)
  12.309 +        {
  12.310 +            final TimedConvertor timeConvertor;
  12.311 +
  12.312 +            timeConvertor = (TimedConvertor)convertor;
  12.313 +            retVal        = timeConvertor.canConvert(from, to, date);
  12.314 +        }
  12.315 +        else
  12.316 +        {
  12.317 +            retVal = false;
  12.318 +        }
  12.319 +
  12.320 +        return (retVal);
  12.321 +    }
  12.322 +}
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/DatedConvertor.java	Sat Oct 18 07:47:34 2008 +0200
    13.3 @@ -0,0 +1,9 @@
    13.4 +package org.apidesign.apifest08.currency;
    13.5 +
    13.6 +
    13.7 +public interface DatedConvertor
    13.8 +    extends ExchangeRateConvertor,
    13.9 +            TimedConvertor
   13.10 +{
   13.11 +    DateRange getDateRange();
   13.12 +}
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/DatedConvertorImpl.java	Sat Oct 18 07:47:34 2008 +0200
    14.3 @@ -0,0 +1,82 @@
    14.4 +package org.apidesign.apifest08.currency;
    14.5 +
    14.6 +import java.math.BigDecimal;
    14.7 +import java.util.Currency;
    14.8 +import java.util.Date;
    14.9 +
   14.10 +
   14.11 +final class DatedConvertorImpl
   14.12 +    extends    ConvertorImpl
   14.13 +    implements DatedConvertor
   14.14 +{
   14.15 +    private final DateRange range;
   14.16 +    
   14.17 +    DatedConvertorImpl(final DateRange rng,
   14.18 +                       final ExchangeRate r)
   14.19 +    {
   14.20 +        super(r);
   14.21 +        
   14.22 +        if(rng == null)
   14.23 +        {
   14.24 +            throw new IllegalArgumentException("rng cannot be null");
   14.25 +        }
   14.26 +
   14.27 +        range = rng;
   14.28 +    }
   14.29 +    
   14.30 +    public DateRange getDateRange()
   14.31 +    {
   14.32 +        return (range);
   14.33 +    }
   14.34 +
   14.35 +    @Override
   14.36 +    public BigDecimal convert(final Currency   from,
   14.37 +                              final Currency   to,
   14.38 +                              final BigDecimal amount)
   14.39 +        throws InvalidConversionException
   14.40 +    {
   14.41 +        final BigDecimal total;
   14.42 +        
   14.43 +        total = convert(from, to, amount, new Date(System.currentTimeMillis()));
   14.44 +        
   14.45 +        return (total);
   14.46 +    }
   14.47 +
   14.48 +    public BigDecimal convert(final Currency   from,
   14.49 +                              final Currency   to,
   14.50 +                              final BigDecimal amount,
   14.51 +                              final Date       date)
   14.52 +        throws InvalidConversionException
   14.53 +    {
   14.54 +        final BigDecimal total;
   14.55 +
   14.56 +        if(range.isInRange(date))
   14.57 +        {
   14.58 +            total = super.convert(from, to, amount);
   14.59 +        }
   14.60 +        else
   14.61 +        {
   14.62 +            throw new InvalidConversionException("cannot convert for date", from);
   14.63 +        }
   14.64 +
   14.65 +        return (total);
   14.66 +    }
   14.67 +
   14.68 +    public boolean canConvert(final Currency from,
   14.69 +                              final Currency to,
   14.70 +                              final Date     date)
   14.71 +    {
   14.72 +        final boolean retVal;
   14.73 +
   14.74 +        if(canConvert(from, to))
   14.75 +        {
   14.76 +            retVal = range.isInRange(date);
   14.77 +        }
   14.78 +        else
   14.79 +        {
   14.80 +            retVal = false;
   14.81 +        }
   14.82 +
   14.83 +        return (retVal);
   14.84 +    }
   14.85 +}
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/ExchangeRate.java	Sat Oct 18 07:47:34 2008 +0200
    15.3 @@ -0,0 +1,326 @@
    15.4 +package org.apidesign.apifest08.currency;
    15.5 +
    15.6 +
    15.7 +import java.math.BigDecimal;
    15.8 +import java.math.RoundingMode;
    15.9 +import java.util.Collections;
   15.10 +import java.util.Currency;
   15.11 +import java.util.HashSet;
   15.12 +import java.util.Set;
   15.13 +
   15.14 +
   15.15 +/**
   15.16 + * The exchange rate between two currencies.
   15.17 + *
   15.18 + * @author D'Arcy Smith
   15.19 + * @version 1.1
   15.20 + */
   15.21 +public final class ExchangeRate
   15.22 +{
   15.23 +    /**
   15.24 +     * 
   15.25 +     */
   15.26 +    private final Currency currencyA;
   15.27 +
   15.28 +    /**
   15.29 +     * 
   15.30 +     */
   15.31 +    private final Currency currencyB;
   15.32 +
   15.33 +    /**
   15.34 +     * 
   15.35 +     */
   15.36 +    private final BigDecimal rateAtoB;
   15.37 +
   15.38 +    /**
   15.39 +     * 
   15.40 +     */
   15.41 +    private final BigDecimal rateBtoA;
   15.42 +
   15.43 +    /**
   15.44 +     * Construct an ExchangeRate with the specified values.
   15.45 +     * 
   15.46 +     * @param a the first currency
   15.47 +     * @param b the second currency
   15.48 +     * @param ra the rate to convert a to b
   15.49 +     * @param rb the rate to covertt b to a
   15.50 +     * @throws IllegalArgumentException if any parameter is null.
   15.51 +     */
   15.52 +    public ExchangeRate(final Currency   a,
   15.53 +                        final Currency   b,
   15.54 +                        final BigDecimal ra,
   15.55 +                        final BigDecimal rb)
   15.56 +    {
   15.57 +        if(a == null)
   15.58 +        {
   15.59 +            throw new IllegalArgumentException("a cannot be null");
   15.60 +        }
   15.61 +        
   15.62 +        if(b == null)
   15.63 +        {
   15.64 +            throw new IllegalArgumentException("b cannot be null");
   15.65 +        }
   15.66 +        
   15.67 +        if(ra == null)
   15.68 +        {
   15.69 +            throw new IllegalArgumentException("ra cannot be null");
   15.70 +        }
   15.71 +        
   15.72 +        if(rb == null)
   15.73 +        {
   15.74 +            throw new IllegalArgumentException("rb cannot be null");
   15.75 +        }
   15.76 +        
   15.77 +        if(ra.compareTo(BigDecimal.ZERO) <= 0)
   15.78 +        {
   15.79 +            throw new IllegalArgumentException("ra cannot be <= 0, was: " + ra);
   15.80 +        }
   15.81 +        
   15.82 +        if(rb.compareTo(BigDecimal.ZERO) <= 0)
   15.83 +        {
   15.84 +            throw new IllegalArgumentException("rb cannot be <= 0, was: " + ra);
   15.85 +        }
   15.86 +        
   15.87 +        currencyA = a;
   15.88 +        currencyB = b;
   15.89 +        rateAtoB  = ra;
   15.90 +        rateBtoA  = rb;
   15.91 +    }
   15.92 +
   15.93 +    /**
   15.94 +     * Get the first currency.
   15.95 +     * 
   15.96 +     * @return the first currency.
   15.97 +     */
   15.98 +    public Currency getCurrencyA()
   15.99 +    {
  15.100 +        return currencyA;
  15.101 +    }
  15.102 +
  15.103 +    /**
  15.104 +     * Get the second currency.
  15.105 +     * 
  15.106 +     * @return the second currency.
  15.107 +     */
  15.108 +    public Currency getCurrencyB()
  15.109 +    {
  15.110 +        return currencyB;
  15.111 +    }
  15.112 +
  15.113 +    /**
  15.114 +     * Get the conversion rate from currencyA to currencyB.
  15.115 +     * 
  15.116 +     * @return the conversion rate from currencyA to currencyB.
  15.117 +     */
  15.118 +    public BigDecimal getRateAtoB()
  15.119 +    {
  15.120 +        return rateAtoB;
  15.121 +    }
  15.122 +
  15.123 +    /**
  15.124 +     * Get the conversion rate from currencyB to currencyA.
  15.125 +     * 
  15.126 +     * @return the conversion rate from currencyB to currencyA.
  15.127 +     */
  15.128 +    public BigDecimal getRateBtoA()
  15.129 +    {
  15.130 +        return rateBtoA;
  15.131 +    }
  15.132 +    
  15.133 +    public static ExchangeRate getExchangeRate(final Currency   a,
  15.134 +                                               final Currency   b,
  15.135 +                                               final BigDecimal va,
  15.136 +                                               final BigDecimal vb)
  15.137 +    {
  15.138 +        final BigDecimal   rateAtoB;
  15.139 +        final BigDecimal   rateBtoA;
  15.140 +        final ExchangeRate rate;    
  15.141 +        
  15.142 +        if(a == null)
  15.143 +        {
  15.144 +            throw new IllegalArgumentException("a cannot be null");
  15.145 +        }
  15.146 +        
  15.147 +        if(b == null)
  15.148 +        {
  15.149 +            throw new IllegalArgumentException("b cannot be null");
  15.150 +        }
  15.151 +
  15.152 +        if(a.equals(b))
  15.153 +        {
  15.154 +            rateAtoB = BigDecimal.ONE;
  15.155 +            rateBtoA = BigDecimal.ONE;
  15.156 +        }
  15.157 +        else 
  15.158 +        {
  15.159 +            rateAtoB = vb.divide(va, 20, RoundingMode.HALF_DOWN);
  15.160 +            rateBtoA = va.divide(vb, 20, RoundingMode.HALF_DOWN);
  15.161 +        }
  15.162 +        
  15.163 +        rate = new ExchangeRate(a, 
  15.164 +                                b,
  15.165 +                                rateAtoB.setScale(20, RoundingMode.HALF_EVEN),
  15.166 +                                rateBtoA.setScale(20, RoundingMode.HALF_EVEN));
  15.167 +        
  15.168 +        return (rate);
  15.169 +    }
  15.170 +
  15.171 +    public BigDecimal convert(final Currency   from,
  15.172 +                              final Currency   to,
  15.173 +                              final BigDecimal amount)
  15.174 +        throws InvalidConversionException
  15.175 +    {
  15.176 +        final BigDecimal result;
  15.177 +        
  15.178 +        if(amount == null)
  15.179 +        {
  15.180 +            throw new IllegalArgumentException("amount cannot be null");
  15.181 +        }
  15.182 +        
  15.183 +        if(from == null)
  15.184 +        {
  15.185 +            throw new IllegalArgumentException("from cannot be null");
  15.186 +        }
  15.187 +        
  15.188 +        if(to == null)
  15.189 +        {
  15.190 +            throw new IllegalArgumentException("to cannot be null");
  15.191 +        }
  15.192 +        
  15.193 +        if(!(from.equals(currencyA)) && (!(from.equals(currencyB))))
  15.194 +        {
  15.195 +            throw new InvalidConversionException("cannot convert from: " + from.getCurrencyCode(), from, currencyA, currencyB);
  15.196 +        }
  15.197 +        
  15.198 +        if(!(to.equals(currencyA)) && (!(to.equals(currencyB))))
  15.199 +        {
  15.200 +            throw new InvalidConversionException("cannot convert to: " + to.getCurrencyCode(), to, currencyA, currencyB);
  15.201 +        }
  15.202 +
  15.203 +        result = amount.multiply(getConversionRate(from, to));
  15.204 +
  15.205 +        return (result.setScale(2, RoundingMode.HALF_DOWN));
  15.206 +    }
  15.207 +    
  15.208 +    /**
  15.209 +     * Check to see if converting between the two currencies is possible.
  15.210 +     * 
  15.211 +     * @param from the currency to convert from.
  15.212 +     * @param to the currency to convert to.
  15.213 +     * @return true if the conversion is possible.
  15.214 +     * @throws IllegalArgumentException if either from or to are null.
  15.215 +     */
  15.216 +    public boolean canConvert(final Currency from, final Currency to)
  15.217 +    {
  15.218 +        if(from == null)
  15.219 +        {
  15.220 +            throw new IllegalArgumentException("from cannot be null");
  15.221 +        }
  15.222 +        
  15.223 +        if(to == null)
  15.224 +        {
  15.225 +            throw new IllegalArgumentException("to cannot be null");
  15.226 +        }
  15.227 +        
  15.228 +        return ((from.equals(currencyA) || from.equals(currencyB)) &&
  15.229 +                (to.equals(currencyA)   || to.equals(currencyB)));
  15.230 +    }
  15.231 +    /**
  15.232 +     * Get the currencies that the convertor supports.
  15.233 +     * 
  15.234 +     * @return the supported currencies.
  15.235 +     */
  15.236 +    public Set<Currency> getCurrencies()
  15.237 +    {
  15.238 +        final Set<Currency> currencies;
  15.239 +        
  15.240 +        currencies = new HashSet<Currency>();
  15.241 +        currencies.add(currencyA);
  15.242 +        currencies.add(currencyB);
  15.243 +
  15.244 +        return (Collections.unmodifiableSet(currencies));
  15.245 +    }
  15.246 +
  15.247 +    /**
  15.248 +     * Get the conversion rate between two currencies.
  15.249 +     * 
  15.250 +     * @param from the currency to convert from.
  15.251 +     * @param to the currency to convert to.
  15.252 +     * @return the conversion rate between the two currencies.
  15.253 +     * @throws InvalidConversionException if canConvert would return false.
  15.254 +     * @throws IllegalArgumentException if either from or to are null.
  15.255 +     */
  15.256 +    public BigDecimal getConversionRate(final Currency from, 
  15.257 +                                        final Currency to)
  15.258 +        throws InvalidConversionException
  15.259 +    {                
  15.260 +        final BigDecimal rate;
  15.261 +        
  15.262 +        if(from == null)
  15.263 +        {
  15.264 +            throw new IllegalArgumentException("from cannot be null");
  15.265 +        }
  15.266 +        
  15.267 +        if(to == null)
  15.268 +        {
  15.269 +            throw new IllegalArgumentException("to cannot be null");
  15.270 +        }
  15.271 +
  15.272 +        if(from.equals(to))
  15.273 +        {
  15.274 +            rate = BigDecimal.ONE;
  15.275 +        }
  15.276 +        else 
  15.277 +        {
  15.278 +            if(from.equals(currencyA))
  15.279 +            {
  15.280 +                rate = rateAtoB;
  15.281 +            }
  15.282 +            else
  15.283 +            {
  15.284 +                rate = rateBtoA;
  15.285 +            }
  15.286 +        }
  15.287 +        
  15.288 +        return (rate);
  15.289 +    }
  15.290 +
  15.291 +    public String toString()
  15.292 +    {
  15.293 +        return (rateAtoB + " : " + rateBtoA);
  15.294 +    }
  15.295 +
  15.296 +    @Override
  15.297 +    public boolean equals(Object obj) {
  15.298 +        if (obj == null) {
  15.299 +            return false;
  15.300 +        }
  15.301 +        if (getClass() != obj.getClass()) {
  15.302 +            return false;
  15.303 +        }
  15.304 +        final ExchangeRate other = (ExchangeRate) obj;
  15.305 +        if (this.currencyA != other.currencyA && (this.currencyA == null || !this.currencyA.equals(other.currencyA))) {
  15.306 +            return false;
  15.307 +        }
  15.308 +        if (this.currencyB != other.currencyB && (this.currencyB == null || !this.currencyB.equals(other.currencyB))) {
  15.309 +            return false;
  15.310 +        }
  15.311 +        if (this.rateAtoB != other.rateAtoB && (this.rateAtoB == null || !this.rateAtoB.equals(other.rateAtoB))) {
  15.312 +            return false;
  15.313 +        }
  15.314 +        if (this.rateBtoA != other.rateBtoA && (this.rateBtoA == null || !this.rateBtoA.equals(other.rateBtoA))) {
  15.315 +            return false;
  15.316 +        }
  15.317 +        return true;
  15.318 +    }
  15.319 +
  15.320 +    @Override
  15.321 +    public int hashCode() {
  15.322 +        int hash = 7;
  15.323 +        hash = 97 * hash + (this.currencyA != null ? this.currencyA.hashCode() : 0);
  15.324 +        hash = 97 * hash + (this.currencyB != null ? this.currencyB.hashCode() : 0);
  15.325 +        hash = 97 * hash + (this.rateAtoB != null ? this.rateAtoB.hashCode() : 0);
  15.326 +        hash = 97 * hash + (this.rateBtoA != null ? this.rateBtoA.hashCode() : 0);
  15.327 +        return hash;
  15.328 +    }
  15.329 +}
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/ExchangeRateConvertor.java	Sat Oct 18 07:47:34 2008 +0200
    16.3 @@ -0,0 +1,8 @@
    16.4 +package org.apidesign.apifest08.currency;
    16.5 +
    16.6 +
    16.7 +public interface ExchangeRateConvertor
    16.8 +    extends Convertor
    16.9 +{
   16.10 +    ExchangeRate getExchangeRate();
   16.11 +}
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/ExchangeRateFinder.java	Sat Oct 18 07:47:34 2008 +0200
    17.3 @@ -0,0 +1,22 @@
    17.4 +package org.apidesign.apifest08.currency;
    17.5 +
    17.6 +import java.util.Currency;
    17.7 +
    17.8 +
    17.9 +/**
   17.10 + * Used to look up the exchange rate between two currencies.
   17.11 + *
   17.12 + * @author D'Arcy Smith
   17.13 + * @version 1.0
   17.14 + */
   17.15 +public interface ExchangeRateFinder
   17.16 +{
   17.17 +    /**
   17.18 +     * Find the exchange rate between two currencies.
   17.19 +     *
   17.20 +     * @param a the currency to convert from.
   17.21 +     * @param b the currency to convert to.
   17.22 +     * @return the exchange rate for conversions between the two currencies.
   17.23 +     */
   17.24 +    ExchangeRate findRate(Currency a, Currency b);
   17.25 +}
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/InvalidConversionException.java	Sat Oct 18 07:47:34 2008 +0200
    18.3 @@ -0,0 +1,95 @@
    18.4 +package org.apidesign.apifest08.currency;
    18.5 +
    18.6 +      
    18.7 +import java.util.Currency;
    18.8 +
    18.9 +
   18.10 +/**
   18.11 + * Thrown when a currency is invalid for a given Convertor.
   18.12 + * 
   18.13 + * @author D'Arcy Smith
   18.14 + * @version 1.0
   18.15 + */
   18.16 +public class InvalidConversionException
   18.17 +    extends Exception
   18.18 +{
   18.19 +    /**
   18.20 +     * The currency that was tried.
   18.21 +     */
   18.22 +    private final Currency badCurrency;
   18.23 +    
   18.24 +    /**
   18.25 +     * A currency that is valid for the Convertor.
   18.26 +     */
   18.27 +    private final Currency currencyA;
   18.28 +
   18.29 +    /**
   18.30 +     * A currency that is valid for the Convertor.
   18.31 +     */
   18.32 +    private final Currency currencyB;
   18.33 +    
   18.34 +    
   18.35 +    /**
   18.36 +     * Construct a new InvalidConversionException with the specified message.
   18.37 +     * 
   18.38 +     * @param msg the message for getMessage.
   18.39 +     * @param bad the currency that is not valid.
   18.40 +     */
   18.41 +    public InvalidConversionException(final String   msg,
   18.42 +                                      final Currency bad)
   18.43 +    {
   18.44 +        this(msg, bad, null, null);
   18.45 +    }
   18.46 +
   18.47 +    /**
   18.48 +     * Construct a new InvalidConversionException with the specified message.
   18.49 +     * 
   18.50 +     * @param msg the message for getMessage.
   18.51 +     * @param bad the currency that is not valid.
   18.52 +     * @param a a valid currency.
   18.53 +     * @param b a valid currency.
   18.54 +     */
   18.55 +    public InvalidConversionException(final String   msg,
   18.56 +                                      final Currency bad,
   18.57 +                                      final Currency a,
   18.58 +                                      final Currency b)
   18.59 +    {
   18.60 +        super(msg);
   18.61 +
   18.62 +        badCurrency = bad;
   18.63 +        currencyA   = a;
   18.64 +        currencyB   = b;
   18.65 +    }
   18.66 +
   18.67 +    /**
   18.68 +     * Get the currency that is not valid.
   18.69 +     * 
   18.70 +     * @return the badCurrency
   18.71 +     */
   18.72 +    public Currency getBadCurrency()
   18.73 +    {
   18.74 +        return (badCurrency);
   18.75 +    }
   18.76 +
   18.77 +    /**
   18.78 +     * Get a currency that is valid.
   18.79 +     * 
   18.80 +     * @return the currencyA passed to the constructor.
   18.81 +     */
   18.82 +    public Currency getCurrencyA()
   18.83 +    {
   18.84 +        return (currencyA);
   18.85 +    }
   18.86 +
   18.87 +    /**
   18.88 +     * Get a currency that is valid.
   18.89 +     * 
   18.90 +     * @return the currencyB passed to the constructor.
   18.91 +     */
   18.92 +    public Currency getCurrencyB()
   18.93 +    {
   18.94 +        return (currencyB);
   18.95 +    }
   18.96 +
   18.97 +
   18.98 +}
   18.99 \ No newline at end of file
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/OnlineConvertor.java	Sat Oct 18 07:47:34 2008 +0200
    19.3 @@ -0,0 +1,175 @@
    19.4 +package org.apidesign.apifest08.currency;
    19.5 +
    19.6 +
    19.7 +import java.math.BigDecimal;
    19.8 +import java.util.Currency;
    19.9 +import java.util.Set;
   19.10 +
   19.11 +
   19.12 +/**
   19.13 + * A Convertor that looks up the exchange rate with each call to "convert".
   19.14 + *
   19.15 + * @author D'Arcy Smith
   19.16 + * @version 1.0
   19.17 + */
   19.18 +public class OnlineConvertor
   19.19 +    implements Convertor
   19.20 +{
   19.21 +    /**
   19.22 +     * The currency to convert from.
   19.23 +     */
   19.24 +    private final Currency currencyA;
   19.25 +
   19.26 +    /**
   19.27 +     * The currency to convert to.
   19.28 +     */
   19.29 +    private final Currency currencyB;
   19.30 +
   19.31 +    /**
   19.32 +     * Used to find the current exchange rate.
   19.33 +     */
   19.34 +    private final ExchangeRateFinder finder;
   19.35 +    
   19.36 +    /**
   19.37 +     * The convertor to perform the conversion.
   19.38 +     */
   19.39 +    private Convertor realConvertor;
   19.40 +
   19.41 +    /**
   19.42 +     * Constructs an OnlinConvertor with the specified currencies.
   19.43 +     * 
   19.44 +     * @param a the currency to convert from.
   19.45 +     * @param b the currency to convert to.
   19.46 +     * @param f the finder used to obtanin the current exchange rate.
   19.47 +     * @throws IllegalArgumentException if either a or b are null.
   19.48 +     */
   19.49 +    public OnlineConvertor(final Currency           a,
   19.50 +                           final Currency           b,
   19.51 +                           final ExchangeRateFinder f)
   19.52 +    {
   19.53 +        if(a == null)        
   19.54 +        {
   19.55 +            throw new IllegalArgumentException("a cannot be null");
   19.56 +        }
   19.57 +        
   19.58 +        if(b == null)
   19.59 +        {
   19.60 +            throw new IllegalArgumentException("b cannot be null");
   19.61 +        }
   19.62 +        
   19.63 +        if(f == null)
   19.64 +        {
   19.65 +            throw new IllegalArgumentException("f cannot be null");
   19.66 +        }
   19.67 +        
   19.68 +        currencyA     = a;
   19.69 +        currencyB     = b;
   19.70 +        finder        = f;
   19.71 +        realConvertor = lookupRate();
   19.72 +    }
   19.73 +
   19.74 +    /**
   19.75 +     * Convert an amount from one currency to another.  Before the conversion takes place 
   19.76 +     * the current exchange rate is looked up.
   19.77 +     *
   19.78 +     * @param from the currency to convert from.
   19.79 +     * @param to the currency to convert to.
   19.80 +     * @param amount the amount to convert.
   19.81 +     * @return the converted amount.
   19.82 +     * @throws IllegalArgumentException if any of the arguments are null.
   19.83 +     * @throws InvalidConversionException if either from or to are not equal to the currencies passed to the constructor.
   19.84 +     */
   19.85 +    public BigDecimal convert(final Currency   from, 
   19.86 +                              final Currency   to, 
   19.87 +                              final BigDecimal amount) 
   19.88 +        throws InvalidConversionException
   19.89 +    {
   19.90 +        final BigDecimal value;
   19.91 +        
   19.92 +        synchronized(this)
   19.93 +        {
   19.94 +            realConvertor = lookupRate();
   19.95 +            value         = realConvertor.convert(from, to, amount);
   19.96 +        }
   19.97 +        
   19.98 +        return (value);
   19.99 +    }
  19.100 +
  19.101 +    /**
  19.102 +     * Lookup the current exchange rate.
  19.103 +     * 
  19.104 +     * @return
  19.105 +     */
  19.106 +    private final Convertor lookupRate()
  19.107 +    {
  19.108 +        final Convertor convertor;
  19.109 +        final ExchangeRate rate;
  19.110 +
  19.111 +        rate      = finder.findRate(currencyA, currencyB);
  19.112 +        convertor = ConvertorFactory.getConvertor(rate.getCurrencyA(), rate.getRateAtoB(), rate.getCurrencyB(), rate.getRateBtoA());
  19.113 +
  19.114 +        return (convertor);
  19.115 +    }
  19.116 +
  19.117 +    /**
  19.118 +     * Check to see if converting between the two currencies is possible.
  19.119 +     * 
  19.120 +     * @param from the currency to convert from.
  19.121 +     * @param to the currency to convert to.
  19.122 +     * @return true if the conversion is possible.
  19.123 +     * @throws IllegalArgumentException if either from or to are null.
  19.124 +     */
  19.125 +    public boolean canConvert(final Currency from, 
  19.126 +                              final Currency to) 
  19.127 +    {
  19.128 +        if(from == null)
  19.129 +        {
  19.130 +            throw new IllegalArgumentException("from cannot be null");
  19.131 +        }
  19.132 +        
  19.133 +        if(to == null)
  19.134 +        {
  19.135 +            throw new IllegalArgumentException("to cannot be null");
  19.136 +        }
  19.137 +        
  19.138 +        return (realConvertor.canConvert(from, to));
  19.139 +    }
  19.140 +
  19.141 +    /**
  19.142 +     * Get the currencies that the convertor supports.
  19.143 +     * 
  19.144 +     * @return the supported currencies.
  19.145 +     */
  19.146 +    public Set<Currency> getCurrencies() 
  19.147 +    {
  19.148 +        return (realConvertor.getCurrencies());
  19.149 +    }
  19.150 +
  19.151 +    /**
  19.152 +     * Get the conversion rate between two currencies.  This does not lookup the current
  19.153 +     * conversion rate (it probably should, but given the way the contest works that might 
  19.154 +     * not be a good idea - if it were the real world way it might be a good idea).
  19.155 +     * 
  19.156 +     * @param from the currency to convert from.
  19.157 +     * @param to the currency to convert to.
  19.158 +     * @return the conversion rate between the two currencies.
  19.159 +     * @throws InvalidConversionException if canConvert would return false.
  19.160 +     * @throws IllegalArgumentException if either from or to are null.
  19.161 +     */
  19.162 +    public BigDecimal getConversionRate(final Currency from, 
  19.163 +                                        final Currency to) 
  19.164 +        throws InvalidConversionException 
  19.165 +    {        
  19.166 +        if(from == null)
  19.167 +        {
  19.168 +            throw new IllegalArgumentException("from cannot be null");
  19.169 +        }
  19.170 +        
  19.171 +        if(to == null)
  19.172 +        {
  19.173 +            throw new IllegalArgumentException("to cannot be null");
  19.174 +        }
  19.175 +        
  19.176 +        return (realConvertor.getConversionRate(from, to));
  19.177 +    }    
  19.178 +}
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/task4/solution04/src/org/apidesign/apifest08/currency/TimedConvertor.java	Sat Oct 18 07:47:34 2008 +0200
    20.3 @@ -0,0 +1,17 @@
    20.4 +package org.apidesign.apifest08.currency;
    20.5 +
    20.6 +import java.math.BigDecimal;
    20.7 +import java.util.Currency;
    20.8 +import java.util.Date;
    20.9 +
   20.10 +
   20.11 +public interface TimedConvertor
   20.12 +    extends Convertor
   20.13 +{
   20.14 +    BigDecimal convert(Currency   from, 
   20.15 +                       Currency   to, 
   20.16 +                       BigDecimal amount,
   20.17 +                       Date       date)
   20.18 +        throws InvalidConversionException;
   20.19 +    boolean canConvert(Currency from, Currency to, Date date);
   20.20 +}
   20.21 \ No newline at end of file
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/task4/solution04/test/org/apidesign/apifest08/test/Task1Test.java	Sat Oct 18 07:47:34 2008 +0200
    21.3 @@ -0,0 +1,223 @@
    21.4 +package org.apidesign.apifest08.test;
    21.5 +
    21.6 +
    21.7 +import java.math.BigDecimal;
    21.8 +import java.util.Currency;
    21.9 +import java.util.Set;
   21.10 +import junit.framework.TestCase;
   21.11 +import org.apidesign.apifest08.currency.Convertor;
   21.12 +import org.apidesign.apifest08.currency.ConvertorFactory;
   21.13 +import org.apidesign.apifest08.currency.InvalidConversionException;
   21.14 +
   21.15 +
   21.16 +/** Finish the Convertor API, and then write bodies of methods inside
   21.17 + * of this class to match the given tasks. To fullfil your task, use the
   21.18 + * API define in the <code>org.apidesign.apifest08.currency</code> package.
   21.19 + * Do not you reflection, or other hacks as your code
   21.20 + * shall run without any runtime permissions.
   21.21 + */
   21.22 +public class Task1Test extends TestCase {
   21.23 +    
   21.24 +    public final static Currency CZK;
   21.25 +    public final static Currency SKK;
   21.26 +    public final static Currency USD;
   21.27 +
   21.28 +    static
   21.29 +    {
   21.30 +        CZK = Currency.getInstance("CZK");
   21.31 +        SKK = Currency.getInstance("SKK");
   21.32 +        USD = Currency.getInstance("USD");
   21.33 +    }
   21.34 +    
   21.35 +    public Task1Test(String testName) {
   21.36 +        super(testName);
   21.37 +    }
   21.38 +
   21.39 +    @Override
   21.40 +    protected void setUp() throws Exception {
   21.41 +    }
   21.42 +
   21.43 +    @Override
   21.44 +    protected void tearDown() throws Exception {
   21.45 +    }
   21.46 +
   21.47 +    /** Create convertor that understands two currencies, CZK and
   21.48 +     *  USD. Make 1 USD == 17 CZK.
   21.49 +     *
   21.50 +     * Creation of the convertor shall not require subclassing of any class
   21.51 +     * or interface on the client side.
   21.52 +     *
   21.53 +     * @return prepared convertor ready for converting USD to CZK and CZK to USD
   21.54 +     */
   21.55 +    public static Convertor createCZKtoUSD()
   21.56 +    {
   21.57 +        return (ConvertorFactory.getConvertor("CZK", BigDecimal.valueOf(17.0),
   21.58 +                                              "USD", BigDecimal.valueOf(1)));
   21.59 +    }
   21.60 +
   21.61 +    /** Create convertor that understands two currencies, CZK and
   21.62 +     *  SKK. Make 100 SKK == 80 CZK.
   21.63 +     *
   21.64 +     * Creation of the convertor shall not require subclassing of any class
   21.65 +     * or interface on the client side.
   21.66 +     * 
   21.67 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
   21.68 +     */
   21.69 +    public static Convertor createSKKtoCZK()
   21.70 +    {
   21.71 +        return (ConvertorFactory.getConvertor(Currency.getInstance("SKK"), BigDecimal.valueOf(100),
   21.72 +                                              Currency.getInstance("CZK"), BigDecimal.valueOf(80)));
   21.73 +    }
   21.74 +    
   21.75 +    /** Use the convertor from <code>createCZKtoUSD</code> method and do few conversions
   21.76 +     * with it.
   21.77 +     */
   21.78 +    public void testCurrencyCZKUSD() throws Exception {
   21.79 +        Convertor  c = createCZKtoUSD();
   21.80 +        BigDecimal result;
   21.81 +        
   21.82 +        // convert $5 to CZK using c:
   21.83 +        // assertEquals("Result is 85 CZK");
   21.84 +        result = c.convert(USD, CZK, BigDecimal.valueOf(5));
   21.85 +        assertEquals(new BigDecimal("85.00"), result);
   21.86 +
   21.87 +        // convert $8 to CZK
   21.88 +        // assertEquals("Result is 136 CZK");
   21.89 +        result = c.convert(USD, CZK, BigDecimal.valueOf(8));
   21.90 +        assertEquals(new BigDecimal("136.00"), result);
   21.91 +
   21.92 +        // convert 1003CZK to USD
   21.93 +        // assertEquals("Result is 59 USD");
   21.94 +        result = c.convert(CZK, USD, BigDecimal.valueOf(1003));
   21.95 +        assertEquals(new BigDecimal("59.00"), result);
   21.96 +    }
   21.97 +
   21.98 +    /** Use the convertor from <code>createSKKtoCZK</code> method and do few conversions
   21.99 +     * with it.
  21.100 +     */
  21.101 +    public void testCurrencySKKCZK() throws Exception {
  21.102 +        Convertor c = createSKKtoCZK();
  21.103 +        BigDecimal result;
  21.104 +        
  21.105 +        // convert 16CZK using c:
  21.106 +        // assertEquals("Result is 20 SKK");
  21.107 +        result = c.convert(CZK, SKK, BigDecimal.valueOf(16));
  21.108 +        assertEquals(new BigDecimal("20.00"), result);
  21.109 +                        
  21.110 +        // convert 500SKK to CZK
  21.111 +        // assertEquals("Result is 400 CZK");
  21.112 +        result = c.convert(SKK, CZK, BigDecimal.valueOf(500));
  21.113 +        assertEquals(new BigDecimal("400.00"), result);
  21.114 +   }
  21.115 +
  21.116 +    /** 
  21.117 +     * Verify that the CZK to USD convertor knows nothing about SKK.
  21.118 +     */
  21.119 +    public void testCannotConvertToSKKwithCZKUSDConvertor() 
  21.120 +        throws Exception 
  21.121 +    {
  21.122 +        Convertor c = createCZKtoUSD();
  21.123 +        
  21.124 +        try
  21.125 +        {
  21.126 +            // convert $5 to SKK, the API shall say this is not possible
  21.127 +            c.convert(USD, SKK, BigDecimal.valueOf(5));
  21.128 +            fail("cannot use the CZKtoUSD converter to convert to SKK");
  21.129 +        }
  21.130 +        catch(InvalidConversionException ex)
  21.131 +        {       
  21.132 +            assertEquals("cannot convert to: SKK", ex.getMessage());
  21.133 +            assertEquals(SKK, ex.getBadCurrency());
  21.134 +            assertEquals(CZK, ex.getCurrencyA());
  21.135 +            assertEquals(USD, ex.getCurrencyB());
  21.136 +        }
  21.137 +
  21.138 +        try
  21.139 +        {
  21.140 +            // convert 500 SKK to CZK, the API shall say this is not possible
  21.141 +            c.convert(SKK, CZK, BigDecimal.valueOf(5));
  21.142 +            fail("cannot use the CZKtoUSD converter to convert from SKK");
  21.143 +        }
  21.144 +        catch(InvalidConversionException ex)
  21.145 +        {            
  21.146 +            assertEquals("cannot convert from: SKK", ex.getMessage());
  21.147 +            assertEquals(SKK, ex.getBadCurrency());
  21.148 +            assertEquals(CZK, ex.getCurrencyA());
  21.149 +            assertEquals(USD, ex.getCurrencyB());
  21.150 +        }
  21.151 +    }
  21.152 +    
  21.153 +    /** 
  21.154 +     * Verify that the CZK to SKK convertor knows nothing about USD.
  21.155 +     */
  21.156 +    public void testCannotConvertToUSDwithSKKCZKConvertor() 
  21.157 +        throws Exception 
  21.158 +    {
  21.159 +        Convertor c = createSKKtoCZK();
  21.160 +        
  21.161 +        try
  21.162 +        {
  21.163 +            // convert $5 to SKK, the API shall say this is not possible
  21.164 +            c.convert(USD, SKK, BigDecimal.valueOf(5));
  21.165 +            fail("cannot use the CZKtoUSD converter to convert to SKK");
  21.166 +        }
  21.167 +        catch(InvalidConversionException ex)
  21.168 +        {       
  21.169 +            assertEquals("cannot convert from: USD", ex.getMessage());
  21.170 +            assertEquals(USD, ex.getBadCurrency());
  21.171 +            assertEquals(SKK, ex.getCurrencyA());
  21.172 +            assertEquals(CZK, ex.getCurrencyB());
  21.173 +        }
  21.174 +
  21.175 +        try
  21.176 +        {
  21.177 +            // convert 500 CZK to USD, the API shall say this is not possible
  21.178 +            c.convert(CZK, USD, BigDecimal.valueOf(500));
  21.179 +            fail("cannot use the CZKtoUSD converter to convert from SKK");
  21.180 +        }
  21.181 +        catch(InvalidConversionException ex)
  21.182 +        {            
  21.183 +            assertEquals("cannot convert to: USD", ex.getMessage());
  21.184 +            assertEquals(USD, ex.getBadCurrency());
  21.185 +            assertEquals(SKK, ex.getCurrencyA());
  21.186 +            assertEquals(CZK, ex.getCurrencyB());
  21.187 +        }
  21.188 +    }
  21.189 +    
  21.190 +    public void testGetCurrencies()
  21.191 +    {
  21.192 +        Convertor           c;
  21.193 +        Set<Currency> currencies;
  21.194 +        
  21.195 +        c          = createSKKtoCZK();
  21.196 +        currencies = c.getCurrencies();        
  21.197 +        assertEquals(2, currencies.size());
  21.198 +        assertTrue(currencies.contains(Currency.getInstance("SKK")));
  21.199 +        assertTrue(currencies.contains(Currency.getInstance("CZK")));
  21.200 +
  21.201 +        c          = createCZKtoUSD();
  21.202 +        currencies = c.getCurrencies();        
  21.203 +        assertEquals(2, currencies.size());
  21.204 +        assertTrue(currencies.contains(Currency.getInstance("USD")));
  21.205 +        assertTrue(currencies.contains(Currency.getInstance("CZK")));
  21.206 +    }
  21.207 +    
  21.208 +    public void testGetConverstionRate()
  21.209 +        throws InvalidConversionException
  21.210 +    {
  21.211 +        Convertor c;
  21.212 +       
  21.213 +        c = createSKKtoCZK();
  21.214 +        assertEquals(1.0,  c.getConversionRate(Currency.getInstance("CZK"), Currency.getInstance("CZK")).doubleValue());
  21.215 +        assertEquals(1.0,  c.getConversionRate(Currency.getInstance("SKK"), Currency.getInstance("SKK")).doubleValue());
  21.216 +        assertEquals(0.80, c.getConversionRate(Currency.getInstance("SKK"), Currency.getInstance("CZK")).doubleValue());
  21.217 +        assertEquals(1.25, c.getConversionRate(Currency.getInstance("CZK"), Currency.getInstance("SKK")).doubleValue());
  21.218 +
  21.219 +        c = createCZKtoUSD();
  21.220 +        assertEquals(1.0,      c.getConversionRate(Currency.getInstance("CZK"), Currency.getInstance("CZK")).doubleValue());
  21.221 +        assertEquals(1.0,      c.getConversionRate(Currency.getInstance("USD"), Currency.getInstance("USD")).doubleValue());
  21.222 +        assertEquals(1.0/17.0, c.getConversionRate(Currency.getInstance("CZK"), Currency.getInstance("USD")).doubleValue(), 0.00000000000000001);
  21.223 +        assertEquals(17.0,     c.getConversionRate(Currency.getInstance("USD"), Currency.getInstance("CZK")).doubleValue(), 0.00000000000000001);
  21.224 +    }
  21.225 +}
  21.226 +
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/task4/solution04/test/org/apidesign/apifest08/test/Task2Test.java	Sat Oct 18 07:47:34 2008 +0200
    22.3 @@ -0,0 +1,179 @@
    22.4 +package org.apidesign.apifest08.test;
    22.5 +
    22.6 +import java.math.BigDecimal;
    22.7 +import java.util.Currency;
    22.8 +import java.util.Set;
    22.9 +import junit.framework.TestCase;
   22.10 +import org.apidesign.apifest08.currency.Convertor;
   22.11 +import org.apidesign.apifest08.currency.ConvertorFactory;
   22.12 +import org.apidesign.apifest08.currency.InvalidConversionException;
   22.13 +
   22.14 +
   22.15 +/** There are many currencies around the world and many banks manipulate
   22.16 + * with more than one or two at the same time. As banks are usually the
   22.17 + * best paying clients, which is true even in case of your Convertor API,
   22.18 + * it is reasonable to listen to their requests.
   22.19 + * <p>
   22.20 + * The quest for today is to enhance your existing convertor API to hold
   22.21 + * information about many currencies and allow conversions between any of them.
   22.22 + * Also, as conversion rates for diferent currencies usually arise from various
   22.23 + * bank departments, there is another important need. There is a need to
   22.24 + * compose two convertors into one by merging all the information about
   22.25 + * currencies they know about.
   22.26 + */
   22.27 +public class Task2Test extends TestCase
   22.28 +{
   22.29 +    private final static Currency CZK;
   22.30 +    private final static Currency SKK;
   22.31 +    private final static Currency USD;
   22.32 +
   22.33 +    static
   22.34 +    {
   22.35 +        CZK = Currency.getInstance("CZK");
   22.36 +        SKK = Currency.getInstance("SKK");
   22.37 +        USD = Currency.getInstance("USD");
   22.38 +    }
   22.39 +
   22.40 +    public Task2Test(String testName)
   22.41 +    {
   22.42 +        super(testName);
   22.43 +    }
   22.44 +
   22.45 +    @Override
   22.46 +    protected void setUp() 
   22.47 +        throws Exception
   22.48 +    {
   22.49 +    }
   22.50 +
   22.51 +    @Override
   22.52 +    protected void tearDown() 
   22.53 +        throws Exception
   22.54 +    {
   22.55 +    }
   22.56 +
   22.57 +    // As in Task1Test, keep in mind, that there are three parts
   22.58 +    // of the whole system:
   22.59 +    // 1. there is someone who knows the current exchange rate
   22.60 +    // 2. there is someone who wants to do the conversion
   22.61 +    // 3. there is the API between 1. and 2. which allows them to communicate
   22.62 +    // 
   22.63 +    // Please backward compatibly enhance your existing API to support following
   22.64 +    // usecases:
   22.65 +    //
   22.66 +    
   22.67 +    /** Create convertor that understands two currencies, CZK and
   22.68 +     *  SKK. Make 100 SKK == 75 CZK. This is method for the group of users that
   22.69 +     *  knows the exchange rate, and needs to use the API to create objects
   22.70 +     *  with the exchange rate. Anyone shall be ready to call this method without
   22.71 +     *  any other method being called previously. The API itself shall know
   22.72 +     *  nothing about any rates, before this method is called.
   22.73 +     */
   22.74 +    public static Convertor createTripleConvertor() {
   22.75 +        // Rates: 1USD = 15CZK
   22.76 +        // Rates: 1USD = 20SKK
   22.77 +        // Rates: 75CZK = 100SKK
   22.78 +        Convertor c = ConvertorFactory.mergeConvertors(
   22.79 +            ConvertorFactory.getConvertor(USD, BigDecimal.ONE, CZK, BigDecimal.valueOf(15.00)),
   22.80 +            ConvertorFactory.getConvertor(USD, BigDecimal.ONE, SKK, BigDecimal.valueOf(20.00))
   22.81 +        );
   22.82 +        
   22.83 +        return c;
   22.84 +    }
   22.85 +
   22.86 +    /** Define convertor that understands three currencies. Use it.
   22.87 +     */
   22.88 +    public void testConvertorForUSDandCZKandSKK() throws Exception {
   22.89 +        Convertor c = createTripleConvertor();
   22.90 +
   22.91 +        // convert $5 to CZK using c:
   22.92 +        // assertEquals("Result is 75 CZK");
   22.93 +        assertEquals(new BigDecimal("75.00"), c.convert(USD, CZK, BigDecimal.valueOf(5.00)));
   22.94 +
   22.95 +        // convert $5 to SKK using c:
   22.96 +        // assertEquals("Result is 100 SKK");
   22.97 +        assertEquals(new BigDecimal("100.00"), c.convert(USD, SKK, BigDecimal.valueOf(5.00)));
   22.98 +
   22.99 +        // convert 200SKK to CZK using c:
  22.100 +        // assertEquals("Result is 150 CZK");
  22.101 +        assertEquals(new BigDecimal("150.00"), c.convert(SKK, CZK, BigDecimal.valueOf(200.00)));
  22.102 +
  22.103 +        // convert 200SKK to USK using c:
  22.104 +        // assertEquals("Result is 10 USD");
  22.105 +        assertEquals(new BigDecimal("10.00"), c.convert(SKK, USD, BigDecimal.valueOf(200.00)));
  22.106 +    }
  22.107 +
  22.108 +    /** Merge all currency rates of convertor 1 with convertor 2.
  22.109 +     * Implement this using your API, preferably this method just delegates
  22.110 +     * into some API method which does the actual work, without requiring
  22.111 +     * API clients to code anything complex.
  22.112 +     */
  22.113 +    public static Convertor merge(Convertor one, Convertor two) {
  22.114 +        return ConvertorFactory.mergeConvertors(one, two);
  22.115 +    }
  22.116 +
  22.117 +    /** Join the convertors from previous task, Task1Test and show that it
  22.118 +     * can be used to do reasonable conversions.
  22.119 +     */
  22.120 +    public void testConvertorComposition() throws Exception {
  22.121 +        Convertor c = merge(
  22.122 +            Task1Test.createCZKtoUSD(),
  22.123 +            Task1Test.createSKKtoCZK()
  22.124 +        );
  22.125 +
  22.126 +        // convert $5 to CZK using c:
  22.127 +        // assertEquals("Result is 85 CZK");
  22.128 +        assertEquals(new BigDecimal("85.00"), c.convert(USD, CZK, BigDecimal.valueOf(5.00)));
  22.129 +
  22.130 +        // convert $8 to CZK using c:
  22.131 +        // assertEquals("Result is 136 CZK");
  22.132 +        assertEquals(new BigDecimal("136.00"), c.convert(USD, CZK, BigDecimal.valueOf(8.00)));
  22.133 +
  22.134 +        // convert 1003CZK to USD using c:
  22.135 +        // assertEquals("Result is 59 USD");
  22.136 +        assertEquals(new BigDecimal("59.00"), c.convert(CZK, USD, BigDecimal.valueOf(1003.00)));
  22.137 +
  22.138 +        // convert 16CZK using c:
  22.139 +        // assertEquals("Result is 20 SKK");
  22.140 +        assertEquals(new BigDecimal("20.00"), c.convert(CZK, SKK, BigDecimal.valueOf(16.00)));
  22.141 +
  22.142 +        // convert 500SKK to CZK using c:
  22.143 +        // assertEquals("Result is 400 CZK");
  22.144 +        assertEquals(new BigDecimal("400.00"), c.convert(SKK, CZK, BigDecimal.valueOf(500.00)));
  22.145 +    }
  22.146 +    
  22.147 +    public void testGetCurrencies()
  22.148 +    {
  22.149 +        Convertor c = merge(
  22.150 +            Task1Test.createCZKtoUSD(),
  22.151 +            Task1Test.createSKKtoCZK()
  22.152 +        );
  22.153 +        Set<Currency> currencies;
  22.154 +        
  22.155 +        currencies = c.getCurrencies();        
  22.156 +        assertEquals(3, currencies.size());
  22.157 +        assertTrue(currencies.contains(Currency.getInstance("SKK")));
  22.158 +        assertTrue(currencies.contains(Currency.getInstance("CZK")));
  22.159 +        assertTrue(currencies.contains(Currency.getInstance("USD")));
  22.160 +    }
  22.161 +    
  22.162 +    public void testGetConverstionRate()
  22.163 +        throws InvalidConversionException
  22.164 +    {
  22.165 +        Convertor c = merge(
  22.166 +            Task1Test.createCZKtoUSD(),
  22.167 +            Task1Test.createSKKtoCZK()
  22.168 +        );
  22.169 +
  22.170 +        assertEquals(1.0,        c.getConversionRate(USD, USD).doubleValue(), 0.0000000000000001);
  22.171 +        assertEquals(17.0,       c.getConversionRate(USD, CZK).doubleValue(), 0.0000000000000001);
  22.172 +        assertEquals(21.25,      c.getConversionRate(USD, SKK).doubleValue(), 0.0000000000000001);
  22.173 +
  22.174 +        assertEquals(1.0 / 17.0, c.getConversionRate(CZK, USD).doubleValue(), 0.0000000000000001);
  22.175 +        assertEquals(1.0,        c.getConversionRate(CZK, CZK).doubleValue(), 0.0000000000000001);
  22.176 +        assertEquals(1.25,       c.getConversionRate(CZK, SKK).doubleValue(), 0.0000000000000001);
  22.177 +
  22.178 +        assertEquals(0.04705882352941176, c.getConversionRate(SKK, USD).doubleValue(), 0.0000000000000001);
  22.179 +        assertEquals(0.8,                 c.getConversionRate(SKK, CZK).doubleValue(), 0.0000000000000001);
  22.180 +        assertEquals(1.0,                 c.getConversionRate(SKK, SKK).doubleValue(), 0.0000000000000001);
  22.181 +    }
  22.182 +}
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/task4/solution04/test/org/apidesign/apifest08/test/Task3Test.java	Sat Oct 18 07:47:34 2008 +0200
    23.3 @@ -0,0 +1,145 @@
    23.4 +package org.apidesign.apifest08.test;
    23.5 +
    23.6 +import java.math.BigDecimal;
    23.7 +import java.util.Currency;
    23.8 +import junit.framework.TestCase;
    23.9 +import org.apidesign.apifest08.currency.Convertor;
   23.10 +import org.apidesign.apifest08.currency.InvalidConversionException;
   23.11 +import org.apidesign.apifest08.currency.OnlineConvertor;
   23.12 +
   23.13 +/** The exchange rates are not always the same. They are changing. Day by day,
   23.14 + * hour by hour, minute by minute. For every bank it is important to always
   23.15 + * have the actual exchange rate available in the system. That is why let's
   23.16 + * create a pluggable convertor that will always have up to date value of its
   23.17 + * exchange rate.
   23.18 + * <p>
   23.19 + * The quest for today is to allow 3rd party developer to write a convertor
   23.20 + * that adjusts its exchange rate everytime it is queried. This convertor is
   23.21 + * written by independent vendor, the vendor knows only your Convertor API,
   23.22 + * he does not know how the whole system looks and how the convertor is supposed
   23.23 + * to be used.
   23.24 + */
   23.25 +public class Task3Test 
   23.26 +    extends TestCase
   23.27 +{
   23.28 +    private final static Currency CZK;
   23.29 +    private final static Currency SKK;
   23.30 +    private final static Currency USD;
   23.31 +
   23.32 +    static
   23.33 +    {
   23.34 +        CZK = Currency.getInstance("CZK");
   23.35 +        SKK = Currency.getInstance("SKK");
   23.36 +        USD = Currency.getInstance("USD");
   23.37 +    }
   23.38 +    
   23.39 +    public Task3Test(String testName)
   23.40 +    {
   23.41 +        super(testName);
   23.42 +    }
   23.43 +
   23.44 +    @Override
   23.45 +    protected void setUp() throws Exception
   23.46 +    {
   23.47 +    }
   23.48 +
   23.49 +    @Override
   23.50 +    protected void tearDown() throws Exception
   23.51 +    {
   23.52 +    }
   23.53 +
   23.54 +    // Backward compatibly enhance your existing API to support following
   23.55 +    // usecases:
   23.56 +    //
   23.57 +
   23.58 +
   23.59 +    /** Without knowing anything about the surrounding system, write an
   23.60 +     * implementation of convertor that will return different rates everytime
   23.61 +     * it is queried. Convert USD to CZK and vice versa. Start with the rate of
   23.62 +     * 1USD = 16CZK and adjust it in favor of CZK by 0.01 CZK with every query.
   23.63 +     * As soon as you reach 1USD = 15CZK adjust it by 0.01 CZK in favor of USD
   23.64 +     * until you reach 1USD = 16CZK
   23.65 +     *
   23.66 +     * @return new instance of "online" USD and CZK convertor starting with rate 1USD = 16CZK
   23.67 +     * @throws InvalidConversionException 
   23.68 +     */
   23.69 +    public static Convertor createOnlineCZKUSDConvertor() 
   23.70 +        throws InvalidConversionException
   23.71 +    {
   23.72 +        final Convertor convertor;
   23.73 +        
   23.74 +        convertor = new OnlineConvertor(USD,
   23.75 +                                        CZK,
   23.76 +                                        new TestExchangeRateFinder(new BigDecimal("15.00"), 
   23.77 +                                                                   new BigDecimal("16.00"),
   23.78 +                                                                   new BigDecimal("16.00"),
   23.79 +                                                                   new BigDecimal("0.01"),
   23.80 +                                                                   new BigDecimal("-0.01")));
   23.81 +
   23.82 +        // initial rate: 1USD = 16CZK
   23.83 +        // 2nd query 1USD = 15.99CZK
   23.84 +        // 3rd query 1USD = 15.98CZK
   23.85 +        // until 1USD = 15.00CZK
   23.86 +        // then 1USD = 15.01CZK
   23.87 +        // then 1USD = 15.02CZK
   23.88 +        // and so on and on up to 1USD = 16CZK
   23.89 +        // and then another round to 15, etc.
   23.90 +        return convertor;
   23.91 +    }
   23.92 +
   23.93 +    public void testFewQueriesForOnlineConvertor() 
   23.94 +        throws InvalidConversionException
   23.95 +    {
   23.96 +        Convertor c = createOnlineCZKUSDConvertor();
   23.97 +        doFewQueriesForOnlineConvertor(c);
   23.98 +    }
   23.99 +
  23.100 +    static void doFewQueriesForOnlineConvertor(Convertor c) 
  23.101 +        throws InvalidConversionException            
  23.102 +    {
  23.103 +        BigDecimal amount;
  23.104 +
  23.105 +        // convert $5 to CZK using c:
  23.106 +        //assertEquals("Result is 80 CZK");
  23.107 +        amount = c.convert(USD, CZK, new BigDecimal("5.00"));
  23.108 +        assertEquals(new BigDecimal("80.00"), amount);
  23.109 +
  23.110 +        // convert $8 to CZK using c:
  23.111 +        //assertEquals("Result is 127.92 CZK");
  23.112 +        amount = c.convert(USD, CZK, new BigDecimal("8.00"));
  23.113 +        assertEquals(new BigDecimal("127.92"), amount);
  23.114 +
  23.115 +        // convert $1 to CZK using c:
  23.116 +        //assertEquals("Result is 15.98 CZK");
  23.117 +        amount = c.convert(USD, CZK, new BigDecimal("1.00"));
  23.118 +        assertEquals(new BigDecimal("15.98"), amount);
  23.119 +
  23.120 +        // convert 15.97CZK to USD using c:
  23.121 +        //assertEquals("Result is 1$");
  23.122 +        amount = c.convert(CZK, USD, new BigDecimal("15.97"));
  23.123 +        assertEquals(new BigDecimal("1.00"), amount);
  23.124 +    }
  23.125 +
  23.126 +    /** Join the convertors and show they behave sane.
  23.127 +     */
  23.128 +    public void testOnlineConvertorComposition() throws Exception {
  23.129 +        BigDecimal amount;
  23.130 +        
  23.131 +        Convertor c = Task2Test.merge(
  23.132 +            createOnlineCZKUSDConvertor(),
  23.133 +            Task1Test.createSKKtoCZK()
  23.134 +        );
  23.135 +
  23.136 +        // convert 16CZK to SKK using c:
  23.137 +        // assertEquals("Result is 20 SKK");
  23.138 +        amount = c.convert(CZK, SKK, new BigDecimal("16.00"));
  23.139 +        assertEquals(new BigDecimal("20.00"), amount);
  23.140 +        
  23.141 +        // convert 500SKK to CZK using c:
  23.142 +        // assertEquals("Result is 400 CZK");
  23.143 +        amount = c.convert(SKK, CZK, new BigDecimal("500.00"));
  23.144 +        assertEquals(new BigDecimal("400.00"), amount);
  23.145 +
  23.146 +        doFewQueriesForOnlineConvertor(c);
  23.147 +    }
  23.148 +}
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/task4/solution04/test/org/apidesign/apifest08/test/Task4Test.java	Sat Oct 18 07:47:34 2008 +0200
    24.3 @@ -0,0 +1,237 @@
    24.4 +package org.apidesign.apifest08.test;
    24.5 +
    24.6 +import java.math.BigDecimal;
    24.7 +import java.util.Calendar;
    24.8 +import java.util.Date;
    24.9 +import java.util.TimeZone;
   24.10 +import junit.framework.TestCase;
   24.11 +import org.apidesign.apifest08.currency.Convertor;
   24.12 +import org.apidesign.apifest08.currency.ConvertorFactory;
   24.13 +import org.apidesign.apifest08.currency.ExchangeRate;
   24.14 +import org.apidesign.apifest08.currency.InvalidConversionException;
   24.15 +import org.apidesign.apifest08.currency.TimedConvertor;
   24.16 +
   24.17 +/** The exchange rates are not always the same. They are changing. However
   24.18 + * as in order to predict the future, one needs to understand own past. That is
   24.19 + * why it is important to know the exchange rate as it was at any time during
   24.20 + * the past.
   24.21 + * <p>
   24.22 + * Today's quest is to enhance the convertor API to deal with dates.
   24.23 + * One shall be able to convert a currency at any date. Each currencies rate shall
   24.24 + * be associated with a range between two Date objects. In order
   24.25 + * to keep compatibility with old API that knew nothing about dates, the
   24.26 + * rates associated then are applicable "for eternity". Any use of existing
   24.27 + * convert methods that do not accept a Date argument, uses the current
   24.28 + * System.currentTimeMillis() as default date.
   24.29 + */
   24.30 +public class Task4Test extends TestCase {
   24.31 +    public Task4Test(String testName) {
   24.32 +        super(testName);
   24.33 +    }
   24.34 +
   24.35 +    private Calendar gmtCalendar;
   24.36 +    
   24.37 +    @Override
   24.38 +    protected void setUp() throws Exception {
   24.39 +        gmtCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
   24.40 +    }
   24.41 +
   24.42 +    @Override
   24.43 +    protected void tearDown() throws Exception {
   24.44 +    }
   24.45 +
   24.46 +    // Backward compatibly enhance your existing API to support following
   24.47 +    // usecases:
   24.48 +    //
   24.49 +
   24.50 +    /** Takes a convertor with any rates associated and creates new convertor
   24.51 +     * that returns the same values as the old one for time between from to till.
   24.52 +     * Otherwise it returns no results. This is just a helper method that
   24.53 +     * shall call some real one in the API.
   24.54 +     * 
   24.55 +     * @param old existing convertor
   24.56 +     * @param from initial date (inclusive)
   24.57 +     * @param till final date (exclusive)
   24.58 +     * @return new convertor
   24.59 +     */
   24.60 +    public static Convertor limitTo(Convertor old, Date from, Date till) {
   24.61 +        final Convertor convertor;
   24.62 +
   24.63 +        convertor = ConvertorFactory.getConvertor(from, till, old);
   24.64 +        
   24.65 +        return convertor;
   24.66 +    }
   24.67 +
   24.68 +
   24.69 +    public void testCompositionOfLimitedConvertors() throws Exception {
   24.70 +
   24.71 +        gmtCalendar.set(2008, Calendar.OCTOBER, 1, 0, 0, 0);
   24.72 +        Date d1 = gmtCalendar.getTime(); // 2008-10-01 0:00 GMT
   24.73 +        gmtCalendar.set(2008, Calendar.OCTOBER, 2, 0, 0, 0);
   24.74 +        Date d2 = gmtCalendar.getTime(); // 2008-10-02 0:00 GMT
   24.75 +        gmtCalendar.set(2008, Calendar.OCTOBER, 3, 0, 0, 0);
   24.76 +        Date d3 = gmtCalendar.getTime(); // 2008-10-03 0:00 GMT
   24.77 +        
   24.78 +        Convertor c = Task2Test.merge(
   24.79 +            limitTo(Task1Test.createCZKtoUSD(), d1, d2),
   24.80 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
   24.81 +        );
   24.82 +
   24.83 +        Date date;
   24.84 +        BigDecimal amount;
   24.85 +
   24.86 +        // convert $5 to CZK using c:
   24.87 +        // cannot convert as no rate is applicable to current date
   24.88 +        try
   24.89 +        {
   24.90 +            c.convert(Task1Test.USD, Task1Test.CZK, new BigDecimal("5.00"));
   24.91 +            fail("test A");
   24.92 +        }
   24.93 +        catch(final InvalidConversionException ex)
   24.94 +        {
   24.95 +        }
   24.96 +
   24.97 +        // convert $8 to CZK using c:
   24.98 +        // cannot convert as no rate is applicable to current date
   24.99 +        try
  24.100 +        {
  24.101 +            c.convert(Task1Test.USD, Task1Test.CZK, new BigDecimal("8.00"));
  24.102 +            fail("test B");
  24.103 +        }
  24.104 +        catch(final InvalidConversionException ex)
  24.105 +        {
  24.106 +        }
  24.107 +
  24.108 +        // convert 1003CZK to USD using c:
  24.109 +        // cannot convert as no rate is applicable to current date
  24.110 +        try
  24.111 +        {
  24.112 +            c.convert(Task1Test.CZK, Task1Test.USD, new BigDecimal("1003.00"));
  24.113 +            fail("test C");
  24.114 +        }
  24.115 +        catch(final InvalidConversionException ex)
  24.116 +        {
  24.117 +        }
  24.118 +
  24.119 +        // convert 16CZK using c:
  24.120 +        // cannot convert as no rate is applicable to current date
  24.121 +        try
  24.122 +        {
  24.123 +            c.convert(Task1Test.CZK, Task1Test.USD, new BigDecimal("16.00"));
  24.124 +            fail("test D");
  24.125 +        }
  24.126 +        catch(final InvalidConversionException ex)
  24.127 +        {
  24.128 +        }
  24.129 +
  24.130 +        // convert 500SKK to CZK using c:
  24.131 +        // cannot convert as no rate is applicable to current date
  24.132 +        try
  24.133 +        {
  24.134 +            c.convert(Task1Test.SKK, Task1Test.CZK, new BigDecimal("500.00"));
  24.135 +            fail("test C");
  24.136 +        }
  24.137 +        catch(final InvalidConversionException ex)
  24.138 +        {
  24.139 +        }
  24.140 +
  24.141 +        // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
  24.142 +        // assertEquals("Result is 85 CZK");
  24.143 +        gmtCalendar.set(2008, Calendar.OCTOBER, 1, 6, 0, 0);
  24.144 +        date = gmtCalendar.getTime();
  24.145 +        amount = ((TimedConvertor)c).convert(Task1Test.USD, Task1Test.CZK, new BigDecimal("5.00"), date);
  24.146 +        assertEquals(new BigDecimal("85.00"), amount);
  24.147 +
  24.148 +        // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
  24.149 +        // assertEquals("Result is 136 CZK");
  24.150 +        gmtCalendar.set(2008, Calendar.OCTOBER, 1, 6, 0, 0);
  24.151 +        date = gmtCalendar.getTime();
  24.152 +        amount = ((TimedConvertor)c).convert(Task1Test.USD, Task1Test.CZK, new BigDecimal("8.00"), date);
  24.153 +        assertEquals(new BigDecimal("136.00"), amount);
  24.154 +
  24.155 +        // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
  24.156 +        // assertEquals("Result is 59 USD");
  24.157 +        gmtCalendar.set(2008, Calendar.OCTOBER, 1, 6, 0, 0);
  24.158 +        date = gmtCalendar.getTime();
  24.159 +        amount = ((TimedConvertor)c).convert(Task1Test.CZK, Task1Test.USD, new BigDecimal("1003.00"), date);
  24.160 +        assertEquals(new BigDecimal("59.00"), amount);
  24.161 +
  24.162 +        // convert 16CZK using c at 2008-10-02 9:00 GMT:
  24.163 +        // assertEquals("Result is 20 SKK");
  24.164 +        gmtCalendar.set(2008, Calendar.OCTOBER, 2, 9, 0, 0);
  24.165 +        date = gmtCalendar.getTime();
  24.166 +        amount = ((TimedConvertor)c).convert(Task1Test.CZK, Task1Test.SKK, new BigDecimal("16.00"), date);
  24.167 +        assertEquals(new BigDecimal("20.00"), amount);
  24.168 +
  24.169 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  24.170 +        // assertEquals("Result is 400 CZK");
  24.171 +        gmtCalendar.set(2008, Calendar.OCTOBER, 2, 9, 0, 0);
  24.172 +        date = gmtCalendar.getTime();
  24.173 +        amount = ((TimedConvertor)c).convert(Task1Test.SKK, Task1Test.CZK, new BigDecimal("500.00"), date);
  24.174 +        assertEquals(new BigDecimal("400.00"), amount);
  24.175 +
  24.176 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  24.177 +        // cannot convert as no rate is applicable to current date
  24.178 +        try
  24.179 +        {
  24.180 +            gmtCalendar.set(2008, Calendar.OCTOBER, 1, 6, 0, 0);
  24.181 +            date = gmtCalendar.getTime();
  24.182 +            ((TimedConvertor)c).convert(Task1Test.SKK, Task1Test.CZK, new BigDecimal("500.00"), date);
  24.183 +            fail("test D");
  24.184 +        }
  24.185 +        catch(final InvalidConversionException ex)
  24.186 +        {
  24.187 +        }
  24.188 +    }
  24.189 +
  24.190 +    /** Create convertor that understands two currencies, CZK and
  24.191 +     *  SKK. Make 100 SKK == 90 CZK.
  24.192 +     *
  24.193 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
  24.194 +     */
  24.195 +    public static Convertor createSKKtoCZK2()
  24.196 +    {
  24.197 +        final ExchangeRate rate;
  24.198 +        final Convertor    convertor;
  24.199 +        
  24.200 +        rate = ExchangeRate.getExchangeRate(Task1Test.SKK,
  24.201 +                                            Task1Test.CZK,
  24.202 +                                            new BigDecimal("100.00"),
  24.203 +                                            new BigDecimal("90.00"));
  24.204 +        convertor = ConvertorFactory.getConvertor(rate);
  24.205 +
  24.206 +        return (convertor);
  24.207 +    }
  24.208 +
  24.209 +    public void testDateConvetorWithTwoDifferentRates() throws Exception {
  24.210 +        gmtCalendar.set(2008, Calendar.OCTOBER, 1, 0, 0, 0);
  24.211 +        Date d1 = gmtCalendar.getTime(); // 2008-10-01 0:00 GMT
  24.212 +        gmtCalendar.set(2008, Calendar.OCTOBER, 2, 0, 0, 0);
  24.213 +        Date d2 = gmtCalendar.getTime(); // 2008-10-02 0:00 GMT
  24.214 +        gmtCalendar.set(2008, Calendar.OCTOBER, 3, 0, 0, 0);
  24.215 +        Date d3 = gmtCalendar.getTime(); // 2008-10-03 0:00 GMT
  24.216 +
  24.217 +        Convertor c = Task2Test.merge(
  24.218 +            limitTo(createSKKtoCZK2(), d1, d2),
  24.219 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
  24.220 +        );
  24.221 +
  24.222 +        Date date;
  24.223 +        BigDecimal amount;
  24.224 +        
  24.225 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  24.226 +        // assertEquals("Result is 400 CZK");
  24.227 +        gmtCalendar.set(2008, Calendar.OCTOBER, 2, 6, 0, 0);
  24.228 +        date = gmtCalendar.getTime();
  24.229 +        amount = ((TimedConvertor)c).convert(Task1Test.SKK, Task1Test.CZK, new BigDecimal("500.00"), date);
  24.230 +        assertEquals(new BigDecimal("400.00"), amount);
  24.231 +
  24.232 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  24.233 +        // assertEquals("Result is 450 CZK");
  24.234 +        gmtCalendar.set(2008, Calendar.OCTOBER, 1, 6, 0, 0);
  24.235 +        date = gmtCalendar.getTime();
  24.236 +        amount = ((TimedConvertor)c).convert(Task1Test.SKK, Task1Test.CZK, new BigDecimal("500.00"), date);
  24.237 +        assertEquals(new BigDecimal("450.00"), amount);
  24.238 +    }
  24.239 +
  24.240 +}
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/task4/solution04/test/org/apidesign/apifest08/test/TestExchangeRateFinder.java	Sat Oct 18 07:47:34 2008 +0200
    25.3 @@ -0,0 +1,61 @@
    25.4 +package org.apidesign.apifest08.test;
    25.5 +
    25.6 +
    25.7 +import java.math.BigDecimal;
    25.8 +import java.util.Currency;
    25.9 +import org.apidesign.apifest08.currency.ExchangeRate;
   25.10 +import org.apidesign.apifest08.currency.ExchangeRateFinder;
   25.11 +
   25.12 +
   25.13 +class TestExchangeRateFinder
   25.14 +    implements ExchangeRateFinder
   25.15 +{
   25.16 +    private final BigDecimal min;
   25.17 +    private final BigDecimal max;
   25.18 +    private final BigDecimal stepUp;
   25.19 +    private final BigDecimal stepDown;
   25.20 +    private BigDecimal step;
   25.21 +    private BigDecimal rate;
   25.22 +    private boolean firstCall;
   25.23 +
   25.24 +    TestExchangeRateFinder(final BigDecimal mn,
   25.25 +                           final BigDecimal mx,
   25.26 +                           final BigDecimal start,
   25.27 +                           final BigDecimal up,
   25.28 +                           final BigDecimal down)
   25.29 +    {
   25.30 +        min       = mn;
   25.31 +        max       = mx;
   25.32 +        rate      = start;
   25.33 +        stepUp    = up;
   25.34 +        stepDown  = down;
   25.35 +        firstCall = true;
   25.36 +    }
   25.37 +    
   25.38 +    public ExchangeRate findRate(Currency a, Currency b) 
   25.39 +    {
   25.40 +        final ExchangeRate value;
   25.41 +
   25.42 +        if(rate.equals(max))
   25.43 +        {
   25.44 +            step = stepDown;
   25.45 +        }
   25.46 +        else if(rate.equals(min))
   25.47 +        {
   25.48 +            step = stepUp;
   25.49 +        }
   25.50 +
   25.51 +        value = new ExchangeRate(a, b, BigDecimal.ONE, rate);
   25.52 +
   25.53 +        if(firstCall)
   25.54 +        {
   25.55 +            firstCall = false;
   25.56 +        }
   25.57 +        else
   25.58 +        {
   25.59 +            rate = rate.add(step);
   25.60 +        }
   25.61 +
   25.62 +        return (value);
   25.63 +    }
   25.64 +}
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/task4/solution06/build.xml	Sat Oct 18 07:47:34 2008 +0200
    26.3 @@ -0,0 +1,69 @@
    26.4 +<?xml version="1.0" encoding="UTF-8"?>
    26.5 +<!-- You may freely edit this file. See commented blocks below for -->
    26.6 +<!-- some examples of how to customize the build. -->
    26.7 +<!-- (If you delete it and reopen the project it will be recreated.) -->
    26.8 +<project name="currency" default="default" basedir=".">
    26.9 +    <description>Builds, tests, and runs the project.</description>
   26.10 +    <import file="nbproject/build-impl.xml"/>
   26.11 +    <!--
   26.12 +
   26.13 +    There exist several targets which are by default empty and which can be 
   26.14 +    used for execution of your tasks. These targets are usually executed 
   26.15 +    before and after some main targets. They are: 
   26.16 +
   26.17 +      -pre-init:                 called before initialization of project properties
   26.18 +      -post-init:                called after initialization of project properties
   26.19 +      -pre-compile:              called before javac compilation
   26.20 +      -post-compile:             called after javac compilation
   26.21 +      -pre-compile-single:       called before javac compilation of single file
   26.22 +      -post-compile-single:      called after javac compilation of single file
   26.23 +      -pre-compile-test:         called before javac compilation of JUnit tests
   26.24 +      -post-compile-test:        called after javac compilation of JUnit tests
   26.25 +      -pre-compile-test-single:  called before javac compilation of single JUnit test
   26.26 +      -post-compile-test-single: called after javac compilation of single JUunit test
   26.27 +      -pre-jar:                  called before JAR building
   26.28 +      -post-jar:                 called after JAR building
   26.29 +      -post-clean:               called after cleaning build products
   26.30 +
   26.31 +    (Targets beginning with '-' are not intended to be called on their own.)
   26.32 +
   26.33 +    Example of inserting an obfuscator after compilation could look like this:
   26.34 +
   26.35 +        <target name="-post-compile">
   26.36 +            <obfuscate>
   26.37 +                <fileset dir="${build.classes.dir}"/>
   26.38 +            </obfuscate>
   26.39 +        </target>
   26.40 +
   26.41 +    For list of available properties check the imported 
   26.42 +    nbproject/build-impl.xml file. 
   26.43 +
   26.44 +
   26.45 +    Another way to customize the build is by overriding existing main targets.
   26.46 +    The targets of interest are: 
   26.47 +
   26.48 +      -init-macrodef-javac:     defines macro for javac compilation
   26.49 +      -init-macrodef-junit:     defines macro for junit execution
   26.50 +      -init-macrodef-debug:     defines macro for class debugging
   26.51 +      -init-macrodef-java:      defines macro for class execution
   26.52 +      -do-jar-with-manifest:    JAR building (if you are using a manifest)
   26.53 +      -do-jar-without-manifest: JAR building (if you are not using a manifest)
   26.54 +      run:                      execution of project 
   26.55 +      -javadoc-build:           Javadoc generation
   26.56 +      test-report:              JUnit report generation
   26.57 +
   26.58 +    An example of overriding the target for project execution could look like this:
   26.59 +
   26.60 +        <target name="run" depends="currency-impl.jar">
   26.61 +            <exec dir="bin" executable="launcher.exe">
   26.62 +                <arg file="${dist.jar}"/>
   26.63 +            </exec>
   26.64 +        </target>
   26.65 +
   26.66 +    Notice that the overridden target depends on the jar target and not only on 
   26.67 +    the compile target as the regular run target does. Again, for a list of available 
   26.68 +    properties which you can use, check the target you are overriding in the
   26.69 +    nbproject/build-impl.xml file. 
   26.70 +
   26.71 +    -->
   26.72 +</project>
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/task4/solution06/nbproject/build-impl.xml	Sat Oct 18 07:47:34 2008 +0200
    27.3 @@ -0,0 +1,642 @@
    27.4 +<?xml version="1.0" encoding="UTF-8"?>
    27.5 +<!--
    27.6 +*** GENERATED FROM project.xml - DO NOT EDIT  ***
    27.7 +***         EDIT ../build.xml INSTEAD         ***
    27.8 +
    27.9 +For the purpose of easier reading the script
   27.10 +is divided into following sections:
   27.11 +
   27.12 +  - initialization
   27.13 +  - compilation
   27.14 +  - jar
   27.15 +  - execution
   27.16 +  - debugging
   27.17 +  - javadoc
   27.18 +  - junit compilation
   27.19 +  - junit execution
   27.20 +  - junit debugging
   27.21 +  - applet
   27.22 +  - cleanup
   27.23 +
   27.24 +        -->
   27.25 +<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="Currency_Convertor_Solution_06-impl">
   27.26 +    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
   27.27 +    <!-- 
   27.28 +                ======================
   27.29 +                INITIALIZATION SECTION 
   27.30 +                ======================
   27.31 +            -->
   27.32 +    <target name="-pre-init">
   27.33 +        <!-- Empty placeholder for easier customization. -->
   27.34 +        <!-- You can override this target in the ../build.xml file. -->
   27.35 +    </target>
   27.36 +    <target depends="-pre-init" name="-init-private">
   27.37 +        <property file="nbproject/private/config.properties"/>
   27.38 +        <property file="nbproject/private/configs/${config}.properties"/>
   27.39 +        <property file="nbproject/private/private.properties"/>
   27.40 +    </target>
   27.41 +    <target depends="-pre-init,-init-private" name="-init-user">
   27.42 +        <property file="${user.properties.file}"/>
   27.43 +        <!-- The two properties below are usually overridden -->
   27.44 +        <!-- by the active platform. Just a fallback. -->
   27.45 +        <property name="default.javac.source" value="1.4"/>
   27.46 +        <property name="default.javac.target" value="1.4"/>
   27.47 +    </target>
   27.48 +    <target depends="-pre-init,-init-private,-init-user" name="-init-project">
   27.49 +        <property file="nbproject/configs/${config}.properties"/>
   27.50 +        <property file="nbproject/project.properties"/>
   27.51 +    </target>
   27.52 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
   27.53 +        <available file="${manifest.file}" property="manifest.available"/>
   27.54 +        <condition property="manifest.available+main.class">
   27.55 +            <and>
   27.56 +                <isset property="manifest.available"/>
   27.57 +                <isset property="main.class"/>
   27.58 +                <not>
   27.59 +                    <equals arg1="${main.class}" arg2="" trim="true"/>
   27.60 +                </not>
   27.61 +            </and>
   27.62 +        </condition>
   27.63 +        <condition property="manifest.available+main.class+mkdist.available">
   27.64 +            <and>
   27.65 +                <istrue value="${manifest.available+main.class}"/>
   27.66 +                <isset property="libs.CopyLibs.classpath"/>
   27.67 +            </and>
   27.68 +        </condition>
   27.69 +        <condition property="have.tests">
   27.70 +            <or>
   27.71 +                <available file="${test.src.dir}"/>
   27.72 +            </or>
   27.73 +        </condition>
   27.74 +        <condition property="have.sources">
   27.75 +            <or>
   27.76 +                <available file="${src.dir}"/>
   27.77 +            </or>
   27.78 +        </condition>
   27.79 +        <condition property="netbeans.home+have.tests">
   27.80 +            <and>
   27.81 +                <isset property="netbeans.home"/>
   27.82 +                <isset property="have.tests"/>
   27.83 +            </and>
   27.84 +        </condition>
   27.85 +        <condition property="no.javadoc.preview">
   27.86 +            <and>
   27.87 +                <isset property="javadoc.preview"/>
   27.88 +                <isfalse value="${javadoc.preview}"/>
   27.89 +            </and>
   27.90 +        </condition>
   27.91 +        <property name="run.jvmargs" value=""/>
   27.92 +        <property name="javac.compilerargs" value=""/>
   27.93 +        <property name="work.dir" value="${basedir}"/>
   27.94 +        <condition property="no.deps">
   27.95 +            <and>
   27.96 +                <istrue value="${no.dependencies}"/>
   27.97 +            </and>
   27.98 +        </condition>
   27.99 +        <property name="javac.debug" value="true"/>
  27.100 +        <property name="javadoc.preview" value="true"/>
  27.101 +        <property name="application.args" value=""/>
  27.102 +        <property name="source.encoding" value="${file.encoding}"/>
  27.103 +        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
  27.104 +            <and>
  27.105 +                <isset property="javadoc.encoding"/>
  27.106 +                <not>
  27.107 +                    <equals arg1="${javadoc.encoding}" arg2=""/>
  27.108 +                </not>
  27.109 +            </and>
  27.110 +        </condition>
  27.111 +        <property name="javadoc.encoding.used" value="${source.encoding}"/>
  27.112 +        <property name="includes" value="**"/>
  27.113 +        <property name="excludes" value=""/>
  27.114 +        <property name="do.depend" value="false"/>
  27.115 +        <condition property="do.depend.true">
  27.116 +            <istrue value="${do.depend}"/>
  27.117 +        </condition>
  27.118 +        <condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
  27.119 +            <and>
  27.120 +                <isset property="jaxws.endorsed.dir"/>
  27.121 +                <available file="nbproject/jaxws-build.xml"/>
  27.122 +            </and>
  27.123 +        </condition>
  27.124 +    </target>
  27.125 +    <target name="-post-init">
  27.126 +        <!-- Empty placeholder for easier customization. -->
  27.127 +        <!-- You can override this target in the ../build.xml file. -->
  27.128 +    </target>
  27.129 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
  27.130 +        <fail unless="src.dir">Must set src.dir</fail>
  27.131 +        <fail unless="test.src.dir">Must set test.src.dir</fail>
  27.132 +        <fail unless="build.dir">Must set build.dir</fail>
  27.133 +        <fail unless="dist.dir">Must set dist.dir</fail>
  27.134 +        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
  27.135 +        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
  27.136 +        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
  27.137 +        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
  27.138 +        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
  27.139 +        <fail unless="dist.jar">Must set dist.jar</fail>
  27.140 +    </target>
  27.141 +    <target name="-init-macrodef-property">
  27.142 +        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
  27.143 +            <attribute name="name"/>
  27.144 +            <attribute name="value"/>
  27.145 +            <sequential>
  27.146 +                <property name="@{name}" value="${@{value}}"/>
  27.147 +            </sequential>
  27.148 +        </macrodef>
  27.149 +    </target>
  27.150 +    <target name="-init-macrodef-javac">
  27.151 +        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
  27.152 +            <attribute default="${src.dir}" name="srcdir"/>
  27.153 +            <attribute default="${build.classes.dir}" name="destdir"/>
  27.154 +            <attribute default="${javac.classpath}" name="classpath"/>
  27.155 +            <attribute default="${includes}" name="includes"/>
  27.156 +            <attribute default="${excludes}" name="excludes"/>
  27.157 +            <attribute default="${javac.debug}" name="debug"/>
  27.158 +            <attribute default="" name="sourcepath"/>
  27.159 +            <element name="customize" optional="true"/>
  27.160 +            <sequential>
  27.161 +                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
  27.162 +                    <classpath>
  27.163 +                        <path path="@{classpath}"/>
  27.164 +                    </classpath>
  27.165 +                    <compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
  27.166 +                    <customize/>
  27.167 +                </javac>
  27.168 +            </sequential>
  27.169 +        </macrodef>
  27.170 +        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
  27.171 +            <attribute default="${src.dir}" name="srcdir"/>
  27.172 +            <attribute default="${build.classes.dir}" name="destdir"/>
  27.173 +            <attribute default="${javac.classpath}" name="classpath"/>
  27.174 +            <sequential>
  27.175 +                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
  27.176 +                    <classpath>
  27.177 +                        <path path="@{classpath}"/>
  27.178 +                    </classpath>
  27.179 +                </depend>
  27.180 +            </sequential>
  27.181 +        </macrodef>
  27.182 +        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
  27.183 +            <attribute default="${build.classes.dir}" name="destdir"/>
  27.184 +            <sequential>
  27.185 +                <fail unless="javac.includes">Must set javac.includes</fail>
  27.186 +                <pathconvert pathsep="," property="javac.includes.binary">
  27.187 +                    <path>
  27.188 +                        <filelist dir="@{destdir}" files="${javac.includes}"/>
  27.189 +                    </path>
  27.190 +                    <globmapper from="*.java" to="*.class"/>
  27.191 +                </pathconvert>
  27.192 +                <delete>
  27.193 +                    <files includes="${javac.includes.binary}"/>
  27.194 +                </delete>
  27.195 +            </sequential>
  27.196 +        </macrodef>
  27.197 +    </target>
  27.198 +    <target name="-init-macrodef-junit">
  27.199 +        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
  27.200 +            <attribute default="${includes}" name="includes"/>
  27.201 +            <attribute default="${excludes}" name="excludes"/>
  27.202 +            <attribute default="**" name="testincludes"/>
  27.203 +            <sequential>
  27.204 +                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
  27.205 +                    <batchtest todir="${build.test.results.dir}">
  27.206 +                        <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
  27.207 +                            <filename name="@{testincludes}"/>
  27.208 +                        </fileset>
  27.209 +                    </batchtest>
  27.210 +                    <classpath>
  27.211 +                        <path path="${run.test.classpath}"/>
  27.212 +                    </classpath>
  27.213 +                    <syspropertyset>
  27.214 +                        <propertyref prefix="test-sys-prop."/>
  27.215 +                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
  27.216 +                    </syspropertyset>
  27.217 +                    <formatter type="brief" usefile="false"/>
  27.218 +                    <formatter type="xml"/>
  27.219 +                    <jvmarg line="${run.jvmargs}"/>
  27.220 +                </junit>
  27.221 +            </sequential>
  27.222 +        </macrodef>
  27.223 +    </target>
  27.224 +    <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
  27.225 +        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
  27.226 +            <attribute default="${main.class}" name="name"/>
  27.227 +            <attribute default="${debug.classpath}" name="classpath"/>
  27.228 +            <attribute default="" name="stopclassname"/>
  27.229 +            <sequential>
  27.230 +                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
  27.231 +                    <classpath>
  27.232 +                        <path path="@{classpath}"/>
  27.233 +                    </classpath>
  27.234 +                </nbjpdastart>
  27.235 +            </sequential>
  27.236 +        </macrodef>
  27.237 +        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
  27.238 +            <attribute default="${build.classes.dir}" name="dir"/>
  27.239 +            <sequential>
  27.240 +                <nbjpdareload>
  27.241 +                    <fileset dir="@{dir}" includes="${fix.classes}">
  27.242 +                        <include name="${fix.includes}*.class"/>
  27.243 +                    </fileset>
  27.244 +                </nbjpdareload>
  27.245 +            </sequential>
  27.246 +        </macrodef>
  27.247 +    </target>
  27.248 +    <target name="-init-debug-args">
  27.249 +        <property name="version-output" value="java version &quot;${ant.java.version}"/>
  27.250 +        <condition property="have-jdk-older-than-1.4">
  27.251 +            <or>
  27.252 +                <contains string="${version-output}" substring="java version &quot;1.0"/>
  27.253 +                <contains string="${version-output}" substring="java version &quot;1.1"/>
  27.254 +                <contains string="${version-output}" substring="java version &quot;1.2"/>
  27.255 +                <contains string="${version-output}" substring="java version &quot;1.3"/>
  27.256 +            </or>
  27.257 +        </condition>
  27.258 +        <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
  27.259 +            <istrue value="${have-jdk-older-than-1.4}"/>
  27.260 +        </condition>
  27.261 +        <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
  27.262 +            <os family="windows"/>
  27.263 +        </condition>
  27.264 +        <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
  27.265 +            <isset property="debug.transport"/>
  27.266 +        </condition>
  27.267 +    </target>
  27.268 +    <target depends="-init-debug-args" name="-init-macrodef-debug">
  27.269 +        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
  27.270 +            <attribute default="${main.class}" name="classname"/>
  27.271 +            <attribute default="${debug.classpath}" name="classpath"/>
  27.272 +            <element name="customize" optional="true"/>
  27.273 +            <sequential>
  27.274 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
  27.275 +                    <jvmarg line="${debug-args-line}"/>
  27.276 +                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
  27.277 +                    <jvmarg line="${run.jvmargs}"/>
  27.278 +                    <classpath>
  27.279 +                        <path path="@{classpath}"/>
  27.280 +                    </classpath>
  27.281 +                    <syspropertyset>
  27.282 +                        <propertyref prefix="run-sys-prop."/>
  27.283 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
  27.284 +                    </syspropertyset>
  27.285 +                    <customize/>
  27.286 +                </java>
  27.287 +            </sequential>
  27.288 +        </macrodef>
  27.289 +    </target>
  27.290 +    <target name="-init-macrodef-java">
  27.291 +        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
  27.292 +            <attribute default="${main.class}" name="classname"/>
  27.293 +            <element name="customize" optional="true"/>
  27.294 +            <sequential>
  27.295 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
  27.296 +                    <jvmarg line="${run.jvmargs}"/>
  27.297 +                    <classpath>
  27.298 +                        <path path="${run.classpath}"/>
  27.299 +                    </classpath>
  27.300 +                    <syspropertyset>
  27.301 +                        <propertyref prefix="run-sys-prop."/>
  27.302 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
  27.303 +                    </syspropertyset>
  27.304 +                    <customize/>
  27.305 +                </java>
  27.306 +            </sequential>
  27.307 +        </macrodef>
  27.308 +    </target>
  27.309 +    <target name="-init-presetdef-jar">
  27.310 +        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
  27.311 +            <jar compress="${jar.compress}" jarfile="${dist.jar}">
  27.312 +                <j2seproject1:fileset dir="${build.classes.dir}"/>
  27.313 +            </jar>
  27.314 +        </presetdef>
  27.315 +    </target>
  27.316 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
  27.317 +    <!--
  27.318 +                ===================
  27.319 +                COMPILATION SECTION
  27.320 +                ===================
  27.321 +            -->
  27.322 +    <target depends="init" name="deps-jar" unless="no.deps"/>
  27.323 +    <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
  27.324 +    <target depends="init" name="-check-automatic-build">
  27.325 +        <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
  27.326 +    </target>
  27.327 +    <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
  27.328 +        <antcall target="clean"/>
  27.329 +    </target>
  27.330 +    <target depends="init,deps-jar" name="-pre-pre-compile">
  27.331 +        <mkdir dir="${build.classes.dir}"/>
  27.332 +    </target>
  27.333 +    <target name="-pre-compile">
  27.334 +        <!-- Empty placeholder for easier customization. -->
  27.335 +        <!-- You can override this target in the ../build.xml file. -->
  27.336 +    </target>
  27.337 +    <target if="do.depend.true" name="-compile-depend">
  27.338 +        <j2seproject3:depend/>
  27.339 +    </target>
  27.340 +    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
  27.341 +        <j2seproject3:javac/>
  27.342 +        <copy todir="${build.classes.dir}">
  27.343 +            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  27.344 +        </copy>
  27.345 +    </target>
  27.346 +    <target name="-post-compile">
  27.347 +        <!-- Empty placeholder for easier customization. -->
  27.348 +        <!-- You can override this target in the ../build.xml file. -->
  27.349 +    </target>
  27.350 +    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
  27.351 +    <target name="-pre-compile-single">
  27.352 +        <!-- Empty placeholder for easier customization. -->
  27.353 +        <!-- You can override this target in the ../build.xml file. -->
  27.354 +    </target>
  27.355 +    <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
  27.356 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
  27.357 +        <j2seproject3:force-recompile/>
  27.358 +        <j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
  27.359 +    </target>
  27.360 +    <target name="-post-compile-single">
  27.361 +        <!-- Empty placeholder for easier customization. -->
  27.362 +        <!-- You can override this target in the ../build.xml file. -->
  27.363 +    </target>
  27.364 +    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
  27.365 +    <!--
  27.366 +                ====================
  27.367 +                JAR BUILDING SECTION
  27.368 +                ====================
  27.369 +            -->
  27.370 +    <target depends="init" name="-pre-pre-jar">
  27.371 +        <dirname file="${dist.jar}" property="dist.jar.dir"/>
  27.372 +        <mkdir dir="${dist.jar.dir}"/>
  27.373 +    </target>
  27.374 +    <target name="-pre-jar">
  27.375 +        <!-- Empty placeholder for easier customization. -->
  27.376 +        <!-- You can override this target in the ../build.xml file. -->
  27.377 +    </target>
  27.378 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
  27.379 +        <j2seproject1:jar/>
  27.380 +    </target>
  27.381 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
  27.382 +        <j2seproject1:jar manifest="${manifest.file}"/>
  27.383 +    </target>
  27.384 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
  27.385 +        <j2seproject1:jar manifest="${manifest.file}">
  27.386 +            <j2seproject1:manifest>
  27.387 +                <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
  27.388 +            </j2seproject1:manifest>
  27.389 +        </j2seproject1:jar>
  27.390 +        <echo>To run this application from the command line without Ant, try:</echo>
  27.391 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
  27.392 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
  27.393 +        <pathconvert property="run.classpath.with.dist.jar">
  27.394 +            <path path="${run.classpath}"/>
  27.395 +            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
  27.396 +        </pathconvert>
  27.397 +        <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
  27.398 +    </target>
  27.399 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
  27.400 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
  27.401 +        <pathconvert property="run.classpath.without.build.classes.dir">
  27.402 +            <path path="${run.classpath}"/>
  27.403 +            <map from="${build.classes.dir.resolved}" to=""/>
  27.404 +        </pathconvert>
  27.405 +        <pathconvert pathsep=" " property="jar.classpath">
  27.406 +            <path path="${run.classpath.without.build.classes.dir}"/>
  27.407 +            <chainedmapper>
  27.408 +                <flattenmapper/>
  27.409 +                <globmapper from="*" to="lib/*"/>
  27.410 +            </chainedmapper>
  27.411 +        </pathconvert>
  27.412 +        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
  27.413 +        <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
  27.414 +            <fileset dir="${build.classes.dir}"/>
  27.415 +            <manifest>
  27.416 +                <attribute name="Main-Class" value="${main.class}"/>
  27.417 +                <attribute name="Class-Path" value="${jar.classpath}"/>
  27.418 +            </manifest>
  27.419 +        </copylibs>
  27.420 +        <echo>To run this application from the command line without Ant, try:</echo>
  27.421 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
  27.422 +        <echo>java -jar "${dist.jar.resolved}"</echo>
  27.423 +    </target>
  27.424 +    <target name="-post-jar">
  27.425 +        <!-- Empty placeholder for easier customization. -->
  27.426 +        <!-- You can override this target in the ../build.xml file. -->
  27.427 +    </target>
  27.428 +    <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
  27.429 +    <!--
  27.430 +                =================
  27.431 +                EXECUTION SECTION
  27.432 +                =================
  27.433 +            -->
  27.434 +    <target depends="init,compile" description="Run a main class." name="run">
  27.435 +        <j2seproject1:java>
  27.436 +            <customize>
  27.437 +                <arg line="${application.args}"/>
  27.438 +            </customize>
  27.439 +        </j2seproject1:java>
  27.440 +    </target>
  27.441 +    <target name="-do-not-recompile">
  27.442 +        <property name="javac.includes.binary" value=""/>
  27.443 +    </target>
  27.444 +    <target depends="init,-do-not-recompile,compile-single" name="run-single">
  27.445 +        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
  27.446 +        <j2seproject1:java classname="${run.class}"/>
  27.447 +    </target>
  27.448 +    <!--
  27.449 +                =================
  27.450 +                DEBUGGING SECTION
  27.451 +                =================
  27.452 +            -->
  27.453 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger">
  27.454 +        <j2seproject1:nbjpdastart name="${debug.class}"/>
  27.455 +    </target>
  27.456 +    <target depends="init,compile" name="-debug-start-debuggee">
  27.457 +        <j2seproject3:debug>
  27.458 +            <customize>
  27.459 +                <arg line="${application.args}"/>
  27.460 +            </customize>
  27.461 +        </j2seproject3:debug>
  27.462 +    </target>
  27.463 +    <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
  27.464 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
  27.465 +        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
  27.466 +    </target>
  27.467 +    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
  27.468 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
  27.469 +        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
  27.470 +        <j2seproject3:debug classname="${debug.class}"/>
  27.471 +    </target>
  27.472 +    <target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
  27.473 +    <target depends="init" name="-pre-debug-fix">
  27.474 +        <fail unless="fix.includes">Must set fix.includes</fail>
  27.475 +        <property name="javac.includes" value="${fix.includes}.java"/>
  27.476 +    </target>
  27.477 +    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
  27.478 +        <j2seproject1:nbjpdareload/>
  27.479 +    </target>
  27.480 +    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
  27.481 +    <!--
  27.482 +                ===============
  27.483 +                JAVADOC SECTION
  27.484 +                ===============
  27.485 +            -->
  27.486 +    <target depends="init" name="-javadoc-build">
  27.487 +        <mkdir dir="${dist.javadoc.dir}"/>
  27.488 +        <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
  27.489 +            <classpath>
  27.490 +                <path path="${javac.classpath}"/>
  27.491 +            </classpath>
  27.492 +            <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
  27.493 +                <filename name="**/*.java"/>
  27.494 +            </fileset>
  27.495 +        </javadoc>
  27.496 +    </target>
  27.497 +    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
  27.498 +        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
  27.499 +    </target>
  27.500 +    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
  27.501 +    <!--
  27.502 +                =========================
  27.503 +                JUNIT COMPILATION SECTION
  27.504 +                =========================
  27.505 +            -->
  27.506 +    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
  27.507 +        <mkdir dir="${build.test.classes.dir}"/>
  27.508 +    </target>
  27.509 +    <target name="-pre-compile-test">
  27.510 +        <!-- Empty placeholder for easier customization. -->
  27.511 +        <!-- You can override this target in the ../build.xml file. -->
  27.512 +    </target>
  27.513 +    <target if="do.depend.true" name="-compile-test-depend">
  27.514 +        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
  27.515 +    </target>
  27.516 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
  27.517 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
  27.518 +        <copy todir="${build.test.classes.dir}">
  27.519 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  27.520 +        </copy>
  27.521 +    </target>
  27.522 +    <target name="-post-compile-test">
  27.523 +        <!-- Empty placeholder for easier customization. -->
  27.524 +        <!-- You can override this target in the ../build.xml file. -->
  27.525 +    </target>
  27.526 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
  27.527 +    <target name="-pre-compile-test-single">
  27.528 +        <!-- Empty placeholder for easier customization. -->
  27.529 +        <!-- You can override this target in the ../build.xml file. -->
  27.530 +    </target>
  27.531 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
  27.532 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
  27.533 +        <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
  27.534 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
  27.535 +        <copy todir="${build.test.classes.dir}">
  27.536 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  27.537 +        </copy>
  27.538 +    </target>
  27.539 +    <target name="-post-compile-test-single">
  27.540 +        <!-- Empty placeholder for easier customization. -->
  27.541 +        <!-- You can override this target in the ../build.xml file. -->
  27.542 +    </target>
  27.543 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
  27.544 +    <!--
  27.545 +                =======================
  27.546 +                JUNIT EXECUTION SECTION
  27.547 +                =======================
  27.548 +            -->
  27.549 +    <target depends="init" if="have.tests" name="-pre-test-run">
  27.550 +        <mkdir dir="${build.test.results.dir}"/>
  27.551 +    </target>
  27.552 +    <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
  27.553 +        <j2seproject3:junit testincludes="**/*Test.java"/>
  27.554 +    </target>
  27.555 +    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
  27.556 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
  27.557 +    </target>
  27.558 +    <target depends="init" if="have.tests" name="test-report"/>
  27.559 +    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
  27.560 +    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
  27.561 +    <target depends="init" if="have.tests" name="-pre-test-run-single">
  27.562 +        <mkdir dir="${build.test.results.dir}"/>
  27.563 +    </target>
  27.564 +    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
  27.565 +        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
  27.566 +        <j2seproject3:junit excludes="" includes="${test.includes}"/>
  27.567 +    </target>
  27.568 +    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
  27.569 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
  27.570 +    </target>
  27.571 +    <target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
  27.572 +    <!--
  27.573 +                =======================
  27.574 +                JUNIT DEBUGGING SECTION
  27.575 +                =======================
  27.576 +            -->
  27.577 +    <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
  27.578 +        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
  27.579 +        <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
  27.580 +        <delete file="${test.report.file}"/>
  27.581 +        <mkdir dir="${build.test.results.dir}"/>
  27.582 +        <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
  27.583 +            <customize>
  27.584 +                <syspropertyset>
  27.585 +                    <propertyref prefix="test-sys-prop."/>
  27.586 +                    <mapper from="test-sys-prop.*" to="*" type="glob"/>
  27.587 +                </syspropertyset>
  27.588 +                <arg value="${test.class}"/>
  27.589 +                <arg value="showoutput=true"/>
  27.590 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
  27.591 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
  27.592 +            </customize>
  27.593 +        </j2seproject3:debug>
  27.594 +    </target>
  27.595 +    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
  27.596 +        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
  27.597 +    </target>
  27.598 +    <target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
  27.599 +    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
  27.600 +        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
  27.601 +    </target>
  27.602 +    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
  27.603 +    <!--
  27.604 +                =========================
  27.605 +                APPLET EXECUTION SECTION
  27.606 +                =========================
  27.607 +            -->
  27.608 +    <target depends="init,compile-single" name="run-applet">
  27.609 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
  27.610 +        <j2seproject1:java classname="sun.applet.AppletViewer">
  27.611 +            <customize>
  27.612 +                <arg value="${applet.url}"/>
  27.613 +            </customize>
  27.614 +        </j2seproject1:java>
  27.615 +    </target>
  27.616 +    <!--
  27.617 +                =========================
  27.618 +                APPLET DEBUGGING  SECTION
  27.619 +                =========================
  27.620 +            -->
  27.621 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
  27.622 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
  27.623 +        <j2seproject3:debug classname="sun.applet.AppletViewer">
  27.624 +            <customize>
  27.625 +                <arg value="${applet.url}"/>
  27.626 +            </customize>
  27.627 +        </j2seproject3:debug>
  27.628 +    </target>
  27.629 +    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
  27.630 +    <!--
  27.631 +                ===============
  27.632 +                CLEANUP SECTION
  27.633 +                ===============
  27.634 +            -->
  27.635 +    <target depends="init" name="deps-clean" unless="no.deps"/>
  27.636 +    <target depends="init" name="-do-clean">
  27.637 +        <delete dir="${build.dir}"/>
  27.638 +        <delete dir="${dist.dir}"/>
  27.639 +    </target>
  27.640 +    <target name="-post-clean">
  27.641 +        <!-- Empty placeholder for easier customization. -->
  27.642 +        <!-- You can override this target in the ../build.xml file. -->
  27.643 +    </target>
  27.644 +    <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
  27.645 +</project>
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/task4/solution06/nbproject/genfiles.properties	Sat Oct 18 07:47:34 2008 +0200
    28.3 @@ -0,0 +1,8 @@
    28.4 +build.xml.data.CRC32=2ab820eb
    28.5 +build.xml.script.CRC32=58a52595
    28.6 +build.xml.stylesheet.CRC32=be360661
    28.7 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
    28.8 +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
    28.9 +nbproject/build-impl.xml.data.CRC32=ff801896
   28.10 +nbproject/build-impl.xml.script.CRC32=a0996c47
   28.11 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/task4/solution06/nbproject/project.properties	Sat Oct 18 07:47:34 2008 +0200
    29.3 @@ -0,0 +1,68 @@
    29.4 +application.title=currency
    29.5 +application.vendor=apidesign.org
    29.6 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8
    29.7 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
    29.8 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=default
    29.9 +build.classes.dir=${build.dir}/classes
   29.10 +build.classes.excludes=**/*.java,**/*.form
   29.11 +# This directory is removed when the project is cleaned:
   29.12 +build.dir=build
   29.13 +build.generated.dir=${build.dir}/generated
   29.14 +# Only compile against the classpath explicitly listed here:
   29.15 +build.sysclasspath=ignore
   29.16 +build.test.classes.dir=${build.dir}/test/classes
   29.17 +build.test.results.dir=${build.dir}/test/results
   29.18 +debug.classpath=\
   29.19 +    ${run.classpath}
   29.20 +debug.test.classpath=\
   29.21 +    ${run.test.classpath}
   29.22 +# This directory is removed when the project is cleaned:
   29.23 +dist.dir=dist
   29.24 +dist.jar=${dist.dir}/currency.jar
   29.25 +dist.javadoc.dir=${dist.dir}/javadoc
   29.26 +excludes=
   29.27 +file.reference.junit-4.4.jar=../../libs/junit-4.4.jar
   29.28 +file.reference.src-apifest08=..
   29.29 +includes=**
   29.30 +jar.compress=false
   29.31 +javac.classpath=
   29.32 +# Space-separated list of extra javac options
   29.33 +javac.compilerargs=
   29.34 +javac.deprecation=false
   29.35 +javac.source=1.5
   29.36 +javac.target=1.5
   29.37 +javac.test.classpath=\
   29.38 +    ${javac.classpath}:\
   29.39 +    ${build.classes.dir}:\
   29.40 +    ${file.reference.junit-4.4.jar}
   29.41 +javadoc.additionalparam=
   29.42 +javadoc.author=false
   29.43 +javadoc.encoding=
   29.44 +javadoc.noindex=false
   29.45 +javadoc.nonavbar=false
   29.46 +javadoc.notree=false
   29.47 +javadoc.private=false
   29.48 +javadoc.splitindex=true
   29.49 +javadoc.use=true
   29.50 +javadoc.version=false
   29.51 +javadoc.windowtitle=
   29.52 +jnlp.codebase.type=local
   29.53 +jnlp.codebase.url=file:/home/jarda/src/apifest08/currency/dist
   29.54 +jnlp.descriptor=application
   29.55 +jnlp.enabled=false
   29.56 +jnlp.offline-allowed=false
   29.57 +jnlp.signed=false
   29.58 +meta.inf.dir=${src.dir}/META-INF
   29.59 +platform.active=default_platform
   29.60 +run.classpath=\
   29.61 +    ${javac.classpath}:\
   29.62 +    ${build.classes.dir}
   29.63 +# Space-separated list of JVM arguments used when running the project
   29.64 +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
   29.65 +# or test-sys-prop.name=value to set system properties for unit tests):
   29.66 +run.jvmargs=
   29.67 +run.test.classpath=\
   29.68 +    ${javac.test.classpath}:\
   29.69 +    ${build.test.classes.dir}
   29.70 +src.dir=src
   29.71 +test.src.dir=test
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/task4/solution06/nbproject/project.xml	Sat Oct 18 07:47:34 2008 +0200
    30.3 @@ -0,0 +1,16 @@
    30.4 +<?xml version="1.0" encoding="UTF-8"?>
    30.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
    30.6 +    <type>org.netbeans.modules.java.j2seproject</type>
    30.7 +    <configuration>
    30.8 +        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
    30.9 +            <name>Currency Convertor Solution 06</name>
   30.10 +            <minimum-ant-version>1.6.5</minimum-ant-version>
   30.11 +            <source-roots>
   30.12 +                <root id="src.dir"/>
   30.13 +            </source-roots>
   30.14 +            <test-roots>
   30.15 +                <root id="test.src.dir"/>
   30.16 +            </test-roots>
   30.17 +        </data>
   30.18 +    </configuration>
   30.19 +</project>
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/task4/solution06/src/org/apidesign/apifest08/currency/Amount.java	Sat Oct 18 07:47:34 2008 +0200
    31.3 @@ -0,0 +1,81 @@
    31.4 +package org.apidesign.apifest08.currency;
    31.5 +
    31.6 +import static org.apidesign.apifest08.currency.Assert.notNull;
    31.7 +
    31.8 +import java.math.BigDecimal;
    31.9 +import java.math.RoundingMode;
   31.10 +import java.util.Currency;
   31.11 +
   31.12 +/**
   31.13 + * An amount representation. Amount is represented as composition of a value and 
   31.14 + * a currency.
   31.15 + */
   31.16 +public final class Amount {
   31.17 +	
   31.18 +	private final BigDecimal value;
   31.19 +	private final Currency currency;
   31.20 +	private final int scale;
   31.21 +	private final RoundingMode roundingMode;
   31.22 +	
   31.23 +	public static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_EVEN; 
   31.24 +	
   31.25 +	public Amount(final BigDecimal value, final Currency currency) {
   31.26 +		notNull(value, "value");
   31.27 +		notNull(currency, "currency");
   31.28 +		this.value = value;
   31.29 +		this.currency = currency;
   31.30 +		this.scale = currency.getDefaultFractionDigits();
   31.31 +		this.roundingMode = DEFAULT_ROUNDING;
   31.32 +	}
   31.33 +	
   31.34 +	public Amount(final BigDecimal value, final Currency currency, final RoundingMode roundingMode) {
   31.35 +		notNull(value, "value");
   31.36 +		notNull(currency, "currency");
   31.37 +		notNull(roundingMode, "roundingMode");
   31.38 +		
   31.39 +		this.value = value;
   31.40 +		this.currency = currency;
   31.41 +		this.scale = currency.getDefaultFractionDigits();
   31.42 +		this.roundingMode = roundingMode;
   31.43 +	}
   31.44 +	
   31.45 +	public Amount(final long value, final Currency currency) {
   31.46 +		this(BigDecimal.valueOf(value), currency);
   31.47 +	}
   31.48 +	
   31.49 +	public Amount(final String value, final Currency currency) {
   31.50 +		this(new BigDecimal(value), currency);
   31.51 +	}
   31.52 +	
   31.53 +	/**
   31.54 +	 * @return the value with scale of the associated currency and rounded by 
   31.55 +	 * the rounding mode. 
   31.56 +	 */
   31.57 +	public BigDecimal getValue() {
   31.58 +		return value.setScale(scale, roundingMode);
   31.59 +	}
   31.60 +	
   31.61 +	/**
   31.62 +	 * @return the raw (no explicit scale, no explicit rounding) value 
   31.63 +	 */
   31.64 +	public BigDecimal getRawValue() {
   31.65 +		return value;
   31.66 +	}
   31.67 +	
   31.68 +	public Currency getCurrency() {
   31.69 +		return currency;
   31.70 +	}
   31.71 +
   31.72 +	public int getScale() {
   31.73 +		return scale;
   31.74 +	}
   31.75 +
   31.76 +	public RoundingMode getRoundingMode() {
   31.77 +		return roundingMode;
   31.78 +	}
   31.79 +
   31.80 +	@Override
   31.81 +	public String toString() {
   31.82 +		return value + ",- " + currency.toString();
   31.83 +	}	
   31.84 +}
    32.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.2 +++ b/task4/solution06/src/org/apidesign/apifest08/currency/Assert.java	Sat Oct 18 07:47:34 2008 +0200
    32.3 @@ -0,0 +1,11 @@
    32.4 +package org.apidesign.apifest08.currency;
    32.5 +
    32.6 +public final class Assert {
    32.7 +	static void notNull(Object value, String argumentName) {
    32.8 +		if(value == null) {
    32.9 +			throw new IllegalArgumentException("The argument '" + argumentName + "' connot not be null");
   32.10 +		}
   32.11 +	}
   32.12 +}
   32.13 +
   32.14 +
    33.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.2 +++ b/task4/solution06/src/org/apidesign/apifest08/currency/ConversionException.java	Sat Oct 18 07:47:34 2008 +0200
    33.3 @@ -0,0 +1,26 @@
    33.4 +package org.apidesign.apifest08.currency;
    33.5 +
    33.6 +/**
    33.7 + * Indicates that a desired conversion cannot be performed.
    33.8 + */
    33.9 +public class ConversionException extends CurrencyException {
   33.10 +	
   33.11 +	private static final long serialVersionUID = 1L;
   33.12 +
   33.13 +	public ConversionException() {
   33.14 +		super();
   33.15 +	}
   33.16 +
   33.17 +	public ConversionException(String message, Throwable cause) {
   33.18 +		super(message, cause);		
   33.19 +	}
   33.20 +
   33.21 +	public ConversionException(String message) {
   33.22 +		super(message);
   33.23 +	}
   33.24 +
   33.25 +	public ConversionException(Throwable cause) {
   33.26 +		super(cause);
   33.27 +	}
   33.28 +	
   33.29 +}
    34.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.2 +++ b/task4/solution06/src/org/apidesign/apifest08/currency/Convertor.java	Sat Oct 18 07:47:34 2008 +0200
    34.3 @@ -0,0 +1,253 @@
    34.4 +package org.apidesign.apifest08.currency;
    34.5 +
    34.6 +import static org.apidesign.apifest08.currency.Assert.notNull;
    34.7 +
    34.8 +import java.math.BigDecimal;
    34.9 +import java.math.RoundingMode;
   34.10 +import java.util.ArrayList;
   34.11 +import java.util.Calendar;
   34.12 +import java.util.Currency;
   34.13 +import java.util.Date;
   34.14 +import java.util.GregorianCalendar;
   34.15 +import java.util.List;
   34.16 +import java.util.TimeZone;
   34.17 +
   34.18 +/**
   34.19 + * Currency covertor.
   34.20 + */
   34.21 +public final class Convertor {
   34.22 +	
   34.23 +	private List<ConvertorDelegate> convertorDelegates = new ArrayList<ConvertorDelegate>();
   34.24 +	
   34.25 +	
   34.26 +	/**
   34.27 +	 * Create new instance of the converter for the given currencies and its rate.
   34.28 +	 * 
   34.29 +	 * @param rateValue the rate between the first and the second currency
   34.30 +	 * @param currencyFirst the first currency
   34.31 +	 * @param currencySecond the second currency
   34.32 +	 */
   34.33 +	public Convertor(BigDecimal rateValue, Currency currencyFirst, Currency currencySecond) {
   34.34 +		notNull(currencyFirst, "currencyFirst");
   34.35 +		notNull(currencySecond, "currencySecond");		
   34.36 +		notNull(rateValue, "rateValue");	
   34.37 +		convertorDelegates.add(new ConvertorDelegate(new StaticRateProvider(rateValue), currencyFirst, currencySecond, null, null));
   34.38 +	}
   34.39 +	
   34.40 +	/**
   34.41 +	 * Create new instance of the converter for the given currencies and its rate. 
   34.42 +	 * A rate value is provided by {@link RateProvider}.
   34.43 +	 * 
   34.44 +	 * @param rateProvider the rate provider
   34.45 +	 * @param currencyFirst the first currency
   34.46 +	 * @param currencySecond the second currency
   34.47 +	 */
   34.48 +	public Convertor(RateProvider rateProvider, Currency currencyFirst, Currency currencySecond) {
   34.49 +		notNull(currencyFirst, "currencyFirst");
   34.50 +		notNull(currencySecond, "currencySecond");		
   34.51 +		notNull(rateProvider, "rateProvider");
   34.52 +		convertorDelegates.add(new ConvertorDelegate(rateProvider, currencyFirst, currencySecond, null, null));
   34.53 +	}
   34.54 +	
   34.55 +	/**
   34.56 +	 * Create new instance of the convertor. The associated rate(s) is timely limited
   34.57 +	 * by a given 'from' and 'till'. 
   34.58 +	 * @param convertor
   34.59 +	 * @param from
   34.60 +	 * @param till
   34.61 +	 */
   34.62 +	public Convertor(Convertor convertor, Date from, Date till) {
   34.63 +	    notNull(convertor, "convertor");
   34.64 +		notNull(from, "from");
   34.65 +		notNull(till, "till");				
   34.66 +		for(ConvertorDelegate delegate: convertor.convertorDelegates) {
   34.67 +			convertorDelegates.add(new ConvertorDelegate(delegate.rateProvider, delegate.first, delegate.second, from, till));
   34.68 +		}
   34.69 +	}
   34.70 +	
   34.71 +	/**
   34.72 +	 * Create new instance of the convertor from the given convertors. 
   34.73 +	 * @param convertors the convertors
   34.74 +	 */
   34.75 +	public Convertor(Convertor... convertors) {
   34.76 +		notNull(convertors, "convertors");
   34.77 +		if(convertors.length == 0) {
   34.78 +			throw new IllegalArgumentException("There must be at least one converter.");
   34.79 +		}
   34.80 +		
   34.81 +		for(Convertor convertor: convertors) {
   34.82 +			if(convertor != null) {
   34.83 +				for(ConvertorDelegate delegate: convertor.convertorDelegates) {
   34.84 +					convertorDelegates.add(new ConvertorDelegate(delegate.rateProvider, delegate.first, delegate.second, delegate.from, delegate.till));
   34.85 +				}
   34.86 +			}
   34.87 +		}
   34.88 +	}
   34.89 +    	
   34.90 +	/**
   34.91 +	 * Converts an amount value between the two currencies of this converter. The rate is taken 
   34.92 +	 * for current time.
   34.93 +	 *  
   34.94 +	 * @param amount an amount
   34.95 +	 * @param fromCurrency an amount currency
   34.96 +	 * @param toCurrency to a target currency
   34.97 +	 * @return a converted amount value
   34.98 +	 * 
   34.99 +	 * @throws ConversionException if the conversion fails
  34.100 +	 * @throws UnsupportedConversionException if the conversion between a given currencies is not supported.
  34.101 +	 */
  34.102 +	public Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency) throws ConversionException {
  34.103 +		return convert(amount, fromCurrency, toCurrency, new Date(System.currentTimeMillis()));
  34.104 +	}
  34.105 +
  34.106 +	/**
  34.107 +	 * Converts an amount value between the two currencies of this converter and its 
  34.108 +	 * associated rate for a given time.
  34.109 +	 *  
  34.110 +	 * @param amount an amount
  34.111 +	 * @param fromCurrency an amount currency
  34.112 +	 * @param toCurrency to a target currency
  34.113 +	 * @param time time
  34.114 +	 * @return a converted amount value
  34.115 +	 * 
  34.116 +	 * @throws ConversionException if the conversion fails
  34.117 +	 * @throws UnsupportedConversionException if the conversion between a given currencies and time is not supported.
  34.118 +	 */
  34.119 +	public Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency, Date time) throws ConversionException {
  34.120 +		notNull(amount, "amount");
  34.121 +		notNull(fromCurrency, "fromCurrency");
  34.122 +		notNull(toCurrency, "toCurrency");		
  34.123 +		notNull(time, "time");
  34.124 +		
  34.125 +		ConvertorDelegate appropriateDelegate = null;
  34.126 +		//try find an appropriate delegate for conversion
  34.127 +		for(ConvertorDelegate delegate : convertorDelegates) {
  34.128 +			if(delegate.isConversionSupported(fromCurrency, toCurrency) && delegate.isConversionSupported(time)) {				
  34.129 +				appropriateDelegate = delegate;				
  34.130 +			}
  34.131 +		}
  34.132 +		if(appropriateDelegate == null) {
  34.133 +			throw new UnsupportedConversionException(fromCurrency, toCurrency, time);
  34.134 +		}
  34.135 +		
  34.136 +		return appropriateDelegate.convert(amount, fromCurrency, toCurrency);
  34.137 +	}
  34.138 +	
  34.139 +	/**
  34.140 +	 * Internal delegate implements a logic for conversion between two currencies
  34.141 +	 * and vice versa. There could be time limitation for the associated rate.
  34.142 +	 * 
  34.143 +	 * @see #isConversionSupported(Currency, Currency)
  34.144 +	 */
  34.145 +	private static class ConvertorDelegate {
  34.146 +		
  34.147 +		private final Currency first;
  34.148 +		private final Currency second;
  34.149 +		private final RateProvider rateProvider;
  34.150 +		private final Date from;
  34.151 +		private final Date till;
  34.152 +		public static final BigDecimal one = new BigDecimal(1);
  34.153 +		
  34.154 +		
  34.155 +		private ConvertorDelegate(RateProvider rateProvider, Currency currencyFirst, Currency currencySecond, Date from, Date till) {
  34.156 +			this.rateProvider = rateProvider;
  34.157 +			this.first = currencyFirst;
  34.158 +			this.second = currencySecond;
  34.159 +			this.from = from;
  34.160 +			this.till = till;
  34.161 +		}
  34.162 +		
  34.163 +		private Amount convert(BigDecimal amount, Currency fromCurrency, Currency toCurrency) throws ConversionException {
  34.164 +			BigDecimal rateValue = getRateValue(fromCurrency, toCurrency);
  34.165 +			BigDecimal result = rateValue.multiply(amount);			
  34.166 +			return new Amount(result, toCurrency);	
  34.167 +		}
  34.168 +		
  34.169 +		private BigDecimal getRateValue(Currency fromCurrency, Currency toCurrency) {		
  34.170 +			
  34.171 +			BigDecimal retVal;
  34.172 +			
  34.173 +			if(first == fromCurrency) {
  34.174 +				BigDecimal rateValue = rateProvider.getRate();
  34.175 +				if(rateValue == null) {
  34.176 +					throw new NullPointerException("Rate cannot be null!");
  34.177 +				}
  34.178 +				retVal = rateValue;
  34.179 +			} else {	
  34.180 +				BigDecimal rateValue = rateProvider.getRate();
  34.181 +				if(rateValue == null) {
  34.182 +					throw new NullPointerException("Rate cannot be null!");
  34.183 +				}
  34.184 +				//reverse rate	
  34.185 +				retVal = one.divide(rateValue, 10 ,RoundingMode.HALF_UP);
  34.186 +			}
  34.187 +			
  34.188 +			return retVal;
  34.189 +		}
  34.190 +		
  34.191 +		/**
  34.192 +		 * @return <code>true</code> if the delegate is able to convert from the given currency
  34.193 +		 * to the given currency and vice versa otherwise <code>false</code>.
  34.194 +		 */
  34.195 +		private boolean isConversionSupported(Currency fromCurrency, Currency toCurrency) {
  34.196 +			return ((fromCurrency == first || fromCurrency == second) && (toCurrency == first || toCurrency == second));
  34.197 +		}
  34.198 +		
  34.199 +		/**
  34.200 +		 * @return <code>true</code> if the delegate is able to convert in a given
  34.201 +		 * time.
  34.202 +		 */
  34.203 +		private boolean isConversionSupported(Date time) {
  34.204 +			boolean retVal;
  34.205 +			if(this.from != null && this.till != null) {
  34.206 +				retVal = getDateInGMT(from).getTime() <= getDateInGMT(time).getTime() && getDateInGMT(till).getTime() >= getDateInGMT(time).getTime();  
  34.207 +		    } else {
  34.208 +				retVal = true; //delegate is applicable "for eternity"				
  34.209 +			}
  34.210 +			return retVal;
  34.211 +		}
  34.212 +		
  34.213 +		private Date getDateInGMT(Date currentDate) {
  34.214 +			TimeZone tz = TimeZone.getTimeZone("GMT");
  34.215 +			
  34.216 +			Calendar mbCal = new GregorianCalendar(tz);
  34.217 +			mbCal.setTimeInMillis(currentDate.getTime());
  34.218 +
  34.219 +			Calendar cal = Calendar.getInstance();
  34.220 +			cal.set(Calendar.YEAR, mbCal.get(Calendar.YEAR));
  34.221 +			cal.set(Calendar.MONTH, mbCal.get(Calendar.MONTH));
  34.222 +			cal.set(Calendar.DAY_OF_MONTH, mbCal.get(Calendar.DAY_OF_MONTH));
  34.223 +			cal.set(Calendar.HOUR_OF_DAY, mbCal.get(Calendar.HOUR_OF_DAY));
  34.224 +			cal.set(Calendar.MINUTE, mbCal.get(Calendar.MINUTE));
  34.225 +			cal.set(Calendar.SECOND, mbCal.get(Calendar.SECOND));
  34.226 +			cal.set(Calendar.MILLISECOND, mbCal.get(Calendar.MILLISECOND));
  34.227 +
  34.228 +			return cal.getTime();
  34.229 +		}
  34.230 +	}
  34.231 +	
  34.232 +	/**
  34.233 +	 * A rate provider. This class represents a way how could be "static" convertor
  34.234 +	 * extended in order converts according to current rate.
  34.235 +	 */
  34.236 +	public static abstract class RateProvider { 
  34.237 +		
  34.238 +		/**
  34.239 +		 * @return a rate between the from currency and the to currency associated with
  34.240 +		 * a given convertor.
  34.241 +		 */
  34.242 +		public abstract BigDecimal getRate();
  34.243 +	}
  34.244 +	
  34.245 +	private static class StaticRateProvider extends RateProvider{
  34.246 +		private final BigDecimal rateValue;
  34.247 +		
  34.248 +		private StaticRateProvider(BigDecimal rateValue){
  34.249 +			this.rateValue = rateValue;
  34.250 +		}
  34.251 +		
  34.252 +		public BigDecimal getRate() {
  34.253 +			return this.rateValue;
  34.254 +		}
  34.255 +	}
  34.256 +}
    35.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.2 +++ b/task4/solution06/src/org/apidesign/apifest08/currency/CurrencyException.java	Sat Oct 18 07:47:34 2008 +0200
    35.3 @@ -0,0 +1,25 @@
    35.4 +package org.apidesign.apifest08.currency;
    35.5 +
    35.6 +/**
    35.7 + * Top level runtime exception for 'currency' API.
    35.8 + */
    35.9 +public class CurrencyException extends RuntimeException{
   35.10 +
   35.11 +	private static final long serialVersionUID = 1L;
   35.12 +
   35.13 +	public CurrencyException() {
   35.14 +		super();		
   35.15 +	}
   35.16 +
   35.17 +	public CurrencyException(String message, Throwable cause) {
   35.18 +		super(message, cause);		
   35.19 +	}
   35.20 +
   35.21 +	public CurrencyException(String message) {
   35.22 +		super(message);		
   35.23 +	}
   35.24 +
   35.25 +	public CurrencyException(Throwable cause) {
   35.26 +		super(cause);		
   35.27 +	}
   35.28 +}
    36.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.2 +++ b/task4/solution06/src/org/apidesign/apifest08/currency/UnsupportedConversionException.java	Sat Oct 18 07:47:34 2008 +0200
    36.3 @@ -0,0 +1,34 @@
    36.4 +package org.apidesign.apifest08.currency;
    36.5 +
    36.6 +import java.util.Currency;
    36.7 +import java.util.Date;
    36.8 +
    36.9 +public final class UnsupportedConversionException extends ConversionException{
   36.10 +
   36.11 +	private static final long serialVersionUID = 1L; 
   36.12 +	
   36.13 +	private Currency from;
   36.14 +	private Currency to;
   36.15 +
   36.16 +	public UnsupportedConversionException(Currency from, Currency to) {
   36.17 +		super("Conversion from  the currency " + from + " to the currency " + to + " or vice versa in not supported.");
   36.18 +		this.from = from;
   36.19 +		this.to = to;
   36.20 +	}
   36.21 +	
   36.22 +	public UnsupportedConversionException(Currency from, Currency to, Date time) {
   36.23 +		super("Conversion from  the currency " + from + " to the currency " + to + " or vice versa in not supported for time " + time );
   36.24 +		this.from = from;
   36.25 +		this.to = to;
   36.26 +	}
   36.27 +
   36.28 +	public Currency getFrom() {
   36.29 +		return from;
   36.30 +	}
   36.31 +
   36.32 +	public Currency getTo() {
   36.33 +		return to;
   36.34 +	}
   36.35 +	
   36.36 +	
   36.37 +}
    37.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    37.2 +++ b/task4/solution06/test/org/apidesign/apifest08/test/Currencies.java	Sat Oct 18 07:47:34 2008 +0200
    37.3 @@ -0,0 +1,9 @@
    37.4 +package org.apidesign.apifest08.test;
    37.5 +
    37.6 +import java.util.Currency;
    37.7 +
    37.8 +public class Currencies {
    37.9 +	public static final Currency CZK = Currency.getInstance("CZK");
   37.10 +	public static final Currency SKK = Currency.getInstance("SKK");
   37.11 +	public static final Currency USD = Currency.getInstance("USD");
   37.12 +}
    38.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    38.2 +++ b/task4/solution06/test/org/apidesign/apifest08/test/Task1Test.java	Sat Oct 18 07:47:34 2008 +0200
    38.3 @@ -0,0 +1,136 @@
    38.4 +package org.apidesign.apifest08.test;
    38.5 +
    38.6 +import static org.apidesign.apifest08.test.Currencies.CZK;
    38.7 +import static org.apidesign.apifest08.test.Currencies.SKK;
    38.8 +import static org.apidesign.apifest08.test.Currencies.USD;
    38.9 +
   38.10 +import java.math.BigDecimal;
   38.11 +
   38.12 +import junit.framework.TestCase;
   38.13 +
   38.14 +import org.apidesign.apifest08.currency.Amount;
   38.15 +import org.apidesign.apifest08.currency.ConversionException;
   38.16 +import org.apidesign.apifest08.currency.Convertor;
   38.17 +import org.apidesign.apifest08.currency.UnsupportedConversionException;
   38.18 +
   38.19 +/** Finish the Convertor API, and then write bodies of methods inside
   38.20 + * of this class to match the given tasks. To fullfil your task, use the
   38.21 + * API define in the <code>org.apidesign.apifest08.currency</code> package.
   38.22 + * Do not you reflection, or other hacks as your code
   38.23 + * shall run without any runtime permissions.
   38.24 + */
   38.25 +public class Task1Test extends TestCase {
   38.26 +    public Task1Test(String testName) {
   38.27 +        super(testName);
   38.28 +    }
   38.29 +
   38.30 +    @Override
   38.31 +    protected void setUp() throws Exception {
   38.32 +    }
   38.33 +
   38.34 +    @Override
   38.35 +    protected void tearDown() throws Exception {
   38.36 +    }
   38.37 +
   38.38 +    /** Create convertor that understands two currencies, CZK and
   38.39 +     *  USD. Make 1 USD == 17 CZK.
   38.40 +     *
   38.41 +     * Creation of the convertor shall not require subclassing of any class
   38.42 +     * or interface on the client side.
   38.43 +     *
   38.44 +     * @return prepared convertor ready for converting USD to CZK and CZK to USD
   38.45 +     */
   38.46 +    public static Convertor createCZKtoUSD() {
   38.47 +        return new Convertor(new BigDecimal(17), USD, CZK);
   38.48 +    }
   38.49 +
   38.50 +    /** Create convertor that understands two currencies, CZK and
   38.51 +     *  SKK. Make 100 SKK == 80 CZK.
   38.52 +     *
   38.53 +     * Creation of the convertor shall not require subclassing of any class
   38.54 +     * or interface on the client side.
   38.55 +     * 
   38.56 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
   38.57 +     */
   38.58 +    public static Convertor createSKKtoCZK() {
   38.59 +    	return new Convertor(new BigDecimal("0.8"), SKK, CZK);
   38.60 +    }
   38.61 +    
   38.62 +    /** Use the convertor from <code>createCZKtoUSD</code> method and do few conversions
   38.63 +     * with it.
   38.64 +     */
   38.65 +    public void testCurrencyCZKUSD() throws Exception {
   38.66 +        Convertor c = createCZKtoUSD();
   38.67 +        // convert $5 to CZK using c:
   38.68 +        Amount result =  c.convert(new BigDecimal(5), USD, CZK);
   38.69 +        assertEquals("Result is 85 CZK", 85, result.getValue().intValue());
   38.70 +
   38.71 +        // convert $8 to CZK
   38.72 +        result =  c.convert(new BigDecimal(8), USD, CZK);        
   38.73 +        assertEquals("Result is 136 CZK", 136, result.getValue().intValue());
   38.74 +
   38.75 +        // convert 1003CZK to USD
   38.76 +        result =  c.convert(new BigDecimal(1003), CZK, USD);
   38.77 +        assertEquals("Result is 59 USD", 59, result.getValue().intValue());
   38.78 +    }
   38.79 +
   38.80 +    /** Use the convertor from <code>createSKKtoCZK</code> method and do few conversions
   38.81 +     * with it.
   38.82 +     */
   38.83 +    public void testCurrencySKKCZK() throws Exception {
   38.84 +        Convertor c = createSKKtoCZK();
   38.85 +        // convert 16CZK using c:
   38.86 +        Amount result =  c.convert(new BigDecimal(16), CZK, SKK);
   38.87 +        assertEquals("Result is 20 SKK", 20, result.getValue().intValue());
   38.88 +
   38.89 +        // convert 500SKK to CZK
   38.90 +        result =  c.convert(new BigDecimal(500), SKK, CZK);
   38.91 +        assertEquals("Result is 400 CZK", 400, result.getValue().intValue());
   38.92 +    }
   38.93 +    
   38.94 +    
   38.95 +    /**
   38.96 +     *  Verify that the CZK to USD convertor knows nothing about SKK.
   38.97 +     */
   38.98 +     public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception {
   38.99 +    	 Convertor c = createCZKtoUSD();
  38.100 +    	 // convert $5 to SKK, the API shall say this is not possible
  38.101 +    	 try {
  38.102 +    		 c.convert(new BigDecimal(5), USD, SKK);
  38.103 +    		 fail("convert $5 to SKK, the API shall say this is not possible");
  38.104 +    	 } catch (ConversionException e) {
  38.105 +    		 //expected
  38.106 +    	 }
  38.107 +    	 
  38.108 +    	 // convert 500 SKK to CZK, the API shall say this is not possible
  38.109 +    	 
  38.110 +    	 try {
  38.111 +    		 c.convert(new BigDecimal("500"), SKK, CZK);
  38.112 +    		 fail("convert 500 SKK to CZK, the API shall say this is not possible");
  38.113 +    	 } catch (ConversionException e) {
  38.114 +    		 //expected
  38.115 +    	 }
  38.116 +     }
  38.117 +    
  38.118 +    /** 
  38.119 +     * Verify that the CZK to SKK convertor knows nothing about USD.
  38.120 +     */
  38.121 +    public void testCannotConvertToSKKwithCZKSKKConvertor() throws Exception {
  38.122 +    	Convertor c = createSKKtoCZK();
  38.123 +    	// convert $5 to SKK, the API shall say this is not possible
  38.124 +    	try {
  38.125 +    		c.convert(new BigDecimal(5), USD, SKK);
  38.126 +    		fail("convert $5 to SKK, the API shall say this is not possible");
  38.127 +    	} catch(ConversionException e) {
  38.128 +    		//expected
  38.129 +    	}
  38.130 +    	
  38.131 +    	try {
  38.132 +    		c.convert(new BigDecimal(500), CZK, USD);	
  38.133 +    		fail("convert 500 CZK to USD, the API shall say this is not possible");
  38.134 +    	} catch(ConversionException e) {
  38.135 +    		//expected
  38.136 +    	}
  38.137 +    } 
  38.138 +}
  38.139 +
    39.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    39.2 +++ b/task4/solution06/test/org/apidesign/apifest08/test/Task2Test.java	Sat Oct 18 07:47:34 2008 +0200
    39.3 @@ -0,0 +1,117 @@
    39.4 +package org.apidesign.apifest08.test;
    39.5 +
    39.6 +import static org.apidesign.apifest08.test.Currencies.CZK;
    39.7 +import static org.apidesign.apifest08.test.Currencies.USD;
    39.8 +import static org.apidesign.apifest08.test.Currencies.SKK;
    39.9 +
   39.10 +import java.math.BigDecimal;
   39.11 +
   39.12 +import junit.framework.TestCase;
   39.13 +import org.apidesign.apifest08.currency.Convertor;
   39.14 +import org.apidesign.apifest08.test.Task1Test;
   39.15 +
   39.16 +/** There are many currencies around the world and many banks manipulate
   39.17 + * with more than one or two at the same time. As banks are usually the
   39.18 + * best paying clients, which is true even in case of your Convertor API,
   39.19 + * it is reasonable to listen to their requests.
   39.20 + * <p>
   39.21 + * The quest for today is to enhance your existing convertor API to hold
   39.22 + * information about many currencies and allow conversions between any of them.
   39.23 + * Also, as conversion rates for diferent currencies usually arise from various
   39.24 + * bank departments, there is another important need. There is a need to
   39.25 + * compose two convertors into one by merging all the information about
   39.26 + * currencies they know about.
   39.27 + */
   39.28 +public class Task2Test extends TestCase {
   39.29 +    public Task2Test(String testName) {
   39.30 +        super(testName);
   39.31 +    }
   39.32 +
   39.33 +    @Override
   39.34 +    protected void setUp() throws Exception {
   39.35 +    }
   39.36 +
   39.37 +    @Override
   39.38 +    protected void tearDown() throws Exception {
   39.39 +    }
   39.40 +
   39.41 +    // As in Task1Test, keep in mind, that there are three parts
   39.42 +    // of the whole system:
   39.43 +    // 1. there is someone who knows the current exchange rate
   39.44 +    // 2. there is someone who wants to do the conversion
   39.45 +    // 3. there is the API between 1. and 2. which allows them to communicate
   39.46 +    // 
   39.47 +    // Please backward compatibly enhance your existing API to support following
   39.48 +    // usecases:
   39.49 +    //
   39.50 +    
   39.51 +    /** Create convertor that understands two currencies, CZK and
   39.52 +     *  SKK. Make 100 SKK == 75 CZK. This is method for the group of users that
   39.53 +     *  knows the exchange rate, and needs to use the API to create objects
   39.54 +     *  with the exchange rate. Anyone shall be ready to call this method without
   39.55 +     *  any other method being called previously. The API itself shall know
   39.56 +     *  nothing about any rates, before this method is called.
   39.57 +     */
   39.58 +    public static Convertor createTripleConvertor() {
   39.59 +        // Rates: 1USD = 15CZK
   39.60 +        // Rates: 1USD = 20SKK
   39.61 +        // Rates: 75CZK = 100SKK
   39.62 +    	Convertor usdCzk = new Convertor(new BigDecimal(15), USD, CZK);
   39.63 +    	Convertor usdSkk = new Convertor(new BigDecimal(20), USD, SKK);
   39.64 +    	Convertor skkCzk = new Convertor(new BigDecimal("0.75"), SKK, CZK);
   39.65 +        return new Convertor(new Convertor[]{usdCzk, usdSkk, skkCzk});
   39.66 +    }
   39.67 +
   39.68 +    /** Define convertor that understands three currencies. Use it.
   39.69 +     */
   39.70 +    public void testConvertorForUSDandCZKandSKK() throws Exception {
   39.71 +        Convertor c = createTripleConvertor();
   39.72 +
   39.73 +        // convert $5 to CZK using c:
   39.74 +        assertEquals("Result is 75 CZK", 75 ,c.convert(new BigDecimal(5), USD, CZK).getValue().intValue());
   39.75 +
   39.76 +        // convert $5 to SKK using c:
   39.77 +        assertEquals("Result is 100 SKK", 100, c.convert(new BigDecimal(5), USD, SKK).getValue().intValue());
   39.78 +
   39.79 +        // convert 200SKK to CZK using c:
   39.80 +        assertEquals("Result is 150 CZK", 150, c.convert(new BigDecimal(200), SKK, CZK).getValue().intValue());
   39.81 +
   39.82 +        // convert 200SKK to USK using c:
   39.83 +        assertEquals("Result is 10 USD", 10, c.convert(new BigDecimal(200), SKK, USD).getValue().intValue());
   39.84 +    }
   39.85 +
   39.86 +    /** Merge all currency rates of convertor 1 with convertor 2.
   39.87 +     * Implement this using your API, preferably this method just delegates
   39.88 +     * into some API method which does the actual work, without requiring
   39.89 +     * API clients to code anything complex.
   39.90 +     */
   39.91 +    public static Convertor merge(Convertor one, Convertor two) {
   39.92 +        return new Convertor(new Convertor[]{one, two});
   39.93 +    }
   39.94 +
   39.95 +    /** Join the convertors from previous task, Task1Test and show that it
   39.96 +     * can be used to do reasonable conversions.
   39.97 +     */
   39.98 +    public void testConvertorComposition() throws Exception {
   39.99 +        Convertor c = merge(
  39.100 +            Task1Test.createCZKtoUSD(),
  39.101 +            Task1Test.createSKKtoCZK()
  39.102 +        );
  39.103 +
  39.104 +        // convert $5 to CZK using c:
  39.105 +        assertEquals("Result is 85 CZK", 85, c.convert(new BigDecimal(5), USD, CZK).getValue().intValue());
  39.106 +
  39.107 +        // convert $8 to CZK using c:
  39.108 +        assertEquals("Result is 136 CZK", 136, c.convert(new BigDecimal(8), USD, CZK).getValue().intValue());
  39.109 +
  39.110 +        // convert 1003CZK to USD using c:
  39.111 +        assertEquals("Result is 59 USD", 59, c.convert(new BigDecimal(1003), CZK, USD).getValue().intValue());
  39.112 +
  39.113 +        // convert 16CZK using c:
  39.114 +        assertEquals("Result is 20 SKK", 20, c.convert(new BigDecimal(16), CZK, SKK).getValue().intValue());
  39.115 +
  39.116 +        // convert 500SKK to CZK using c:
  39.117 +        assertEquals("Result is 400 CZK", 400, c.convert(new BigDecimal(500), SKK, CZK).getValue().intValue());
  39.118 +
  39.119 +    }
  39.120 +}
    40.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    40.2 +++ b/task4/solution06/test/org/apidesign/apifest08/test/Task3Test.java	Sat Oct 18 07:47:34 2008 +0200
    40.3 @@ -0,0 +1,111 @@
    40.4 +package org.apidesign.apifest08.test;
    40.5 +
    40.6 +import java.math.BigDecimal;
    40.7 +import java.math.RoundingMode;
    40.8 +
    40.9 +import junit.framework.TestCase;
   40.10 +import org.apidesign.apifest08.currency.Convertor;
   40.11 +import static org.apidesign.apifest08.test.Currencies.*;
   40.12 +
   40.13 +/** The exchange rates are not always the same. They are changing. Day by day,
   40.14 + * hour by hour, minute by minute. For every bank it is important to always
   40.15 + * have the actual exchange rate available in the system. That is why let's
   40.16 + * create a pluggable convertor that will always have up to date value of its
   40.17 + * exchange rate.
   40.18 + * <p>
   40.19 + * The quest for today is to allow 3rd party developer to write a convertor
   40.20 + * that adjusts its exchange rate everytime it is queried. This convertor is
   40.21 + * written by independent vendor, the vendor knows only your Convertor API,
   40.22 + * he does not know how the whole system looks and how the convertor is supposed
   40.23 + * to be used.
   40.24 + */
   40.25 +public class Task3Test extends TestCase {
   40.26 +    public Task3Test(String testName) {
   40.27 +        super(testName);
   40.28 +    }
   40.29 +
   40.30 +    @Override
   40.31 +    protected void setUp() throws Exception {
   40.32 +    }
   40.33 +
   40.34 +    @Override
   40.35 +    protected void tearDown() throws Exception {
   40.36 +    }
   40.37 +
   40.38 +    // Backward compatibly enhance your existing API to support following
   40.39 +    // usecases:
   40.40 +    //
   40.41 +
   40.42 +
   40.43 +    /** Without knowing anything about the surrounding system, write an
   40.44 +     * implementation of convertor that will return different rates everytime
   40.45 +     * it is queried. Convert USD to CZK and vice versa. Start with the rate of
   40.46 +     * 1USD = 16CZK and adjust it in favor of CZK by 0.01 CZK with every query.
   40.47 +     * As soon as you reach 1USD = 15CZK adjust it by 0.01 CZK in favor of USD
   40.48 +     * until you reach 1USD = 16CZK
   40.49 +     *
   40.50 +     * @return new instance of "online" USD and CZK convertor starting with rate 1USD = 16CZK
   40.51 +     */
   40.52 +    public static Convertor createOnlineCZKUSDConvertor() {
   40.53 +    	Convertor.RateProvider rateProvider = new Convertor.RateProvider() {
   40.54 +    		private BigDecimal seed = new BigDecimal("16.01").setScale(2);
   40.55 +    		
   40.56 +			@Override
   40.57 +			public BigDecimal getRate() {
   40.58 +				if(seed.equals(new BigDecimal("15").setScale(2))) {
   40.59 +					seed = seed.add(new BigDecimal("0.01")).setScale(2);
   40.60 +				} else {
   40.61 +					seed = seed.subtract(new BigDecimal("0.01")).setScale(2);
   40.62 +				}	
   40.63 +				return seed;
   40.64 +			}
   40.65 +    		
   40.66 +    	};
   40.67 +    	
   40.68 +        // initial rate: 1USD = 16CZK
   40.69 +        // 2nd query 1USD = 15.99CZK
   40.70 +        // 3rd query 1USD = 15.98CZK
   40.71 +        // until 1USD = 15.00CZK
   40.72 +        // then 1USD = 15.01CZK
   40.73 +        // then 1USD = 15.02CZK
   40.74 +        // and so on and on up to 1USD = 16CZK
   40.75 +        // and then another round to 15, etc.
   40.76 +        return new Convertor(rateProvider, USD, CZK);
   40.77 +    }
   40.78 +
   40.79 +    public void testFewQueriesForOnlineConvertor() {
   40.80 +        Convertor c = createOnlineCZKUSDConvertor();
   40.81 +        doFewQueriesForOnlineConvertor(c);
   40.82 +    }
   40.83 +
   40.84 +    static void doFewQueriesForOnlineConvertor(Convertor c) {
   40.85 +        // convert $5 to CZK using c:
   40.86 +        assertEquals("Result is 80 CZK",80 ,c.convert(new BigDecimal(5), USD, CZK).getValue().intValue());
   40.87 +
   40.88 +        // convert $8 to CZK using c:
   40.89 +        assertEquals("Result is 127.92 CZK", 127.92d , c.convert(new BigDecimal(8), USD, CZK).getValue().doubleValue());
   40.90 +
   40.91 +        // convert $1 to CZK using c:
   40.92 +        assertEquals("Result is 15.98 CZK", 15.98d,  c.convert(new BigDecimal(1), USD, CZK).getValue().doubleValue());
   40.93 +
   40.94 +        // convert 15.97CZK to USD using c:
   40.95 +        assertEquals("Result is 1$", 1, c.convert(new BigDecimal("15.97").setScale(2), CZK, USD).getValue().intValue());
   40.96 +    }
   40.97 +
   40.98 +    /** Join the convertors and show they behave sane.
   40.99 +     */
  40.100 +    public void testOnlineConvertorComposition() throws Exception {
  40.101 +        Convertor c = Task2Test.merge(
  40.102 +            createOnlineCZKUSDConvertor(),
  40.103 +            Task1Test.createSKKtoCZK()
  40.104 +        );
  40.105 +
  40.106 +        // convert 16CZK to SKK using c:
  40.107 +        assertEquals("Result is 20 SKK", 20, c.convert(new BigDecimal(16), CZK, SKK).getValue().intValue());
  40.108 +
  40.109 +        // convert 500SKK to CZK using c:
  40.110 +        assertEquals("Result is 400 CZK", 400, c.convert(new BigDecimal(500), SKK, CZK).getValue().intValue());
  40.111 +
  40.112 +        doFewQueriesForOnlineConvertor(c);
  40.113 +    }
  40.114 +}
    41.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    41.2 +++ b/task4/solution06/test/org/apidesign/apifest08/test/Task4Test.java	Sat Oct 18 07:47:34 2008 +0200
    41.3 @@ -0,0 +1,161 @@
    41.4 +package org.apidesign.apifest08.test;
    41.5 +
    41.6 +import java.math.BigDecimal;
    41.7 +import java.text.SimpleDateFormat;
    41.8 +import java.util.Date;
    41.9 +import junit.framework.TestCase;
   41.10 +import org.apidesign.apifest08.currency.Convertor;
   41.11 +import org.apidesign.apifest08.currency.UnsupportedConversionException;
   41.12 +
   41.13 +import static org.apidesign.apifest08.test.Currencies.*;
   41.14 +
   41.15 +/** The exchange rates are not always the same. They are changing. However
   41.16 + * as in order to predict the future, one needs to understand own past. That is
   41.17 + * why it is important to know the exchange rate as it was at any time during
   41.18 + * the past.
   41.19 + * <p>
   41.20 + * Today's quest is to enhance the convertor API to deal with dates.
   41.21 + * One shall be able to convert a currency at any date. Each currencies rate shall
   41.22 + * be associated with a range between two Date objects. In order
   41.23 + * to keep compatibility with old API that knew nothing about dates, the
   41.24 + * rates associated then are applicable "for eternity". Any use of existing
   41.25 + * convert methods that do not accept a Date argument, uses the current
   41.26 + * System.currentTimeMillis() as default date.
   41.27 + */
   41.28 +public class Task4Test extends TestCase {
   41.29 +    public Task4Test(String testName) {
   41.30 +        super(testName);
   41.31 +    }
   41.32 +
   41.33 +    @Override
   41.34 +    protected void setUp() throws Exception {
   41.35 +    }
   41.36 +
   41.37 +    @Override
   41.38 +    protected void tearDown() throws Exception {
   41.39 +    }
   41.40 +
   41.41 +    // Backward compatibly enhance your existing API to support following
   41.42 +    // usecases:
   41.43 +    //
   41.44 +
   41.45 +    /** Takes a convertor with any rates associated and creates new convertor
   41.46 +     * that returns the same values as the old one for time between from to till.
   41.47 +     * Otherwise it returns no results. This is just a helper method that
   41.48 +     * shall call some real one in the API.
   41.49 +     * 
   41.50 +     * @param old existing convertor
   41.51 +     * @param from initial date (inclusive)
   41.52 +     * @param till final date (exclusive)
   41.53 +     * @return new convertor
   41.54 +     */
   41.55 +    public static Convertor limitTo(Convertor old, Date from, Date till) {
   41.56 +        return new Convertor(old, from, till);
   41.57 +    }
   41.58 +
   41.59 +
   41.60 +    public void testCompositionOfLimitedConvertors() throws Exception {
   41.61 +        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
   41.62 +        
   41.63 +        Date d1 = df.parse("2008-10-01 0:00 GMT"); 
   41.64 +        Date d2 = df.parse("2008-10-02 0:00 GMT"); 
   41.65 +        Date d3 = df.parse("2008-10-03 0:00 GMT");
   41.66 +        
   41.67 +        Convertor c = Task2Test.merge(
   41.68 +            limitTo(Task1Test.createCZKtoUSD(), d1, d2),
   41.69 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
   41.70 +        );
   41.71 +
   41.72 +        // convert $5 to CZK using c:
   41.73 +        try {
   41.74 +        	c.convert(new BigDecimal(5), USD , CZK);
   41.75 +        	fail("cannot convert as no rate is applicable to current date");
   41.76 +        } catch(UnsupportedConversionException e) {
   41.77 +        	//expected
   41.78 +        }
   41.79 +
   41.80 +        // convert $8 to CZK using c:
   41.81 +        try {
   41.82 +        	c.convert(new BigDecimal(8), USD , CZK);
   41.83 +        	fail("cannot convert as no rate is applicable to current date");
   41.84 +        } catch(UnsupportedConversionException e) {
   41.85 +        	//expected
   41.86 +        }
   41.87 +
   41.88 +        // convert 1003CZK to USD using c:
   41.89 +        try {
   41.90 +        	c.convert(new BigDecimal(1003), CZK, USD);
   41.91 +        	fail("cannot convert as no rate is applicable to current date");
   41.92 +        } catch(UnsupportedConversionException e) {
   41.93 +        	//expected
   41.94 +        }
   41.95 +
   41.96 +        // convert 16CZK using c:
   41.97 +        try {
   41.98 +        	c.convert(new BigDecimal(16), CZK, USD);
   41.99 +        	fail("cannot convert as no rate is applicable to current date");
  41.100 +        } catch(UnsupportedConversionException e) {
  41.101 +        	//expected
  41.102 +        }
  41.103 +
  41.104 +        // convert 500SKK to CZK using c:
  41.105 +        try {
  41.106 +        	c.convert(new BigDecimal(500), SKK, CZK);
  41.107 +        	fail("cannot convert as no rate is applicable to current date");
  41.108 +        } catch(UnsupportedConversionException e) {
  41.109 +        	//expected
  41.110 +        }
  41.111 +
  41.112 +        // convert $5 to CZK using c at 2008-10-01 6:00 GMT:         
  41.113 +        assertEquals("Result is 85 CZK", 85, c.convert(new BigDecimal(5), USD, CZK, df.parse("2008-10-01 6:00 GMT")).getValue().intValue());
  41.114 +
  41.115 +        // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
  41.116 +        assertEquals("Result is 136 CZK", 136, c.convert(new BigDecimal(8), USD, CZK, df.parse("2008-10-01 6:00 GMT")).getValue().intValue());
  41.117 +     
  41.118 +        // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
  41.119 +        assertEquals("Result is 59 USD", 59, c.convert(new BigDecimal(1003), CZK, USD, df.parse("2008-10-01 6:00 GMT")).getValue().intValue());
  41.120 +
  41.121 +        // convert 16CZK using c at 2008-10-02 9:00 GMT:
  41.122 +        assertEquals("Result is 20 SKK", 20, c.convert(new BigDecimal(16), CZK, SKK, df.parse("2008-10-02 9:00 GMT")).getValue().intValue());
  41.123 +
  41.124 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  41.125 +        assertEquals("Result is 400 CZK", 400, c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-02 9:00 GMT")).getValue().intValue());
  41.126 +
  41.127 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  41.128 +        try {
  41.129 +        	c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-01 6:00 GMT"));
  41.130 +        	fail("cannot convert as no rate is applicable to current date");
  41.131 +        } catch(UnsupportedConversionException e) {
  41.132 +        	//expected
  41.133 +        }
  41.134 +    }
  41.135 +
  41.136 +    /** Create convertor that understands two currencies, CZK and
  41.137 +     *  SKK. Make 100 SKK == 90 CZK.
  41.138 +     *
  41.139 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
  41.140 +     */
  41.141 +    public static Convertor createSKKtoCZK2() {
  41.142 +    	return new Convertor(new BigDecimal("0.9"), SKK, CZK);
  41.143 +    }
  41.144 +
  41.145 +    public void testDateConvetorWithTwoDifferentRates() throws Exception {
  41.146 +        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
  41.147 +        
  41.148 +        Date d1 = df.parse("2008-10-01 0:00 GMT"); 
  41.149 +        Date d2 = df.parse("2008-10-02 0:00 GMT"); 
  41.150 +        Date d3 = df.parse("2008-10-03 0:00 GMT");
  41.151 +
  41.152 +        Convertor c = Task2Test.merge(
  41.153 +            limitTo(createSKKtoCZK2(), d1, d2),
  41.154 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
  41.155 +        );
  41.156 +
  41.157 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  41.158 +        assertEquals("Result is 400 CZK", 400, c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-02 9:00 GMT")).getValue().intValue());
  41.159 +
  41.160 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  41.161 +        assertEquals("Result is 450 CZK", 450, c.convert(new BigDecimal(500), SKK, CZK, df.parse("2008-10-01 6:00 GMT")).getValue().intValue());
  41.162 +    }
  41.163 +
  41.164 +}
    42.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    42.2 +++ b/task4/solution07/build.xml	Sat Oct 18 07:47:34 2008 +0200
    42.3 @@ -0,0 +1,69 @@
    42.4 +<?xml version="1.0" encoding="UTF-8"?>
    42.5 +<!-- You may freely edit this file. See commented blocks below for -->
    42.6 +<!-- some examples of how to customize the build. -->
    42.7 +<!-- (If you delete it and reopen the project it will be recreated.) -->
    42.8 +<project name="currency" default="default" basedir=".">
    42.9 +    <description>Builds, tests, and runs the project.</description>
   42.10 +    <import file="nbproject/build-impl.xml"/>
   42.11 +    <!--
   42.12 +
   42.13 +    There exist several targets which are by default empty and which can be 
   42.14 +    used for execution of your tasks. These targets are usually executed 
   42.15 +    before and after some main targets. They are: 
   42.16 +
   42.17 +      -pre-init:                 called before initialization of project properties
   42.18 +      -post-init:                called after initialization of project properties
   42.19 +      -pre-compile:              called before javac compilation
   42.20 +      -post-compile:             called after javac compilation
   42.21 +      -pre-compile-single:       called before javac compilation of single file
   42.22 +      -post-compile-single:      called after javac compilation of single file
   42.23 +      -pre-compile-test:         called before javac compilation of JUnit tests
   42.24 +      -post-compile-test:        called after javac compilation of JUnit tests
   42.25 +      -pre-compile-test-single:  called before javac compilation of single JUnit test
   42.26 +      -post-compile-test-single: called after javac compilation of single JUunit test
   42.27 +      -pre-jar:                  called before JAR building
   42.28 +      -post-jar:                 called after JAR building
   42.29 +      -post-clean:               called after cleaning build products
   42.30 +
   42.31 +    (Targets beginning with '-' are not intended to be called on their own.)
   42.32 +
   42.33 +    Example of inserting an obfuscator after compilation could look like this:
   42.34 +
   42.35 +        <target name="-post-compile">
   42.36 +            <obfuscate>
   42.37 +                <fileset dir="${build.classes.dir}"/>
   42.38 +            </obfuscate>
   42.39 +        </target>
   42.40 +
   42.41 +    For list of available properties check the imported 
   42.42 +    nbproject/build-impl.xml file. 
   42.43 +
   42.44 +
   42.45 +    Another way to customize the build is by overriding existing main targets.
   42.46 +    The targets of interest are: 
   42.47 +
   42.48 +      -init-macrodef-javac:     defines macro for javac compilation
   42.49 +      -init-macrodef-junit:     defines macro for junit execution
   42.50 +      -init-macrodef-debug:     defines macro for class debugging
   42.51 +      -init-macrodef-java:      defines macro for class execution
   42.52 +      -do-jar-with-manifest:    JAR building (if you are using a manifest)
   42.53 +      -do-jar-without-manifest: JAR building (if you are not using a manifest)
   42.54 +      run:                      execution of project 
   42.55 +      -javadoc-build:           Javadoc generation
   42.56 +      test-report:              JUnit report generation
   42.57 +
   42.58 +    An example of overriding the target for project execution could look like this:
   42.59 +
   42.60 +        <target name="run" depends="currency-impl.jar">
   42.61 +            <exec dir="bin" executable="launcher.exe">
   42.62 +                <arg file="${dist.jar}"/>
   42.63 +            </exec>
   42.64 +        </target>
   42.65 +
   42.66 +    Notice that the overridden target depends on the jar target and not only on 
   42.67 +    the compile target as the regular run target does. Again, for a list of available 
   42.68 +    properties which you can use, check the target you are overriding in the
   42.69 +    nbproject/build-impl.xml file. 
   42.70 +
   42.71 +    -->
   42.72 +</project>
    43.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    43.2 +++ b/task4/solution07/nbproject/build-impl.xml	Sat Oct 18 07:47:34 2008 +0200
    43.3 @@ -0,0 +1,642 @@
    43.4 +<?xml version="1.0" encoding="UTF-8"?>
    43.5 +<!--
    43.6 +*** GENERATED FROM project.xml - DO NOT EDIT  ***
    43.7 +***         EDIT ../build.xml INSTEAD         ***
    43.8 +
    43.9 +For the purpose of easier reading the script
   43.10 +is divided into following sections:
   43.11 +
   43.12 +  - initialization
   43.13 +  - compilation
   43.14 +  - jar
   43.15 +  - execution
   43.16 +  - debugging
   43.17 +  - javadoc
   43.18 +  - junit compilation
   43.19 +  - junit execution
   43.20 +  - junit debugging
   43.21 +  - applet
   43.22 +  - cleanup
   43.23 +
   43.24 +        -->
   43.25 +<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="Currency_Convertor_Solution_07-impl">
   43.26 +    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
   43.27 +    <!-- 
   43.28 +                ======================
   43.29 +                INITIALIZATION SECTION 
   43.30 +                ======================
   43.31 +            -->
   43.32 +    <target name="-pre-init">
   43.33 +        <!-- Empty placeholder for easier customization. -->
   43.34 +        <!-- You can override this target in the ../build.xml file. -->
   43.35 +    </target>
   43.36 +    <target depends="-pre-init" name="-init-private">
   43.37 +        <property file="nbproject/private/config.properties"/>
   43.38 +        <property file="nbproject/private/configs/${config}.properties"/>
   43.39 +        <property file="nbproject/private/private.properties"/>
   43.40 +    </target>
   43.41 +    <target depends="-pre-init,-init-private" name="-init-user">
   43.42 +        <property file="${user.properties.file}"/>
   43.43 +        <!-- The two properties below are usually overridden -->
   43.44 +        <!-- by the active platform. Just a fallback. -->
   43.45 +        <property name="default.javac.source" value="1.4"/>
   43.46 +        <property name="default.javac.target" value="1.4"/>
   43.47 +    </target>
   43.48 +    <target depends="-pre-init,-init-private,-init-user" name="-init-project">
   43.49 +        <property file="nbproject/configs/${config}.properties"/>
   43.50 +        <property file="nbproject/project.properties"/>
   43.51 +    </target>
   43.52 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
   43.53 +        <available file="${manifest.file}" property="manifest.available"/>
   43.54 +        <condition property="manifest.available+main.class">
   43.55 +            <and>
   43.56 +                <isset property="manifest.available"/>
   43.57 +                <isset property="main.class"/>
   43.58 +                <not>
   43.59 +                    <equals arg1="${main.class}" arg2="" trim="true"/>
   43.60 +                </not>
   43.61 +            </and>
   43.62 +        </condition>
   43.63 +        <condition property="manifest.available+main.class+mkdist.available">
   43.64 +            <and>
   43.65 +                <istrue value="${manifest.available+main.class}"/>
   43.66 +                <isset property="libs.CopyLibs.classpath"/>
   43.67 +            </and>
   43.68 +        </condition>
   43.69 +        <condition property="have.tests">
   43.70 +            <or>
   43.71 +                <available file="${test.src.dir}"/>
   43.72 +            </or>
   43.73 +        </condition>
   43.74 +        <condition property="have.sources">
   43.75 +            <or>
   43.76 +                <available file="${src.dir}"/>
   43.77 +            </or>
   43.78 +        </condition>
   43.79 +        <condition property="netbeans.home+have.tests">
   43.80 +            <and>
   43.81 +                <isset property="netbeans.home"/>
   43.82 +                <isset property="have.tests"/>
   43.83 +            </and>
   43.84 +        </condition>
   43.85 +        <condition property="no.javadoc.preview">
   43.86 +            <and>
   43.87 +                <isset property="javadoc.preview"/>
   43.88 +                <isfalse value="${javadoc.preview}"/>
   43.89 +            </and>
   43.90 +        </condition>
   43.91 +        <property name="run.jvmargs" value=""/>
   43.92 +        <property name="javac.compilerargs" value=""/>
   43.93 +        <property name="work.dir" value="${basedir}"/>
   43.94 +        <condition property="no.deps">
   43.95 +            <and>
   43.96 +                <istrue value="${no.dependencies}"/>
   43.97 +            </and>
   43.98 +        </condition>
   43.99 +        <property name="javac.debug" value="true"/>
  43.100 +        <property name="javadoc.preview" value="true"/>
  43.101 +        <property name="application.args" value=""/>
  43.102 +        <property name="source.encoding" value="${file.encoding}"/>
  43.103 +        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
  43.104 +            <and>
  43.105 +                <isset property="javadoc.encoding"/>
  43.106 +                <not>
  43.107 +                    <equals arg1="${javadoc.encoding}" arg2=""/>
  43.108 +                </not>
  43.109 +            </and>
  43.110 +        </condition>
  43.111 +        <property name="javadoc.encoding.used" value="${source.encoding}"/>
  43.112 +        <property name="includes" value="**"/>
  43.113 +        <property name="excludes" value=""/>
  43.114 +        <property name="do.depend" value="false"/>
  43.115 +        <condition property="do.depend.true">
  43.116 +            <istrue value="${do.depend}"/>
  43.117 +        </condition>
  43.118 +        <condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
  43.119 +            <and>
  43.120 +                <isset property="jaxws.endorsed.dir"/>
  43.121 +                <available file="nbproject/jaxws-build.xml"/>
  43.122 +            </and>
  43.123 +        </condition>
  43.124 +    </target>
  43.125 +    <target name="-post-init">
  43.126 +        <!-- Empty placeholder for easier customization. -->
  43.127 +        <!-- You can override this target in the ../build.xml file. -->
  43.128 +    </target>
  43.129 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
  43.130 +        <fail unless="src.dir">Must set src.dir</fail>
  43.131 +        <fail unless="test.src.dir">Must set test.src.dir</fail>
  43.132 +        <fail unless="build.dir">Must set build.dir</fail>
  43.133 +        <fail unless="dist.dir">Must set dist.dir</fail>
  43.134 +        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
  43.135 +        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
  43.136 +        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
  43.137 +        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
  43.138 +        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
  43.139 +        <fail unless="dist.jar">Must set dist.jar</fail>
  43.140 +    </target>
  43.141 +    <target name="-init-macrodef-property">
  43.142 +        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
  43.143 +            <attribute name="name"/>
  43.144 +            <attribute name="value"/>
  43.145 +            <sequential>
  43.146 +                <property name="@{name}" value="${@{value}}"/>
  43.147 +            </sequential>
  43.148 +        </macrodef>
  43.149 +    </target>
  43.150 +    <target name="-init-macrodef-javac">
  43.151 +        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
  43.152 +            <attribute default="${src.dir}" name="srcdir"/>
  43.153 +            <attribute default="${build.classes.dir}" name="destdir"/>
  43.154 +            <attribute default="${javac.classpath}" name="classpath"/>
  43.155 +            <attribute default="${includes}" name="includes"/>
  43.156 +            <attribute default="${excludes}" name="excludes"/>
  43.157 +            <attribute default="${javac.debug}" name="debug"/>
  43.158 +            <attribute default="" name="sourcepath"/>
  43.159 +            <element name="customize" optional="true"/>
  43.160 +            <sequential>
  43.161 +                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
  43.162 +                    <classpath>
  43.163 +                        <path path="@{classpath}"/>
  43.164 +                    </classpath>
  43.165 +                    <compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
  43.166 +                    <customize/>
  43.167 +                </javac>
  43.168 +            </sequential>
  43.169 +        </macrodef>
  43.170 +        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
  43.171 +            <attribute default="${src.dir}" name="srcdir"/>
  43.172 +            <attribute default="${build.classes.dir}" name="destdir"/>
  43.173 +            <attribute default="${javac.classpath}" name="classpath"/>
  43.174 +            <sequential>
  43.175 +                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
  43.176 +                    <classpath>
  43.177 +                        <path path="@{classpath}"/>
  43.178 +                    </classpath>
  43.179 +                </depend>
  43.180 +            </sequential>
  43.181 +        </macrodef>
  43.182 +        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
  43.183 +            <attribute default="${build.classes.dir}" name="destdir"/>
  43.184 +            <sequential>
  43.185 +                <fail unless="javac.includes">Must set javac.includes</fail>
  43.186 +                <pathconvert pathsep="," property="javac.includes.binary">
  43.187 +                    <path>
  43.188 +                        <filelist dir="@{destdir}" files="${javac.includes}"/>
  43.189 +                    </path>
  43.190 +                    <globmapper from="*.java" to="*.class"/>
  43.191 +                </pathconvert>
  43.192 +                <delete>
  43.193 +                    <files includes="${javac.includes.binary}"/>
  43.194 +                </delete>
  43.195 +            </sequential>
  43.196 +        </macrodef>
  43.197 +    </target>
  43.198 +    <target name="-init-macrodef-junit">
  43.199 +        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
  43.200 +            <attribute default="${includes}" name="includes"/>
  43.201 +            <attribute default="${excludes}" name="excludes"/>
  43.202 +            <attribute default="**" name="testincludes"/>
  43.203 +            <sequential>
  43.204 +                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
  43.205 +                    <batchtest todir="${build.test.results.dir}">
  43.206 +                        <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
  43.207 +                            <filename name="@{testincludes}"/>
  43.208 +                        </fileset>
  43.209 +                    </batchtest>
  43.210 +                    <classpath>
  43.211 +                        <path path="${run.test.classpath}"/>
  43.212 +                    </classpath>
  43.213 +                    <syspropertyset>
  43.214 +                        <propertyref prefix="test-sys-prop."/>
  43.215 +                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
  43.216 +                    </syspropertyset>
  43.217 +                    <formatter type="brief" usefile="false"/>
  43.218 +                    <formatter type="xml"/>
  43.219 +                    <jvmarg line="${run.jvmargs}"/>
  43.220 +                </junit>
  43.221 +            </sequential>
  43.222 +        </macrodef>
  43.223 +    </target>
  43.224 +    <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
  43.225 +        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
  43.226 +            <attribute default="${main.class}" name="name"/>
  43.227 +            <attribute default="${debug.classpath}" name="classpath"/>
  43.228 +            <attribute default="" name="stopclassname"/>
  43.229 +            <sequential>
  43.230 +                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
  43.231 +                    <classpath>
  43.232 +                        <path path="@{classpath}"/>
  43.233 +                    </classpath>
  43.234 +                </nbjpdastart>
  43.235 +            </sequential>
  43.236 +        </macrodef>
  43.237 +        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
  43.238 +            <attribute default="${build.classes.dir}" name="dir"/>
  43.239 +            <sequential>
  43.240 +                <nbjpdareload>
  43.241 +                    <fileset dir="@{dir}" includes="${fix.classes}">
  43.242 +                        <include name="${fix.includes}*.class"/>
  43.243 +                    </fileset>
  43.244 +                </nbjpdareload>
  43.245 +            </sequential>
  43.246 +        </macrodef>
  43.247 +    </target>
  43.248 +    <target name="-init-debug-args">
  43.249 +        <property name="version-output" value="java version &quot;${ant.java.version}"/>
  43.250 +        <condition property="have-jdk-older-than-1.4">
  43.251 +            <or>
  43.252 +                <contains string="${version-output}" substring="java version &quot;1.0"/>
  43.253 +                <contains string="${version-output}" substring="java version &quot;1.1"/>
  43.254 +                <contains string="${version-output}" substring="java version &quot;1.2"/>
  43.255 +                <contains string="${version-output}" substring="java version &quot;1.3"/>
  43.256 +            </or>
  43.257 +        </condition>
  43.258 +        <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
  43.259 +            <istrue value="${have-jdk-older-than-1.4}"/>
  43.260 +        </condition>
  43.261 +        <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
  43.262 +            <os family="windows"/>
  43.263 +        </condition>
  43.264 +        <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
  43.265 +            <isset property="debug.transport"/>
  43.266 +        </condition>
  43.267 +    </target>
  43.268 +    <target depends="-init-debug-args" name="-init-macrodef-debug">
  43.269 +        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
  43.270 +            <attribute default="${main.class}" name="classname"/>
  43.271 +            <attribute default="${debug.classpath}" name="classpath"/>
  43.272 +            <element name="customize" optional="true"/>
  43.273 +            <sequential>
  43.274 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
  43.275 +                    <jvmarg line="${debug-args-line}"/>
  43.276 +                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
  43.277 +                    <jvmarg line="${run.jvmargs}"/>
  43.278 +                    <classpath>
  43.279 +                        <path path="@{classpath}"/>
  43.280 +                    </classpath>
  43.281 +                    <syspropertyset>
  43.282 +                        <propertyref prefix="run-sys-prop."/>
  43.283 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
  43.284 +                    </syspropertyset>
  43.285 +                    <customize/>
  43.286 +                </java>
  43.287 +            </sequential>
  43.288 +        </macrodef>
  43.289 +    </target>
  43.290 +    <target name="-init-macrodef-java">
  43.291 +        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
  43.292 +            <attribute default="${main.class}" name="classname"/>
  43.293 +            <element name="customize" optional="true"/>
  43.294 +            <sequential>
  43.295 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
  43.296 +                    <jvmarg line="${run.jvmargs}"/>
  43.297 +                    <classpath>
  43.298 +                        <path path="${run.classpath}"/>
  43.299 +                    </classpath>
  43.300 +                    <syspropertyset>
  43.301 +                        <propertyref prefix="run-sys-prop."/>
  43.302 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
  43.303 +                    </syspropertyset>
  43.304 +                    <customize/>
  43.305 +                </java>
  43.306 +            </sequential>
  43.307 +        </macrodef>
  43.308 +    </target>
  43.309 +    <target name="-init-presetdef-jar">
  43.310 +        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
  43.311 +            <jar compress="${jar.compress}" jarfile="${dist.jar}">
  43.312 +                <j2seproject1:fileset dir="${build.classes.dir}"/>
  43.313 +            </jar>
  43.314 +        </presetdef>
  43.315 +    </target>
  43.316 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
  43.317 +    <!--
  43.318 +                ===================
  43.319 +                COMPILATION SECTION
  43.320 +                ===================
  43.321 +            -->
  43.322 +    <target depends="init" name="deps-jar" unless="no.deps"/>
  43.323 +    <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
  43.324 +    <target depends="init" name="-check-automatic-build">
  43.325 +        <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
  43.326 +    </target>
  43.327 +    <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
  43.328 +        <antcall target="clean"/>
  43.329 +    </target>
  43.330 +    <target depends="init,deps-jar" name="-pre-pre-compile">
  43.331 +        <mkdir dir="${build.classes.dir}"/>
  43.332 +    </target>
  43.333 +    <target name="-pre-compile">
  43.334 +        <!-- Empty placeholder for easier customization. -->
  43.335 +        <!-- You can override this target in the ../build.xml file. -->
  43.336 +    </target>
  43.337 +    <target if="do.depend.true" name="-compile-depend">
  43.338 +        <j2seproject3:depend/>
  43.339 +    </target>
  43.340 +    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
  43.341 +        <j2seproject3:javac/>
  43.342 +        <copy todir="${build.classes.dir}">
  43.343 +            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  43.344 +        </copy>
  43.345 +    </target>
  43.346 +    <target name="-post-compile">
  43.347 +        <!-- Empty placeholder for easier customization. -->
  43.348 +        <!-- You can override this target in the ../build.xml file. -->
  43.349 +    </target>
  43.350 +    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
  43.351 +    <target name="-pre-compile-single">
  43.352 +        <!-- Empty placeholder for easier customization. -->
  43.353 +        <!-- You can override this target in the ../build.xml file. -->
  43.354 +    </target>
  43.355 +    <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
  43.356 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
  43.357 +        <j2seproject3:force-recompile/>
  43.358 +        <j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
  43.359 +    </target>
  43.360 +    <target name="-post-compile-single">
  43.361 +        <!-- Empty placeholder for easier customization. -->
  43.362 +        <!-- You can override this target in the ../build.xml file. -->
  43.363 +    </target>
  43.364 +    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
  43.365 +    <!--
  43.366 +                ====================
  43.367 +                JAR BUILDING SECTION
  43.368 +                ====================
  43.369 +            -->
  43.370 +    <target depends="init" name="-pre-pre-jar">
  43.371 +        <dirname file="${dist.jar}" property="dist.jar.dir"/>
  43.372 +        <mkdir dir="${dist.jar.dir}"/>
  43.373 +    </target>
  43.374 +    <target name="-pre-jar">
  43.375 +        <!-- Empty placeholder for easier customization. -->
  43.376 +        <!-- You can override this target in the ../build.xml file. -->
  43.377 +    </target>
  43.378 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
  43.379 +        <j2seproject1:jar/>
  43.380 +    </target>
  43.381 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
  43.382 +        <j2seproject1:jar manifest="${manifest.file}"/>
  43.383 +    </target>
  43.384 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
  43.385 +        <j2seproject1:jar manifest="${manifest.file}">
  43.386 +            <j2seproject1:manifest>
  43.387 +                <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
  43.388 +            </j2seproject1:manifest>
  43.389 +        </j2seproject1:jar>
  43.390 +        <echo>To run this application from the command line without Ant, try:</echo>
  43.391 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
  43.392 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
  43.393 +        <pathconvert property="run.classpath.with.dist.jar">
  43.394 +            <path path="${run.classpath}"/>
  43.395 +            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
  43.396 +        </pathconvert>
  43.397 +        <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
  43.398 +    </target>
  43.399 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
  43.400 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
  43.401 +        <pathconvert property="run.classpath.without.build.classes.dir">
  43.402 +            <path path="${run.classpath}"/>
  43.403 +            <map from="${build.classes.dir.resolved}" to=""/>
  43.404 +        </pathconvert>
  43.405 +        <pathconvert pathsep=" " property="jar.classpath">
  43.406 +            <path path="${run.classpath.without.build.classes.dir}"/>
  43.407 +            <chainedmapper>
  43.408 +                <flattenmapper/>
  43.409 +                <globmapper from="*" to="lib/*"/>
  43.410 +            </chainedmapper>
  43.411 +        </pathconvert>
  43.412 +        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
  43.413 +        <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
  43.414 +            <fileset dir="${build.classes.dir}"/>
  43.415 +            <manifest>
  43.416 +                <attribute name="Main-Class" value="${main.class}"/>
  43.417 +                <attribute name="Class-Path" value="${jar.classpath}"/>
  43.418 +            </manifest>
  43.419 +        </copylibs>
  43.420 +        <echo>To run this application from the command line without Ant, try:</echo>
  43.421 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
  43.422 +        <echo>java -jar "${dist.jar.resolved}"</echo>
  43.423 +    </target>
  43.424 +    <target name="-post-jar">
  43.425 +        <!-- Empty placeholder for easier customization. -->
  43.426 +        <!-- You can override this target in the ../build.xml file. -->
  43.427 +    </target>
  43.428 +    <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
  43.429 +    <!--
  43.430 +                =================
  43.431 +                EXECUTION SECTION
  43.432 +                =================
  43.433 +            -->
  43.434 +    <target depends="init,compile" description="Run a main class." name="run">
  43.435 +        <j2seproject1:java>
  43.436 +            <customize>
  43.437 +                <arg line="${application.args}"/>
  43.438 +            </customize>
  43.439 +        </j2seproject1:java>
  43.440 +    </target>
  43.441 +    <target name="-do-not-recompile">
  43.442 +        <property name="javac.includes.binary" value=""/>
  43.443 +    </target>
  43.444 +    <target depends="init,-do-not-recompile,compile-single" name="run-single">
  43.445 +        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
  43.446 +        <j2seproject1:java classname="${run.class}"/>
  43.447 +    </target>
  43.448 +    <!--
  43.449 +                =================
  43.450 +                DEBUGGING SECTION
  43.451 +                =================
  43.452 +            -->
  43.453 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger">
  43.454 +        <j2seproject1:nbjpdastart name="${debug.class}"/>
  43.455 +    </target>
  43.456 +    <target depends="init,compile" name="-debug-start-debuggee">
  43.457 +        <j2seproject3:debug>
  43.458 +            <customize>
  43.459 +                <arg line="${application.args}"/>
  43.460 +            </customize>
  43.461 +        </j2seproject3:debug>
  43.462 +    </target>
  43.463 +    <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
  43.464 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
  43.465 +        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
  43.466 +    </target>
  43.467 +    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
  43.468 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
  43.469 +        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
  43.470 +        <j2seproject3:debug classname="${debug.class}"/>
  43.471 +    </target>
  43.472 +    <target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
  43.473 +    <target depends="init" name="-pre-debug-fix">
  43.474 +        <fail unless="fix.includes">Must set fix.includes</fail>
  43.475 +        <property name="javac.includes" value="${fix.includes}.java"/>
  43.476 +    </target>
  43.477 +    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
  43.478 +        <j2seproject1:nbjpdareload/>
  43.479 +    </target>
  43.480 +    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
  43.481 +    <!--
  43.482 +                ===============
  43.483 +                JAVADOC SECTION
  43.484 +                ===============
  43.485 +            -->
  43.486 +    <target depends="init" name="-javadoc-build">
  43.487 +        <mkdir dir="${dist.javadoc.dir}"/>
  43.488 +        <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
  43.489 +            <classpath>
  43.490 +                <path path="${javac.classpath}"/>
  43.491 +            </classpath>
  43.492 +            <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
  43.493 +                <filename name="**/*.java"/>
  43.494 +            </fileset>
  43.495 +        </javadoc>
  43.496 +    </target>
  43.497 +    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
  43.498 +        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
  43.499 +    </target>
  43.500 +    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
  43.501 +    <!--
  43.502 +                =========================
  43.503 +                JUNIT COMPILATION SECTION
  43.504 +                =========================
  43.505 +            -->
  43.506 +    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
  43.507 +        <mkdir dir="${build.test.classes.dir}"/>
  43.508 +    </target>
  43.509 +    <target name="-pre-compile-test">
  43.510 +        <!-- Empty placeholder for easier customization. -->
  43.511 +        <!-- You can override this target in the ../build.xml file. -->
  43.512 +    </target>
  43.513 +    <target if="do.depend.true" name="-compile-test-depend">
  43.514 +        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
  43.515 +    </target>
  43.516 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
  43.517 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
  43.518 +        <copy todir="${build.test.classes.dir}">
  43.519 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  43.520 +        </copy>
  43.521 +    </target>
  43.522 +    <target name="-post-compile-test">
  43.523 +        <!-- Empty placeholder for easier customization. -->
  43.524 +        <!-- You can override this target in the ../build.xml file. -->
  43.525 +    </target>
  43.526 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
  43.527 +    <target name="-pre-compile-test-single">
  43.528 +        <!-- Empty placeholder for easier customization. -->
  43.529 +        <!-- You can override this target in the ../build.xml file. -->
  43.530 +    </target>
  43.531 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
  43.532 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
  43.533 +        <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
  43.534 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
  43.535 +        <copy todir="${build.test.classes.dir}">
  43.536 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  43.537 +        </copy>
  43.538 +    </target>
  43.539 +    <target name="-post-compile-test-single">
  43.540 +        <!-- Empty placeholder for easier customization. -->
  43.541 +        <!-- You can override this target in the ../build.xml file. -->
  43.542 +    </target>
  43.543 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
  43.544 +    <!--
  43.545 +                =======================
  43.546 +                JUNIT EXECUTION SECTION
  43.547 +                =======================
  43.548 +            -->
  43.549 +    <target depends="init" if="have.tests" name="-pre-test-run">
  43.550 +        <mkdir dir="${build.test.results.dir}"/>
  43.551 +    </target>
  43.552 +    <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
  43.553 +        <j2seproject3:junit testincludes="**/*Test.java"/>
  43.554 +    </target>
  43.555 +    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
  43.556 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
  43.557 +    </target>
  43.558 +    <target depends="init" if="have.tests" name="test-report"/>
  43.559 +    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
  43.560 +    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
  43.561 +    <target depends="init" if="have.tests" name="-pre-test-run-single">
  43.562 +        <mkdir dir="${build.test.results.dir}"/>
  43.563 +    </target>
  43.564 +    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
  43.565 +        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
  43.566 +        <j2seproject3:junit excludes="" includes="${test.includes}"/>
  43.567 +    </target>
  43.568 +    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
  43.569 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
  43.570 +    </target>
  43.571 +    <target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
  43.572 +    <!--
  43.573 +                =======================
  43.574 +                JUNIT DEBUGGING SECTION
  43.575 +                =======================
  43.576 +            -->
  43.577 +    <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
  43.578 +        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
  43.579 +        <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
  43.580 +        <delete file="${test.report.file}"/>
  43.581 +        <mkdir dir="${build.test.results.dir}"/>
  43.582 +        <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
  43.583 +            <customize>
  43.584 +                <syspropertyset>
  43.585 +                    <propertyref prefix="test-sys-prop."/>
  43.586 +                    <mapper from="test-sys-prop.*" to="*" type="glob"/>
  43.587 +                </syspropertyset>
  43.588 +                <arg value="${test.class}"/>
  43.589 +                <arg value="showoutput=true"/>
  43.590 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
  43.591 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
  43.592 +            </customize>
  43.593 +        </j2seproject3:debug>
  43.594 +    </target>
  43.595 +    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
  43.596 +        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
  43.597 +    </target>
  43.598 +    <target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
  43.599 +    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
  43.600 +        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
  43.601 +    </target>
  43.602 +    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
  43.603 +    <!--
  43.604 +                =========================
  43.605 +                APPLET EXECUTION SECTION
  43.606 +                =========================
  43.607 +            -->
  43.608 +    <target depends="init,compile-single" name="run-applet">
  43.609 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
  43.610 +        <j2seproject1:java classname="sun.applet.AppletViewer">
  43.611 +            <customize>
  43.612 +                <arg value="${applet.url}"/>
  43.613 +            </customize>
  43.614 +        </j2seproject1:java>
  43.615 +    </target>
  43.616 +    <!--
  43.617 +                =========================
  43.618 +                APPLET DEBUGGING  SECTION
  43.619 +                =========================
  43.620 +            -->
  43.621 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
  43.622 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
  43.623 +        <j2seproject3:debug classname="sun.applet.AppletViewer">
  43.624 +            <customize>
  43.625 +                <arg value="${applet.url}"/>
  43.626 +            </customize>
  43.627 +        </j2seproject3:debug>
  43.628 +    </target>
  43.629 +    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
  43.630 +    <!--
  43.631 +                ===============
  43.632 +                CLEANUP SECTION
  43.633 +                ===============
  43.634 +            -->
  43.635 +    <target depends="init" name="deps-clean" unless="no.deps"/>
  43.636 +    <target depends="init" name="-do-clean">
  43.637 +        <delete dir="${build.dir}"/>
  43.638 +        <delete dir="${dist.dir}"/>
  43.639 +    </target>
  43.640 +    <target name="-post-clean">
  43.641 +        <!-- Empty placeholder for easier customization. -->
  43.642 +        <!-- You can override this target in the ../build.xml file. -->
  43.643 +    </target>
  43.644 +    <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
  43.645 +</project>
    44.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    44.2 +++ b/task4/solution07/nbproject/genfiles.properties	Sat Oct 18 07:47:34 2008 +0200
    44.3 @@ -0,0 +1,8 @@
    44.4 +build.xml.data.CRC32=2ab820eb
    44.5 +build.xml.script.CRC32=58a52595
    44.6 +build.xml.stylesheet.CRC32=be360661
    44.7 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
    44.8 +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
    44.9 +nbproject/build-impl.xml.data.CRC32=979fc7ba
   44.10 +nbproject/build-impl.xml.script.CRC32=92452d37
   44.11 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5
    45.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    45.2 +++ b/task4/solution07/nbproject/project.properties	Sat Oct 18 07:47:34 2008 +0200
    45.3 @@ -0,0 +1,68 @@
    45.4 +application.title=currency
    45.5 +application.vendor=apidesign.org
    45.6 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8
    45.7 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
    45.8 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=default
    45.9 +build.classes.dir=${build.dir}/classes
   45.10 +build.classes.excludes=**/*.java,**/*.form
   45.11 +# This directory is removed when the project is cleaned:
   45.12 +build.dir=build
   45.13 +build.generated.dir=${build.dir}/generated
   45.14 +# Only compile against the classpath explicitly listed here:
   45.15 +build.sysclasspath=ignore
   45.16 +build.test.classes.dir=${build.dir}/test/classes
   45.17 +build.test.results.dir=${build.dir}/test/results
   45.18 +debug.classpath=\
   45.19 +    ${run.classpath}
   45.20 +debug.test.classpath=\
   45.21 +    ${run.test.classpath}
   45.22 +# This directory is removed when the project is cleaned:
   45.23 +dist.dir=dist
   45.24 +dist.jar=${dist.dir}/currency.jar
   45.25 +dist.javadoc.dir=${dist.dir}/javadoc
   45.26 +excludes=
   45.27 +file.reference.junit-4.4.jar=../../libs/junit-4.4.jar
   45.28 +file.reference.src-apifest08=..
   45.29 +includes=**
   45.30 +jar.compress=false
   45.31 +javac.classpath=
   45.32 +# Space-separated list of extra javac options
   45.33 +javac.compilerargs=
   45.34 +javac.deprecation=false
   45.35 +javac.source=1.5
   45.36 +javac.target=1.5
   45.37 +javac.test.classpath=\
   45.38 +    ${javac.classpath}:\
   45.39 +    ${build.classes.dir}:\
   45.40 +    ${file.reference.junit-4.4.jar}
   45.41 +javadoc.additionalparam=
   45.42 +javadoc.author=false
   45.43 +javadoc.encoding=
   45.44 +javadoc.noindex=false
   45.45 +javadoc.nonavbar=false
   45.46 +javadoc.notree=false
   45.47 +javadoc.private=false
   45.48 +javadoc.splitindex=true
   45.49 +javadoc.use=true
   45.50 +javadoc.version=false
   45.51 +javadoc.windowtitle=
   45.52 +jnlp.codebase.type=local
   45.53 +jnlp.codebase.url=file:/home/jarda/src/apifest08/currency/dist
   45.54 +jnlp.descriptor=application
   45.55 +jnlp.enabled=false
   45.56 +jnlp.offline-allowed=false
   45.57 +jnlp.signed=false
   45.58 +meta.inf.dir=${src.dir}/META-INF
   45.59 +platform.active=default_platform
   45.60 +run.classpath=\
   45.61 +    ${javac.classpath}:\
   45.62 +    ${build.classes.dir}
   45.63 +# Space-separated list of JVM arguments used when running the project
   45.64 +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
   45.65 +# or test-sys-prop.name=value to set system properties for unit tests):
   45.66 +run.jvmargs=
   45.67 +run.test.classpath=\
   45.68 +    ${javac.test.classpath}:\
   45.69 +    ${build.test.classes.dir}
   45.70 +src.dir=src
   45.71 +test.src.dir=test
    46.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    46.2 +++ b/task4/solution07/nbproject/project.xml	Sat Oct 18 07:47:34 2008 +0200
    46.3 @@ -0,0 +1,16 @@
    46.4 +<?xml version="1.0" encoding="UTF-8"?>
    46.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
    46.6 +    <type>org.netbeans.modules.java.j2seproject</type>
    46.7 +    <configuration>
    46.8 +        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
    46.9 +            <name>Currency Convertor Solution 07</name>
   46.10 +            <minimum-ant-version>1.6.5</minimum-ant-version>
   46.11 +            <source-roots>
   46.12 +                <root id="src.dir"/>
   46.13 +            </source-roots>
   46.14 +            <test-roots>
   46.15 +                <root id="test.src.dir"/>
   46.16 +            </test-roots>
   46.17 +        </data>
   46.18 +    </configuration>
   46.19 +</project>
    47.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    47.2 +++ b/task4/solution07/src/org/apidesign/apifest08/currency/ConversionRate.java	Sat Oct 18 07:47:34 2008 +0200
    47.3 @@ -0,0 +1,91 @@
    47.4 +package org.apidesign.apifest08.currency;
    47.5 +
    47.6 +import java.math.BigDecimal;
    47.7 +import java.math.RoundingMode;
    47.8 +
    47.9 +/**
   47.10 + * A rate of conversion from one currency to another.
   47.11 + * @author jdvorak
   47.12 + */
   47.13 +public class ConversionRate {
   47.14 +
   47.15 +    private final MonetaryAmount srcUnitAmount;
   47.16 +    private final MonetaryAmount tgtUnitAmount;
   47.17 +    private final int tgtScale;
   47.18 +    private final RoundingMode roundingMode;
   47.19 +
   47.20 +    /**
   47.21 +     * A new conversion rate that gives tgtUnitAmount per every srcUnitAmount.
   47.22 +     * @param srcUnitAmount the amount of source currency
   47.23 +     * @param tgtUnitAmount the corresponding amount of target currency
   47.24 +     * @param tgtScale the scale of the target amounts
   47.25 +     * @param roundingMode the rounding mode to use when producing the target amounts
   47.26 +     */
   47.27 +    public ConversionRate( final MonetaryAmount srcUnitAmount, final MonetaryAmount tgtUnitAmount, final int targetScale, final RoundingMode roundingMode ) {
   47.28 +        this.srcUnitAmount = srcUnitAmount;
   47.29 +        this.tgtUnitAmount = tgtUnitAmount;
   47.30 +        this.tgtScale = targetScale;
   47.31 +        this.roundingMode = roundingMode;
   47.32 +    }
   47.33 +
   47.34 +    /**
   47.35 +     * A new conversion rate that gives tgtUnitAmount per every srcUnitAmount, default number of fraction digits and the given rounding mode.
   47.36 +     * @param srcUnitAmount the amount of source currency
   47.37 +     * @param tgtUnitAmount the corresponding amount of target currency
   47.38 +     * @param roundingMode the rounding mode to use
   47.39 +     */
   47.40 +    public ConversionRate( final MonetaryAmount srcUnitAmount, final MonetaryAmount tgtUnitAmount, final RoundingMode roundingMode ) {
   47.41 +        this( srcUnitAmount, tgtUnitAmount, tgtUnitAmount.getCurrency().getDefaultFractionDigits(), roundingMode );
   47.42 +    }
   47.43 +
   47.44 +    /**
   47.45 +     * A new conversion rate that gives tgtUnitAmount per every srcUnitAmount, default number of fraction digits and {@link RoundingMode#HALF_EVEN}.
   47.46 +     * @param srcUnitAmount the amount of source currency
   47.47 +     * @param tgtUnitAmount the corresponding amount of target currency
   47.48 +     */
   47.49 +    public ConversionRate( final MonetaryAmount srcUnitAmount, final MonetaryAmount tgtUnitAmount ) {
   47.50 +        this( srcUnitAmount, tgtUnitAmount, RoundingMode.HALF_EVEN );
   47.51 +    }
   47.52 +
   47.53 +    public RoundingMode getRoundingMode() {
   47.54 +        return roundingMode;
   47.55 +    }
   47.56 +
   47.57 +    public MonetaryAmount getSrcUnitAmount() {
   47.58 +        return srcUnitAmount;
   47.59 +    }
   47.60 +
   47.61 +    public int getTgtScale() {
   47.62 +        return tgtScale;
   47.63 +    }
   47.64 +
   47.65 +    public MonetaryAmount getTgtUnitAmount() {
   47.66 +        return tgtUnitAmount;
   47.67 +    }
   47.68 +    
   47.69 +    /**
   47.70 +     * Multiplies the given amount with the given rate.
   47.71 +     * @param srcAmount
   47.72 +     * @return
   47.73 +     */
   47.74 +    public BigDecimal convert( final BigDecimal srcAmount ) {
   47.75 +        return srcAmount
   47.76 +            .multiply( tgtUnitAmount.getAmount() )
   47.77 +            .divide( srcUnitAmount.getAmount(), tgtScale, roundingMode );
   47.78 +    }
   47.79 +    
   47.80 +    /**
   47.81 +     * Creates a monetary amount that corresponds to the given source amount multiplied by the rate.
   47.82 +     * @param srcAmount the source amount
   47.83 +     * @return the monetary amount in the target currency
   47.84 +     * @throws IllegalArgumentException if the currency of srcAmount is not equal to the source currency of this rate
   47.85 +     */
   47.86 +    public MonetaryAmount convert( final MonetaryAmount srcAmount ) {
   47.87 +        if ( srcUnitAmount.getCurrency().equals( srcAmount.getCurrency() ) ) {
   47.88 +            return new MonetaryAmount( convert( srcAmount.getAmount() ), tgtUnitAmount.getCurrency() );
   47.89 +        } else {
   47.90 +            throw new IllegalArgumentException( "This rate converts from " + srcUnitAmount.getCurrency() + ", but a conversion from " + srcAmount.getCurrency() + " is attempted" );
   47.91 +        }
   47.92 +    }
   47.93 +    
   47.94 +}
    48.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    48.2 +++ b/task4/solution07/src/org/apidesign/apifest08/currency/Convertor.java	Sat Oct 18 07:47:34 2008 +0200
    48.3 @@ -0,0 +1,109 @@
    48.4 +package org.apidesign.apifest08.currency;
    48.5 +
    48.6 +import java.util.Currency;
    48.7 +
    48.8 +/** This is the skeleton class for your API. You need to make it public, so
    48.9 + * it is accessible to your client code (currently in Task1Test.java) file.
   48.10 + * <p>
   48.11 + * Feel free to create additional classes or rename this one, just keep all
   48.12 + * the API and its implementation in this package. Do not spread it outside
   48.13 + * to other packages.
   48.14 + */
   48.15 +public interface Convertor {
   48.16 +    
   48.17 +    /**
   48.18 +     * Converts by taking a request and producing a response.
   48.19 +     * If a convertor finds it cannot perform the requested conversion,
   48.20 +     * it should return a non-null {@link ConversionResult} that has null {@link ConversionResult#getNetAmount()}.
   48.21 +     * A convertor must not convert to a different currency than the one specified in the request.
   48.22 +     * <p>
   48.23 +     * When the need comes to extend the semantics, one subclasses the ConversionRequest and/or ConversionResult classes.
   48.24 +     * <p>
   48.25 +     * This method can be called as many times as you like.
   48.26 +     * A {@link Convertor} shall be considered immutable wrt calls to {@link #convert(org.apidesign.apifest08.currency.Convertor.ConversionRequest).
   48.27 +     * This method of a single {@link Convertor} can be called from many threads concurrently.
   48.28 +     * @param req the conversion request; mustn't be null
   48.29 +     * @return the result of carrying out the conversion request; never null
   48.30 +     * @throws IllegalRequestSubtypeException when the particular implementation cannot handle a specific ConversionRequest type
   48.31 +     */
   48.32 +    public ConversionResult convert( final ConversionRequest req ) throws IllegalRequestSubtypeException;
   48.33 +
   48.34 +    /**
   48.35 +     * The request for converting a monetary amout into another currency.
   48.36 +     * Immutable.
   48.37 +     */
   48.38 +    public class ConversionRequest {
   48.39 +        
   48.40 +        private final MonetaryAmount srcAmount;
   48.41 +        private final Currency tgtCurrency;
   48.42 +
   48.43 +        /**
   48.44 +         * A request to convert srcAmount into tgtCurrency.
   48.45 +         * @param srcAmount the source amount; must not be null
   48.46 +         * @param tgtCurrency the currency we want it in afterwards; must not be null
   48.47 +         */
   48.48 +        public ConversionRequest( final MonetaryAmount srcAmount, final Currency tgtCurrency ) {
   48.49 +            this.srcAmount = srcAmount;
   48.50 +            this.tgtCurrency = tgtCurrency;
   48.51 +            if ( srcAmount == null ) {
   48.52 +                throw new NullPointerException( "The source amount" );
   48.53 +            }
   48.54 +            if ( tgtCurrency == null ) {
   48.55 +                throw new NullPointerException( "The target currency" );
   48.56 +            }
   48.57 +            if ( srcAmount.getCurrency().equals( tgtCurrency ) ) {
   48.58 +                throw new IllegalArgumentException( "Cannot request conversion from " + srcAmount.getCurrency() + " to " + tgtCurrency );
   48.59 +            }
   48.60 +        }
   48.61 +
   48.62 +        /**
   48.63 +         * The source amount.
   48.64 +         */
   48.65 +        public MonetaryAmount getSrcAmount() {
   48.66 +            return srcAmount;
   48.67 +        }
   48.68 +
   48.69 +        /**
   48.70 +         * The target currency.
   48.71 +         */
   48.72 +        public Currency getTgtCurrency() {
   48.73 +            return tgtCurrency;
   48.74 +        }
   48.75 +        
   48.76 +    }
   48.77 +    
   48.78 +    /**
   48.79 +     * The result of converting a monetary amount into another currency.
   48.80 +     * For now it records just the net amount one recieves from the conversion.
   48.81 +     * Immutable.
   48.82 +     * <p>
   48.83 +     * <b>Extension note:</b> 
   48.84 +     * Other items can be added further down the road, as the need for them arises.
   48.85 +     * These items might provide info on other aspects of the conversion,
   48.86 +     * such as the fee or a reason why the conversion might not be admissible.
   48.87 +     */
   48.88 +    public class ConversionResult {
   48.89 +        
   48.90 +        private final MonetaryAmount netAmount;
   48.91 +        
   48.92 +        /**
   48.93 +         * A new conversion result.
   48.94 +         * @param netAmount the amount one recieves from the conversion; 
   48.95 +         * null means the conversion was not admissible
   48.96 +         */
   48.97 +        public ConversionResult( final MonetaryAmount netAmount ) {
   48.98 +            this.netAmount = netAmount;
   48.99 +        }
  48.100 +        
  48.101 +        /**
  48.102 +         * The amount one recieves from the conversion.
  48.103 +         * If null, the conversion is not admissible.
  48.104 +         * @return the amount
  48.105 +         */
  48.106 +        public MonetaryAmount getNetAmount() {
  48.107 +            return netAmount;
  48.108 +        }
  48.109 +        
  48.110 +    }
  48.111 +    
  48.112 +}
    49.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    49.2 +++ b/task4/solution07/src/org/apidesign/apifest08/currency/DelegatingConvertor.java	Sat Oct 18 07:47:34 2008 +0200
    49.3 @@ -0,0 +1,28 @@
    49.4 +/*
    49.5 + * To change this template, choose Tools | Templates
    49.6 + * and open the template in the editor.
    49.7 + */
    49.8 +
    49.9 +package org.apidesign.apifest08.currency;
   49.10 +
   49.11 +/**
   49.12 + *
   49.13 + * @author jdvorak
   49.14 + */
   49.15 +public class DelegatingConvertor implements Convertor {
   49.16 +    
   49.17 +    private final Convertor underlyingConvertor;
   49.18 +
   49.19 +    public DelegatingConvertor( final Convertor underlyingConvertor ) {
   49.20 +        this.underlyingConvertor = underlyingConvertor;
   49.21 +    }
   49.22 +
   49.23 +    protected Convertor getUnderlyingConvertor() {
   49.24 +        return underlyingConvertor;
   49.25 +    }
   49.26 +    
   49.27 +    public ConversionResult convert( final ConversionRequest req ) {
   49.28 +        return underlyingConvertor.convert( req );
   49.29 +    }
   49.30 +    
   49.31 +}
    50.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    50.2 +++ b/task4/solution07/src/org/apidesign/apifest08/currency/IllegalRequestSubtypeException.java	Sat Oct 18 07:47:34 2008 +0200
    50.3 @@ -0,0 +1,30 @@
    50.4 +/*
    50.5 + * To change this template, choose Tools | Templates
    50.6 + * and open the template in the editor.
    50.7 + */
    50.8 +
    50.9 +package org.apidesign.apifest08.currency;
   50.10 +
   50.11 +/**
   50.12 + * Rised when a {@link Convertor} implementation cannot handle a particular subtype of {@link Convertor.ConversionRequest}.
   50.13 + * @author jdvorak
   50.14 + */
   50.15 +public class IllegalRequestSubtypeException extends IllegalArgumentException {
   50.16 +
   50.17 +    public IllegalRequestSubtypeException() {
   50.18 +        super();
   50.19 +    }
   50.20 +    
   50.21 +    public IllegalRequestSubtypeException( final String msg ) {
   50.22 +        super( msg );
   50.23 +    }
   50.24 +    
   50.25 +    public IllegalRequestSubtypeException( final Throwable cause ) {
   50.26 +        super( cause );
   50.27 +    }
   50.28 +    
   50.29 +    public IllegalRequestSubtypeException( final String msg, final Throwable cause ) {
   50.30 +        super( msg, cause );
   50.31 +    }
   50.32 +    
   50.33 +}
    51.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    51.2 +++ b/task4/solution07/src/org/apidesign/apifest08/currency/MonetaryAmount.java	Sat Oct 18 07:47:34 2008 +0200
    51.3 @@ -0,0 +1,89 @@
    51.4 +package org.apidesign.apifest08.currency;
    51.5 +
    51.6 +import java.math.BigDecimal;
    51.7 +import java.util.Currency;
    51.8 +
    51.9 +/**
   51.10 + * An amount of a currency.
   51.11 + * Immutable.
   51.12 + * @author jdvorak
   51.13 + */
   51.14 +public class MonetaryAmount {
   51.15 +
   51.16 +    private final BigDecimal amount;
   51.17 +    private final Currency currency;
   51.18 +    
   51.19 +    /**
   51.20 +     * A new amount.
   51.21 +     * @param amount the quantity of the currency; must not be null
   51.22 +     * @param currency the currency; must not be null
   51.23 +     */
   51.24 +    public MonetaryAmount( final BigDecimal amount, final Currency currency ) {
   51.25 +        this.amount = amount;
   51.26 +        this.currency = currency;
   51.27 +        if ( amount == null ) {
   51.28 +            throw new NullPointerException( "The amount" );
   51.29 +        }
   51.30 +        if ( currency == null ) {
   51.31 +            throw new NullPointerException( "The currency" );
   51.32 +        }
   51.33 +    }
   51.34 +    
   51.35 +    /**
   51.36 +     * A new amount.
   51.37 +     * @param amount the quantity of the currency; must not be null
   51.38 +     * @param currency the currency; must not be null
   51.39 +     */
   51.40 +    public MonetaryAmount( final double amount, final Currency currency ) {
   51.41 +        this( new BigDecimal( amount ), currency );
   51.42 +    }
   51.43 +    
   51.44 +    /**
   51.45 +     * The amount.
   51.46 +     * @return the amount
   51.47 +     */
   51.48 +    public BigDecimal getAmount() {
   51.49 +        return amount;
   51.50 +    }
   51.51 +    
   51.52 +    /**
   51.53 +     * The currency.
   51.54 +     * @return the currency
   51.55 +     */
   51.56 +    public Currency getCurrency() {
   51.57 +        return currency;
   51.58 +    }
   51.59 +
   51.60 +    /**
   51.61 +     * The string representation of the monetary amount.
   51.62 +     * @return the amount, a non-breakable space, the currency
   51.63 +     */
   51.64 +    @Override
   51.65 +    public String toString() {
   51.66 +        return amount.toPlainString() + "\u00a0" + currency.toString();
   51.67 +    }
   51.68 +    
   51.69 +    /**
   51.70 +     * Two monetary amounts are equal to each other iff they have equal amounts of equal currencies.
   51.71 +     * @param other the other object
   51.72 +     * @return equality
   51.73 +     */
   51.74 +    @Override
   51.75 +    public boolean equals( final Object other ) {
   51.76 +        if ( other instanceof MonetaryAmount ) {
   51.77 +            final MonetaryAmount otherMonetaryAmount = (MonetaryAmount) other;
   51.78 +            return getAmount().equals( otherMonetaryAmount.getAmount() ) && getCurrency().equals( otherMonetaryAmount.getCurrency() );
   51.79 +        }
   51.80 +        return false;
   51.81 +    }
   51.82 +    
   51.83 +    /**
   51.84 +     * The hash code combines the hash codes of the amount and of the currency.
   51.85 +     * @return hash code
   51.86 +     */
   51.87 +    @Override
   51.88 +    public int hashCode() {
   51.89 +        return amount.hashCode() * 37 + currency.hashCode();
   51.90 +    }
   51.91 +    
   51.92 +}
    52.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    52.2 +++ b/task4/solution07/src/org/apidesign/apifest08/currency/TableConvertor.java	Sat Oct 18 07:47:34 2008 +0200
    52.3 @@ -0,0 +1,71 @@
    52.4 +package org.apidesign.apifest08.currency;
    52.5 +
    52.6 +import java.util.Currency;
    52.7 +import java.util.HashMap;
    52.8 +import java.util.Map;
    52.9 +
   52.10 +/**
   52.11 + * A {@link Convertor} that works from a pre-set conversion table.
   52.12 + * First use {@link #putIntoTable(org.apidesign.apifest08.currency.ConversionRate)} to set the conversion table.
   52.13 + * Then invoke the {@link #convert(org.apidesign.apifest08.currency.Convertor.ConversionRequest)} method as many times as you wish.
   52.14 + * @author jdvorak
   52.15 + */
   52.16 +public class TableConvertor implements Convertor {
   52.17 +
   52.18 +    private final Map<Currency, Map<Currency, ConversionRate>> conversionTable = new HashMap<Currency, Map<Currency, ConversionRate>>();
   52.19 +    
   52.20 +    public TableConvertor() {
   52.21 +    }
   52.22 +
   52.23 +    /**
   52.24 +     * Puts a rate into the table.
   52.25 +     * @param rate
   52.26 +     */
   52.27 +    public void putIntoTable( final ConversionRate rate ) {
   52.28 +        final Currency srcCurrency = rate.getSrcUnitAmount().getCurrency();
   52.29 +        final Currency tgtCurrency = rate.getTgtUnitAmount().getCurrency();
   52.30 +        synchronized ( conversionTable ) {
   52.31 +            Map<Currency, ConversionRate> targetTable = conversionTable.get( srcCurrency );
   52.32 +            if ( targetTable == null ) {
   52.33 +                targetTable = new HashMap<Currency, ConversionRate>();
   52.34 +                conversionTable.put( srcCurrency, targetTable );
   52.35 +            }
   52.36 +            targetTable.put( tgtCurrency, rate );
   52.37 +        }
   52.38 +    }
   52.39 +    
   52.40 +    /**
   52.41 +     * Carries out the conversion.
   52.42 +     * If the table does not contain a conversion from the source currency to the target one,
   52.43 +     * a {@link ConversionResult} is returned that has null netAmount.
   52.44 +     * This implementation works with any {@link ConversionRequest}, it won't throw {@link IllegalRequestSubtypeException}.
   52.45 +     * @param req the conversion request
   52.46 +     * @return the conversion result
   52.47 +     */
   52.48 +    public ConversionResult convert( final ConversionRequest req ) {
   52.49 +        final Currency srcCurrency = req.getSrcAmount().getCurrency();
   52.50 +        final Currency tgtCurrency = req.getTgtCurrency();
   52.51 +        final ConversionRate rate = findConversionRate( srcCurrency, tgtCurrency );
   52.52 +        if ( rate != null ) {
   52.53 +            final MonetaryAmount tgtAmount = rate.convert( req.getSrcAmount() );
   52.54 +            return new ConversionResult( tgtAmount );
   52.55 +        } else {
   52.56 +            return new ConversionResult( null );    // did not find the pair of currencies in the table
   52.57 +        }
   52.58 +    }
   52.59 +
   52.60 +    /**
   52.61 +     * Looks up the conversion between the given currencies in the table.
   52.62 +     * @param srcCurrency the source currency
   52.63 +     * @param tgtCurrency the target currency
   52.64 +     * @return the conversion rate; null means no conversion between the currencies was found in the table
   52.65 +     */
   52.66 +    protected ConversionRate findConversionRate( final Currency srcCurrency, final Currency tgtCurrency ) {
   52.67 +        synchronized ( conversionTable ) {
   52.68 +            final Map<Currency, ConversionRate> targetTable = conversionTable.get(srcCurrency);
   52.69 +            final ConversionRate rate = (targetTable != null) ? targetTable.get(tgtCurrency) : null;
   52.70 +            return rate;
   52.71 +        }
   52.72 +    }
   52.73 +
   52.74 +}
    53.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    53.2 +++ b/task4/solution07/src/org/apidesign/apifest08/currency/TimeRangeSpecificConvertor.java	Sat Oct 18 07:47:34 2008 +0200
    53.3 @@ -0,0 +1,109 @@
    53.4 +package org.apidesign.apifest08.currency;
    53.5 +
    53.6 +import java.util.Currency;
    53.7 +import java.util.Date;
    53.8 +import org.apidesign.apifest08.currency.Convertor.ConversionRequest;
    53.9 +import org.apidesign.apifest08.currency.Convertor.ConversionResult;
   53.10 +
   53.11 +/**
   53.12 + * This {@link Convertor} delegates to an underlying one, provided that the request specifies time between from (included) and till (excluded).
   53.13 + * Otherwise it just refuses to convert.
   53.14 + * @author jdvorak
   53.15 + */
   53.16 +public class TimeRangeSpecificConvertor extends DelegatingConvertor {
   53.17 +
   53.18 +    private final Date from;
   53.19 +    private final Date till;
   53.20 +
   53.21 +    /**
   53.22 +     * A new time range specific convertor.
   53.23 +     * @param old the underlying convertor one delegates to
   53.24 +     * @param from the beginning of the time interval
   53.25 +     * @param till the end of the time interval
   53.26 +     * @throws IllegalArgumentException unless from comes before till
   53.27 +     */
   53.28 +    public TimeRangeSpecificConvertor( final Convertor old, final Date from, final Date till ) {
   53.29 +        super( old );
   53.30 +        this.from = from;
   53.31 +        this.till = till;
   53.32 +        if (! from.before( till ) ) {
   53.33 +            throw new IllegalArgumentException( "from must come before till" );
   53.34 +        }
   53.35 +    }
   53.36 +
   53.37 +    /**
   53.38 +     * The beginning of the time interval.
   53.39 +     */
   53.40 +    public Date getFrom() {
   53.41 +        return from;
   53.42 +    }
   53.43 +
   53.44 +    /**
   53.45 +     * The end of the time interval.
   53.46 +     */
   53.47 +    public Date getTill() {
   53.48 +        return till;
   53.49 +    }
   53.50 +
   53.51 +    /**
   53.52 +     * The conversion method.
   53.53 +     * Takes a {@link TimeSpecificConversionRequest}, other {@link ConversionRequest}s will result in an exception being thrown.
   53.54 +     * @param req the request; must not be null; must be {@link TimeSpecificConversionRequest} or a subclass thereof
   53.55 +     * @return the response
   53.56 +     * @throws IllegalRequestSubtypeException iff req is not instance of {@link TimeSpecificConversionRequest}
   53.57 +     */
   53.58 +    @Override
   53.59 +    public ConversionResult convert( final ConversionRequest req ) {
   53.60 +        if ( req instanceof TimeSpecificConversionRequest ) {
   53.61 +            final Date time = ( (TimeSpecificConversionRequest) req ).getTime();
   53.62 +            if ( ( from == null || !from.after(time) ) && ( till == null || time.before(till) ) ) {
   53.63 +                return super.convert( req );
   53.64 +            } else {
   53.65 +                return new ConversionResult( null );
   53.66 +            }
   53.67 +        } else {
   53.68 +            throw new IllegalRequestSubtypeException( this.getClass().getName() + ".convert() requires a TimeSpecificConversionRequest to be passed in" );
   53.69 +        }
   53.70 +    }
   53.71 +
   53.72 +    /**
   53.73 +     * The request for converting a monetary amount into another currency using the conditions that apply at a particular time.
   53.74 +     * Immutable.
   53.75 +     */
   53.76 +    public static class TimeSpecificConversionRequest extends Convertor.ConversionRequest {
   53.77 +
   53.78 +        private final Date time;
   53.79 +
   53.80 +        /**
   53.81 +         * A request to convert srcAmount into tgtCurrency at the current time.
   53.82 +         * @param srcAmount the source amount; must not be null
   53.83 +         * @param tgtCurrency the currency we want it in afterwards; must not be null
   53.84 +         */
   53.85 +        public TimeSpecificConversionRequest( final MonetaryAmount srcAmount, final Currency tgtCurrency ) {
   53.86 +            this( srcAmount, tgtCurrency, new Date() );
   53.87 +        }
   53.88 +
   53.89 +        /**
   53.90 +         * A request to convert srcAmount into tgtCurrency at given time.
   53.91 +         * @param srcAmount the source amount; must not be null
   53.92 +         * @param tgtCurrency the currency we want it in afterwards; must not be null
   53.93 +         * @param time the time instant when the conversion is to be carried out
   53.94 +         */
   53.95 +        public TimeSpecificConversionRequest( final MonetaryAmount srcAmount, final Currency tgtCurrency, final Date time ) {
   53.96 +            super( srcAmount, tgtCurrency );
   53.97 +            this.time = time;
   53.98 +            if ( time == null ) {
   53.99 +                throw new NullPointerException( "The time of conversion" );
  53.100 +            }
  53.101 +        }
  53.102 +
  53.103 +        /**
  53.104 +         * The time as of which the conversion is to be carried out.
  53.105 +         */
  53.106 +        public Date getTime() {
  53.107 +            return time;
  53.108 +        }
  53.109 +
  53.110 +    }
  53.111 +
  53.112 +}
    54.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    54.2 +++ b/task4/solution07/src/org/apidesign/apifest08/currency/anothervendor/ZigZaggingBidirectionalConvertor.java	Sat Oct 18 07:47:34 2008 +0200
    54.3 @@ -0,0 +1,130 @@
    54.4 +/*
    54.5 + * To change this template, choose Tools | Templates
    54.6 + * and open the template in the editor.
    54.7 + */
    54.8 +
    54.9 +package org.apidesign.apifest08.currency.anothervendor;
   54.10 +
   54.11 +import java.math.BigDecimal;
   54.12 +import java.util.Currency;
   54.13 +import java.util.Iterator;
   54.14 +import org.apidesign.apifest08.currency.Convertor;
   54.15 +import org.apidesign.apifest08.currency.IllegalRequestSubtypeException;
   54.16 +import org.apidesign.apifest08.currency.MonetaryAmount;
   54.17 +
   54.18 +
   54.19 +/**
   54.20 + * A convertor that changes the rate by a step amount on every conversion it performs.
   54.21 + * @author jdvorak
   54.22 + */
   54.23 +public class ZigZaggingBidirectionalConvertor implements Convertor {
   54.24 +
   54.25 +    private final MonetaryAmount srcRateAmount;
   54.26 +    
   54.27 +    private final Currency tgtCurrency;
   54.28 +    
   54.29 +    private final Iterator<BigDecimal> rateIterator;
   54.30 +
   54.31 +    /**
   54.32 +     * Costructor with the precise amounts.
   54.33 +     * @param srcRateAmount
   54.34 +     * @param tgtCurrency
   54.35 +     * @param startAmount
   54.36 +     * @param endAmount
   54.37 +     * @param stepAmount
   54.38 +     */
   54.39 +    public ZigZaggingBidirectionalConvertor( final MonetaryAmount srcRateAmount, final Currency tgtCurrency, final BigDecimal startAmount, final BigDecimal endAmount, final BigDecimal stepAmount ) {
   54.40 +        this.srcRateAmount = srcRateAmount;
   54.41 +        this.tgtCurrency = tgtCurrency;
   54.42 +        this.rateIterator = new ZigZaggingBigDecimalIterator( startAmount, endAmount, stepAmount );
   54.43 +    }
   54.44 +
   54.45 +    /**
   54.46 +     * Do the conversion; updates the rate.
   54.47 +     * @param req
   54.48 +     * @return
   54.49 +     * @throws org.apidesign.apifest08.currency.IllegalRequestSubtypeException
   54.50 +     */
   54.51 +    public synchronized ConversionResult convert( ConversionRequest req ) throws IllegalRequestSubtypeException {
   54.52 +        if ( tgtCurrency.equals( req.getTgtCurrency() ) ) {
   54.53 +            if ( srcRateAmount.getCurrency().equals( req.getSrcAmount().getCurrency() ) ) {
   54.54 +                final BigDecimal tgtRateAmount = rateIterator.next();
   54.55 +                final BigDecimal tgtAmount = req.getSrcAmount().getAmount().multiply( tgtRateAmount ).divide( srcRateAmount.getAmount() );
   54.56 +                return new ConversionResult( new MonetaryAmount( tgtAmount, tgtCurrency ) );
   54.57 +            }
   54.58 +        }
   54.59 +        if ( srcRateAmount.getCurrency().equals( req.getTgtCurrency() ) ) {
   54.60 +            if ( tgtCurrency.equals( req.getSrcAmount().getCurrency() ) ) {
   54.61 +                final BigDecimal tgtRateAmount = rateIterator.next();
   54.62 +                final BigDecimal tgtAmount = req.getSrcAmount().getAmount().multiply( srcRateAmount.getAmount() ).divide( tgtRateAmount );
   54.63 +                return new ConversionResult( new MonetaryAmount( tgtAmount, srcRateAmount.getCurrency() ) );
   54.64 +            }
   54.65 +        }
   54.66 +        return new ConversionResult( null );
   54.67 +    }
   54.68 +
   54.69 +}
   54.70 +
   54.71 +/**
   54.72 + * An iterator that goes zig first, then zag, then zig again, then zag again, ad libitum.
   54.73 + * @author jdvorak
   54.74 + */
   54.75 +class ZigZaggingBigDecimalIterator implements Iterator<BigDecimal> {
   54.76 +
   54.77 +    private BigDecimal zigBounce;
   54.78 +    
   54.79 +    private BigDecimal zagBounce;
   54.80 +    
   54.81 +    private BigDecimal step;
   54.82 +    
   54.83 +    private BigDecimal currentValue;
   54.84 +    
   54.85 +    protected ZigZaggingBigDecimalIterator( final BigDecimal zagBounce, final BigDecimal zigBounce, final BigDecimal step ) {
   54.86 +        this. zigBounce = zigBounce;
   54.87 +        this.zagBounce = zagBounce;
   54.88 +        this.currentValue = zagBounce;
   54.89 +        this.step = step;
   54.90 +        
   54.91 +        if (  zigBounce == null ) {
   54.92 +            throw new NullPointerException( "zigAmount" );
   54.93 +        }
   54.94 +        if ( zagBounce == null ) {
   54.95 +            throw new NullPointerException( "zagAmount" );
   54.96 +        }
   54.97 +        if ( step == null ) {
   54.98 +            throw new NullPointerException( "stepAmount" );
   54.99 +        }
  54.100 +        final int stepSign = step.signum();
  54.101 +        if ( stepSign == 0 ) {
  54.102 +            throw new IllegalArgumentException( "stepAmount can't be zero" );
  54.103 +        }
  54.104 +        if ( stepSign * zigBounce.compareTo( zagBounce ) < 0 ) {
  54.105 +            throw new IllegalArgumentException( "stepAmount shall have the same sign as endAmount - startAmount" );
  54.106 +        }
  54.107 +    }
  54.108 +    
  54.109 +    public boolean hasNext() {
  54.110 +        return true;
  54.111 +    }
  54.112 +
  54.113 +    public BigDecimal next() {
  54.114 +        final BigDecimal result = currentValue;
  54.115 +        
  54.116 +        currentValue = currentValue.add( step );
  54.117 +        final int stepSign = step.signum();
  54.118 +        final int currentMinusZigSign = currentValue.compareTo( zigBounce );
  54.119 +        if ( stepSign * currentMinusZigSign >= 0 ) {
  54.120 +            final BigDecimal temp = zigBounce;
  54.121 +            zigBounce = zagBounce;
  54.122 +            zagBounce = temp;
  54.123 +            step = step.negate();
  54.124 +        }
  54.125 +        
  54.126 +        return result;
  54.127 +    }
  54.128 +
  54.129 +    public void remove() {
  54.130 +        throw new UnsupportedOperationException("Removal is not supported.");
  54.131 +    }
  54.132 +    
  54.133 +}
  54.134 \ No newline at end of file
    55.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    55.2 +++ b/task4/solution07/test/org/apidesign/apifest08/test/ContractImposingDelegatingConvertor.java	Sat Oct 18 07:47:34 2008 +0200
    55.3 @@ -0,0 +1,53 @@
    55.4 +/*
    55.5 + * To change this template, choose Tools | Templates
    55.6 + * and open the template in the editor.
    55.7 + */
    55.8 +
    55.9 +package org.apidesign.apifest08.test;
   55.10 +
   55.11 +import java.math.BigDecimal;
   55.12 +import java.util.Currency;
   55.13 +import junit.framework.Assert;
   55.14 +import org.apidesign.apifest08.currency.Convertor;
   55.15 +import org.apidesign.apifest08.currency.DelegatingConvertor;
   55.16 +import org.apidesign.apifest08.currency.MonetaryAmount;
   55.17 +
   55.18 +/**
   55.19 + * A delegating convertor that checks preconditions and postconditions.
   55.20 + * Useful for testing.
   55.21 + * @author jdvorak
   55.22 + */
   55.23 +public class ContractImposingDelegatingConvertor extends DelegatingConvertor {
   55.24 +
   55.25 +    public ContractImposingDelegatingConvertor( final Convertor underlyingConvertor ) {
   55.26 +        super( underlyingConvertor );
   55.27 +    }
   55.28 +
   55.29 +    @Override
   55.30 +    public ConversionResult convert( final ConversionRequest req ) {
   55.31 +        Assert.assertNotNull( "The request", req );
   55.32 +        final ConversionResult result = super.convert( req );
   55.33 +        Assert.assertNotNull( "Result of the convert() call", result );
   55.34 +        final MonetaryAmount netAmount = result.getNetAmount();
   55.35 +        if ( netAmount != null ) {
   55.36 +            Assert.assertEquals( "Converted to a different currency than specified in the request", req.getTgtCurrency(), netAmount.getCurrency() );
   55.37 +        }
   55.38 +        return result;
   55.39 +    }
   55.40 +
   55.41 +    /**
   55.42 +     * Do some tests on our own.
   55.43 +     * @return this
   55.44 +     */
   55.45 +    public Convertor test() {
   55.46 +        try {
   55.47 +            final Currency aCurrency = Currency.getInstance( "EUR" );
   55.48 +            new ConversionRequest( new MonetaryAmount( BigDecimal.ONE, aCurrency ), aCurrency );
   55.49 +            Assert.fail( "Should have thrown an IllegalArgumentException" );
   55.50 +        } catch ( final IllegalArgumentException e ) {
   55.51 +            Assert.assertEquals( "Cannot request conversion from EUR to EUR", e.getMessage() );
   55.52 +        }
   55.53 +        return this;
   55.54 +    }
   55.55 +
   55.56 +}
    56.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    56.2 +++ b/task4/solution07/test/org/apidesign/apifest08/test/Task1Test.java	Sat Oct 18 07:47:34 2008 +0200
    56.3 @@ -0,0 +1,177 @@
    56.4 +package org.apidesign.apifest08.test;
    56.5 +
    56.6 +import java.util.Currency;
    56.7 +import junit.framework.TestCase;
    56.8 +import org.apidesign.apifest08.currency.ConversionRate;
    56.9 +import org.apidesign.apifest08.currency.Convertor;
   56.10 +import org.apidesign.apifest08.currency.MonetaryAmount;
   56.11 +import org.apidesign.apifest08.currency.TableConvertor;
   56.12 +
   56.13 +/** Finish the Convertor API, and then write bodies of methods inside
   56.14 + * of this class to match the given tasks. To fullfil your task, use the
   56.15 + * API define in the <code>org.apidesign.apifest08.currency</code> package.
   56.16 + * Do not you reflection, or other hacks as your code
   56.17 + * shall run without any runtime permissions.
   56.18 + */
   56.19 +public class Task1Test extends TestCase {
   56.20 +    public Task1Test(String testName) {
   56.21 +        super(testName);
   56.22 +    }
   56.23 +
   56.24 +    @Override
   56.25 +    protected void setUp() throws Exception {
   56.26 +    }
   56.27 +
   56.28 +    @Override
   56.29 +    protected void tearDown() throws Exception {
   56.30 +    }
   56.31 +
   56.32 +    //
   56.33 +    // Imagine that there are three parts of the whole system:
   56.34 +    // 1. there is someone who knows the current exchange rate
   56.35 +    // 2. there is someone who wants to do the conversion
   56.36 +    // 3. there is the API between 1. and 2. which allows them to communicate
   56.37 +    // Please design such API
   56.38 +    //
   56.39 +
   56.40 +    protected static final Currency CZK = Currency.getInstance( "CZK" );
   56.41 +    protected static final Currency SKK = Currency.getInstance( "SKK" );
   56.42 +    protected static final Currency USD = Currency.getInstance( "USD" );
   56.43 +    
   56.44 +    /** Create convertor that understands two currencies, CZK and
   56.45 +     *  USD. Make 1 USD == 17 CZK.
   56.46 +     *  USD. Make 1 USD == 17 CZK. This is a method provided for #1 group -
   56.47 +     *  e.g. those that know the exchange rate. They somehow need to create
   56.48 +     *  the objects from the API and tell them the exchange rate. The API itself
   56.49 +     *  knows nothing about any rates, before the createCZKtoUSD method is called.
   56.50 +     *
   56.51 +     * Creation of the convertor shall not require subclassing of any class
   56.52 +     * or interface on the client side.
   56.53 +     *
   56.54 +     * @return prepared convertor ready for converting USD to CZK and CZK to USD
   56.55 +     */
   56.56 +    public static Convertor createCZKtoUSD() {
   56.57 +        final TableConvertor convertor = new TableConvertor();
   56.58 +        final MonetaryAmount amountInCZK = new MonetaryAmount( 17, CZK );
   56.59 +        final MonetaryAmount amountInUSD = new MonetaryAmount( 1, USD );
   56.60 +        convertor.putIntoTable( new ConversionRate( amountInCZK, amountInUSD ) );
   56.61 +        convertor.putIntoTable( new ConversionRate( amountInUSD, amountInCZK ) );
   56.62 +        return new ContractImposingDelegatingConvertor( convertor ).test();
   56.63 +    }
   56.64 +
   56.65 +    /** Create convertor that understands two currencies, CZK and
   56.66 +     *  SKK. Make 100 SKK == 80 CZK. Again this is method for the #1 group -
   56.67 +     *  it knows the exchange rate, and needs to use the API to create objects
   56.68 +     *  with the exchange rate. Anyone shall be ready to call this method without
   56.69 +     *  any other method being called previously. The API itself shall know
   56.70 +     *  nothing about any rates, before this method is called.
   56.71 +     *
   56.72 +     * Creation of the convertor shall not require subclassing of any class
   56.73 +     * or interface on the client side.
   56.74 +     * 
   56.75 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
   56.76 +     */
   56.77 +    public static Convertor createSKKtoCZK() {
   56.78 +        final TableConvertor convertor = new TableConvertor();
   56.79 +        final MonetaryAmount amountInSKK = new MonetaryAmount( 100, SKK );
   56.80 +        final MonetaryAmount amountInCZK = new MonetaryAmount( 80, CZK );
   56.81 +        convertor.putIntoTable( new ConversionRate( amountInSKK, amountInCZK ) );
   56.82 +        convertor.putIntoTable( new ConversionRate( amountInCZK, amountInSKK ) );
   56.83 +        return new ContractImposingDelegatingConvertor( convertor ).test();
   56.84 +    }
   56.85 +
   56.86 +    //
   56.87 +    // now the methods for group #2 follow:
   56.88 +    // this group knows nothing about exchange rates, but knows how to use
   56.89 +    // the API to do conversions. It somehow (by calling one of the factory
   56.90 +    // methods) gets objects from the API and uses them to do the conversions.
   56.91 +    //
   56.92 +
   56.93 +    /** Use the convertor from <code>createCZKtoUSD</code> method and do few conversions
   56.94 +     * with it.
   56.95 +     */
   56.96 +    public void testCurrencyCZKUSD() throws Exception {
   56.97 +        final Convertor c = createCZKtoUSD();
   56.98 +        
   56.99 +        // convert $5 to CZK using c:
  56.100 +        final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), CZK ) );
  56.101 +        final MonetaryAmount a1 = r1.getNetAmount();
  56.102 +        // assertEquals("Result is 85 CZK");
  56.103 +        assertNotNull( a1 );
  56.104 +        assertEquals( 85.0, a1.getAmount().doubleValue() );
  56.105 +        assertEquals( CZK, a1.getCurrency() );
  56.106 +
  56.107 +        // convert $8 to CZK
  56.108 +        final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 8, USD ), CZK ) );
  56.109 +        final MonetaryAmount a2 = r2.getNetAmount();
  56.110 +        // assertEquals("Result is 136 CZK");
  56.111 +        assertNotNull( a2 );
  56.112 +        assertEquals( 136.0, a2.getAmount().doubleValue() );
  56.113 +        assertEquals( CZK, a2.getCurrency() );
  56.114 +
  56.115 +        // convert 1003CZK to USD
  56.116 +        final Convertor.ConversionResult r3 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 1003, CZK ), USD ) );
  56.117 +        final MonetaryAmount a3 = r3.getNetAmount();
  56.118 +        // assertEquals("Result is 59 USD");
  56.119 +        assertNotNull( a3 );
  56.120 +        assertEquals( 59.0, a3.getAmount().doubleValue() );
  56.121 +        assertEquals( USD, a3.getCurrency() );
  56.122 +    }
  56.123 +
  56.124 +    /** Use the convertor from <code>createSKKtoCZK</code> method and do few conversions
  56.125 +     * with it.
  56.126 +     */
  56.127 +    public void testCurrencySKKCZK() throws Exception {
  56.128 +        final Convertor c = createSKKtoCZK();
  56.129 +        
  56.130 +        // convert 16CZK using c:
  56.131 +        final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 16, CZK ), SKK ) );
  56.132 +        final MonetaryAmount a1 = r1.getNetAmount();
  56.133 +        // assertEquals("Result is 20 SKK");
  56.134 +        assertNotNull( a1 );
  56.135 +        assertEquals( 20.0, a1.getAmount().doubleValue() );
  56.136 +        assertEquals( SKK, a1.getCurrency() );
  56.137 +        
  56.138 +        // convert 500SKK to CZK
  56.139 +        final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 500, SKK ), CZK ) );
  56.140 +        final MonetaryAmount a2 = r2.getNetAmount();
  56.141 +        // assertEquals("Result is 400 CZK");
  56.142 +        assertNotNull( a2 );
  56.143 +        assertEquals( 400.0, a2.getAmount().doubleValue() );
  56.144 +        assertEquals( CZK, a2.getCurrency() );
  56.145 +    }
  56.146 +
  56.147 +    /** Verify that the CZK to USD convertor knows nothing about SKK.
  56.148 +    */
  56.149 +    public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception {
  56.150 +        final Convertor c = createCZKtoUSD();
  56.151 +        
  56.152 +        // convert $5 to SKK, the API shall say this is not possible
  56.153 +        final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), SKK ) );
  56.154 +        final MonetaryAmount a1 = r1.getNetAmount();
  56.155 +        assertNull( a1 );
  56.156 +
  56.157 +        // convert 500 SKK to CZK, the API shall say this is not possible
  56.158 +        final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, SKK ), CZK ) );
  56.159 +        final MonetaryAmount a2 = r2.getNetAmount();
  56.160 +        assertNull( a2 );
  56.161 +    }
  56.162 +
  56.163 +    /** Verify that the CZK to SKK convertor knows nothing about USD.
  56.164 +    */
  56.165 +    public void testCannotConvertToUSDwithCZKSKKConvertor() throws Exception {
  56.166 +        final Convertor c = createSKKtoCZK();
  56.167 +        
  56.168 +        // convert $5 to SKK, the API shall say this is not possible
  56.169 +        final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), SKK ) );
  56.170 +        final MonetaryAmount a1 = r1.getNetAmount();
  56.171 +        assertNull( a1 );
  56.172 +        
  56.173 +        // convert 500 CZK to USD, the API shall say this is not possible
  56.174 +        final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, CZK ), USD ) );
  56.175 +        final MonetaryAmount a2 = r2.getNetAmount();
  56.176 +        assertNull( a2 );
  56.177 +    }
  56.178 +
  56.179 +}
  56.180 +
    57.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    57.2 +++ b/task4/solution07/test/org/apidesign/apifest08/test/Task2Test.java	Sat Oct 18 07:47:34 2008 +0200
    57.3 @@ -0,0 +1,192 @@
    57.4 +package org.apidesign.apifest08.test;
    57.5 +
    57.6 +import java.util.Currency;
    57.7 +import junit.framework.TestCase;
    57.8 +import org.apidesign.apifest08.currency.ConversionRate;
    57.9 +import org.apidesign.apifest08.currency.Convertor;
   57.10 +import org.apidesign.apifest08.currency.IllegalRequestSubtypeException;
   57.11 +import org.apidesign.apifest08.currency.MonetaryAmount;
   57.12 +import org.apidesign.apifest08.currency.TableConvertor;
   57.13 +
   57.14 +/** There are many currencies around the world and many banks manipulate
   57.15 + * with more than one or two at the same time. As banks are usually the
   57.16 + * best paying clients, which is true even in case of your Convertor API,
   57.17 + * it is reasonable to listen to their requests.
   57.18 + * <p>
   57.19 + * The quest for today is to enhance your existing convertor API to hold
   57.20 + * information about many currencies and allow conversions between any of them.
   57.21 + * Also, as conversion rates for diferent currencies usually arise from various
   57.22 + * bank departments, there is another important need. There is a need to
   57.23 + * compose two convertors into one by merging all the information about
   57.24 + * currencies they know about.
   57.25 + */
   57.26 +public class Task2Test extends TestCase {
   57.27 +    public Task2Test(String testName) {
   57.28 +        super(testName);
   57.29 +    }
   57.30 +
   57.31 +    @Override
   57.32 +    protected void setUp() throws Exception {
   57.33 +    }
   57.34 +
   57.35 +    @Override
   57.36 +    protected void tearDown() throws Exception {
   57.37 +    }
   57.38 +
   57.39 +    protected static final Currency CZK = Currency.getInstance( "CZK" );
   57.40 +    protected static final Currency SKK = Currency.getInstance( "SKK" );
   57.41 +    protected static final Currency USD = Currency.getInstance( "USD" );
   57.42 +
   57.43 +    // As in Task1Test, keep in mind, that there are three parts
   57.44 +    // of the whole system:
   57.45 +    // 1. there is someone who knows the current exchange rate
   57.46 +    // 2. there is someone who wants to do the conversion
   57.47 +    // 3. there is the API between 1. and 2. which allows them to communicate
   57.48 +    // 
   57.49 +    // Please backward compatibly enhance your existing API to support following
   57.50 +    // usecases:
   57.51 +    //
   57.52 +    
   57.53 +    /** Create convertor that understands two currencies, CZK and
   57.54 +     *  SKK. Make 100 SKK == 75 CZK. This is method for the group of users that
   57.55 +     *  knows the exchange rate, and needs to use the API to create objects
   57.56 +     *  with the exchange rate. Anyone shall be ready to call this method without
   57.57 +     *  any other method being called previously. The API itself shall know
   57.58 +     *  nothing about any rates, before this method is called.
   57.59 +     */
   57.60 +    public static Convertor createTripleConvertor() {
   57.61 +        // Rates: 1USD = 15CZK
   57.62 +        // Rates: 1USD = 20SKK
   57.63 +        // Rates: 75CZK = 100SKK
   57.64 +        final TableConvertor convertor = new TableConvertor();
   57.65 +        final MonetaryAmount amountInUSD = new MonetaryAmount( 1, USD );
   57.66 +        final MonetaryAmount amountInCZK = new MonetaryAmount( 15, CZK );
   57.67 +        final MonetaryAmount amountInSKK = new MonetaryAmount( 20, SKK );
   57.68 +        convertor.putIntoTable( new ConversionRate( amountInCZK, amountInUSD ) );
   57.69 +        convertor.putIntoTable( new ConversionRate( amountInUSD, amountInCZK ) );
   57.70 +        convertor.putIntoTable( new ConversionRate( amountInSKK, amountInUSD ) );
   57.71 +        convertor.putIntoTable( new ConversionRate( amountInUSD, amountInSKK ) );
   57.72 +        convertor.putIntoTable( new ConversionRate( amountInSKK, amountInCZK ) );
   57.73 +        convertor.putIntoTable( new ConversionRate( amountInCZK, amountInSKK ) );
   57.74 +        return new ContractImposingDelegatingConvertor( convertor ).test();
   57.75 +    }
   57.76 +
   57.77 +    /** Define convertor that understands three currencies. Use it.
   57.78 +     */
   57.79 +    public void testConvertorForUSDandCZKandSKK() throws Exception {
   57.80 +        final Convertor c = createTripleConvertor();
   57.81 +
   57.82 +        // convert $5 to CZK using c:
   57.83 +        final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), CZK ) );
   57.84 +        final MonetaryAmount a1 = r1.getNetAmount();
   57.85 +        // assertEquals("Result is 75 CZK");
   57.86 +        assertNotNull( a1 );
   57.87 +        assertEquals( 75.0, a1.getAmount().doubleValue() );
   57.88 +        assertEquals( CZK, a1.getCurrency() );
   57.89 +
   57.90 +        // convert $5 to SKK using c:
   57.91 +        final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), SKK ) );
   57.92 +        final MonetaryAmount a2 = r2.getNetAmount();
   57.93 +        // assertEquals("Result is 100 SKK");
   57.94 +        assertNotNull( a2 );
   57.95 +        assertEquals( 100.0, a2.getAmount().doubleValue() );
   57.96 +        assertEquals( SKK, a2.getCurrency() );
   57.97 +
   57.98 +        // convert 200SKK to CZK using c:
   57.99 +        final Convertor.ConversionResult r3 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 200, SKK ), CZK ) );
  57.100 +        final MonetaryAmount a3 = r3.getNetAmount();
  57.101 +        // assertEquals("Result is 150 CZK");
  57.102 +        assertNotNull( a3 );
  57.103 +        assertEquals( 150.0, a3.getAmount().doubleValue() );
  57.104 +        assertEquals( CZK, a3.getCurrency() );
  57.105 +
  57.106 +        // convert 200SKK to USK using c:
  57.107 +        final Convertor.ConversionResult r4 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 200, SKK ), USD ) );
  57.108 +        final MonetaryAmount a4 = r4.getNetAmount();
  57.109 +        // assertEquals("Result is 10 USD");
  57.110 +        assertNotNull( a4 );
  57.111 +        assertEquals( 10.0, a4.getAmount().doubleValue() );
  57.112 +        assertEquals( USD, a4.getCurrency() );
  57.113 +    }
  57.114 +
  57.115 +    /** Merge all currency rates of convertor 1 with convertor 2.
  57.116 +     * Implement this using your API, preferably this method just delegates
  57.117 +     * into some API method which does the actual work, without requiring
  57.118 +     * API clients to code anything complex.
  57.119 +     */
  57.120 +    public static Convertor merge( final Convertor one, final Convertor two ) {
  57.121 +        return new Convertor() {
  57.122 +
  57.123 +            public ConversionResult convert( ConversionRequest req ) throws IllegalRequestSubtypeException {
  57.124 +                final ConversionResult res1 = one.convert( req );
  57.125 +                final ConversionResult res2 = two.convert( req );
  57.126 +                if ( res1.getNetAmount() != null ) {
  57.127 +                    if ( res2.getNetAmount() != null ) {
  57.128 +                        // TODO check if they arrive at the same thing
  57.129 +                        return res1;
  57.130 +                    } else {
  57.131 +                        return res1;
  57.132 +                    }
  57.133 +                } else {
  57.134 +                    if ( res2.getNetAmount() != null ) {
  57.135 +                        return res2;
  57.136 +                    } else {
  57.137 +                        // neither converts
  57.138 +                        return new ConversionResult( null );
  57.139 +                    }                    
  57.140 +                }
  57.141 +            }
  57.142 +            
  57.143 +        };
  57.144 +    }
  57.145 +
  57.146 +    /** Join the convertors from previous task, Task1Test and show that it
  57.147 +     * can be used to do reasonable conversions.
  57.148 +     */
  57.149 +    public void testConvertorComposition() throws Exception {
  57.150 +        final Convertor c = merge(
  57.151 +            Task1Test.createCZKtoUSD(),
  57.152 +            Task1Test.createSKKtoCZK()
  57.153 +        );
  57.154 +
  57.155 +        // convert $5 to CZK using c:
  57.156 +        final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), CZK ) );
  57.157 +        final MonetaryAmount a1 = r1.getNetAmount();
  57.158 +        // assertEquals("Result is 85 CZK");
  57.159 +        assertNotNull( a1 );
  57.160 +        assertEquals( 85.0, a1.getAmount().doubleValue() );
  57.161 +        assertEquals( CZK, a1.getCurrency() );
  57.162 +
  57.163 +        // convert $8 to CZK
  57.164 +        final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 8, USD ), CZK ) );
  57.165 +        final MonetaryAmount a2 = r2.getNetAmount();
  57.166 +        // assertEquals("Result is 136 CZK");
  57.167 +        assertNotNull( a2 );
  57.168 +        assertEquals( 136.0, a2.getAmount().doubleValue() );
  57.169 +        assertEquals( CZK, a2.getCurrency() );
  57.170 +
  57.171 +        // convert 1003CZK to USD
  57.172 +        final Convertor.ConversionResult r3 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 1003, CZK ), USD ) );
  57.173 +        final MonetaryAmount a3 = r3.getNetAmount();
  57.174 +        // assertEquals("Result is 59 USD");
  57.175 +        assertNotNull( a3 );
  57.176 +        assertEquals( 59.0, a3.getAmount().doubleValue() );
  57.177 +        assertEquals( USD, a3.getCurrency() );
  57.178 +
  57.179 +        // convert 16CZK using c:
  57.180 +        final Convertor.ConversionResult r4 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 16, CZK ), SKK ) );
  57.181 +        final MonetaryAmount a4 = r4.getNetAmount();
  57.182 +        // assertEquals("Result is 20 SKK");
  57.183 +        assertNotNull( a4 );
  57.184 +        assertEquals( 20.0, a4.getAmount().doubleValue() );
  57.185 +        assertEquals( SKK, a4.getCurrency() );
  57.186 +
  57.187 +        // convert 500SKK to CZK using c:
  57.188 +        final Convertor.ConversionResult r5 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 500, SKK ), CZK ) );
  57.189 +        final MonetaryAmount a5 = r5.getNetAmount();
  57.190 +        // assertEquals("Result is 400 CZK");
  57.191 +        assertNotNull( a5 );
  57.192 +        assertEquals( 400.0, a5.getAmount().doubleValue() );
  57.193 +        assertEquals( CZK, a5.getCurrency() );
  57.194 +    }
  57.195 +}
    58.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    58.2 +++ b/task4/solution07/test/org/apidesign/apifest08/test/Task3Test.java	Sat Oct 18 07:47:34 2008 +0200
    58.3 @@ -0,0 +1,135 @@
    58.4 +package org.apidesign.apifest08.test;
    58.5 +
    58.6 +import java.math.BigDecimal;
    58.7 +import java.util.Currency;
    58.8 +import junit.framework.TestCase;
    58.9 +import org.apidesign.apifest08.currency.Convertor;
   58.10 +import org.apidesign.apifest08.currency.MonetaryAmount;
   58.11 +import org.apidesign.apifest08.currency.anothervendor.ZigZaggingBidirectionalConvertor;
   58.12 +
   58.13 +/** The exchange rates are not always the same. They are changing. Day by day,
   58.14 + * hour by hour, minute by minute. For every bank it is important to always
   58.15 + * have the actual exchange rate available in the system. That is why let's
   58.16 + * create a pluggable convertor that will always have up to date value of its
   58.17 + * exchange rate.
   58.18 + * <p>
   58.19 + * The quest for today is to allow 3rd party developer to write a convertor
   58.20 + * that adjusts its exchange rate everytime it is queried. This convertor is
   58.21 + * written by independent vendor, the vendor knows only your Convertor API,
   58.22 + * he does not know how the whole system looks and how the convertor is supposed
   58.23 + * to be used.
   58.24 + */
   58.25 +public class Task3Test extends TestCase {
   58.26 +    public Task3Test(String testName) {
   58.27 +        super(testName);
   58.28 +    }
   58.29 +
   58.30 +    @Override
   58.31 +    protected void setUp() throws Exception {
   58.32 +    }
   58.33 +
   58.34 +    @Override
   58.35 +    protected void tearDown() throws Exception {
   58.36 +    }
   58.37 +
   58.38 +    protected static final Currency CZK = Currency.getInstance( "CZK" );
   58.39 +    protected static final Currency SKK = Currency.getInstance( "SKK" );
   58.40 +    protected static final Currency USD = Currency.getInstance( "USD" );
   58.41 +
   58.42 +    // Backward compatibly enhance your existing API to support following
   58.43 +    // usecases:
   58.44 +    //
   58.45 +
   58.46 +
   58.47 +    /** Without knowing anything about the surrounding system, write an
   58.48 +     * implementation of convertor that will return different rates everytime
   58.49 +     * it is queried. Convert USD to CZK and vice versa. Start with the rate of
   58.50 +     * 1USD = 16CZK and adjust it in favor of CZK by 0.01 CZK with every query.
   58.51 +     * As soon as you reach 1USD = 15CZK adjust it by 0.01 CZK in favor of USD
   58.52 +     * until you reach 1USD = 16CZK
   58.53 +     *
   58.54 +     * @return new instance of "online" USD and CZK convertor starting with rate 1USD = 16CZK
   58.55 +     */
   58.56 +    public static Convertor createOnlineCZKUSDConvertor() {
   58.57 +        // initial rate: 1USD = 16CZK
   58.58 +        // 2nd query 1USD = 15.99CZK
   58.59 +        // 3rd query 1USD = 15.98CZK
   58.60 +        // until 1USD = 15.00CZK
   58.61 +        // then 1USD = 15.01CZK
   58.62 +        // then 1USD = 15.02CZK
   58.63 +        // and so on and on up to 1USD = 16CZK
   58.64 +        // and then another round to 15, etc.
   58.65 +        final MonetaryAmount oneUSD = new MonetaryAmount( 1, USD );
   58.66 +        final BigDecimal startAmount = new BigDecimal( 16 );
   58.67 +        final BigDecimal endAmount = new BigDecimal( 15 );
   58.68 +        final BigDecimal stepAmount = BigDecimal.ONE.movePointLeft( 2 ).negate();
   58.69 +        return new ContractImposingDelegatingConvertor( new ZigZaggingBidirectionalConvertor( oneUSD, CZK, startAmount, endAmount, stepAmount ) );
   58.70 +    }
   58.71 +
   58.72 +    public void testFewQueriesForOnlineConvertor() {
   58.73 +        Convertor c = createOnlineCZKUSDConvertor();
   58.74 +        doFewQueriesForOnlineConvertor(c);
   58.75 +    }
   58.76 +
   58.77 +    static void doFewQueriesForOnlineConvertor( final Convertor c ) {
   58.78 +        // convert $5 to CZK using c:
   58.79 +        final Convertor.ConversionResult r1 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 5, USD ), CZK ) );
   58.80 +        final MonetaryAmount a1 = r1.getNetAmount();
   58.81 +        //assertEquals("Result is 80 CZK");
   58.82 +        assertNotNull( a1 );
   58.83 +        assertEquals( 80.0, a1.getAmount().doubleValue() );
   58.84 +        assertEquals( CZK, a1.getCurrency() );
   58.85 +
   58.86 +        // convert $8 to CZK using c:
   58.87 +        final Convertor.ConversionResult r2 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 8, USD ), CZK ) );
   58.88 +        final MonetaryAmount a2 = r2.getNetAmount();
   58.89 +        //assertEquals("Result is 127.92 CZK");
   58.90 +        assertNotNull( a2 );
   58.91 +        assertEquals( 12792.0, a2.getAmount().movePointRight( 2 ).doubleValue() );
   58.92 +        assertEquals( CZK, a2.getCurrency() );
   58.93 +
   58.94 +        // convert $1 to CZK using c:
   58.95 +        final Convertor.ConversionResult r3 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 1, USD ), CZK ) );
   58.96 +        final MonetaryAmount a3 = r3.getNetAmount();
   58.97 +        //assertEquals("Result is 15.98 CZK");
   58.98 +        assertNotNull( a3 );
   58.99 +        assertEquals( 1598.0, a3.getAmount().movePointRight( 2 ).doubleValue() );
  58.100 +        assertEquals( CZK, a3.getCurrency() );
  58.101 +
  58.102 +        // convert 15.97CZK to USD using c:
  58.103 +        final BigDecimal s4 = new BigDecimal( 1597 ).movePointLeft( 2 );
  58.104 +        final Convertor.ConversionResult r4 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( s4, CZK ), USD ) );
  58.105 +        final MonetaryAmount a4 = r4.getNetAmount();
  58.106 +        //assertEquals("Result is 1$");
  58.107 +        assertNotNull( a4 );
  58.108 +        assertEquals( 1.0, a4.getAmount().doubleValue() );
  58.109 +        assertEquals( USD, a4.getCurrency() );
  58.110 +    }
  58.111 +
  58.112 +    /** Join the convertors and show they behave sane.
  58.113 +     */
  58.114 +    public void testOnlineConvertorComposition() throws Exception {
  58.115 +        final Convertor c = Task2Test.merge(
  58.116 +            createOnlineCZKUSDConvertor(),
  58.117 +            Task1Test.createSKKtoCZK()
  58.118 +        );
  58.119 +
  58.120 +        // convert 16CZK using c:
  58.121 +        final Convertor.ConversionResult r4 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 16, CZK ), SKK ) );
  58.122 +        final MonetaryAmount a4 = r4.getNetAmount();
  58.123 +        // assertEquals("Result is 20 SKK");
  58.124 +        assertNotNull( a4 );
  58.125 +        assertEquals( 20.0, a4.getAmount().doubleValue() );
  58.126 +        assertEquals( SKK, a4.getCurrency() );
  58.127 +
  58.128 +        // convert 500SKK to CZK using c:
  58.129 +        final Convertor.ConversionResult r5 = c.convert( new Convertor.ConversionRequest( new MonetaryAmount( 500, SKK ), CZK ) );
  58.130 +        final MonetaryAmount a5 = r5.getNetAmount();
  58.131 +        // assertEquals("Result is 400 CZK");
  58.132 +        assertNotNull( a5 );
  58.133 +        assertEquals( 400.0, a5.getAmount().doubleValue() );
  58.134 +        assertEquals( CZK, a5.getCurrency() );
  58.135 +
  58.136 +        doFewQueriesForOnlineConvertor(c);
  58.137 +    }
  58.138 +}
    59.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    59.2 +++ b/task4/solution07/test/org/apidesign/apifest08/test/Task4Test.java	Sat Oct 18 07:47:34 2008 +0200
    59.3 @@ -0,0 +1,204 @@
    59.4 +package org.apidesign.apifest08.test;
    59.5 +
    59.6 +import org.apidesign.apifest08.currency.TimeRangeSpecificConvertor;
    59.7 +import java.text.DateFormat;
    59.8 +import java.text.ParseException;
    59.9 +import java.text.SimpleDateFormat;
   59.10 +import java.util.Currency;
   59.11 +import java.util.Date;
   59.12 +import junit.framework.TestCase;
   59.13 +import org.apidesign.apifest08.currency.ConversionRate;
   59.14 +import org.apidesign.apifest08.currency.Convertor;
   59.15 +import org.apidesign.apifest08.currency.Convertor.ConversionResult;
   59.16 +import org.apidesign.apifest08.currency.MonetaryAmount;
   59.17 +import org.apidesign.apifest08.currency.TableConvertor;
   59.18 +import org.apidesign.apifest08.currency.TimeRangeSpecificConvertor.TimeSpecificConversionRequest;
   59.19 +
   59.20 +/** The exchange rates are not always the same. They are changing. However
   59.21 + * as in order to predict the future, one needs to understand own past. That is
   59.22 + * why it is important to know the exchange rate as it was at any time during
   59.23 + * the past.
   59.24 + * <p>
   59.25 + * Today's quest is to enhance the convertor API to deal with dates.
   59.26 + * One shall be able to convert a currency at any date. Each currencies rate shall
   59.27 + * be associated with a range between two Date objects. In order
   59.28 + * to keep compatibility with old API that knew nothing about dates, the
   59.29 + * rates associated then are applicable "for eternity". Any use of existing
   59.30 + * convert methods that do not accept a Date argument, uses the current
   59.31 + * System.currentTimeMillis() as default date.
   59.32 + */
   59.33 +public class Task4Test extends TestCase {
   59.34 +    public Task4Test(String testName) {
   59.35 +        super(testName);
   59.36 +    }
   59.37 +
   59.38 +    @Override
   59.39 +    protected void setUp() throws Exception {
   59.40 +    }
   59.41 +
   59.42 +    @Override
   59.43 +    protected void tearDown() throws Exception {
   59.44 +    }
   59.45 +
   59.46 +    protected static final Currency CZK = Currency.getInstance( "CZK" );
   59.47 +    protected static final Currency SKK = Currency.getInstance( "SKK" );
   59.48 +    protected static final Currency USD = Currency.getInstance( "USD" );
   59.49 +
   59.50 +    // Backward compatibly enhance your existing API to support following
   59.51 +    // usecases:
   59.52 +    //
   59.53 +
   59.54 +    /** Takes a convertor with any rates associated and creates new convertor
   59.55 +     * that returns the same values as the old one for time between from to till.
   59.56 +     * Otherwise it returns no results. This is just a helper method that
   59.57 +     * shall call some real one in the API.
   59.58 +     * 
   59.59 +     * @param old existing convertor
   59.60 +     * @param from initial date (inclusive); null means since the Big Bang
   59.61 +     * @param till final date (exclusive); null means until the End of Universe
   59.62 +     * @return new convertor
   59.63 +     */
   59.64 +    public static Convertor limitTo( final Convertor old, final Date from, final Date till ) {
   59.65 +        return new TimeRangeSpecificConvertor( old, from, till );
   59.66 +    }
   59.67 +        
   59.68 +    private static final DateFormat DATE_FORMAT = new SimpleDateFormat( "yyyy-MM-dd HH:mm Z" );
   59.69 +    
   59.70 +    protected static Date parseGmtDate( final String string ) {
   59.71 +        final String zonedString = string + " GMT+0:00";
   59.72 +        try {
   59.73 +            return DATE_FORMAT.parse( zonedString );
   59.74 +        } catch ( final ParseException ex ) {
   59.75 +            throw new IllegalArgumentException( "Cannot parse " + zonedString, ex );
   59.76 +        }
   59.77 +    }
   59.78 +
   59.79 +    public void testCompositionOfLimitedConvertors() throws Exception {
   59.80 +        final Date d1 = parseGmtDate( "2008-10-01 0:00" );
   59.81 +        final Date d2 = parseGmtDate( "2008-10-02 0:00" );
   59.82 +        final Date d3 = parseGmtDate( "2008-10-03 0:00" );
   59.83 +        
   59.84 +        final Convertor c = Task2Test.merge(
   59.85 +            limitTo(Task1Test.createCZKtoUSD(), d1, d2),
   59.86 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
   59.87 +        );
   59.88 +
   59.89 +        // convert $5 to CZK using c:
   59.90 +        final ConversionResult r1 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 5, USD ), CZK ) );
   59.91 +        final MonetaryAmount a1 = r1.getNetAmount();
   59.92 +        // cannot convert as no rate is applicable to current date
   59.93 +        assertNull( a1 );
   59.94 +
   59.95 +        // convert $8 to CZK using c:
   59.96 +        final ConversionResult r2 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 8, USD ), CZK ) );
   59.97 +        final MonetaryAmount a2 = r2.getNetAmount();
   59.98 +        // cannot convert as no rate is applicable to current date
   59.99 +        assertNull( a2 );
  59.100 +
  59.101 +        // convert 1003CZK to USD using c:
  59.102 +        final ConversionResult r3 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 1003, CZK ), USD ) );
  59.103 +        final MonetaryAmount a3 = r3.getNetAmount();
  59.104 +        // cannot convert as no rate is applicable to current date
  59.105 +        assertNull( a3 );
  59.106 +
  59.107 +        // convert 16CZK using c:
  59.108 +        final ConversionResult r4 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 16, CZK ), USD ) );
  59.109 +        final MonetaryAmount a4 = r4.getNetAmount();
  59.110 +        // cannot convert as no rate is applicable to current date
  59.111 +        assertNull( a4 );
  59.112 +
  59.113 +        // convert 500SKK to CZK using c:
  59.114 +        final ConversionResult r5 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK ) );
  59.115 +        final MonetaryAmount a5 = r5.getNetAmount();
  59.116 +        // cannot convert as no rate is applicable to current date
  59.117 +        assertNull( a5 );
  59.118 +
  59.119 +        // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
  59.120 +        final ConversionResult r6 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 5, USD ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
  59.121 +        final MonetaryAmount a6 = r6.getNetAmount();
  59.122 +        // assertEquals("Result is 85 CZK");
  59.123 +        assertNotNull( a6 );
  59.124 +        assertEquals( 85.0, a6.getAmount().doubleValue() );
  59.125 +        assertEquals( CZK, a6.getCurrency() );
  59.126 +
  59.127 +        // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
  59.128 +        final ConversionResult r7 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 8, USD ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
  59.129 +        final MonetaryAmount a7 = r7.getNetAmount();
  59.130 +        // assertEquals("Result is 136 CZK");
  59.131 +        assertNotNull( a7 );
  59.132 +        assertEquals( 136.0, a7.getAmount().doubleValue() );
  59.133 +        assertEquals( CZK, a7.getCurrency() );
  59.134 +
  59.135 +        // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
  59.136 +        final ConversionResult r8 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 1003, CZK ), USD, parseGmtDate( "2008-10-01 6:00" ) ) );
  59.137 +        final MonetaryAmount a8 = r8.getNetAmount();
  59.138 +        // assertEquals("Result is 59 USD");
  59.139 +        assertNotNull( a8 );
  59.140 +        assertEquals( 59.0, a8.getAmount().doubleValue() );
  59.141 +        assertEquals( USD, a8.getCurrency() );
  59.142 +
  59.143 +        // convert 16CZK using c at 2008-10-02 9:00 GMT:
  59.144 +        final ConversionResult r9 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 16, CZK ), SKK, parseGmtDate( "2008-10-02 9:00" ) ) );
  59.145 +        final MonetaryAmount a9 = r9.getNetAmount();
  59.146 +        // assertEquals("Result is 20 SKK");
  59.147 +        assertNotNull( a9 );
  59.148 +        assertEquals( 20.0, a9.getAmount().doubleValue() );
  59.149 +        assertEquals( SKK, a9.getCurrency() );
  59.150 +
  59.151 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  59.152 +        final ConversionResult r10 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-02 9:00" ) ) );
  59.153 +        final MonetaryAmount a10 = r10.getNetAmount();
  59.154 +        // assertEquals("Result is 400 CZK");
  59.155 +        assertNotNull( a10 );
  59.156 +        assertEquals( 400.0, a10.getAmount().doubleValue() );
  59.157 +        assertEquals( CZK, a10.getCurrency() );
  59.158 +
  59.159 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  59.160 +        final ConversionResult r11 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
  59.161 +        final MonetaryAmount a11 = r11.getNetAmount();
  59.162 +        // cannot convert as no rate is applicable to current date
  59.163 +        assertNull( a11 );
  59.164 +    }
  59.165 +
  59.166 +    /** Create convertor that understands two currencies, CZK and
  59.167 +     *  SKK. Make 100 SKK == 90 CZK.
  59.168 +     *
  59.169 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
  59.170 +     */
  59.171 +    public static Convertor createSKKtoCZK2() {
  59.172 +        final TableConvertor convertor = new TableConvertor();
  59.173 +        final MonetaryAmount amountInSKK = new MonetaryAmount( 100, SKK );
  59.174 +        final MonetaryAmount amountInCZK = new MonetaryAmount( 90, CZK );
  59.175 +        convertor.putIntoTable( new ConversionRate( amountInSKK, amountInCZK ) );
  59.176 +        convertor.putIntoTable( new ConversionRate( amountInCZK, amountInSKK ) );
  59.177 +        return new ContractImposingDelegatingConvertor( convertor ).test();
  59.178 +    }
  59.179 +
  59.180 +    public void testDateConvetorWithTwoDifferentRates() throws Exception {
  59.181 +        final Date d1 = parseGmtDate( "2008-10-01 0:00" );
  59.182 +        final Date d2 = parseGmtDate( "2008-10-02 0:00" );
  59.183 +        final Date d3 = parseGmtDate( "2008-10-03 0:00" );
  59.184 +
  59.185 +        final Convertor c = Task2Test.merge(
  59.186 +            limitTo(createSKKtoCZK2(), d1, d2),
  59.187 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
  59.188 +        );
  59.189 +
  59.190 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  59.191 +        final ConversionResult r1 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-02 9:00" ) ) );
  59.192 +        final MonetaryAmount a1 = r1.getNetAmount();
  59.193 +        // assertEquals("Result is 400 CZK");
  59.194 +        assertNotNull( a1 );
  59.195 +        assertEquals( 400.0, a1.getAmount().doubleValue() );
  59.196 +        assertEquals( CZK, a1.getCurrency() );
  59.197 +
  59.198 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  59.199 +        final ConversionResult r2 = c.convert( new TimeSpecificConversionRequest( new MonetaryAmount( 500, SKK ), CZK, parseGmtDate( "2008-10-01 6:00" ) ) );
  59.200 +        final MonetaryAmount a2 = r2.getNetAmount();
  59.201 +        // assertEquals("Result is 450 CZK");
  59.202 +        assertNotNull( a2 );
  59.203 +        assertEquals( 450.0, a2.getAmount().doubleValue() );
  59.204 +        assertEquals( CZK, a2.getCurrency() );        
  59.205 +    }
  59.206 +
  59.207 +}
    60.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    60.2 +++ b/task4/solution11/build.xml	Sat Oct 18 07:47:34 2008 +0200
    60.3 @@ -0,0 +1,69 @@
    60.4 +<?xml version="1.0" encoding="UTF-8"?>
    60.5 +<!-- You may freely edit this file. See commented blocks below for -->
    60.6 +<!-- some examples of how to customize the build. -->
    60.7 +<!-- (If you delete it and reopen the project it will be recreated.) -->
    60.8 +<project name="currency" default="default" basedir=".">
    60.9 +    <description>Builds, tests, and runs the project.</description>
   60.10 +    <import file="nbproject/build-impl.xml"/>
   60.11 +    <!--
   60.12 +
   60.13 +    There exist several targets which are by default empty and which can be 
   60.14 +    used for execution of your tasks. These targets are usually executed 
   60.15 +    before and after some main targets. They are: 
   60.16 +
   60.17 +      -pre-init:                 called before initialization of project properties
   60.18 +      -post-init:                called after initialization of project properties
   60.19 +      -pre-compile:              called before javac compilation
   60.20 +      -post-compile:             called after javac compilation
   60.21 +      -pre-compile-single:       called before javac compilation of single file
   60.22 +      -post-compile-single:      called after javac compilation of single file
   60.23 +      -pre-compile-test:         called before javac compilation of JUnit tests
   60.24 +      -post-compile-test:        called after javac compilation of JUnit tests
   60.25 +      -pre-compile-test-single:  called before javac compilation of single JUnit test
   60.26 +      -post-compile-test-single: called after javac compilation of single JUunit test
   60.27 +      -pre-jar:                  called before JAR building
   60.28 +      -post-jar:                 called after JAR building
   60.29 +      -post-clean:               called after cleaning build products
   60.30 +
   60.31 +    (Targets beginning with '-' are not intended to be called on their own.)
   60.32 +
   60.33 +    Example of inserting an obfuscator after compilation could look like this:
   60.34 +
   60.35 +        <target name="-post-compile">
   60.36 +            <obfuscate>
   60.37 +                <fileset dir="${build.classes.dir}"/>
   60.38 +            </obfuscate>
   60.39 +        </target>
   60.40 +
   60.41 +    For list of available properties check the imported 
   60.42 +    nbproject/build-impl.xml file. 
   60.43 +
   60.44 +
   60.45 +    Another way to customize the build is by overriding existing main targets.
   60.46 +    The targets of interest are: 
   60.47 +
   60.48 +      -init-macrodef-javac:     defines macro for javac compilation
   60.49 +      -init-macrodef-junit:     defines macro for junit execution
   60.50 +      -init-macrodef-debug:     defines macro for class debugging
   60.51 +      -init-macrodef-java:      defines macro for class execution
   60.52 +      -do-jar-with-manifest:    JAR building (if you are using a manifest)
   60.53 +      -do-jar-without-manifest: JAR building (if you are not using a manifest)
   60.54 +      run:                      execution of project 
   60.55 +      -javadoc-build:           Javadoc generation
   60.56 +      test-report:              JUnit report generation
   60.57 +
   60.58 +    An example of overriding the target for project execution could look like this:
   60.59 +
   60.60 +        <target name="run" depends="currency-impl.jar">
   60.61 +            <exec dir="bin" executable="launcher.exe">
   60.62 +                <arg file="${dist.jar}"/>
   60.63 +            </exec>
   60.64 +        </target>
   60.65 +
   60.66 +    Notice that the overridden target depends on the jar target and not only on 
   60.67 +    the compile target as the regular run target does. Again, for a list of available 
   60.68 +    properties which you can use, check the target you are overriding in the
   60.69 +    nbproject/build-impl.xml file. 
   60.70 +
   60.71 +    -->
   60.72 +</project>
    61.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    61.2 +++ b/task4/solution11/nbproject/build-impl.xml	Sat Oct 18 07:47:34 2008 +0200
    61.3 @@ -0,0 +1,629 @@
    61.4 +<?xml version="1.0" encoding="UTF-8"?>
    61.5 +<!--
    61.6 +*** GENERATED FROM project.xml - DO NOT EDIT  ***
    61.7 +***         EDIT ../build.xml INSTEAD         ***
    61.8 +
    61.9 +For the purpose of easier reading the script
   61.10 +is divided into following sections:
   61.11 +
   61.12 +  - initialization
   61.13 +  - compilation
   61.14 +  - jar
   61.15 +  - execution
   61.16 +  - debugging
   61.17 +  - javadoc
   61.18 +  - junit compilation
   61.19 +  - junit execution
   61.20 +  - junit debugging
   61.21 +  - applet
   61.22 +  - cleanup
   61.23 +
   61.24 +        -->
   61.25 +<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="Currency_Convertor_Solution_11-impl">
   61.26 +    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
   61.27 +    <!-- 
   61.28 +                ======================
   61.29 +                INITIALIZATION SECTION 
   61.30 +                ======================
   61.31 +            -->
   61.32 +    <target name="-pre-init">
   61.33 +        <!-- Empty placeholder for easier customization. -->
   61.34 +        <!-- You can override this target in the ../build.xml file. -->
   61.35 +    </target>
   61.36 +    <target depends="-pre-init" name="-init-private">
   61.37 +        <property file="nbproject/private/config.properties"/>
   61.38 +        <property file="nbproject/private/configs/${config}.properties"/>
   61.39 +        <property file="nbproject/private/private.properties"/>
   61.40 +    </target>
   61.41 +    <target depends="-pre-init,-init-private" name="-init-user">
   61.42 +        <property file="${user.properties.file}"/>
   61.43 +        <!-- The two properties below are usually overridden -->
   61.44 +        <!-- by the active platform. Just a fallback. -->
   61.45 +        <property name="default.javac.source" value="1.4"/>
   61.46 +        <property name="default.javac.target" value="1.4"/>
   61.47 +    </target>
   61.48 +    <target depends="-pre-init,-init-private,-init-user" name="-init-project">
   61.49 +        <property file="nbproject/configs/${config}.properties"/>
   61.50 +        <property file="nbproject/project.properties"/>
   61.51 +    </target>
   61.52 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
   61.53 +        <available file="${manifest.file}" property="manifest.available"/>
   61.54 +        <condition property="manifest.available+main.class">
   61.55 +            <and>
   61.56 +                <isset property="manifest.available"/>
   61.57 +                <isset property="main.class"/>
   61.58 +                <not>
   61.59 +                    <equals arg1="${main.class}" arg2="" trim="true"/>
   61.60 +                </not>
   61.61 +            </and>
   61.62 +        </condition>
   61.63 +        <condition property="manifest.available+main.class+mkdist.available">
   61.64 +            <and>
   61.65 +                <istrue value="${manifest.available+main.class}"/>
   61.66 +                <isset property="libs.CopyLibs.classpath"/>
   61.67 +            </and>
   61.68 +        </condition>
   61.69 +        <condition property="have.tests">
   61.70 +            <or>
   61.71 +                <available file="${test.src.dir}"/>
   61.72 +            </or>
   61.73 +        </condition>
   61.74 +        <condition property="have.sources">
   61.75 +            <or>
   61.76 +                <available file="${src.dir}"/>
   61.77 +            </or>
   61.78 +        </condition>
   61.79 +        <condition property="netbeans.home+have.tests">
   61.80 +            <and>
   61.81 +                <isset property="netbeans.home"/>
   61.82 +                <isset property="have.tests"/>
   61.83 +            </and>
   61.84 +        </condition>
   61.85 +        <condition property="no.javadoc.preview">
   61.86 +            <and>
   61.87 +                <isset property="javadoc.preview"/>
   61.88 +                <isfalse value="${javadoc.preview}"/>
   61.89 +            </and>
   61.90 +        </condition>
   61.91 +        <property name="run.jvmargs" value=""/>
   61.92 +        <property name="javac.compilerargs" value=""/>
   61.93 +        <property name="work.dir" value="${basedir}"/>
   61.94 +        <condition property="no.deps">
   61.95 +            <and>
   61.96 +                <istrue value="${no.dependencies}"/>
   61.97 +            </and>
   61.98 +        </condition>
   61.99 +        <property name="javac.debug" value="true"/>
  61.100 +        <property name="javadoc.preview" value="true"/>
  61.101 +        <property name="application.args" value=""/>
  61.102 +        <property name="source.encoding" value="${file.encoding}"/>
  61.103 +        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
  61.104 +            <and>
  61.105 +                <isset property="javadoc.encoding"/>
  61.106 +                <not>
  61.107 +                    <equals arg1="${javadoc.encoding}" arg2=""/>
  61.108 +                </not>
  61.109 +            </and>
  61.110 +        </condition>
  61.111 +        <property name="javadoc.encoding.used" value="${source.encoding}"/>
  61.112 +        <property name="includes" value="**"/>
  61.113 +        <property name="excludes" value=""/>
  61.114 +        <property name="do.depend" value="false"/>
  61.115 +        <condition property="do.depend.true">
  61.116 +            <istrue value="${do.depend}"/>
  61.117 +        </condition>
  61.118 +        <condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
  61.119 +            <and>
  61.120 +                <isset property="jaxws.endorsed.dir"/>
  61.121 +                <available file="nbproject/jaxws-build.xml"/>
  61.122 +            </and>
  61.123 +        </condition>
  61.124 +    </target>
  61.125 +    <target name="-post-init">
  61.126 +        <!-- Empty placeholder for easier customization. -->
  61.127 +        <!-- You can override this target in the ../build.xml file. -->
  61.128 +    </target>
  61.129 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
  61.130 +        <fail unless="src.dir">Must set src.dir</fail>
  61.131 +        <fail unless="test.src.dir">Must set test.src.dir</fail>
  61.132 +        <fail unless="build.dir">Must set build.dir</fail>
  61.133 +        <fail unless="dist.dir">Must set dist.dir</fail>
  61.134 +        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
  61.135 +        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
  61.136 +        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
  61.137 +        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
  61.138 +        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
  61.139 +        <fail unless="dist.jar">Must set dist.jar</fail>
  61.140 +    </target>
  61.141 +    <target name="-init-macrodef-property">
  61.142 +        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
  61.143 +            <attribute name="name"/>
  61.144 +            <attribute name="value"/>
  61.145 +            <sequential>
  61.146 +                <property name="@{name}" value="${@{value}}"/>
  61.147 +            </sequential>
  61.148 +        </macrodef>
  61.149 +    </target>
  61.150 +    <target name="-init-macrodef-javac">
  61.151 +        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
  61.152 +            <attribute default="${src.dir}" name="srcdir"/>
  61.153 +            <attribute default="${build.classes.dir}" name="destdir"/>
  61.154 +            <attribute default="${javac.classpath}" name="classpath"/>
  61.155 +            <attribute default="${includes}" name="includes"/>
  61.156 +            <attribute default="${excludes}" name="excludes"/>
  61.157 +            <attribute default="${javac.debug}" name="debug"/>
  61.158 +            <attribute default="" name="sourcepath"/>
  61.159 +            <element name="customize" optional="true"/>
  61.160 +            <sequential>
  61.161 +                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
  61.162 +                    <classpath>
  61.163 +                        <path path="@{classpath}"/>
  61.164 +                    </classpath>
  61.165 +                    <compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
  61.166 +                    <customize/>
  61.167 +                </javac>
  61.168 +            </sequential>
  61.169 +        </macrodef>
  61.170 +        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
  61.171 +            <attribute default="${src.dir}" name="srcdir"/>
  61.172 +            <attribute default="${build.classes.dir}" name="destdir"/>
  61.173 +            <attribute default="${javac.classpath}" name="classpath"/>
  61.174 +            <sequential>
  61.175 +                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
  61.176 +                    <classpath>
  61.177 +                        <path path="@{classpath}"/>
  61.178 +                    </classpath>
  61.179 +                </depend>
  61.180 +            </sequential>
  61.181 +        </macrodef>
  61.182 +        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
  61.183 +            <attribute default="${build.classes.dir}" name="destdir"/>
  61.184 +            <sequential>
  61.185 +                <fail unless="javac.includes">Must set javac.includes</fail>
  61.186 +                <pathconvert pathsep="," property="javac.includes.binary">
  61.187 +                    <path>
  61.188 +                        <filelist dir="@{destdir}" files="${javac.includes}"/>
  61.189 +                    </path>
  61.190 +                    <globmapper from="*.java" to="*.class"/>
  61.191 +                </pathconvert>
  61.192 +                <delete>
  61.193 +                    <files includes="${javac.includes.binary}"/>
  61.194 +                </delete>
  61.195 +            </sequential>
  61.196 +        </macrodef>
  61.197 +    </target>
  61.198 +    <target name="-init-macrodef-junit">
  61.199 +        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
  61.200 +            <attribute default="${includes}" name="includes"/>
  61.201 +            <attribute default="${excludes}" name="excludes"/>
  61.202 +            <attribute default="**" name="testincludes"/>
  61.203 +            <sequential>
  61.204 +                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
  61.205 +                    <batchtest todir="${build.test.results.dir}">
  61.206 +                        <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
  61.207 +                            <filename name="@{testincludes}"/>
  61.208 +                        </fileset>
  61.209 +                    </batchtest>
  61.210 +                    <classpath>
  61.211 +                        <path path="${run.test.classpath}"/>
  61.212 +                    </classpath>
  61.213 +                    <syspropertyset>
  61.214 +                        <propertyref prefix="test-sys-prop."/>
  61.215 +                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
  61.216 +                    </syspropertyset>
  61.217 +                    <formatter type="brief" usefile="false"/>
  61.218 +                    <formatter type="xml"/>
  61.219 +                    <jvmarg line="${run.jvmargs}"/>
  61.220 +                </junit>
  61.221 +            </sequential>
  61.222 +        </macrodef>
  61.223 +    </target>
  61.224 +    <target name="-init-macrodef-nbjpda">
  61.225 +        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
  61.226 +            <attribute default="${main.class}" name="name"/>
  61.227 +            <attribute default="${debug.classpath}" name="classpath"/>
  61.228 +            <attribute default="" name="stopclassname"/>
  61.229 +            <sequential>
  61.230 +                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="dt_socket">
  61.231 +                    <classpath>
  61.232 +                        <path path="@{classpath}"/>
  61.233 +                    </classpath>
  61.234 +                </nbjpdastart>
  61.235 +            </sequential>
  61.236 +        </macrodef>
  61.237 +        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
  61.238 +            <attribute default="${build.classes.dir}" name="dir"/>
  61.239 +            <sequential>
  61.240 +                <nbjpdareload>
  61.241 +                    <fileset dir="@{dir}" includes="${fix.classes}">
  61.242 +                        <include name="${fix.includes}*.class"/>
  61.243 +                    </fileset>
  61.244 +                </nbjpdareload>
  61.245 +            </sequential>
  61.246 +        </macrodef>
  61.247 +    </target>
  61.248 +    <target name="-init-debug-args">
  61.249 +        <property name="version-output" value="java version &quot;${ant.java.version}"/>
  61.250 +        <condition property="have-jdk-older-than-1.4">
  61.251 +            <or>
  61.252 +                <contains string="${version-output}" substring="java version &quot;1.0"/>
  61.253 +                <contains string="${version-output}" substring="java version &quot;1.1"/>
  61.254 +                <contains string="${version-output}" substring="java version &quot;1.2"/>
  61.255 +                <contains string="${version-output}" substring="java version &quot;1.3"/>
  61.256 +            </or>
  61.257 +        </condition>
  61.258 +        <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
  61.259 +            <istrue value="${have-jdk-older-than-1.4}"/>
  61.260 +        </condition>
  61.261 +    </target>
  61.262 +    <target depends="-init-debug-args" name="-init-macrodef-debug">
  61.263 +        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
  61.264 +            <attribute default="${main.class}" name="classname"/>
  61.265 +            <attribute default="${debug.classpath}" name="classpath"/>
  61.266 +            <element name="customize" optional="true"/>
  61.267 +            <sequential>
  61.268 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
  61.269 +                    <jvmarg line="${debug-args-line}"/>
  61.270 +                    <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
  61.271 +                    <jvmarg line="${run.jvmargs}"/>
  61.272 +                    <classpath>
  61.273 +                        <path path="@{classpath}"/>
  61.274 +                    </classpath>
  61.275 +                    <syspropertyset>
  61.276 +                        <propertyref prefix="run-sys-prop."/>
  61.277 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
  61.278 +                    </syspropertyset>
  61.279 +                    <customize/>
  61.280 +                </java>
  61.281 +            </sequential>
  61.282 +        </macrodef>
  61.283 +    </target>
  61.284 +    <target name="-init-macrodef-java">
  61.285 +        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
  61.286 +            <attribute default="${main.class}" name="classname"/>
  61.287 +            <element name="customize" optional="true"/>
  61.288 +            <sequential>
  61.289 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
  61.290 +                    <jvmarg line="${run.jvmargs}"/>
  61.291 +                    <classpath>
  61.292 +                        <path path="${run.classpath}"/>
  61.293 +                    </classpath>
  61.294 +                    <syspropertyset>
  61.295 +                        <propertyref prefix="run-sys-prop."/>
  61.296 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
  61.297 +                    </syspropertyset>
  61.298 +                    <customize/>
  61.299 +                </java>
  61.300 +            </sequential>
  61.301 +        </macrodef>
  61.302 +    </target>
  61.303 +    <target name="-init-presetdef-jar">
  61.304 +        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
  61.305 +            <jar compress="${jar.compress}" jarfile="${dist.jar}">
  61.306 +                <j2seproject1:fileset dir="${build.classes.dir}"/>
  61.307 +            </jar>
  61.308 +        </presetdef>
  61.309 +    </target>
  61.310 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
  61.311 +    <!--
  61.312 +                ===================
  61.313 +                COMPILATION SECTION
  61.314 +                ===================
  61.315 +            -->
  61.316 +    <target depends="init" name="deps-jar" unless="no.deps"/>
  61.317 +    <target depends="init,deps-jar" name="-pre-pre-compile">
  61.318 +        <mkdir dir="${build.classes.dir}"/>
  61.319 +    </target>
  61.320 +    <target name="-pre-compile">
  61.321 +        <!-- Empty placeholder for easier customization. -->
  61.322 +        <!-- You can override this target in the ../build.xml file. -->
  61.323 +    </target>
  61.324 +    <target if="do.depend.true" name="-compile-depend">
  61.325 +        <j2seproject3:depend/>
  61.326 +    </target>
  61.327 +    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
  61.328 +        <j2seproject3:javac/>
  61.329 +        <copy todir="${build.classes.dir}">
  61.330 +            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  61.331 +        </copy>
  61.332 +    </target>
  61.333 +    <target name="-post-compile">
  61.334 +        <!-- Empty placeholder for easier customization. -->
  61.335 +        <!-- You can override this target in the ../build.xml file. -->
  61.336 +    </target>
  61.337 +    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
  61.338 +    <target name="-pre-compile-single">
  61.339 +        <!-- Empty placeholder for easier customization. -->
  61.340 +        <!-- You can override this target in the ../build.xml file. -->
  61.341 +    </target>
  61.342 +    <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
  61.343 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
  61.344 +        <j2seproject3:force-recompile/>
  61.345 +        <j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
  61.346 +    </target>
  61.347 +    <target name="-post-compile-single">
  61.348 +        <!-- Empty placeholder for easier customization. -->
  61.349 +        <!-- You can override this target in the ../build.xml file. -->
  61.350 +    </target>
  61.351 +    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
  61.352 +    <!--
  61.353 +                ====================
  61.354 +                JAR BUILDING SECTION
  61.355 +                ====================
  61.356 +            -->
  61.357 +    <target depends="init" name="-pre-pre-jar">
  61.358 +        <dirname file="${dist.jar}" property="dist.jar.dir"/>
  61.359 +        <mkdir dir="${dist.jar.dir}"/>
  61.360 +    </target>
  61.361 +    <target name="-pre-jar">
  61.362 +        <!-- Empty placeholder for easier customization. -->
  61.363 +        <!-- You can override this target in the ../build.xml file. -->
  61.364 +    </target>
  61.365 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
  61.366 +        <j2seproject1:jar/>
  61.367 +    </target>
  61.368 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
  61.369 +        <j2seproject1:jar manifest="${manifest.file}"/>
  61.370 +    </target>
  61.371 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
  61.372 +        <j2seproject1:jar manifest="${manifest.file}">
  61.373 +            <j2seproject1:manifest>
  61.374 +                <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
  61.375 +            </j2seproject1:manifest>
  61.376 +        </j2seproject1:jar>
  61.377 +        <echo>To run this application from the command line without Ant, try:</echo>
  61.378 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
  61.379 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
  61.380 +        <pathconvert property="run.classpath.with.dist.jar">
  61.381 +            <path path="${run.classpath}"/>
  61.382 +            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
  61.383 +        </pathconvert>
  61.384 +        <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
  61.385 +    </target>
  61.386 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
  61.387 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
  61.388 +        <pathconvert property="run.classpath.without.build.classes.dir">
  61.389 +            <path path="${run.classpath}"/>
  61.390 +            <map from="${build.classes.dir.resolved}" to=""/>
  61.391 +        </pathconvert>
  61.392 +        <pathconvert pathsep=" " property="jar.classpath">
  61.393 +            <path path="${run.classpath.without.build.classes.dir}"/>
  61.394 +            <chainedmapper>
  61.395 +                <flattenmapper/>
  61.396 +                <globmapper from="*" to="lib/*"/>
  61.397 +            </chainedmapper>
  61.398 +        </pathconvert>
  61.399 +        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
  61.400 +        <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
  61.401 +            <fileset dir="${build.classes.dir}"/>
  61.402 +            <manifest>
  61.403 +                <attribute name="Main-Class" value="${main.class}"/>
  61.404 +                <attribute name="Class-Path" value="${jar.classpath}"/>
  61.405 +            </manifest>
  61.406 +        </copylibs>
  61.407 +        <echo>To run this application from the command line without Ant, try:</echo>
  61.408 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
  61.409 +        <echo>java -jar "${dist.jar.resolved}"</echo>
  61.410 +    </target>
  61.411 +    <target name="-post-jar">
  61.412 +        <!-- Empty placeholder for easier customization. -->
  61.413 +        <!-- You can override this target in the ../build.xml file. -->
  61.414 +    </target>
  61.415 +    <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
  61.416 +    <!--
  61.417 +                =================
  61.418 +                EXECUTION SECTION
  61.419 +                =================
  61.420 +            -->
  61.421 +    <target depends="init,compile" description="Run a main class." name="run">
  61.422 +        <j2seproject1:java>
  61.423 +            <customize>
  61.424 +                <arg line="${application.args}"/>
  61.425 +            </customize>
  61.426 +        </j2seproject1:java>
  61.427 +    </target>
  61.428 +    <target name="-do-not-recompile">
  61.429 +        <property name="javac.includes.binary" value=""/>
  61.430 +    </target>
  61.431 +    <target depends="init,-do-not-recompile,compile-single" name="run-single">
  61.432 +        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
  61.433 +        <j2seproject1:java classname="${run.class}"/>
  61.434 +    </target>
  61.435 +    <!--
  61.436 +                =================
  61.437 +                DEBUGGING SECTION
  61.438 +                =================
  61.439 +            -->
  61.440 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger">
  61.441 +        <j2seproject1:nbjpdastart name="${debug.class}"/>
  61.442 +    </target>
  61.443 +    <target depends="init,compile" name="-debug-start-debuggee">
  61.444 +        <j2seproject3:debug>
  61.445 +            <customize>
  61.446 +                <arg line="${application.args}"/>
  61.447 +            </customize>
  61.448 +        </j2seproject3:debug>
  61.449 +    </target>
  61.450 +    <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
  61.451 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
  61.452 +        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
  61.453 +    </target>
  61.454 +    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
  61.455 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
  61.456 +        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
  61.457 +        <j2seproject3:debug classname="${debug.class}"/>
  61.458 +    </target>
  61.459 +    <target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
  61.460 +    <target depends="init" name="-pre-debug-fix">
  61.461 +        <fail unless="fix.includes">Must set fix.includes</fail>
  61.462 +        <property name="javac.includes" value="${fix.includes}.java"/>
  61.463 +    </target>
  61.464 +    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
  61.465 +        <j2seproject1:nbjpdareload/>
  61.466 +    </target>
  61.467 +    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
  61.468 +    <!--
  61.469 +                ===============
  61.470 +                JAVADOC SECTION
  61.471 +                ===============
  61.472 +            -->
  61.473 +    <target depends="init" name="-javadoc-build">
  61.474 +        <mkdir dir="${dist.javadoc.dir}"/>
  61.475 +        <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
  61.476 +            <classpath>
  61.477 +                <path path="${javac.classpath}"/>
  61.478 +            </classpath>
  61.479 +            <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
  61.480 +                <filename name="**/*.java"/>
  61.481 +            </fileset>
  61.482 +        </javadoc>
  61.483 +    </target>
  61.484 +    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
  61.485 +        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
  61.486 +    </target>
  61.487 +    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
  61.488 +    <!--
  61.489 +                =========================
  61.490 +                JUNIT COMPILATION SECTION
  61.491 +                =========================
  61.492 +            -->
  61.493 +    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
  61.494 +        <mkdir dir="${build.test.classes.dir}"/>
  61.495 +    </target>
  61.496 +    <target name="-pre-compile-test">
  61.497 +        <!-- Empty placeholder for easier customization. -->
  61.498 +        <!-- You can override this target in the ../build.xml file. -->
  61.499 +    </target>
  61.500 +    <target if="do.depend.true" name="-compile-test-depend">
  61.501 +        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
  61.502 +    </target>
  61.503 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
  61.504 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
  61.505 +        <copy todir="${build.test.classes.dir}">
  61.506 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  61.507 +        </copy>
  61.508 +    </target>
  61.509 +    <target name="-post-compile-test">
  61.510 +        <!-- Empty placeholder for easier customization. -->
  61.511 +        <!-- You can override this target in the ../build.xml file. -->
  61.512 +    </target>
  61.513 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
  61.514 +    <target name="-pre-compile-test-single">
  61.515 +        <!-- Empty placeholder for easier customization. -->
  61.516 +        <!-- You can override this target in the ../build.xml file. -->
  61.517 +    </target>
  61.518 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
  61.519 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
  61.520 +        <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
  61.521 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
  61.522 +        <copy todir="${build.test.classes.dir}">
  61.523 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  61.524 +        </copy>
  61.525 +    </target>
  61.526 +    <target name="-post-compile-test-single">
  61.527 +        <!-- Empty placeholder for easier customization. -->
  61.528 +        <!-- You can override this target in the ../build.xml file. -->
  61.529 +    </target>
  61.530 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
  61.531 +    <!--
  61.532 +                =======================
  61.533 +                JUNIT EXECUTION SECTION
  61.534 +                =======================
  61.535 +            -->
  61.536 +    <target depends="init" if="have.tests" name="-pre-test-run">
  61.537 +        <mkdir dir="${build.test.results.dir}"/>
  61.538 +    </target>
  61.539 +    <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
  61.540 +        <j2seproject3:junit testincludes="**/*Test.java"/>
  61.541 +    </target>
  61.542 +    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
  61.543 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
  61.544 +    </target>
  61.545 +    <target depends="init" if="have.tests" name="test-report"/>
  61.546 +    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
  61.547 +    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
  61.548 +    <target depends="init" if="have.tests" name="-pre-test-run-single">
  61.549 +        <mkdir dir="${build.test.results.dir}"/>
  61.550 +    </target>
  61.551 +    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
  61.552 +        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
  61.553 +        <j2seproject3:junit excludes="" includes="${test.includes}"/>
  61.554 +    </target>
  61.555 +    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
  61.556 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
  61.557 +    </target>
  61.558 +    <target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
  61.559 +    <!--
  61.560 +                =======================
  61.561 +                JUNIT DEBUGGING SECTION
  61.562 +                =======================
  61.563 +            -->
  61.564 +    <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
  61.565 +        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
  61.566 +        <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
  61.567 +        <delete file="${test.report.file}"/>
  61.568 +        <mkdir dir="${build.test.results.dir}"/>
  61.569 +        <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
  61.570 +            <customize>
  61.571 +                <syspropertyset>
  61.572 +                    <propertyref prefix="test-sys-prop."/>
  61.573 +                    <mapper from="test-sys-prop.*" to="*" type="glob"/>
  61.574 +                </syspropertyset>
  61.575 +                <arg value="${test.class}"/>
  61.576 +                <arg value="showoutput=true"/>
  61.577 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
  61.578 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
  61.579 +            </customize>
  61.580 +        </j2seproject3:debug>
  61.581 +    </target>
  61.582 +    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
  61.583 +        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
  61.584 +    </target>
  61.585 +    <target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
  61.586 +    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
  61.587 +        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
  61.588 +    </target>
  61.589 +    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
  61.590 +    <!--
  61.591 +                =========================
  61.592 +                APPLET EXECUTION SECTION
  61.593 +                =========================
  61.594 +            -->
  61.595 +    <target depends="init,compile-single" name="run-applet">
  61.596 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
  61.597 +        <j2seproject1:java classname="sun.applet.AppletViewer">
  61.598 +            <customize>
  61.599 +                <arg value="${applet.url}"/>
  61.600 +            </customize>
  61.601 +        </j2seproject1:java>
  61.602 +    </target>
  61.603 +    <!--
  61.604 +                =========================
  61.605 +                APPLET DEBUGGING  SECTION
  61.606 +                =========================
  61.607 +            -->
  61.608 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
  61.609 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
  61.610 +        <j2seproject3:debug classname="sun.applet.AppletViewer">
  61.611 +            <customize>
  61.612 +                <arg value="${applet.url}"/>
  61.613 +            </customize>
  61.614 +        </j2seproject3:debug>
  61.615 +    </target>
  61.616 +    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
  61.617 +    <!--
  61.618 +                ===============
  61.619 +                CLEANUP SECTION
  61.620 +                ===============
  61.621 +            -->
  61.622 +    <target depends="init" name="deps-clean" unless="no.deps"/>
  61.623 +    <target depends="init" name="-do-clean">
  61.624 +        <delete dir="${build.dir}"/>
  61.625 +        <delete dir="${dist.dir}"/>
  61.626 +    </target>
  61.627 +    <target name="-post-clean">
  61.628 +        <!-- Empty placeholder for easier customization. -->
  61.629 +        <!-- You can override this target in the ../build.xml file. -->
  61.630 +    </target>
  61.631 +    <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
  61.632 +</project>
    62.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    62.2 +++ b/task4/solution11/nbproject/genfiles.properties	Sat Oct 18 07:47:34 2008 +0200
    62.3 @@ -0,0 +1,8 @@
    62.4 +build.xml.data.CRC32=2ab820eb
    62.5 +build.xml.script.CRC32=58a52595
    62.6 +build.xml.stylesheet.CRC32=be360661
    62.7 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
    62.8 +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
    62.9 +nbproject/build-impl.xml.data.CRC32=0e1e702f
   62.10 +nbproject/build-impl.xml.script.CRC32=c899f2cf
   62.11 +nbproject/build-impl.xml.stylesheet.CRC32=487672f9
    63.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    63.2 +++ b/task4/solution11/nbproject/project.properties	Sat Oct 18 07:47:34 2008 +0200
    63.3 @@ -0,0 +1,68 @@
    63.4 +application.title=currency
    63.5 +application.vendor=apidesign.org
    63.6 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8
    63.7 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
    63.8 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=default
    63.9 +build.classes.dir=${build.dir}/classes
   63.10 +build.classes.excludes=**/*.java,**/*.form
   63.11 +# This directory is removed when the project is cleaned:
   63.12 +build.dir=build
   63.13 +build.generated.dir=${build.dir}/generated
   63.14 +# Only compile against the classpath explicitly listed here:
   63.15 +build.sysclasspath=ignore
   63.16 +build.test.classes.dir=${build.dir}/test/classes
   63.17 +build.test.results.dir=${build.dir}/test/results
   63.18 +debug.classpath=\
   63.19 +    ${run.classpath}
   63.20 +debug.test.classpath=\
   63.21 +    ${run.test.classpath}
   63.22 +# This directory is removed when the project is cleaned:
   63.23 +dist.dir=dist
   63.24 +dist.jar=${dist.dir}/currency.jar
   63.25 +dist.javadoc.dir=${dist.dir}/javadoc
   63.26 +excludes=
   63.27 +file.reference.junit-4.4.jar=../../libs/junit-4.4.jar
   63.28 +file.reference.src-apifest08=..
   63.29 +includes=**
   63.30 +jar.compress=false
   63.31 +javac.classpath=
   63.32 +# Space-separated list of extra javac options
   63.33 +javac.compilerargs=-Xlint:unchecked
   63.34 +javac.deprecation=false
   63.35 +javac.source=1.5
   63.36 +javac.target=1.5
   63.37 +javac.test.classpath=\
   63.38 +    ${javac.classpath}:\
   63.39 +    ${build.classes.dir}:\
   63.40 +    ${file.reference.junit-4.4.jar}
   63.41 +javadoc.additionalparam=
   63.42 +javadoc.author=false
   63.43 +javadoc.encoding=
   63.44 +javadoc.noindex=false
   63.45 +javadoc.nonavbar=false
   63.46 +javadoc.notree=false
   63.47 +javadoc.private=false
   63.48 +javadoc.splitindex=true
   63.49 +javadoc.use=true
   63.50 +javadoc.version=false
   63.51 +javadoc.windowtitle=
   63.52 +jnlp.codebase.type=local
   63.53 +jnlp.codebase.url=file:/home/jarda/src/apifest08/currency/dist
   63.54 +jnlp.descriptor=application
   63.55 +jnlp.enabled=false
   63.56 +jnlp.offline-allowed=false
   63.57 +jnlp.signed=false
   63.58 +meta.inf.dir=${src.dir}/META-INF
   63.59 +platform.active=default_platform
   63.60 +run.classpath=\
   63.61 +    ${javac.classpath}:\
   63.62 +    ${build.classes.dir}
   63.63 +# Space-separated list of JVM arguments used when running the project
   63.64 +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
   63.65 +# or test-sys-prop.name=value to set system properties for unit tests):
   63.66 +run.jvmargs=
   63.67 +run.test.classpath=\
   63.68 +    ${javac.test.classpath}:\
   63.69 +    ${build.test.classes.dir}
   63.70 +src.dir=src
   63.71 +test.src.dir=test
    64.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    64.2 +++ b/task4/solution11/nbproject/project.xml	Sat Oct 18 07:47:34 2008 +0200
    64.3 @@ -0,0 +1,16 @@
    64.4 +<?xml version="1.0" encoding="UTF-8"?>
    64.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
    64.6 +    <type>org.netbeans.modules.java.j2seproject</type>
    64.7 +    <configuration>
    64.8 +        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
    64.9 +            <name>Currency Convertor Solution 11</name>
   64.10 +            <minimum-ant-version>1.6.5</minimum-ant-version>
   64.11 +            <source-roots>
   64.12 +                <root id="src.dir"/>
   64.13 +            </source-roots>
   64.14 +            <test-roots>
   64.15 +                <root id="test.src.dir"/>
   64.16 +            </test-roots>
   64.17 +        </data>
   64.18 +    </configuration>
   64.19 +</project>
    65.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    65.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/Computer.java	Sat Oct 18 07:47:34 2008 +0200
    65.3 @@ -0,0 +1,61 @@
    65.4 +package org.apidesign.apifest08.currency;
    65.5 +
    65.6 +/**
    65.7 + * Interface declaring method for computing conversion.
    65.8 + * 
    65.9 + * Because of a vague definition of currency amount's type,
   65.10 + * the interface has a generic type.
   65.11 + * 
   65.12 + * @author ked
   65.13 + * @see http://wiki.apidesign.org/wiki/APIDesignPatterns:ResponseReply
   65.14 + */
   65.15 +interface Computer<AmountType> {
   65.16 +
   65.17 +    void compute(ComputerRequest<AmountType> request, ComputerResponse<AmountType> response);
   65.18 +
   65.19 +    final class ComputerRequest<AmountType> {
   65.20 +
   65.21 +        private AmountType input;
   65.22 +        private AmountType inputCurrencyRatio;
   65.23 +        private AmountType outputCurrencyRatio;
   65.24 +
   65.25 +        AmountType getInput() {
   65.26 +            return input;
   65.27 +        }
   65.28 +
   65.29 +        void setInput(AmountType input) {
   65.30 +            this.input = input;
   65.31 +        }
   65.32 +
   65.33 +        AmountType getInputCurrencyRatio() {
   65.34 +            return inputCurrencyRatio;
   65.35 +        }
   65.36 +
   65.37 +        void setInputCurrencyRatio(AmountType inputCurrencyRatio) {
   65.38 +            this.inputCurrencyRatio = inputCurrencyRatio;
   65.39 +        }
   65.40 +
   65.41 +        AmountType getOutputCurrencyRatio() {
   65.42 +            return outputCurrencyRatio;
   65.43 +        }
   65.44 +
   65.45 +        void setOutputCurrencyRatio(AmountType outputCurrencyRatio) {
   65.46 +            this.outputCurrencyRatio = outputCurrencyRatio;
   65.47 +        }
   65.48 +    }
   65.49 +
   65.50 +    final class ComputerResponse<AmountType> {
   65.51 +
   65.52 +        private AmountType result;
   65.53 +
   65.54 +        AmountType getResult() {
   65.55 +            return result;
   65.56 +        }
   65.57 +
   65.58 +        void setResult(AmountType result) {
   65.59 +            this.result = result;
   65.60 +        }
   65.61 +    }
   65.62 +    
   65.63 +    
   65.64 +}
   65.65 \ No newline at end of file
    66.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    66.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/Convertor.java	Sat Oct 18 07:47:34 2008 +0200
    66.3 @@ -0,0 +1,389 @@
    66.4 +package org.apidesign.apifest08.currency;
    66.5 +
    66.6 +import java.util.ArrayList;
    66.7 +import java.util.Collection;
    66.8 +import java.util.Date;
    66.9 +import java.util.HashSet;
   66.10 +import java.util.List;
   66.11 +import java.util.Set;
   66.12 +import org.apidesign.apifest08.currency.Computer.ComputerRequest;
   66.13 +import org.apidesign.apifest08.currency.Computer.ComputerResponse;
   66.14 +
   66.15 +/**
   66.16 + * Convertor.
   66.17 + * 
   66.18 + * In Task 1's version provides conversion between currency values
   66.19 + * with amount stored in integer or double, that are identified
   66.20 + * with string value. Exchange rates are immutable.
   66.21 + * 
   66.22 + * In Task2's version provides support for multiple exchange rates
   66.23 + * between different currencies & merging exchange rates from
   66.24 + * existing convertors into new convertor's instance.
   66.25 + * No time for javadoc these features, sorry.
   66.26 + * 
   66.27 + * In Task3's version supports reading of current exchange rates
   66.28 + * from data sources. Data sources are merged during convertors' merging
   66.29 + * as well as static exchange rates.
   66.30 + * No time for javadoc, again.
   66.31 + * 
   66.32 + * In Task4's version takes into account validity range of data sources,
   66.33 + * can convert using an exchange rate value according to the specified instant
   66.34 + * of the time and provides a method for creating a new convertor with the same
   66.35 + * data sources as the old one, but with their validity ranges limited
   66.36 + * to the specified range.
   66.37 + * As usual, no time for javadoc.
   66.38 + * 
   66.39 + * @author ked
   66.40 + */
   66.41 +public final class Convertor<AmountType, IdentifierType> {
   66.42 +
   66.43 +    Computer<AmountType> computer;
   66.44 +    // each static exchange rate is a special case of an exchange rate data source
   66.45 +    // historically separated
   66.46 +    List<ExchangeRateDataSource<AmountType, IdentifierType>> staticExchangeRateDataSources =
   66.47 +            new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
   66.48 +    List<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources =
   66.49 +            new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
   66.50 +
   66.51 +    // ---
   66.52 +    // BASICS
   66.53 +    // ---
   66.54 +    Convertor(Computer<AmountType> computer) {
   66.55 +        this.computer = computer;
   66.56 +    }
   66.57 +
   66.58 +    void addExchangeRateDataSources(
   66.59 +            List<ExchangeRateDataSource<AmountType, IdentifierType>> target,
   66.60 +            Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources) {
   66.61 +        for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : exchangeRateDataSources) {
   66.62 +            if (isOverlappingExchangeRate(
   66.63 +                    exchangeRateDataSource.getCurrencyAIdentifier(),
   66.64 +                    exchangeRateDataSource.getCurrencyBIdentifier(),
   66.65 +                    exchangeRateDataSource.getValidFrom(),
   66.66 +                    exchangeRateDataSource.getValidTill())) {
   66.67 +                throw new IllegalArgumentException("Duplicate exchange rate!");
   66.68 +            }
   66.69 +            target.add(exchangeRateDataSource);
   66.70 +        }
   66.71 +    }
   66.72 +
   66.73 +    ExchangeRateValue<AmountType, IdentifierType> findExchangeRate(
   66.74 +            IdentifierType currencyA,
   66.75 +            IdentifierType currencyB,
   66.76 +            Date instant) {
   66.77 +        ExchangeRateValue<AmountType, IdentifierType> result = null;
   66.78 +        result = findExchangeRateInternal(staticExchangeRateDataSources, currencyA, currencyB, instant);
   66.79 +        if (result != null) {
   66.80 +            return result;
   66.81 +        }
   66.82 +        result = findExchangeRateInternal(exchangeRateDataSources, currencyA, currencyB, instant);
   66.83 +        return result;
   66.84 +    }
   66.85 +
   66.86 +    ExchangeRateValue<AmountType, IdentifierType> findExchangeRateInternal(
   66.87 +            List<ExchangeRateDataSource<AmountType, IdentifierType>> where,
   66.88 +            IdentifierType currencyA,
   66.89 +            IdentifierType currencyB,
   66.90 +            Date instant) {
   66.91 +        for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : where) {
   66.92 +            if (((exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyA) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyB)) ||
   66.93 +                    (exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyB) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyA))) &&
   66.94 +                    DateUtil.isInRange(instant, exchangeRateDataSource.getValidFrom(), exchangeRateDataSource.getValidTill())) {
   66.95 +                return exchangeRateDataSource.getExchangeRate();
   66.96 +            }
   66.97 +        }
   66.98 +        return null;
   66.99 +    }
  66.100 +
  66.101 +    boolean isOverlappingExchangeRate(
  66.102 +            IdentifierType currencyA,
  66.103 +            IdentifierType currencyB,
  66.104 +            Date from,
  66.105 +            Date to) {
  66.106 +        boolean result = false;
  66.107 +        result = isOverlappingExchangeRateInternal(staticExchangeRateDataSources, currencyA, currencyB, from, to);
  66.108 +        if (result == true) {
  66.109 +            return result;
  66.110 +        }
  66.111 +        result = isOverlappingExchangeRateInternal(exchangeRateDataSources, currencyA, currencyB, from, to);
  66.112 +        return result;
  66.113 +    }
  66.114 +
  66.115 +    boolean isOverlappingExchangeRateInternal(
  66.116 +            List<ExchangeRateDataSource<AmountType, IdentifierType>> where,
  66.117 +            IdentifierType currencyA,
  66.118 +            IdentifierType currencyB,
  66.119 +            Date from,
  66.120 +            Date to) {
  66.121 +        for (ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource : where) {
  66.122 +            if (((exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyA) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyB)) ||
  66.123 +                    (exchangeRateDataSource.getCurrencyAIdentifier().equals(currencyB) && exchangeRateDataSource.getCurrencyBIdentifier().equals(currencyA))) &&
  66.124 +                    DateUtil.isRangesOverlapping(from, to, exchangeRateDataSource.getValidFrom(), exchangeRateDataSource.getValidTill())) {
  66.125 +                return true;
  66.126 +            }
  66.127 +        }
  66.128 +        return false;
  66.129 +    }
  66.130 +
  66.131 +    /**
  66.132 +     * Convert an amount of the one currency to an amount of the another one currency
  66.133 +     * with respect to previously specified exchange rates.
  66.134 +     * 
  66.135 +     * @param targetCurrency an identifier of the requested currency
  66.136 +     * @param currencyValue an amount of the another one currency
  66.137 +     * @return an amount of the requested currency
  66.138 +     */
  66.139 +    public CurrencyValue<AmountType, IdentifierType> convert(
  66.140 +            IdentifierType targetCurrency,
  66.141 +            CurrencyValue<AmountType, IdentifierType> currencyValue) {
  66.142 +        return convert(targetCurrency, currencyValue, new Date()); // System.currentTimeMillis()
  66.143 +    }
  66.144 +
  66.145 +    public CurrencyValue<AmountType, IdentifierType> convert(
  66.146 +            IdentifierType targetCurrency,
  66.147 +            CurrencyValue<AmountType, IdentifierType> currencyValue,
  66.148 +            Date instant) {
  66.149 +        ExchangeRateValue<AmountType, IdentifierType> exchangeRate =
  66.150 +                findExchangeRate(currencyValue.getIdentifier(), targetCurrency, instant);
  66.151 +        if (exchangeRate == null) {
  66.152 +            throw new IllegalArgumentException("Inappropriate currencies to convert!");
  66.153 +        }
  66.154 +
  66.155 +        ComputerRequest<AmountType> computerRequest = new ComputerRequest<AmountType>();
  66.156 +        computerRequest.setInput(currencyValue.getAmount());
  66.157 +
  66.158 +        IdentifierType targetCurrencyRef; // just for backward compatibility :-(
  66.159 +        if (exchangeRate.getCurrencyA().getIdentifier().equals(targetCurrency)) {
  66.160 +            computerRequest.setInputCurrencyRatio(exchangeRate.getCurrencyB().getAmount());
  66.161 +            computerRequest.setOutputCurrencyRatio(exchangeRate.getCurrencyA().getAmount());
  66.162 +            targetCurrencyRef = exchangeRate.getCurrencyA().getIdentifier();
  66.163 +        } else {
  66.164 +            computerRequest.setInputCurrencyRatio(exchangeRate.getCurrencyA().getAmount());
  66.165 +            computerRequest.setOutputCurrencyRatio(exchangeRate.getCurrencyB().getAmount());
  66.166 +            targetCurrencyRef = exchangeRate.getCurrencyB().getIdentifier();
  66.167 +        }
  66.168 +
  66.169 +        ComputerResponse<AmountType> computerResponse = new ComputerResponse<AmountType>();
  66.170 +        computer.compute(computerRequest, computerResponse);
  66.171 +
  66.172 +        return CurrencyValue.getCurrencyValue(
  66.173 +                computerResponse.getResult(),
  66.174 +                targetCurrencyRef);
  66.175 +    }
  66.176 +
  66.177 +    // ---
  66.178 +    // LIMITING
  66.179 +    // ---
  66.180 +    Collection<ExchangeRateDataSource<AmountType, IdentifierType>> limitDataSources(
  66.181 +            Collection<ExchangeRateDataSource<AmountType, IdentifierType>> source,
  66.182 +            Date from, Date till) {
  66.183 +        Collection<ExchangeRateDataSource<AmountType, IdentifierType>> result =
  66.184 +                new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
  66.185 +
  66.186 +        for (ExchangeRateDataSource<AmountType, IdentifierType> dataSource : source) {
  66.187 +            result.add(ExchangeRateDataSource.getExchangeRateDataSource(
  66.188 +                    dataSource.getCurrencyAIdentifier(), dataSource.getCurrencyBIdentifier(),
  66.189 +                    dataSource.getExchangeRateProvider(),
  66.190 +                    DateUtil.getRangesIntersectionBottom(dataSource.getValidFrom(), from),
  66.191 +                    DateUtil.getRangesIntersectionTop(dataSource.getValidTill(), till)));
  66.192 +        }
  66.193 +
  66.194 +        return result;
  66.195 +    }
  66.196 +
  66.197 +    public Convertor<AmountType, IdentifierType> limitConvertor(Date from, Date till) {
  66.198 +        Collection<ExchangeRateDataSource<AmountType, IdentifierType>> limitedStatic =
  66.199 +                limitDataSources(staticExchangeRateDataSources, from, till);
  66.200 +        Collection<ExchangeRateDataSource<AmountType, IdentifierType>> limited =
  66.201 +                limitDataSources(exchangeRateDataSources, from, till);
  66.202 +
  66.203 +        Convertor<AmountType, IdentifierType> c = new Convertor<AmountType, IdentifierType>(computer);
  66.204 +        c.addExchangeRateDataSources(c.staticExchangeRateDataSources, limitedStatic);
  66.205 +        c.addExchangeRateDataSources(c.exchangeRateDataSources, limited);
  66.206 +        return c;
  66.207 +    }
  66.208 +
  66.209 +    // ---
  66.210 +    // MERGING
  66.211 +    // ---
  66.212 +    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> mergeConvertors(
  66.213 +            Computer<AmountType> computer,
  66.214 +            Collection<Convertor<AmountType, IdentifierType>> convertors) {
  66.215 +        Set<ExchangeRateDataSource<AmountType, IdentifierType>> mergedStatic =
  66.216 +                new HashSet<ExchangeRateDataSource<AmountType, IdentifierType>>();
  66.217 +        Set<ExchangeRateDataSource<AmountType, IdentifierType>> merged =
  66.218 +                new HashSet<ExchangeRateDataSource<AmountType, IdentifierType>>();
  66.219 +        for (Convertor<AmountType, IdentifierType> convertor : convertors) {
  66.220 +            mergedStatic.addAll(convertor.staticExchangeRateDataSources);
  66.221 +        }
  66.222 +        for (Convertor<AmountType, IdentifierType> convertor : convertors) {
  66.223 +            merged.addAll(convertor.exchangeRateDataSources);
  66.224 +        }
  66.225 +
  66.226 +        Convertor<AmountType, IdentifierType> c = new Convertor<AmountType, IdentifierType>(computer);
  66.227 +        c.addExchangeRateDataSources(c.staticExchangeRateDataSources, mergedStatic);
  66.228 +        c.addExchangeRateDataSources(c.exchangeRateDataSources, merged);
  66.229 +        return c;
  66.230 +    }
  66.231 +
  66.232 +    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> mergeConvertors(
  66.233 +            Computer<AmountType> computer,
  66.234 +            Convertor<AmountType, IdentifierType> convertorA,
  66.235 +            Convertor<AmountType, IdentifierType> convertorB) {
  66.236 +        Collection<Convertor<AmountType, IdentifierType>> convertors =
  66.237 +                new ArrayList<Convertor<AmountType, IdentifierType>>();
  66.238 +        convertors.add(convertorA);
  66.239 +        convertors.add(convertorB);
  66.240 +        return mergeConvertors(computer, convertors);
  66.241 +    }
  66.242 +
  66.243 +    public static Convertor<Double, String> mergeConvertorsDoubleString(
  66.244 +            Collection<Convertor<Double, String>> convertors) {
  66.245 +        return mergeConvertors(DoubleComputer, convertors);
  66.246 +    }
  66.247 +
  66.248 +    public static Convertor<Double, String> mergeConvertorsDoubleString(
  66.249 +            Convertor<Double, String> convertorA,
  66.250 +            Convertor<Double, String> convertorB) {
  66.251 +        return mergeConvertors(DoubleComputer, convertorA, convertorB);
  66.252 +    }
  66.253 +
  66.254 +    public static Convertor<Integer, String> mergeConvertorsIntegerString(
  66.255 +            Collection<Convertor<Integer, String>> convertors) {
  66.256 +        return mergeConvertors(IntegerComputer, convertors);
  66.257 +    }
  66.258 +
  66.259 +    public static Convertor<Integer, String> mergeConvertorsIntegerString(
  66.260 +            Convertor<Integer, String> convertorA,
  66.261 +            Convertor<Integer, String> convertorB) {
  66.262 +        return mergeConvertors(IntegerComputer, convertorA, convertorB);
  66.263 +    }
  66.264 +
  66.265 +    // ---
  66.266 +    // CREATION
  66.267 +    // ---
  66.268 +    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertor(
  66.269 +            Computer<AmountType> computer, Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates) {
  66.270 +        Convertor<AmountType, IdentifierType> c = new Convertor<AmountType, IdentifierType>(computer);
  66.271 +        Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources =
  66.272 +                new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
  66.273 +        for (ExchangeRateValue<AmountType, IdentifierType> exchangeRate : exchangeRates) {
  66.274 +            exchangeRateDataSources.add(
  66.275 +                    ExchangeRateDataSource.getExchangeRateDataSource(
  66.276 +                    exchangeRate.getCurrencyA().getIdentifier(), exchangeRate.getCurrencyB().getIdentifier(),
  66.277 +                    StaticExchangeRateProvider.getStaticExchangeRateProvider(exchangeRate)));
  66.278 +        }
  66.279 +        c.addExchangeRateDataSources(c.staticExchangeRateDataSources, exchangeRateDataSources);
  66.280 +        return c;
  66.281 +    }
  66.282 +
  66.283 +    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertorDataSource(
  66.284 +            Computer<AmountType> computer, Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources) {
  66.285 +        Convertor<AmountType, IdentifierType> c = new Convertor<AmountType, IdentifierType>(computer);
  66.286 +        c.addExchangeRateDataSources(c.exchangeRateDataSources, exchangeRateDataSources);
  66.287 +        return c;
  66.288 +    }
  66.289 +
  66.290 +    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertor(
  66.291 +            Computer<AmountType> computer, ExchangeRateValue<AmountType, IdentifierType> exchangeRate) {
  66.292 +        Collection<ExchangeRateValue<AmountType, IdentifierType>> exchangeRates =
  66.293 +                new ArrayList<ExchangeRateValue<AmountType, IdentifierType>>();
  66.294 +        exchangeRates.add(exchangeRate);
  66.295 +        return getConvertor(computer, exchangeRates);
  66.296 +    }
  66.297 +
  66.298 +    static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> getConvertorDataSource(
  66.299 +            Computer<AmountType> computer, ExchangeRateDataSource<AmountType, IdentifierType> exchangeRateDataSource) {
  66.300 +        Collection<ExchangeRateDataSource<AmountType, IdentifierType>> exchangeRateDataSources =
  66.301 +                new ArrayList<ExchangeRateDataSource<AmountType, IdentifierType>>();
  66.302 +        exchangeRateDataSources.add(exchangeRateDataSource);
  66.303 +        return getConvertorDataSource(computer, exchangeRateDataSources);
  66.304 +    }
  66.305 +
  66.306 +    public static Convertor<Double, String> getConvertorDoubleString(
  66.307 +            Collection<ExchangeRateValue<Double, String>> exchangeRates) {
  66.308 +        return getConvertor(DoubleComputer, exchangeRates);
  66.309 +    }
  66.310 +
  66.311 +    public static Convertor<Double, String> getConvertorDoubleString(
  66.312 +            ExchangeRateValue<Double, String> exchangeRate) {
  66.313 +        return getConvertor(DoubleComputer, exchangeRate);
  66.314 +    }
  66.315 +
  66.316 +    public static Convertor<Double, String> getConvertorDataSourceDoubleString(
  66.317 +            Collection<ExchangeRateDataSource<Double, String>> exchangeRateDataSources) {
  66.318 +        return getConvertorDataSource(DoubleComputer, exchangeRateDataSources);
  66.319 +    }
  66.320 +
  66.321 +    public static Convertor<Double, String> getConvertorDataSourceDoubleString(
  66.322 +            ExchangeRateDataSource<Double, String> exchangeRateDataSource) {
  66.323 +        return getConvertorDataSource(DoubleComputer, exchangeRateDataSource);
  66.324 +    }
  66.325 +
  66.326 +    public static Convertor<Integer, String> getConvertorIntegerString(
  66.327 +            Collection<ExchangeRateValue<Integer, String>> exchangeRates) {
  66.328 +        return getConvertor(IntegerComputer, exchangeRates);
  66.329 +    }
  66.330 +
  66.331 +    public static Convertor<Integer, String> getConvertorIntegerString(
  66.332 +            ExchangeRateValue<Integer, String> exchangeRate) {
  66.333 +        return getConvertor(IntegerComputer, exchangeRate);
  66.334 +    }
  66.335 +
  66.336 +    public static Convertor<Integer, String> getConvertorDataSourceIntegerString(
  66.337 +            Collection<ExchangeRateDataSource<Integer, String>> exchangeRateDataSources) {
  66.338 +        return getConvertorDataSource(IntegerComputer, exchangeRateDataSources);
  66.339 +    }
  66.340 +
  66.341 +    public static Convertor<Integer, String> getConvertorDataSourceIntegerString(
  66.342 +            ExchangeRateDataSource<Integer, String> exchangeRateDataSource) {
  66.343 +        return getConvertorDataSource(IntegerComputer, exchangeRateDataSource);
  66.344 +    }
  66.345 +
  66.346 +    // ---
  66.347 +    // BACKWARD COMPATIBILITY - CREATION
  66.348 +    // ---
  66.349 +    /**
  66.350 +     * Creates convertor for Double|String values with specified exchange rate
  66.351 +     * between two currencies.
  66.352 +     * 
  66.353 +     * @param firstCurrencyExchangeRate first currency
  66.354 +     * @param secondCurrencyExchangeRate second currency
  66.355 +     * @return convertor
  66.356 +     */
  66.357 +    public static Convertor<Double, String> getConvertorDoubleString(
  66.358 +            CurrencyValue<Double, String> firstCurrencyExchangeRate,
  66.359 +            CurrencyValue<Double, String> secondCurrencyExchangeRate) {
  66.360 +        return getConvertorDoubleString(ExchangeRateValue.getExchangeRate(firstCurrencyExchangeRate, secondCurrencyExchangeRate));
  66.361 +    }
  66.362 +
  66.363 +    /**
  66.364 +     * Creates convertor for Integer|String values with specified exchange rate
  66.365 +     * between two currencies.
  66.366 +     * 
  66.367 +     * @param firstCurrencyExchangeRate first currency
  66.368 +     * @param secondCurrencyExchangeRate second currency
  66.369 +     * @return convertor
  66.370 +     */
  66.371 +    public static Convertor<Integer, String> getConvertorIntegerString(
  66.372 +            CurrencyValue<Integer, String> firstCurrencyExchangeRate,
  66.373 +            CurrencyValue<Integer, String> secondCurrencyExchangeRate) {
  66.374 +        return getConvertorIntegerString(ExchangeRateValue.getExchangeRate(firstCurrencyExchangeRate, secondCurrencyExchangeRate));
  66.375 +    }
  66.376 +
  66.377 +    // ---
  66.378 +    // COMPUTERS
  66.379 +    // ---
  66.380 +    static final Computer<Double> DoubleComputer = new Computer<Double>() {
  66.381 +
  66.382 +        public void compute(ComputerRequest<Double> request, ComputerResponse<Double> response) {
  66.383 +            response.setResult(request.getInput() * request.getOutputCurrencyRatio() / request.getInputCurrencyRatio());
  66.384 +        }
  66.385 +    };
  66.386 +    static final Computer<Integer> IntegerComputer = new Computer<Integer>() {
  66.387 +
  66.388 +        public void compute(ComputerRequest<Integer> request, ComputerResponse<Integer> response) {
  66.389 +            response.setResult(request.getInput() * request.getOutputCurrencyRatio() / request.getInputCurrencyRatio());
  66.390 +        }
  66.391 +    };
  66.392 +}
    67.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    67.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/CurrencyValue.java	Sat Oct 18 07:47:34 2008 +0200
    67.3 @@ -0,0 +1,72 @@
    67.4 +package org.apidesign.apifest08.currency;
    67.5 +
    67.6 +import java.io.Serializable;
    67.7 +
    67.8 +/**
    67.9 + * Value class, holding an amount of the currency & an identifier of the currency.
   67.10 + * Designed to be an immutable.
   67.11 + * 
   67.12 + * Because of a vague definition of types of the both fields,
   67.13 + * the class has generic types, used as types of the fields.
   67.14 + * These types should be immutable classes, too.
   67.15 + * 
   67.16 + * @author ked
   67.17 + */
   67.18 +public final class CurrencyValue<AmountType, IdentifierType> implements Serializable {
   67.19 +
   67.20 +    private final AmountType amount;
   67.21 +    private final IdentifierType identifier;
   67.22 +
   67.23 +    private CurrencyValue(AmountType amount, IdentifierType identifier) {
   67.24 +        this.amount = amount;
   67.25 +        this.identifier = identifier;
   67.26 +    }
   67.27 +
   67.28 +    public AmountType getAmount() {
   67.29 +        return amount;
   67.30 +    }
   67.31 +
   67.32 +    public IdentifierType getIdentifier() {
   67.33 +        return identifier;
   67.34 +    }
   67.35 +
   67.36 +    @Override
   67.37 +    public boolean equals(Object obj) {
   67.38 +        if (obj == null) {
   67.39 +            return false;
   67.40 +        }
   67.41 +        if (getClass() != obj.getClass()) {
   67.42 +            return false;
   67.43 +        }
   67.44 +        final CurrencyValue other = (CurrencyValue) obj;
   67.45 +        if (this.amount != other.amount && (this.amount == null || !this.amount.equals(other.amount))) {
   67.46 +            return false;
   67.47 +        }
   67.48 +        if (this.identifier != other.identifier && (this.identifier == null || !this.identifier.equals(other.identifier))) {
   67.49 +            return false;
   67.50 +        }
   67.51 +        return true;
   67.52 +    }
   67.53 +
   67.54 +    @Override
   67.55 +    public int hashCode() {
   67.56 +        int hash = 7;
   67.57 +        hash = 97 * hash + (this.amount != null ? this.amount.hashCode() : 0);
   67.58 +        hash = 97 * hash + (this.identifier != null ? this.identifier.hashCode() : 0);
   67.59 +        return hash;
   67.60 +    }
   67.61 +
   67.62 +    /**
   67.63 +     * Creates new instance.
   67.64 +     * Generic types of the new instance are derived from types of the parameters.
   67.65 +     * 
   67.66 +     * @param <AmountType> type of the currency amount
   67.67 +     * @param <IdentifierType> type of the currency identifier
   67.68 +     * @param amount currency amount
   67.69 +     * @param identifier currency identifier
   67.70 +     * @return new instance
   67.71 +     */
   67.72 +    public static <AmountType, IdentifierType> CurrencyValue<AmountType, IdentifierType> getCurrencyValue(AmountType amount, IdentifierType identifier) {
   67.73 +        return new CurrencyValue<AmountType, IdentifierType>(amount, identifier);
   67.74 +    }
   67.75 +}
    68.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    68.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/DateUtil.java	Sat Oct 18 07:47:34 2008 +0200
    68.3 @@ -0,0 +1,68 @@
    68.4 +package org.apidesign.apifest08.currency;
    68.5 +
    68.6 +import java.util.Date;
    68.7 +
    68.8 +/**
    68.9 + * Date util helper class.
   68.10 + * @author ked
   68.11 + */
   68.12 +final class DateUtil {
   68.13 +
   68.14 +    private DateUtil() {};
   68.15 +
   68.16 +    static boolean isInRange(Date instant, Date from, Date till) {
   68.17 +        if ((from == null || instant.equals(from) || instant.after(from)) &&
   68.18 +                (till == null || instant.before(till))) {
   68.19 +            return true;
   68.20 +        } else {
   68.21 +            return false;
   68.22 +        }
   68.23 +    }
   68.24 +
   68.25 +    static boolean isRangesOverlapping(Date fromA, Date tillA, Date fromB, Date tillB) {
   68.26 +        if ((fromA == null && tillA == null) || (fromB == null && tillB == null)) {
   68.27 +            return true;
   68.28 +        }
   68.29 +        if (fromA != null && isInRange(fromA, fromB, tillB)) {
   68.30 +            return true;
   68.31 +        }
   68.32 +        if (tillA != null && !tillA.equals(fromB) && isInRange(tillA, fromB, tillB)) {
   68.33 +            return true;
   68.34 +        }
   68.35 +        if (fromB != null && isInRange(fromB, fromA, tillA)) {
   68.36 +            return true;
   68.37 +        }
   68.38 +        if (tillB != null && !tillB.equals(fromA) && isInRange(tillB, fromA, tillA)) {
   68.39 +            return true;
   68.40 +        }
   68.41 +        return false;
   68.42 +    }
   68.43 +
   68.44 +    static Date getRangesIntersectionBottom(Date fromA, Date fromB) {
   68.45 +        if (fromA == null) {
   68.46 +            return fromB;
   68.47 +        }
   68.48 +        if (fromB == null) {
   68.49 +            return fromA;
   68.50 +        }
   68.51 +        if (fromA.after(fromB)) {
   68.52 +            return fromA;
   68.53 +        } else {
   68.54 +            return fromB;
   68.55 +        }
   68.56 +    }
   68.57 +
   68.58 +    static Date getRangesIntersectionTop(Date tillA, Date tillB) {
   68.59 +        if (tillA == null) {
   68.60 +            return tillB;
   68.61 +        }
   68.62 +        if (tillB == null) {
   68.63 +            return tillA;
   68.64 +        }
   68.65 +        if (tillA.before(tillB)) {
   68.66 +            return tillA;
   68.67 +        } else {
   68.68 +            return tillB;
   68.69 +        }
   68.70 +    }
   68.71 +}
    69.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    69.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/ExchangeRateDataSource.java	Sat Oct 18 07:47:34 2008 +0200
    69.3 @@ -0,0 +1,154 @@
    69.4 +package org.apidesign.apifest08.currency;
    69.5 +
    69.6 +import java.util.Date;
    69.7 +import org.apidesign.apifest08.currency.ExchangeRateProvider.ExchangeRateRequest;
    69.8 +import org.apidesign.apifest08.currency.ExchangeRateProvider.ExchangeRateResponse;
    69.9 +
   69.10 +/**
   69.11 + * Exchange rate data source.
   69.12 + * 
   69.13 + * @author ked
   69.14 + */
   69.15 +public final class ExchangeRateDataSource<AmountType, IdentifierType> {
   69.16 +
   69.17 +    private final IdentifierType currencyAIdentifier;
   69.18 +    private final IdentifierType currencyBIdentifier;
   69.19 +    private final ExchangeRateProvider<AmountType, IdentifierType> exchangeRateProvider;
   69.20 +    private final Date validFrom;
   69.21 +    private final Date validTill;
   69.22 +
   69.23 +    private ExchangeRateDataSource(
   69.24 +            IdentifierType currencyAIdentifier,
   69.25 +            IdentifierType currencyBIdentifier,
   69.26 +            ExchangeRateProvider<AmountType, IdentifierType> exchangeRateProvider,
   69.27 +            Date validFrom,
   69.28 +            Date validTill) {
   69.29 +        if (currencyAIdentifier == null ||
   69.30 +                currencyBIdentifier == null ||
   69.31 +                currencyAIdentifier.equals(currencyBIdentifier)) {
   69.32 +            throw new IllegalArgumentException("Inappropriate exchange rates' identifiers!");
   69.33 +        }
   69.34 +        if (validFrom != null &&
   69.35 +                validTill != null &&
   69.36 +                !validTill.after(validFrom)) {
   69.37 +            throw new IllegalArgumentException("Inappropriate exchange rate validity!");
   69.38 +        }
   69.39 +
   69.40 +        this.currencyAIdentifier = currencyAIdentifier;
   69.41 +        this.currencyBIdentifier = currencyBIdentifier;
   69.42 +        this.exchangeRateProvider = exchangeRateProvider;
   69.43 +        this.validFrom = validFrom;
   69.44 +        this.validTill = validTill;
   69.45 +    }
   69.46 +
   69.47 +    public IdentifierType getCurrencyAIdentifier() {
   69.48 +        return currencyAIdentifier;
   69.49 +    }
   69.50 +
   69.51 +    public IdentifierType getCurrencyBIdentifier() {
   69.52 +        return currencyBIdentifier;
   69.53 +    }
   69.54 +
   69.55 +    ExchangeRateProvider<AmountType, IdentifierType> getExchangeRateProvider() {
   69.56 +        return exchangeRateProvider;
   69.57 +    }
   69.58 +
   69.59 +    public Date getValidFrom() {
   69.60 +        return validFrom != null ? (Date) validFrom.clone() : null;
   69.61 +    }
   69.62 +
   69.63 +    public Date getValidTill() {
   69.64 +        return validTill != null ? (Date) validTill.clone() : null;
   69.65 +    }
   69.66 +
   69.67 +    public ExchangeRateValue<AmountType, IdentifierType> getExchangeRate() {
   69.68 +        return getExchangeRate(new Date()); // System.currentTimeMillis()
   69.69 +    }
   69.70 +
   69.71 +    public ExchangeRateValue<AmountType, IdentifierType> getExchangeRate(Date instant) {
   69.72 +        ExchangeRateRequest<AmountType, IdentifierType> request =
   69.73 +                new ExchangeRateRequest<AmountType, IdentifierType>();
   69.74 +        ExchangeRateResponse<AmountType, IdentifierType> response =
   69.75 +                new ExchangeRateResponse<AmountType, IdentifierType>();
   69.76 +
   69.77 +        request.setCurrencyAIdentifier(currencyAIdentifier);
   69.78 +        request.setCurrencyBIdentifier(currencyBIdentifier);
   69.79 +        request.setInstant(instant);
   69.80 +
   69.81 +        exchangeRateProvider.getExchangeRate(request, response);
   69.82 +
   69.83 +        ExchangeRateValue<AmountType, IdentifierType> result = response.getExchangeRate();
   69.84 +        if (result.getCurrencyA().getIdentifier().equals(currencyAIdentifier) &&
   69.85 +                result.getCurrencyB().getIdentifier().equals(currencyBIdentifier)) {
   69.86 +            return result;
   69.87 +        } else {
   69.88 +            throw new IllegalStateException("Data source's provider returned inappropriate exchange rate!");
   69.89 +        }
   69.90 +    }
   69.91 +
   69.92 +    public static <AmountType, IdentifierType> ExchangeRateDataSource<AmountType, IdentifierType> getExchangeRateDataSource(
   69.93 +            IdentifierType currencyAIdentifier,
   69.94 +            IdentifierType currencyBIdentifier,
   69.95 +            ExchangeRateProvider<AmountType, IdentifierType> exchangeRateProvider) {
   69.96 +        return getExchangeRateDataSource(
   69.97 +                currencyAIdentifier,
   69.98 +                currencyBIdentifier,
   69.99 +                exchangeRateProvider,
  69.100 +                null,
  69.101 +                null);
  69.102 +    }
  69.103 +
  69.104 +    public static <AmountType, IdentifierType> ExchangeRateDataSource<AmountType, IdentifierType> getExchangeRateDataSource(
  69.105 +            IdentifierType currencyAIdentifier,
  69.106 +            IdentifierType currencyBIdentifier,
  69.107 +            ExchangeRateProvider<AmountType, IdentifierType> exchangeRateProvider,
  69.108 +            Date validFrom,
  69.109 +            Date validTill) {
  69.110 +        return new ExchangeRateDataSource<AmountType, IdentifierType>(
  69.111 +                currencyAIdentifier,
  69.112 +                currencyBIdentifier,
  69.113 +                exchangeRateProvider,
  69.114 +                validFrom,
  69.115 +                validTill);
  69.116 +    }
  69.117 +
  69.118 +    @Override
  69.119 +    public boolean equals(Object obj) {
  69.120 +        if (obj == null) {
  69.121 +            return false;
  69.122 +        }
  69.123 +        if (getClass() != obj.getClass()) {
  69.124 +            return false;
  69.125 +        }
  69.126 +        final ExchangeRateDataSource other =
  69.127 +                (ExchangeRateDataSource) obj;
  69.128 +        if (this.currencyAIdentifier != other.currencyAIdentifier &&
  69.129 +                (this.currencyAIdentifier == null || !this.currencyAIdentifier.equals(other.currencyAIdentifier))) {
  69.130 +            return false;
  69.131 +        }
  69.132 +        if (this.currencyBIdentifier != other.currencyBIdentifier &&
  69.133 +                (this.currencyBIdentifier == null || !this.currencyBIdentifier.equals(other.currencyBIdentifier))) {
  69.134 +            return false;
  69.135 +        }
  69.136 +        if (this.exchangeRateProvider != other.exchangeRateProvider &&
  69.137 +                (this.exchangeRateProvider == null || !this.exchangeRateProvider.equals(other.exchangeRateProvider))) {
  69.138 +            return false;
  69.139 +        }
  69.140 +        if (this.validFrom != other.validFrom && (this.validFrom == null || !this.validFrom.equals(other.validFrom))) {
  69.141 +            return false;
  69.142 +        }
  69.143 +        if (this.validTill != other.validTill && (this.validTill == null || !this.validTill.equals(other.validTill))) {
  69.144 +            return false;
  69.145 +        }
  69.146 +        return true;
  69.147 +    }
  69.148 +
  69.149 +    @Override
  69.150 +    public int hashCode() {
  69.151 +        int hash = 7;
  69.152 +        hash = 83 * hash + (this.currencyAIdentifier != null ? this.currencyAIdentifier.hashCode() : 0);
  69.153 +        hash = 83 * hash + (this.currencyBIdentifier != null ? this.currencyBIdentifier.hashCode() : 0);
  69.154 +        hash = 83 * hash + (this.exchangeRateProvider != null ? this.exchangeRateProvider.hashCode() : 0);
  69.155 +        return hash;
  69.156 +    }
  69.157 +}
    70.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    70.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/ExchangeRateProvider.java	Sat Oct 18 07:47:34 2008 +0200
    70.3 @@ -0,0 +1,66 @@
    70.4 +package org.apidesign.apifest08.currency;
    70.5 +
    70.6 +import java.util.Date;
    70.7 +
    70.8 +/**
    70.9 + * Exchange rate provider.
   70.10 + * 
   70.11 + * @author ked
   70.12 + * @see http://wiki.apidesign.org/wiki/APIDesignPatterns:ResponseReply
   70.13 + */
   70.14 +public interface ExchangeRateProvider<AmountType, IdentifierType> {
   70.15 +
   70.16 +    public void getExchangeRate(
   70.17 +            ExchangeRateRequest<AmountType, IdentifierType> request,
   70.18 +            ExchangeRateResponse<AmountType, IdentifierType> response);
   70.19 +
   70.20 +    public final class ExchangeRateRequest<AmountType, IdentifierType> {
   70.21 +
   70.22 +        private IdentifierType currencyAIdentifier;
   70.23 +        private IdentifierType currencyBIdentifier;
   70.24 +        private Date instant;
   70.25 +
   70.26 +        ExchangeRateRequest() {
   70.27 +        }
   70.28 +
   70.29 +        public IdentifierType getCurrencyAIdentifier() {
   70.30 +            return currencyAIdentifier;
   70.31 +        }
   70.32 +
   70.33 +        void setCurrencyAIdentifier(IdentifierType currencyAIdentifier) {
   70.34 +            this.currencyAIdentifier = currencyAIdentifier;
   70.35 +        }
   70.36 +
   70.37 +        public IdentifierType getCurrencyBIdentifier() {
   70.38 +            return currencyBIdentifier;
   70.39 +        }
   70.40 +
   70.41 +        void setCurrencyBIdentifier(IdentifierType currencyBIdentifier) {
   70.42 +            this.currencyBIdentifier = currencyBIdentifier;
   70.43 +        }
   70.44 +
   70.45 +        public Date getInstant() {
   70.46 +            return instant;
   70.47 +        }
   70.48 +
   70.49 +        void setInstant(Date instant) {
   70.50 +            this.instant = instant;
   70.51 +        }
   70.52 +    }
   70.53 +
   70.54 +    public final class ExchangeRateResponse<AmountType, IdentifierType> {
   70.55 +
   70.56 +        private ExchangeRateValue<AmountType, IdentifierType> exchangeRate;
   70.57 +
   70.58 +        ExchangeRateResponse() {
   70.59 +        }
   70.60 +
   70.61 +        ExchangeRateValue<AmountType, IdentifierType> getExchangeRate() {
   70.62 +            return exchangeRate;
   70.63 +        }
   70.64 +
   70.65 +        public void setExchangeRate(ExchangeRateValue<AmountType, IdentifierType> exchangeRate) {
   70.66 +            this.exchangeRate = exchangeRate;
   70.67 +        }
   70.68 +    }
   70.69 +}
    71.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    71.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/ExchangeRateValue.java	Sat Oct 18 07:47:34 2008 +0200
    71.3 @@ -0,0 +1,78 @@
    71.4 +package org.apidesign.apifest08.currency;
    71.5 +
    71.6 +import java.io.Serializable;
    71.7 +
    71.8 +/**
    71.9 + * Value class, holding an exchange rate between two currencies.
   71.10 + * Designed to be an immutable.
   71.11 + * 
   71.12 + * @author ked
   71.13 + */
   71.14 +public final class ExchangeRateValue<AmountType, IdentifierType> implements Serializable {
   71.15 +
   71.16 +    private final CurrencyValue<AmountType, IdentifierType> currencyA;
   71.17 +    private final CurrencyValue<AmountType, IdentifierType> currencyB;
   71.18 +
   71.19 +    private ExchangeRateValue(
   71.20 +            CurrencyValue<AmountType, IdentifierType> currencyA,
   71.21 +            CurrencyValue<AmountType, IdentifierType> currencyB) {
   71.22 +        if (currencyA.getIdentifier() == null ||
   71.23 +            currencyB.getIdentifier() == null ||
   71.24 +            currencyA.getIdentifier().equals(currencyB)) {
   71.25 +                throw new IllegalArgumentException("Inappropriate exchange rates' identifiers!");
   71.26 +        }
   71.27 +
   71.28 +        this.currencyA = currencyA;
   71.29 +        this.currencyB = currencyB;
   71.30 +    }
   71.31 +
   71.32 +    public CurrencyValue<AmountType, IdentifierType> getCurrencyA() {
   71.33 +        return currencyA;
   71.34 +    }
   71.35 +
   71.36 +    public CurrencyValue<AmountType, IdentifierType> getCurrencyB() {
   71.37 +        return currencyB;
   71.38 +    }
   71.39 +
   71.40 +    @Override
   71.41 +    public boolean equals(Object obj) {
   71.42 +        if (obj == null) {
   71.43 +            return false;
   71.44 +        }
   71.45 +        if (getClass() != obj.getClass()) {
   71.46 +            return false;
   71.47 +        }
   71.48 +        final ExchangeRateValue other = (ExchangeRateValue) obj;
   71.49 +        if (this.currencyA != other.currencyA && (this.currencyA == null || !this.currencyA.equals(other.currencyA))) {
   71.50 +            return false;
   71.51 +        }
   71.52 +        if (this.currencyB != other.currencyB && (this.currencyB == null || !this.currencyB.equals(other.currencyB))) {
   71.53 +            return false;
   71.54 +        }
   71.55 +        return true;
   71.56 +    }
   71.57 +
   71.58 +    @Override
   71.59 +    public int hashCode() {
   71.60 +        int hash = 3;
   71.61 +        hash = 71 * hash + (this.currencyA != null ? this.currencyA.hashCode() : 0);
   71.62 +        hash = 71 * hash + (this.currencyB != null ? this.currencyB.hashCode() : 0);
   71.63 +        return hash;
   71.64 +    }
   71.65 +
   71.66 +    /**
   71.67 +     * Creates new instance.
   71.68 +     * Generic types of the new instance are derived from types of the parameters.
   71.69 +     * 
   71.70 +     * @param <AmountType> type of the currency amount
   71.71 +     * @param <IdentifierType> type of the currency identifier
   71.72 +     * @param currencyA one currency of the exchange rate
   71.73 +     * @param currencyB another currency of the exchange rate
   71.74 +     * @return new instance
   71.75 +     */
   71.76 +    public static <AmountType, IdentifierType> ExchangeRateValue<AmountType, IdentifierType> getExchangeRate(
   71.77 +            CurrencyValue<AmountType, IdentifierType> currencyA,
   71.78 +            CurrencyValue<AmountType, IdentifierType> currencyB) {
   71.79 +        return new ExchangeRateValue<AmountType, IdentifierType>(currencyA, currencyB);
   71.80 +    }
   71.81 +}
    72.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    72.2 +++ b/task4/solution11/src/org/apidesign/apifest08/currency/StaticExchangeRateProvider.java	Sat Oct 18 07:47:34 2008 +0200
    72.3 @@ -0,0 +1,48 @@
    72.4 +package org.apidesign.apifest08.currency;
    72.5 +
    72.6 +/**
    72.7 + * Static exchange rate provider.
    72.8 + * 
    72.9 + * @author ked
   72.10 + */
   72.11 +final class StaticExchangeRateProvider<AmountType, IdentifierType> implements ExchangeRateProvider<AmountType, IdentifierType> {
   72.12 +
   72.13 +    final ExchangeRateValue<AmountType, IdentifierType> exchangeRate;
   72.14 +
   72.15 +    private StaticExchangeRateProvider(ExchangeRateValue<AmountType, IdentifierType> exchangeRate) {
   72.16 +        this.exchangeRate = exchangeRate;
   72.17 +    }
   72.18 +
   72.19 +    public void getExchangeRate(ExchangeRateRequest<AmountType, IdentifierType> request, ExchangeRateResponse<AmountType, IdentifierType> response) {
   72.20 +        response.setExchangeRate(exchangeRate);
   72.21 +    }
   72.22 +
   72.23 +    static <AmountType, IdentifierType> StaticExchangeRateProvider<AmountType, IdentifierType> getStaticExchangeRateProvider(ExchangeRateValue<AmountType, IdentifierType> exchangeRate) {
   72.24 +        return new StaticExchangeRateProvider<AmountType, IdentifierType>(exchangeRate);
   72.25 +    }
   72.26 +
   72.27 +    @Override
   72.28 +    public boolean equals(Object obj) {
   72.29 +        if (obj == null) {
   72.30 +            return false;
   72.31 +        }
   72.32 +
   72.33 +        if (getClass() != obj.getClass()) {
   72.34 +            return false;
   72.35 +        }
   72.36 +
   72.37 +        final StaticExchangeRateProvider other = (StaticExchangeRateProvider) obj;
   72.38 +        if (this.exchangeRate != other.exchangeRate && (this.exchangeRate == null || !this.exchangeRate.equals(other.exchangeRate))) {
   72.39 +            return false;
   72.40 +        }
   72.41 +
   72.42 +        return true;
   72.43 +    }
   72.44 +
   72.45 +    @Override
   72.46 +    public int hashCode() {
   72.47 +        int hash = 7;
   72.48 +        hash = 67 * hash + (this.exchangeRate != null ? this.exchangeRate.hashCode() : 0);
   72.49 +        return hash;
   72.50 +    }
   72.51 +}
    73.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    73.2 +++ b/task4/solution11/test/org/apidesign/apifest08/test/Task1Test.java	Sat Oct 18 07:47:34 2008 +0200
    73.3 @@ -0,0 +1,157 @@
    73.4 +package org.apidesign.apifest08.test;
    73.5 +
    73.6 +import junit.framework.TestCase;
    73.7 +import org.apidesign.apifest08.currency.Convertor;
    73.8 +import org.apidesign.apifest08.currency.CurrencyValue;
    73.9 +
   73.10 +/** Finish the Convertor API, and then write bodies of methods inside
   73.11 + * of this class to match the given tasks. To fullfil your task, use the
   73.12 + * API define in the <code>org.apidesign.apifest08.currency</code> package.
   73.13 + * Do not you reflection, or other hacks as your code
   73.14 + * shall run without any runtime permissions.
   73.15 + */
   73.16 +public class Task1Test extends TestCase {
   73.17 +    public Task1Test(String testName) {
   73.18 +        super(testName);
   73.19 +    }
   73.20 +
   73.21 +    @Override
   73.22 +    protected void setUp() throws Exception {
   73.23 +    }
   73.24 +
   73.25 +    @Override
   73.26 +    protected void tearDown() throws Exception {
   73.27 +    }
   73.28 +
   73.29 +    //
   73.30 +    // Imagine that there are three parts of the whole system:
   73.31 +    // 1. there is someone who knows the current exchange rate
   73.32 +    // 2. there is someone who wants to do the conversion
   73.33 +    // 3. there is the API between 1. and 2. which allows them to communicate
   73.34 +    // Please design such API
   73.35 +    //
   73.36 +
   73.37 +    /** Create convertor that understands two currencies, CZK and
   73.38 +     *  USD. Make 1 USD == 17 CZK. This is a method provided for #1 group -
   73.39 +     *  e.g. those that know the exchange rate. They somehow need to create
   73.40 +     *  the objects from the API and tell them the exchange rate. The API itself
   73.41 +     *  knows nothing about any rates, before the createCZKtoUSD method is called.
   73.42 +     *
   73.43 +     * Creation of the convertor shall not require subclassing of any class
   73.44 +     * or interface on the client side.
   73.45 +     *
   73.46 +     * @return prepared convertor ready for converting USD to CZK and CZK to USD
   73.47 +     */
   73.48 +    public static Convertor<Double, String> createCZKtoUSD() {
   73.49 +        return Convertor.getConvertorDoubleString(
   73.50 +                CurrencyValue.getCurrencyValue(1d, "USD"),
   73.51 +                CurrencyValue.getCurrencyValue(17d, "CZK")
   73.52 +        );
   73.53 +    }
   73.54 +
   73.55 +    /** Create convertor that understands two currencies, CZK and
   73.56 +     *  SKK. Make 100 SKK == 80 CZK. Again this is method for the #1 group -
   73.57 +     *  it knows the exchange rate, and needs to use the API to create objects
   73.58 +     *  with the exchange rate. Anyone shall be ready to call this method without
   73.59 +     *  any other method being called previously. The API itself shall know
   73.60 +     *  nothing about any rates, before this method is called.
   73.61 +     *
   73.62 +     * Creation of the convertor shall not require subclassing of any class
   73.63 +     * or interface on the client side.
   73.64 +     * 
   73.65 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
   73.66 +     */
   73.67 +    public static Convertor<Double, String> createSKKtoCZK() {
   73.68 +        return Convertor.getConvertorDoubleString(
   73.69 +                CurrencyValue.getCurrencyValue(100d, "SKK"),
   73.70 +                CurrencyValue.getCurrencyValue(80d, "CZK")
   73.71 +        );
   73.72 +    }
   73.73 +
   73.74 +    //
   73.75 +    // now the methods for group #2 follow:
   73.76 +    // this group knows nothing about exchange rates, but knows how to use
   73.77 +    // the API to do conversions. It somehow (by calling one of the factory
   73.78 +    // methods) gets objects from the API and uses them to do the conversions.
   73.79 +    //
   73.80 +    
   73.81 +    /** Use the convertor from <code>createCZKtoUSD</code> method and do few conversions
   73.82 +     * with it.
   73.83 +     */
   73.84 +    public void testCurrencyCZKUSD() throws Exception {
   73.85 +        Convertor<Double, String> c = createCZKtoUSD();
   73.86 +        
   73.87 +        CurrencyValue<Double, String> result;
   73.88 +        
   73.89 +        // convert $5 to CZK using c:
   73.90 +        // assertEquals("Result is 85 CZK");
   73.91 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(5d, "USD"));
   73.92 +        assertEquals(CurrencyValue.getCurrencyValue(85d, "CZK"), result);
   73.93 +
   73.94 +        // convert $8 to CZK
   73.95 +        // assertEquals("Result is 136 CZK");
   73.96 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(8d, "USD"));
   73.97 +        assertEquals(CurrencyValue.getCurrencyValue(136d, "CZK"), result);
   73.98 +
   73.99 +        // convert 1003CZK to USD
  73.100 +        // assertEquals("Result is 59 USD");
  73.101 +        result = c.convert("USD", CurrencyValue.getCurrencyValue(1003d, "CZK"));
  73.102 +        assertEquals(CurrencyValue.getCurrencyValue(59d, "USD"), result);
  73.103 +    }
  73.104 +
  73.105 +    /** Use the convertor from <code>createSKKtoCZK</code> method and do few conversions
  73.106 +     * with it.
  73.107 +     */
  73.108 +    public void testCurrencySKKCZK() throws Exception {
  73.109 +        Convertor<Double, String> c = createSKKtoCZK();
  73.110 +        
  73.111 +        CurrencyValue<Double, String> result;
  73.112 +        
  73.113 +        // convert 16CZK using c:
  73.114 +        // assertEquals("Result is 20 SKK");
  73.115 +        result = c.convert("SKK", CurrencyValue.getCurrencyValue(16d, "CZK"));
  73.116 +        assertEquals(CurrencyValue.getCurrencyValue(20d, "SKK"), result);
  73.117 +
  73.118 +        // convert 500SKK to CZK
  73.119 +        // assertEquals("Result is 400 CZK");
  73.120 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"));
  73.121 +        assertEquals(CurrencyValue.getCurrencyValue(400d, "CZK"), result);
  73.122 +    }
  73.123 +
  73.124 +    /** Verify that the CZK to USD convertor knows nothing about SKK.
  73.125 +     */
  73.126 +    public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception {
  73.127 +        Convertor<Double, String> c = createCZKtoUSD();
  73.128 +        try {
  73.129 +            // convert $5 to SKK, the API shall say this is not possible
  73.130 +            c.convert("SKK", CurrencyValue.getCurrencyValue(16d, "CZK"));
  73.131 +            fail("Should not convert");
  73.132 +        } catch (Exception e) {
  73.133 +        }
  73.134 +        try {
  73.135 +            // convert 500 SKK to CZK, the API shall say this is not possible
  73.136 +            c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"));
  73.137 +            fail("Should not convert");
  73.138 +        } catch (Exception e) {
  73.139 +        }
  73.140 +        
  73.141 +    }
  73.142 +
  73.143 +    /** Verify that the CZK to SKK convertor knows nothing about USD.
  73.144 +     */
  73.145 +    public void testCannotConvertToUSDwithSKKCZKConvertor() throws Exception {
  73.146 +        Convertor<Double, String> c = createSKKtoCZK();
  73.147 +        try {
  73.148 +            // convert $5 to SKK, the API shall say this is not possible
  73.149 +            c.convert("SKK", CurrencyValue.getCurrencyValue(5d, "USD"));
  73.150 +            fail("Should not convert");
  73.151 +        } catch (Exception e) {
  73.152 +        }
  73.153 +        try {
  73.154 +            // convert 500 CZK to USD, the API shall say this is not possible
  73.155 +            c.convert("USD", CurrencyValue.getCurrencyValue(500d, "CZK"));
  73.156 +            fail("Should not convert");
  73.157 +        } catch (Exception e) {
  73.158 +        }
  73.159 +    }
  73.160 +}
    74.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    74.2 +++ b/task4/solution11/test/org/apidesign/apifest08/test/Task2Test.java	Sat Oct 18 07:47:34 2008 +0200
    74.3 @@ -0,0 +1,140 @@
    74.4 +package org.apidesign.apifest08.test;
    74.5 +
    74.6 +import java.util.ArrayList;
    74.7 +import java.util.Collection;
    74.8 +import junit.framework.TestCase;
    74.9 +import org.apidesign.apifest08.currency.Convertor;
   74.10 +import org.apidesign.apifest08.currency.CurrencyValue;
   74.11 +import org.apidesign.apifest08.currency.ExchangeRateValue;
   74.12 +
   74.13 +/** There are many currencies around the world and many banks manipulate
   74.14 + * with more than one or two at the same time. As banks are usually the
   74.15 + * best paying clients, which is true even in case of your Convertor API,
   74.16 + * it is reasonable to listen to their requests.
   74.17 + * <p>
   74.18 + * The quest for today is to enhance your existing convertor API to hold
   74.19 + * information about many currencies and allow conversions between any of them.
   74.20 + * Also, as conversion rates for diferent currencies usually arise from various
   74.21 + * bank departments, there is another important need. There is a need to
   74.22 + * compose two convertors into one by merging all the information about
   74.23 + * currencies they know about.
   74.24 + */
   74.25 +public class Task2Test extends TestCase {
   74.26 +
   74.27 +    public Task2Test(String testName) {
   74.28 +        super(testName);
   74.29 +    }
   74.30 +
   74.31 +    @Override
   74.32 +    protected void setUp() throws Exception {
   74.33 +    }
   74.34 +
   74.35 +    @Override
   74.36 +    protected void tearDown() throws Exception {
   74.37 +    }
   74.38 +
   74.39 +    // As in Task1Test, keep in mind, that there are three parts
   74.40 +    // of the whole system:
   74.41 +    // 1. there is someone who knows the current exchange rate
   74.42 +    // 2. there is someone who wants to do the conversion
   74.43 +    // 3. there is the API between 1. and 2. which allows them to communicate
   74.44 +    // 
   74.45 +    // Please backward compatibly enhance your existing API to support following
   74.46 +    // usecases:
   74.47 +    //
   74.48 +    /** Create convertor that understands two currencies, CZK and
   74.49 +     *  SKK. Make 100 SKK == 75 CZK. This is method for the group of users that
   74.50 +     *  knows the exchange rate, and needs to use the API to create objects
   74.51 +     *  with the exchange rate. Anyone shall be ready to call this method without
   74.52 +     *  any other method being called previously. The API itself shall know
   74.53 +     *  nothing about any rates, before this method is called.
   74.54 +     */
   74.55 +    public static Convertor<Double, String> createTripleConvertor() {
   74.56 +        // Rates: 1USD = 15CZK
   74.57 +        // Rates: 1USD = 20SKK
   74.58 +        // Rates: 75CZK = 100SKK
   74.59 +        Collection<ExchangeRateValue<Double, String>> exchangeRates =
   74.60 +                new ArrayList<ExchangeRateValue<Double, String>>();
   74.61 +        exchangeRates.add(ExchangeRateValue.getExchangeRate(
   74.62 +                CurrencyValue.getCurrencyValue(1d, "USD"),
   74.63 +                CurrencyValue.getCurrencyValue(15d, "CZK")));
   74.64 +        exchangeRates.add(ExchangeRateValue.getExchangeRate(
   74.65 +                CurrencyValue.getCurrencyValue(1d, "USD"),
   74.66 +                CurrencyValue.getCurrencyValue(20d, "SKK")));
   74.67 +        exchangeRates.add(ExchangeRateValue.getExchangeRate(
   74.68 +                CurrencyValue.getCurrencyValue(75d, "CZK"),
   74.69 +                CurrencyValue.getCurrencyValue(100d, "SKK")));
   74.70 +        return Convertor.getConvertorDoubleString(exchangeRates);
   74.71 +    }
   74.72 +
   74.73 +    /** Define convertor that understands three currencies. Use it.
   74.74 +     */
   74.75 +    public void testConvertorForUSDandCZKandSKK() throws Exception {
   74.76 +        Convertor<Double, String> c = createTripleConvertor();
   74.77 +
   74.78 +        CurrencyValue<Double, String> result;
   74.79 +        // convert $5 to CZK using c:
   74.80 +        // assertEquals("Result is 75 CZK");
   74.81 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(5d, "USD"));
   74.82 +        assertEquals(CurrencyValue.getCurrencyValue(75d, "CZK"), result);
   74.83 +
   74.84 +        // convert $5 to SKK using c:
   74.85 +        // assertEquals("Result is 100 SKK");
   74.86 +        result = c.convert("SKK", CurrencyValue.getCurrencyValue(5d, "USD"));
   74.87 +        assertEquals(CurrencyValue.getCurrencyValue(100d, "SKK"), result);
   74.88 +
   74.89 +        // convert 200SKK to CZK using c:
   74.90 +        // assertEquals("Result is 150 CZK");
   74.91 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(200d, "SKK"));
   74.92 +        assertEquals(CurrencyValue.getCurrencyValue(150d, "CZK"), result);
   74.93 +
   74.94 +        // convert 200SKK to USK using c:
   74.95 +        // assertEquals("Result is 10 USD");
   74.96 +        result = c.convert("USD", CurrencyValue.getCurrencyValue(200d, "SKK"));
   74.97 +        assertEquals(CurrencyValue.getCurrencyValue(10d, "USD"), result);
   74.98 +    }
   74.99 +
  74.100 +    /** Merge all currency rates of convertor 1 with convertor 2.
  74.101 +     * Implement this using your API, preferably this method just delegates
  74.102 +     * into some API method which does the actual work, without requiring
  74.103 +     * API clients to code anything complex.
  74.104 +     */
  74.105 +    public static Convertor<Double, String> merge(Convertor<Double, String> one, Convertor<Double, String> two) {
  74.106 +        return Convertor.mergeConvertorsDoubleString(one, two);
  74.107 +    }
  74.108 +
  74.109 +    /** Join the convertors from previous task, Task1Test and show that it
  74.110 +     * can be used to do reasonable conversions.
  74.111 +     */
  74.112 +    public void testConvertorComposition() throws Exception {
  74.113 +        Convertor<Double, String> c = merge(
  74.114 +                Task1Test.createCZKtoUSD(),
  74.115 +                Task1Test.createSKKtoCZK());
  74.116 +
  74.117 +        CurrencyValue<Double, String> result;
  74.118 +        // convert $5 to CZK using c:
  74.119 +        // assertEquals("Result is 85 CZK");
  74.120 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(5d, "USD"));
  74.121 +        assertEquals(CurrencyValue.getCurrencyValue(85d, "CZK"), result);
  74.122 +
  74.123 +        // convert $8 to CZK using c:
  74.124 +        // assertEquals("Result is 136 CZK");
  74.125 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(8d, "USD"));
  74.126 +        assertEquals(CurrencyValue.getCurrencyValue(136d, "CZK"), result);
  74.127 +
  74.128 +        // convert 1003CZK to USD using c:
  74.129 +        // assertEquals("Result is 59 USD");
  74.130 +        result = c.convert("USD", CurrencyValue.getCurrencyValue(1003d, "CZK"));
  74.131 +        assertEquals(CurrencyValue.getCurrencyValue(59d, "USD"), result);
  74.132 +
  74.133 +        // convert 16CZK using c:
  74.134 +        // assertEquals("Result is 20 SKK");
  74.135 +        result = c.convert("SKK", CurrencyValue.getCurrencyValue(16d, "CZK"));
  74.136 +        assertEquals(CurrencyValue.getCurrencyValue(20d, "SKK"), result);
  74.137 +
  74.138 +        // convert 500SKK to CZK using c:
  74.139 +        // assertEquals("Result is 400 CZK");
  74.140 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"));
  74.141 +        assertEquals(CurrencyValue.getCurrencyValue(400d, "CZK"), result);
  74.142 +    }
  74.143 +}
    75.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    75.2 +++ b/task4/solution11/test/org/apidesign/apifest08/test/Task3Test.java	Sat Oct 18 07:47:34 2008 +0200
    75.3 @@ -0,0 +1,136 @@
    75.4 +package org.apidesign.apifest08.test;
    75.5 +
    75.6 +import junit.framework.TestCase;
    75.7 +import org.apidesign.apifest08.currency.Convertor;
    75.8 +import org.apidesign.apifest08.currency.CurrencyValue;
    75.9 +import org.apidesign.apifest08.currency.ExchangeRateDataSource;
   75.10 +import org.apidesign.apifest08.currency.ExchangeRateProvider;
   75.11 +import org.apidesign.apifest08.currency.ExchangeRateProvider.ExchangeRateRequest;
   75.12 +import org.apidesign.apifest08.currency.ExchangeRateProvider.ExchangeRateResponse;
   75.13 +import org.apidesign.apifest08.currency.ExchangeRateValue;
   75.14 +
   75.15 +/** The exchange rates are not always the same. They are changing. Day by day,
   75.16 + * hour by hour, minute by minute. For every bank it is important to always
   75.17 + * have the actual exchange rate available in the system. That is why let's
   75.18 + * create a pluggable convertor that will always have up to date value of its
   75.19 + * exchange rate.
   75.20 + * <p>
   75.21 + * The quest for today is to allow 3rd party developer to write a convertor
   75.22 + * that adjusts its exchange rate everytime it is queried. This convertor is
   75.23 + * written by independent vendor, the vendor knows only your Convertor API,
   75.24 + * he does not know how the whole system looks and how the convertor is supposed
   75.25 + * to be used.
   75.26 + */
   75.27 +public class Task3Test extends TestCase {
   75.28 +
   75.29 +    public Task3Test(String testName) {
   75.30 +        super(testName);
   75.31 +    }
   75.32 +
   75.33 +    @Override
   75.34 +    protected void setUp() throws Exception {
   75.35 +    }
   75.36 +
   75.37 +    @Override
   75.38 +    protected void tearDown() throws Exception {
   75.39 +    }
   75.40 +
   75.41 +    // Backward compatibly enhance your existing API to support following
   75.42 +    // usecases:
   75.43 +    //
   75.44 +    /** Without knowing anything about the surrounding system, write an
   75.45 +     * implementation of convertor that will return different rates everytime
   75.46 +     * it is queried. Convert USD to CZK and vice versa. Start with the rate of
   75.47 +     * 1USD = 16CZK and adjust it in favor of CZK by 0.01 CZK with every query.
   75.48 +     * As soon as you reach 1USD = 15CZK adjust it by 0.01 CZK in favor of USD
   75.49 +     * until you reach 1USD = 16CZK
   75.50 +     *
   75.51 +     * @return new instance of "online" USD and CZK convertor starting with rate 1USD = 16CZK
   75.52 +     */
   75.53 +    public static Convertor<Double, String> createOnlineCZKUSDConvertor() {
   75.54 +        // initial rate: 1USD = 16CZK
   75.55 +        // 2nd query 1USD = 15.99CZK
   75.56 +        // 3rd query 1USD = 15.98CZK
   75.57 +        // until 1USD = 15.00CZK
   75.58 +        // then 1USD = 15.01CZK
   75.59 +        // then 1USD = 15.02CZK
   75.60 +        // and so on and on up to 1USD = 16CZK
   75.61 +        // and then another round to 15, etc.
   75.62 +        return Convertor.getConvertorDataSourceDoubleString(
   75.63 +                ExchangeRateDataSource.getExchangeRateDataSource(
   75.64 +                "USD", "CZK", new ExchangeRateProvider<Double, String>() {
   75.65 +
   75.66 +            double currentRate = 16d;
   75.67 +            double step;
   75.68 +
   75.69 +            public void getExchangeRate(ExchangeRateRequest<Double, String> request,
   75.70 +                    ExchangeRateResponse<Double, String> response) {
   75.71 +                if ((request.getCurrencyAIdentifier().equals("CZK") && request.getCurrencyBIdentifier().equals("USD")) ||
   75.72 +                        (request.getCurrencyAIdentifier().equals("USD") && request.getCurrencyBIdentifier().equals("CZK"))) {
   75.73 +                    response.setExchangeRate(ExchangeRateValue.getExchangeRate(
   75.74 +                            CurrencyValue.getCurrencyValue(1d, "USD"),
   75.75 +                            CurrencyValue.getCurrencyValue(currentRate, "CZK")));
   75.76 +
   75.77 +                    if (currentRate == 16d) {
   75.78 +                        step = -0.01;
   75.79 +                    }
   75.80 +                    if (currentRate == 15d) {
   75.81 +                        step = 0.01;
   75.82 +                    }
   75.83 +                    currentRate += step;
   75.84 +                } else {
   75.85 +                    throw new IllegalArgumentException("No exchange rate for requested currencies!");
   75.86 +                }
   75.87 +            }
   75.88 +        }));
   75.89 +    }
   75.90 +
   75.91 +    public void testFewQueriesForOnlineConvertor() {
   75.92 +        Convertor<Double, String> c = createOnlineCZKUSDConvertor();
   75.93 +        doFewQueriesForOnlineConvertor(c);
   75.94 +    }
   75.95 +
   75.96 +    static void doFewQueriesForOnlineConvertor(Convertor<Double, String> c) {
   75.97 +        CurrencyValue<Double, String> result;
   75.98 +        // convert $5 to CZK using c:
   75.99 +        //assertEquals("Result is 80 CZK");
  75.100 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(5d, "USD"));
  75.101 +        assertEquals(CurrencyValue.getCurrencyValue(80d, "CZK"), result);
  75.102 +
  75.103 +        // convert $8 to CZK using c:
  75.104 +        //assertEquals("Result is 127.92 CZK");
  75.105 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(8d, "USD"));
  75.106 +        assertEquals(CurrencyValue.getCurrencyValue(127.92d, "CZK"), result);
  75.107 +
  75.108 +        // convert $1 to CZK using c:
  75.109 +        //assertEquals("Result is 15.98 CZK");
  75.110 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(1d, "USD"));
  75.111 +        assertEquals(CurrencyValue.getCurrencyValue(15.98d, "CZK"), result);
  75.112 +
  75.113 +        // convert 15.97CZK to USD using c:
  75.114 +        //assertEquals("Result is 1$");
  75.115 +        result = c.convert("USD", CurrencyValue.getCurrencyValue(15.97d, "CZK"));
  75.116 +        assertEquals(CurrencyValue.getCurrencyValue(1d, "USD"), result);
  75.117 +    }
  75.118 +
  75.119 +    /** Join the convertors and show they behave sane.
  75.120 +     */
  75.121 +    public void testOnlineConvertorComposition() throws Exception {
  75.122 +        Convertor<Double, String> c = Task2Test.merge(
  75.123 +                createOnlineCZKUSDConvertor(),
  75.124 +                Task1Test.createSKKtoCZK());
  75.125 +
  75.126 +        CurrencyValue<Double, String> result;
  75.127 +        // convert 16CZK to SKK using c:
  75.128 +        // assertEquals("Result is 20 SKK");
  75.129 +        result = c.convert("SKK", CurrencyValue.getCurrencyValue(16d, "CZK"));
  75.130 +        assertEquals(CurrencyValue.getCurrencyValue(20d, "SKK"), result);
  75.131 +
  75.132 +        // convert 500SKK to CZK using c:
  75.133 +        // assertEquals("Result is 400 CZK");
  75.134 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"));
  75.135 +        assertEquals(CurrencyValue.getCurrencyValue(400d, "CZK"), result);
  75.136 +
  75.137 +        doFewQueriesForOnlineConvertor(c);
  75.138 +    }
  75.139 +}
    76.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    76.2 +++ b/task4/solution11/test/org/apidesign/apifest08/test/Task4Test.java	Sat Oct 18 07:47:34 2008 +0200
    76.3 @@ -0,0 +1,194 @@
    76.4 +package org.apidesign.apifest08.test;
    76.5 +
    76.6 +import java.util.Calendar;
    76.7 +import java.util.Date;
    76.8 +import java.util.GregorianCalendar;
    76.9 +import java.util.TimeZone;
   76.10 +import junit.framework.TestCase;
   76.11 +import org.apidesign.apifest08.currency.Convertor;
   76.12 +import org.apidesign.apifest08.currency.CurrencyValue;
   76.13 +import org.apidesign.apifest08.currency.ExchangeRateValue;
   76.14 +
   76.15 +/** The exchange rates are not always the same. They are changing. However
   76.16 + * as in order to predict the future, one needs to understand own past. That is
   76.17 + * why it is important to know the exchange rate as it was at any time during
   76.18 + * the past.
   76.19 + * <p>
   76.20 + * Today's quest is to enhance the convertor API to deal with dates.
   76.21 + * One shall be able to convert a currency at any date. Each currencies rate shall
   76.22 + * be associated with a range between two Date objects. In order
   76.23 + * to keep compatibility with old API that knew nothing about dates, the
   76.24 + * rates associated then are applicable "for eternity". Any use of existing
   76.25 + * convert methods that do not accept a Date argument, uses the current
   76.26 + * System.currentTimeMillis() as default date.
   76.27 + */
   76.28 +public class Task4Test extends TestCase {
   76.29 +    public Task4Test(String testName) {
   76.30 +        super(testName);
   76.31 +    }
   76.32 +
   76.33 +    @Override
   76.34 +    protected void setUp() throws Exception {
   76.35 +    }
   76.36 +
   76.37 +    @Override
   76.38 +    protected void tearDown() throws Exception {
   76.39 +    }
   76.40 +
   76.41 +    // Backward compatibly enhance your existing API to support following
   76.42 +    // usecases:
   76.43 +    //
   76.44 +
   76.45 +    /** Takes a convertor with any rates associated and creates new convertor
   76.46 +     * that returns the same values as the old one for time between from to till.
   76.47 +     * Otherwise it returns no results. This is just a helper method that
   76.48 +     * shall call some real one in the API.
   76.49 +     * 
   76.50 +     * @param old existing convertor
   76.51 +     * @param from initial date (inclusive)
   76.52 +     * @param till final date (exclusive)
   76.53 +     * @return new convertor
   76.54 +     */
   76.55 +    public static <AmountType, IdentifierType> Convertor<AmountType, IdentifierType> limitTo(Convertor<AmountType, IdentifierType> old, Date from, Date till) {
   76.56 +        return old.limitConvertor(from, till);
   76.57 +    }
   76.58 +
   76.59 +
   76.60 +    public void testCompositionOfLimitedConvertors() throws Exception {
   76.61 +        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
   76.62 +        cal.clear();
   76.63 +        cal.set(2008, 9, 1, 0, 0);
   76.64 +        Date d1 = cal.getTime(); // 2008-10-01 0:00 GMT
   76.65 +        cal.set(2008, 9, 2, 0, 0);
   76.66 +        Date d2 = cal.getTime(); // 2008-10-02 0:00 GMT
   76.67 +        cal.set(2008, 9, 3, 0, 0);
   76.68 +        Date d3 = cal.getTime(); // 2008-10-03 0:00 GMT
   76.69 +        
   76.70 +        Convertor<Double, String> c = Task2Test.merge(
   76.71 +            limitTo(Task1Test.createCZKtoUSD(), d1, d2),
   76.72 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
   76.73 +        );
   76.74 +
   76.75 +        CurrencyValue<Double, String> result;
   76.76 +        // convert $5 to CZK using c:
   76.77 +        // cannot convert as no rate is applicable to current date
   76.78 +        try {
   76.79 +            c.convert("CZK", CurrencyValue.getCurrencyValue(5d, "USD"));
   76.80 +            fail("Should not convert");
   76.81 +        } catch (Exception e) {
   76.82 +        }
   76.83 +        
   76.84 +        // convert $8 to CZK using c:
   76.85 +        // cannot convert as no rate is applicable to current date
   76.86 +        try {
   76.87 +            c.convert("CZK", CurrencyValue.getCurrencyValue(8d, "USD"));
   76.88 +            fail("Should not convert");
   76.89 +        } catch (Exception e) {
   76.90 +        }
   76.91 +
   76.92 +        // convert 1003CZK to USD using c:
   76.93 +        // cannot convert as no rate is applicable to current date
   76.94 +        try {
   76.95 +            c.convert("USD", CurrencyValue.getCurrencyValue(1003d, "CZK"));
   76.96 +            fail("Should not convert");
   76.97 +        } catch (Exception e) {
   76.98 +        }
   76.99 +
  76.100 +        // convert 16CZK using c:
  76.101 +        // cannot convert as no rate is applicable to current date
  76.102 +        try {
  76.103 +            c.convert("SKK", CurrencyValue.getCurrencyValue(16d, "CZK"));
  76.104 +            fail("Should not convert");
  76.105 +        } catch (Exception e) {
  76.106 +        }
  76.107 +
  76.108 +        // convert 500SKK to CZK using c:
  76.109 +        // cannot convert as no rate is applicable to current date
  76.110 +        try {
  76.111 +            c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"));
  76.112 +            fail("Should not convert");
  76.113 +        } catch (Exception e) {
  76.114 +        }
  76.115 +
  76.116 +        // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
  76.117 +        // assertEquals("Result is 85 CZK");
  76.118 +        cal.set(2008, 9, 1, 6, 0);
  76.119 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(5d, "USD"), cal.getTime());
  76.120 +        assertEquals(CurrencyValue.getCurrencyValue(85d, "CZK"), result);
  76.121 +
  76.122 +        // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
  76.123 +        // assertEquals("Result is 136 CZK");
  76.124 +        cal.set(2008, 9, 1, 6, 0);
  76.125 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(8d, "USD"), cal.getTime());
  76.126 +        assertEquals(CurrencyValue.getCurrencyValue(136d, "CZK"), result);
  76.127 +
  76.128 +        // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
  76.129 +        // assertEquals("Result is 59 USD");
  76.130 +        cal.set(2008, 9, 1, 6, 0);
  76.131 +        result = c.convert("USD", CurrencyValue.getCurrencyValue(1003d, "CZK"), cal.getTime());
  76.132 +        assertEquals(CurrencyValue.getCurrencyValue(59d, "USD"), result);
  76.133 +
  76.134 +        // convert 16CZK using c at 2008-10-02 9:00 GMT:
  76.135 +        // assertEquals("Result is 20 SKK");
  76.136 +        cal.set(2008, 9, 2, 9, 0);
  76.137 +        result = c.convert("SKK", CurrencyValue.getCurrencyValue(16d, "CZK"), cal.getTime());
  76.138 +        assertEquals(CurrencyValue.getCurrencyValue(20d, "SKK"), result);
  76.139 +
  76.140 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  76.141 +        // assertEquals("Result is 400 CZK");
  76.142 +        cal.set(2008, 9, 2, 9, 0);
  76.143 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"), cal.getTime());
  76.144 +        assertEquals(CurrencyValue.getCurrencyValue(400d, "CZK"), result);
  76.145 +
  76.146 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  76.147 +        // cannot convert as no rate is applicable to current date
  76.148 +        cal.set(2008, 9, 1, 6, 0);
  76.149 +        try {
  76.150 +            result = c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"), cal.getTime());
  76.151 +            fail("Should not convert");
  76.152 +        } catch (Exception e) {
  76.153 +        }
  76.154 +    }
  76.155 +
  76.156 +    /** Create convertor that understands two currencies, CZK and
  76.157 +     *  SKK. Make 100 SKK == 90 CZK.
  76.158 +     *
  76.159 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
  76.160 +     */
  76.161 +    public static Convertor<Double, String> createSKKtoCZK2() {
  76.162 +        return Convertor.getConvertorDoubleString(
  76.163 +                ExchangeRateValue.getExchangeRate(
  76.164 +                    CurrencyValue.getCurrencyValue(100d, "SKK"),
  76.165 +                    CurrencyValue.getCurrencyValue(90d, "CZK")));
  76.166 +    }
  76.167 +
  76.168 +    public void testDateConvetorWithTwoDifferentRates() throws Exception {
  76.169 +        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
  76.170 +        cal.clear();
  76.171 +        cal.set(2008, 9, 1, 0, 0);
  76.172 +        Date d1 = cal.getTime(); // 2008-10-01 0:00 GMT
  76.173 +        cal.set(2008, 9, 2, 0, 0);
  76.174 +        Date d2 = cal.getTime(); // 2008-10-02 0:00 GMT
  76.175 +        cal.set(2008, 9, 3, 0, 0);
  76.176 +        Date d3 = cal.getTime(); // 2008-10-03 0:00 GMT
  76.177 +
  76.178 +        Convertor<Double, String> c = Task2Test.merge(
  76.179 +            limitTo(createSKKtoCZK2(), d1, d2),
  76.180 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
  76.181 +        );
  76.182 +
  76.183 +        CurrencyValue<Double, String> result;
  76.184 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  76.185 +        // assertEquals("Result is 400 CZK");
  76.186 +        cal.set(2008, 9, 2, 9, 0);
  76.187 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"), cal.getTime());
  76.188 +        assertEquals(CurrencyValue.getCurrencyValue(400d, "CZK"), result);
  76.189 +
  76.190 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  76.191 +        // assertEquals("Result is 450 CZK");
  76.192 +        cal.set(2008, 9, 1, 6, 0);
  76.193 +        result = c.convert("CZK", CurrencyValue.getCurrencyValue(500d, "SKK"), cal.getTime());
  76.194 +        assertEquals(CurrencyValue.getCurrencyValue(450d, "CZK"), result);
  76.195 +    }
  76.196 +
  76.197 +}
    77.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    77.2 +++ b/task4/solution13/build.xml	Sat Oct 18 07:47:34 2008 +0200
    77.3 @@ -0,0 +1,69 @@
    77.4 +<?xml version="1.0" encoding="UTF-8"?>
    77.5 +<!-- You may freely edit this file. See commented blocks below for -->
    77.6 +<!-- some examples of how to customize the build. -->
    77.7 +<!-- (If you delete it and reopen the project it will be recreated.) -->
    77.8 +<project name="currency" default="default" basedir=".">
    77.9 +    <description>Builds, tests, and runs the project.</description>
   77.10 +    <import file="nbproject/build-impl.xml"/>
   77.11 +    <!--
   77.12 +
   77.13 +    There exist several targets which are by default empty and which can be 
   77.14 +    used for execution of your tasks. These targets are usually executed 
   77.15 +    before and after some main targets. They are: 
   77.16 +
   77.17 +      -pre-init:                 called before initialization of project properties
   77.18 +      -post-init:                called after initialization of project properties
   77.19 +      -pre-compile:              called before javac compilation
   77.20 +      -post-compile:             called after javac compilation
   77.21 +      -pre-compile-single:       called before javac compilation of single file
   77.22 +      -post-compile-single:      called after javac compilation of single file
   77.23 +      -pre-compile-test:         called before javac compilation of JUnit tests
   77.24 +      -post-compile-test:        called after javac compilation of JUnit tests
   77.25 +      -pre-compile-test-single:  called before javac compilation of single JUnit test
   77.26 +      -post-compile-test-single: called after javac compilation of single JUunit test
   77.27 +      -pre-jar:                  called before JAR building
   77.28 +      -post-jar:                 called after JAR building
   77.29 +      -post-clean:               called after cleaning build products
   77.30 +
   77.31 +    (Targets beginning with '-' are not intended to be called on their own.)
   77.32 +
   77.33 +    Example of inserting an obfuscator after compilation could look like this:
   77.34 +
   77.35 +        <target name="-post-compile">
   77.36 +            <obfuscate>
   77.37 +                <fileset dir="${build.classes.dir}"/>
   77.38 +            </obfuscate>
   77.39 +        </target>
   77.40 +
   77.41 +    For list of available properties check the imported 
   77.42 +    nbproject/build-impl.xml file. 
   77.43 +
   77.44 +
   77.45 +    Another way to customize the build is by overriding existing main targets.
   77.46 +    The targets of interest are: 
   77.47 +
   77.48 +      -init-macrodef-javac:     defines macro for javac compilation
   77.49 +      -init-macrodef-junit:     defines macro for junit execution
   77.50 +      -init-macrodef-debug:     defines macro for class debugging
   77.51 +      -init-macrodef-java:      defines macro for class execution
   77.52 +      -do-jar-with-manifest:    JAR building (if you are using a manifest)
   77.53 +      -do-jar-without-manifest: JAR building (if you are not using a manifest)
   77.54 +      run:                      execution of project 
   77.55 +      -javadoc-build:           Javadoc generation
   77.56 +      test-report:              JUnit report generation
   77.57 +
   77.58 +    An example of overriding the target for project execution could look like this:
   77.59 +
   77.60 +        <target name="run" depends="currency-impl.jar">
   77.61 +            <exec dir="bin" executable="launcher.exe">
   77.62 +                <arg file="${dist.jar}"/>
   77.63 +            </exec>
   77.64 +        </target>
   77.65 +
   77.66 +    Notice that the overridden target depends on the jar target and not only on 
   77.67 +    the compile target as the regular run target does. Again, for a list of available 
   77.68 +    properties which you can use, check the target you are overriding in the
   77.69 +    nbproject/build-impl.xml file. 
   77.70 +
   77.71 +    -->
   77.72 +</project>
    78.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    78.2 +++ b/task4/solution13/nbproject/build-impl.xml	Sat Oct 18 07:47:34 2008 +0200
    78.3 @@ -0,0 +1,642 @@
    78.4 +<?xml version="1.0" encoding="UTF-8"?>
    78.5 +<!--
    78.6 +*** GENERATED FROM project.xml - DO NOT EDIT  ***
    78.7 +***         EDIT ../build.xml INSTEAD         ***
    78.8 +
    78.9 +For the purpose of easier reading the script
   78.10 +is divided into following sections:
   78.11 +
   78.12 +  - initialization
   78.13 +  - compilation
   78.14 +  - jar
   78.15 +  - execution
   78.16 +  - debugging
   78.17 +  - javadoc
   78.18 +  - junit compilation
   78.19 +  - junit execution
   78.20 +  - junit debugging
   78.21 +  - applet
   78.22 +  - cleanup
   78.23 +
   78.24 +        -->
   78.25 +<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="Currency_Convertor_Solution_13-impl">
   78.26 +    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
   78.27 +    <!-- 
   78.28 +                ======================
   78.29 +                INITIALIZATION SECTION 
   78.30 +                ======================
   78.31 +            -->
   78.32 +    <target name="-pre-init">
   78.33 +        <!-- Empty placeholder for easier customization. -->
   78.34 +        <!-- You can override this target in the ../build.xml file. -->
   78.35 +    </target>
   78.36 +    <target depends="-pre-init" name="-init-private">
   78.37 +        <property file="nbproject/private/config.properties"/>
   78.38 +        <property file="nbproject/private/configs/${config}.properties"/>
   78.39 +        <property file="nbproject/private/private.properties"/>
   78.40 +    </target>
   78.41 +    <target depends="-pre-init,-init-private" name="-init-user">
   78.42 +        <property file="${user.properties.file}"/>
   78.43 +        <!-- The two properties below are usually overridden -->
   78.44 +        <!-- by the active platform. Just a fallback. -->
   78.45 +        <property name="default.javac.source" value="1.4"/>
   78.46 +        <property name="default.javac.target" value="1.4"/>
   78.47 +    </target>
   78.48 +    <target depends="-pre-init,-init-private,-init-user" name="-init-project">
   78.49 +        <property file="nbproject/configs/${config}.properties"/>
   78.50 +        <property file="nbproject/project.properties"/>
   78.51 +    </target>
   78.52 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
   78.53 +        <available file="${manifest.file}" property="manifest.available"/>
   78.54 +        <condition property="manifest.available+main.class">
   78.55 +            <and>
   78.56 +                <isset property="manifest.available"/>
   78.57 +                <isset property="main.class"/>
   78.58 +                <not>
   78.59 +                    <equals arg1="${main.class}" arg2="" trim="true"/>
   78.60 +                </not>
   78.61 +            </and>
   78.62 +        </condition>
   78.63 +        <condition property="manifest.available+main.class+mkdist.available">
   78.64 +            <and>
   78.65 +                <istrue value="${manifest.available+main.class}"/>
   78.66 +                <isset property="libs.CopyLibs.classpath"/>
   78.67 +            </and>
   78.68 +        </condition>
   78.69 +        <condition property="have.tests">
   78.70 +            <or>
   78.71 +                <available file="${test.src.dir}"/>
   78.72 +            </or>
   78.73 +        </condition>
   78.74 +        <condition property="have.sources">
   78.75 +            <or>
   78.76 +                <available file="${src.dir}"/>
   78.77 +            </or>
   78.78 +        </condition>
   78.79 +        <condition property="netbeans.home+have.tests">
   78.80 +            <and>
   78.81 +                <isset property="netbeans.home"/>
   78.82 +                <isset property="have.tests"/>
   78.83 +            </and>
   78.84 +        </condition>
   78.85 +        <condition property="no.javadoc.preview">
   78.86 +            <and>
   78.87 +                <isset property="javadoc.preview"/>
   78.88 +                <isfalse value="${javadoc.preview}"/>
   78.89 +            </and>
   78.90 +        </condition>
   78.91 +        <property name="run.jvmargs" value=""/>
   78.92 +        <property name="javac.compilerargs" value=""/>
   78.93 +        <property name="work.dir" value="${basedir}"/>
   78.94 +        <condition property="no.deps">
   78.95 +            <and>
   78.96 +                <istrue value="${no.dependencies}"/>
   78.97 +            </and>
   78.98 +        </condition>
   78.99 +        <property name="javac.debug" value="true"/>
  78.100 +        <property name="javadoc.preview" value="true"/>
  78.101 +        <property name="application.args" value=""/>
  78.102 +        <property name="source.encoding" value="${file.encoding}"/>
  78.103 +        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
  78.104 +            <and>
  78.105 +                <isset property="javadoc.encoding"/>
  78.106 +                <not>
  78.107 +                    <equals arg1="${javadoc.encoding}" arg2=""/>
  78.108 +                </not>
  78.109 +            </and>
  78.110 +        </condition>
  78.111 +        <property name="javadoc.encoding.used" value="${source.encoding}"/>
  78.112 +        <property name="includes" value="**"/>
  78.113 +        <property name="excludes" value=""/>
  78.114 +        <property name="do.depend" value="false"/>
  78.115 +        <condition property="do.depend.true">
  78.116 +            <istrue value="${do.depend}"/>
  78.117 +        </condition>
  78.118 +        <condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
  78.119 +            <and>
  78.120 +                <isset property="jaxws.endorsed.dir"/>
  78.121 +                <available file="nbproject/jaxws-build.xml"/>
  78.122 +            </and>
  78.123 +        </condition>
  78.124 +    </target>
  78.125 +    <target name="-post-init">
  78.126 +        <!-- Empty placeholder for easier customization. -->
  78.127 +        <!-- You can override this target in the ../build.xml file. -->
  78.128 +    </target>
  78.129 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
  78.130 +        <fail unless="src.dir">Must set src.dir</fail>
  78.131 +        <fail unless="test.src.dir">Must set test.src.dir</fail>
  78.132 +        <fail unless="build.dir">Must set build.dir</fail>
  78.133 +        <fail unless="dist.dir">Must set dist.dir</fail>
  78.134 +        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
  78.135 +        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
  78.136 +        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
  78.137 +        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
  78.138 +        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
  78.139 +        <fail unless="dist.jar">Must set dist.jar</fail>
  78.140 +    </target>
  78.141 +    <target name="-init-macrodef-property">
  78.142 +        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
  78.143 +            <attribute name="name"/>
  78.144 +            <attribute name="value"/>
  78.145 +            <sequential>
  78.146 +                <property name="@{name}" value="${@{value}}"/>
  78.147 +            </sequential>
  78.148 +        </macrodef>
  78.149 +    </target>
  78.150 +    <target name="-init-macrodef-javac">
  78.151 +        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
  78.152 +            <attribute default="${src.dir}" name="srcdir"/>
  78.153 +            <attribute default="${build.classes.dir}" name="destdir"/>
  78.154 +            <attribute default="${javac.classpath}" name="classpath"/>
  78.155 +            <attribute default="${includes}" name="includes"/>
  78.156 +            <attribute default="${excludes}" name="excludes"/>
  78.157 +            <attribute default="${javac.debug}" name="debug"/>
  78.158 +            <attribute default="" name="sourcepath"/>
  78.159 +            <element name="customize" optional="true"/>
  78.160 +            <sequential>
  78.161 +                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
  78.162 +                    <classpath>
  78.163 +                        <path path="@{classpath}"/>
  78.164 +                    </classpath>
  78.165 +                    <compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
  78.166 +                    <customize/>
  78.167 +                </javac>
  78.168 +            </sequential>
  78.169 +        </macrodef>
  78.170 +        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
  78.171 +            <attribute default="${src.dir}" name="srcdir"/>
  78.172 +            <attribute default="${build.classes.dir}" name="destdir"/>
  78.173 +            <attribute default="${javac.classpath}" name="classpath"/>
  78.174 +            <sequential>
  78.175 +                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
  78.176 +                    <classpath>
  78.177 +                        <path path="@{classpath}"/>
  78.178 +                    </classpath>
  78.179 +                </depend>
  78.180 +            </sequential>
  78.181 +        </macrodef>
  78.182 +        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
  78.183 +            <attribute default="${build.classes.dir}" name="destdir"/>
  78.184 +            <sequential>
  78.185 +                <fail unless="javac.includes">Must set javac.includes</fail>
  78.186 +                <pathconvert pathsep="," property="javac.includes.binary">
  78.187 +                    <path>
  78.188 +                        <filelist dir="@{destdir}" files="${javac.includes}"/>
  78.189 +                    </path>
  78.190 +                    <globmapper from="*.java" to="*.class"/>
  78.191 +                </pathconvert>
  78.192 +                <delete>
  78.193 +                    <files includes="${javac.includes.binary}"/>
  78.194 +                </delete>
  78.195 +            </sequential>
  78.196 +        </macrodef>
  78.197 +    </target>
  78.198 +    <target name="-init-macrodef-junit">
  78.199 +        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
  78.200 +            <attribute default="${includes}" name="includes"/>
  78.201 +            <attribute default="${excludes}" name="excludes"/>
  78.202 +            <attribute default="**" name="testincludes"/>
  78.203 +            <sequential>
  78.204 +                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
  78.205 +                    <batchtest todir="${build.test.results.dir}">
  78.206 +                        <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
  78.207 +                            <filename name="@{testincludes}"/>
  78.208 +                        </fileset>
  78.209 +                    </batchtest>
  78.210 +                    <classpath>
  78.211 +                        <path path="${run.test.classpath}"/>
  78.212 +                    </classpath>
  78.213 +                    <syspropertyset>
  78.214 +                        <propertyref prefix="test-sys-prop."/>
  78.215 +                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
  78.216 +                    </syspropertyset>
  78.217 +                    <formatter type="brief" usefile="false"/>
  78.218 +                    <formatter type="xml"/>
  78.219 +                    <jvmarg line="${run.jvmargs}"/>
  78.220 +                </junit>
  78.221 +            </sequential>
  78.222 +        </macrodef>
  78.223 +    </target>
  78.224 +    <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
  78.225 +        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
  78.226 +            <attribute default="${main.class}" name="name"/>
  78.227 +            <attribute default="${debug.classpath}" name="classpath"/>
  78.228 +            <attribute default="" name="stopclassname"/>
  78.229 +            <sequential>
  78.230 +                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
  78.231 +                    <classpath>
  78.232 +                        <path path="@{classpath}"/>
  78.233 +                    </classpath>
  78.234 +                </nbjpdastart>
  78.235 +            </sequential>
  78.236 +        </macrodef>
  78.237 +        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
  78.238 +            <attribute default="${build.classes.dir}" name="dir"/>
  78.239 +            <sequential>
  78.240 +                <nbjpdareload>
  78.241 +                    <fileset dir="@{dir}" includes="${fix.classes}">
  78.242 +                        <include name="${fix.includes}*.class"/>
  78.243 +                    </fileset>
  78.244 +                </nbjpdareload>
  78.245 +            </sequential>
  78.246 +        </macrodef>
  78.247 +    </target>
  78.248 +    <target name="-init-debug-args">
  78.249 +        <property name="version-output" value="java version &quot;${ant.java.version}"/>
  78.250 +        <condition property="have-jdk-older-than-1.4">
  78.251 +            <or>
  78.252 +                <contains string="${version-output}" substring="java version &quot;1.0"/>
  78.253 +                <contains string="${version-output}" substring="java version &quot;1.1"/>
  78.254 +                <contains string="${version-output}" substring="java version &quot;1.2"/>
  78.255 +                <contains string="${version-output}" substring="java version &quot;1.3"/>
  78.256 +            </or>
  78.257 +        </condition>
  78.258 +        <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
  78.259 +            <istrue value="${have-jdk-older-than-1.4}"/>
  78.260 +        </condition>
  78.261 +        <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
  78.262 +            <os family="windows"/>
  78.263 +        </condition>
  78.264 +        <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
  78.265 +            <isset property="debug.transport"/>
  78.266 +        </condition>
  78.267 +    </target>
  78.268 +    <target depends="-init-debug-args" name="-init-macrodef-debug">
  78.269 +        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
  78.270 +            <attribute default="${main.class}" name="classname"/>
  78.271 +            <attribute default="${debug.classpath}" name="classpath"/>
  78.272 +            <element name="customize" optional="true"/>
  78.273 +            <sequential>
  78.274 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
  78.275 +                    <jvmarg line="${debug-args-line}"/>
  78.276 +                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
  78.277 +                    <jvmarg line="${run.jvmargs}"/>
  78.278 +                    <classpath>
  78.279 +                        <path path="@{classpath}"/>
  78.280 +                    </classpath>
  78.281 +                    <syspropertyset>
  78.282 +                        <propertyref prefix="run-sys-prop."/>
  78.283 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
  78.284 +                    </syspropertyset>
  78.285 +                    <customize/>
  78.286 +                </java>
  78.287 +            </sequential>
  78.288 +        </macrodef>
  78.289 +    </target>
  78.290 +    <target name="-init-macrodef-java">
  78.291 +        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
  78.292 +            <attribute default="${main.class}" name="classname"/>
  78.293 +            <element name="customize" optional="true"/>
  78.294 +            <sequential>
  78.295 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
  78.296 +                    <jvmarg line="${run.jvmargs}"/>
  78.297 +                    <classpath>
  78.298 +                        <path path="${run.classpath}"/>
  78.299 +                    </classpath>
  78.300 +                    <syspropertyset>
  78.301 +                        <propertyref prefix="run-sys-prop."/>
  78.302 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
  78.303 +                    </syspropertyset>
  78.304 +                    <customize/>
  78.305 +                </java>
  78.306 +            </sequential>
  78.307 +        </macrodef>
  78.308 +    </target>
  78.309 +    <target name="-init-presetdef-jar">
  78.310 +        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
  78.311 +            <jar compress="${jar.compress}" jarfile="${dist.jar}">
  78.312 +                <j2seproject1:fileset dir="${build.classes.dir}"/>
  78.313 +            </jar>
  78.314 +        </presetdef>
  78.315 +    </target>
  78.316 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
  78.317 +    <!--
  78.318 +                ===================
  78.319 +                COMPILATION SECTION
  78.320 +                ===================
  78.321 +            -->
  78.322 +    <target depends="init" name="deps-jar" unless="no.deps"/>
  78.323 +    <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
  78.324 +    <target depends="init" name="-check-automatic-build">
  78.325 +        <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
  78.326 +    </target>
  78.327 +    <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
  78.328 +        <antcall target="clean"/>
  78.329 +    </target>
  78.330 +    <target depends="init,deps-jar" name="-pre-pre-compile">
  78.331 +        <mkdir dir="${build.classes.dir}"/>
  78.332 +    </target>
  78.333 +    <target name="-pre-compile">
  78.334 +        <!-- Empty placeholder for easier customization. -->
  78.335 +        <!-- You can override this target in the ../build.xml file. -->
  78.336 +    </target>
  78.337 +    <target if="do.depend.true" name="-compile-depend">
  78.338 +        <j2seproject3:depend/>
  78.339 +    </target>
  78.340 +    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
  78.341 +        <j2seproject3:javac/>
  78.342 +        <copy todir="${build.classes.dir}">
  78.343 +            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  78.344 +        </copy>
  78.345 +    </target>
  78.346 +    <target name="-post-compile">
  78.347 +        <!-- Empty placeholder for easier customization. -->
  78.348 +        <!-- You can override this target in the ../build.xml file. -->
  78.349 +    </target>
  78.350 +    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
  78.351 +    <target name="-pre-compile-single">
  78.352 +        <!-- Empty placeholder for easier customization. -->
  78.353 +        <!-- You can override this target in the ../build.xml file. -->
  78.354 +    </target>
  78.355 +    <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
  78.356 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
  78.357 +        <j2seproject3:force-recompile/>
  78.358 +        <j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
  78.359 +    </target>
  78.360 +    <target name="-post-compile-single">
  78.361 +        <!-- Empty placeholder for easier customization. -->
  78.362 +        <!-- You can override this target in the ../build.xml file. -->
  78.363 +    </target>
  78.364 +    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
  78.365 +    <!--
  78.366 +                ====================
  78.367 +                JAR BUILDING SECTION
  78.368 +                ====================
  78.369 +            -->
  78.370 +    <target depends="init" name="-pre-pre-jar">
  78.371 +        <dirname file="${dist.jar}" property="dist.jar.dir"/>
  78.372 +        <mkdir dir="${dist.jar.dir}"/>
  78.373 +    </target>
  78.374 +    <target name="-pre-jar">
  78.375 +        <!-- Empty placeholder for easier customization. -->
  78.376 +        <!-- You can override this target in the ../build.xml file. -->
  78.377 +    </target>
  78.378 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
  78.379 +        <j2seproject1:jar/>
  78.380 +    </target>
  78.381 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
  78.382 +        <j2seproject1:jar manifest="${manifest.file}"/>
  78.383 +    </target>
  78.384 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
  78.385 +        <j2seproject1:jar manifest="${manifest.file}">
  78.386 +            <j2seproject1:manifest>
  78.387 +                <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
  78.388 +            </j2seproject1:manifest>
  78.389 +        </j2seproject1:jar>
  78.390 +        <echo>To run this application from the command line without Ant, try:</echo>
  78.391 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
  78.392 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
  78.393 +        <pathconvert property="run.classpath.with.dist.jar">
  78.394 +            <path path="${run.classpath}"/>
  78.395 +            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
  78.396 +        </pathconvert>
  78.397 +        <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
  78.398 +    </target>
  78.399 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
  78.400 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
  78.401 +        <pathconvert property="run.classpath.without.build.classes.dir">
  78.402 +            <path path="${run.classpath}"/>
  78.403 +            <map from="${build.classes.dir.resolved}" to=""/>
  78.404 +        </pathconvert>
  78.405 +        <pathconvert pathsep=" " property="jar.classpath">
  78.406 +            <path path="${run.classpath.without.build.classes.dir}"/>
  78.407 +            <chainedmapper>
  78.408 +                <flattenmapper/>
  78.409 +                <globmapper from="*" to="lib/*"/>
  78.410 +            </chainedmapper>
  78.411 +        </pathconvert>
  78.412 +        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
  78.413 +        <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
  78.414 +            <fileset dir="${build.classes.dir}"/>
  78.415 +            <manifest>
  78.416 +                <attribute name="Main-Class" value="${main.class}"/>
  78.417 +                <attribute name="Class-Path" value="${jar.classpath}"/>
  78.418 +            </manifest>
  78.419 +        </copylibs>
  78.420 +        <echo>To run this application from the command line without Ant, try:</echo>
  78.421 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
  78.422 +        <echo>java -jar "${dist.jar.resolved}"</echo>
  78.423 +    </target>
  78.424 +    <target name="-post-jar">
  78.425 +        <!-- Empty placeholder for easier customization. -->
  78.426 +        <!-- You can override this target in the ../build.xml file. -->
  78.427 +    </target>
  78.428 +    <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
  78.429 +    <!--
  78.430 +                =================
  78.431 +                EXECUTION SECTION
  78.432 +                =================
  78.433 +            -->
  78.434 +    <target depends="init,compile" description="Run a main class." name="run">
  78.435 +        <j2seproject1:java>
  78.436 +            <customize>
  78.437 +                <arg line="${application.args}"/>
  78.438 +            </customize>
  78.439 +        </j2seproject1:java>
  78.440 +    </target>
  78.441 +    <target name="-do-not-recompile">
  78.442 +        <property name="javac.includes.binary" value=""/>
  78.443 +    </target>
  78.444 +    <target depends="init,-do-not-recompile,compile-single" name="run-single">
  78.445 +        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
  78.446 +        <j2seproject1:java classname="${run.class}"/>
  78.447 +    </target>
  78.448 +    <!--
  78.449 +                =================
  78.450 +                DEBUGGING SECTION
  78.451 +                =================
  78.452 +            -->
  78.453 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger">
  78.454 +        <j2seproject1:nbjpdastart name="${debug.class}"/>
  78.455 +    </target>
  78.456 +    <target depends="init,compile" name="-debug-start-debuggee">
  78.457 +        <j2seproject3:debug>
  78.458 +            <customize>
  78.459 +                <arg line="${application.args}"/>
  78.460 +            </customize>
  78.461 +        </j2seproject3:debug>
  78.462 +    </target>
  78.463 +    <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
  78.464 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
  78.465 +        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
  78.466 +    </target>
  78.467 +    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
  78.468 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
  78.469 +        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
  78.470 +        <j2seproject3:debug classname="${debug.class}"/>
  78.471 +    </target>
  78.472 +    <target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
  78.473 +    <target depends="init" name="-pre-debug-fix">
  78.474 +        <fail unless="fix.includes">Must set fix.includes</fail>
  78.475 +        <property name="javac.includes" value="${fix.includes}.java"/>
  78.476 +    </target>
  78.477 +    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
  78.478 +        <j2seproject1:nbjpdareload/>
  78.479 +    </target>
  78.480 +    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
  78.481 +    <!--
  78.482 +                ===============
  78.483 +                JAVADOC SECTION
  78.484 +                ===============
  78.485 +            -->
  78.486 +    <target depends="init" name="-javadoc-build">
  78.487 +        <mkdir dir="${dist.javadoc.dir}"/>
  78.488 +        <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
  78.489 +            <classpath>
  78.490 +                <path path="${javac.classpath}"/>
  78.491 +            </classpath>
  78.492 +            <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
  78.493 +                <filename name="**/*.java"/>
  78.494 +            </fileset>
  78.495 +        </javadoc>
  78.496 +    </target>
  78.497 +    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
  78.498 +        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
  78.499 +    </target>
  78.500 +    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
  78.501 +    <!--
  78.502 +                =========================
  78.503 +                JUNIT COMPILATION SECTION
  78.504 +                =========================
  78.505 +            -->
  78.506 +    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
  78.507 +        <mkdir dir="${build.test.classes.dir}"/>
  78.508 +    </target>
  78.509 +    <target name="-pre-compile-test">
  78.510 +        <!-- Empty placeholder for easier customization. -->
  78.511 +        <!-- You can override this target in the ../build.xml file. -->
  78.512 +    </target>
  78.513 +    <target if="do.depend.true" name="-compile-test-depend">
  78.514 +        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
  78.515 +    </target>
  78.516 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
  78.517 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
  78.518 +        <copy todir="${build.test.classes.dir}">
  78.519 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  78.520 +        </copy>
  78.521 +    </target>
  78.522 +    <target name="-post-compile-test">
  78.523 +        <!-- Empty placeholder for easier customization. -->
  78.524 +        <!-- You can override this target in the ../build.xml file. -->
  78.525 +    </target>
  78.526 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
  78.527 +    <target name="-pre-compile-test-single">
  78.528 +        <!-- Empty placeholder for easier customization. -->
  78.529 +        <!-- You can override this target in the ../build.xml file. -->
  78.530 +    </target>
  78.531 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
  78.532 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
  78.533 +        <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
  78.534 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
  78.535 +        <copy todir="${build.test.classes.dir}">
  78.536 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
  78.537 +        </copy>
  78.538 +    </target>
  78.539 +    <target name="-post-compile-test-single">
  78.540 +        <!-- Empty placeholder for easier customization. -->
  78.541 +        <!-- You can override this target in the ../build.xml file. -->
  78.542 +    </target>
  78.543 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
  78.544 +    <!--
  78.545 +                =======================
  78.546 +                JUNIT EXECUTION SECTION
  78.547 +                =======================
  78.548 +            -->
  78.549 +    <target depends="init" if="have.tests" name="-pre-test-run">
  78.550 +        <mkdir dir="${build.test.results.dir}"/>
  78.551 +    </target>
  78.552 +    <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
  78.553 +        <j2seproject3:junit testincludes="**/*Test.java"/>
  78.554 +    </target>
  78.555 +    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
  78.556 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
  78.557 +    </target>
  78.558 +    <target depends="init" if="have.tests" name="test-report"/>
  78.559 +    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
  78.560 +    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
  78.561 +    <target depends="init" if="have.tests" name="-pre-test-run-single">
  78.562 +        <mkdir dir="${build.test.results.dir}"/>
  78.563 +    </target>
  78.564 +    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
  78.565 +        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
  78.566 +        <j2seproject3:junit excludes="" includes="${test.includes}"/>
  78.567 +    </target>
  78.568 +    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
  78.569 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
  78.570 +    </target>
  78.571 +    <target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
  78.572 +    <!--
  78.573 +                =======================
  78.574 +                JUNIT DEBUGGING SECTION
  78.575 +                =======================
  78.576 +            -->
  78.577 +    <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
  78.578 +        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
  78.579 +        <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
  78.580 +        <delete file="${test.report.file}"/>
  78.581 +        <mkdir dir="${build.test.results.dir}"/>
  78.582 +        <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
  78.583 +            <customize>
  78.584 +                <syspropertyset>
  78.585 +                    <propertyref prefix="test-sys-prop."/>
  78.586 +                    <mapper from="test-sys-prop.*" to="*" type="glob"/>
  78.587 +                </syspropertyset>
  78.588 +                <arg value="${test.class}"/>
  78.589 +                <arg value="showoutput=true"/>
  78.590 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
  78.591 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
  78.592 +            </customize>
  78.593 +        </j2seproject3:debug>
  78.594 +    </target>
  78.595 +    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
  78.596 +        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
  78.597 +    </target>
  78.598 +    <target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
  78.599 +    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
  78.600 +        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
  78.601 +    </target>
  78.602 +    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
  78.603 +    <!--
  78.604 +                =========================
  78.605 +                APPLET EXECUTION SECTION
  78.606 +                =========================
  78.607 +            -->
  78.608 +    <target depends="init,compile-single" name="run-applet">
  78.609 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
  78.610 +        <j2seproject1:java classname="sun.applet.AppletViewer">
  78.611 +            <customize>
  78.612 +                <arg value="${applet.url}"/>
  78.613 +            </customize>
  78.614 +        </j2seproject1:java>
  78.615 +    </target>
  78.616 +    <!--
  78.617 +                =========================
  78.618 +                APPLET DEBUGGING  SECTION
  78.619 +                =========================
  78.620 +            -->
  78.621 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
  78.622 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
  78.623 +        <j2seproject3:debug classname="sun.applet.AppletViewer">
  78.624 +            <customize>
  78.625 +                <arg value="${applet.url}"/>
  78.626 +            </customize>
  78.627 +        </j2seproject3:debug>
  78.628 +    </target>
  78.629 +    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
  78.630 +    <!--
  78.631 +                ===============
  78.632 +                CLEANUP SECTION
  78.633 +                ===============
  78.634 +            -->
  78.635 +    <target depends="init" name="deps-clean" unless="no.deps"/>
  78.636 +    <target depends="init" name="-do-clean">
  78.637 +        <delete dir="${build.dir}"/>
  78.638 +        <delete dir="${dist.dir}"/>
  78.639 +    </target>
  78.640 +    <target name="-post-clean">
  78.641 +        <!-- Empty placeholder for easier customization. -->
  78.642 +        <!-- You can override this target in the ../build.xml file. -->
  78.643 +    </target>
  78.644 +    <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
  78.645 +</project>
    79.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    79.2 +++ b/task4/solution13/nbproject/genfiles.properties	Sat Oct 18 07:47:34 2008 +0200
    79.3 @@ -0,0 +1,8 @@
    79.4 +build.xml.data.CRC32=2ab820eb
    79.5 +build.xml.script.CRC32=58a52595
    79.6 +build.xml.stylesheet.CRC32=be360661
    79.7 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
    79.8 +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
    79.9 +nbproject/build-impl.xml.data.CRC32=de21ce77
   79.10 +nbproject/build-impl.xml.script.CRC32=0903858a
   79.11 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5
    80.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    80.2 +++ b/task4/solution13/nbproject/project.properties	Sat Oct 18 07:47:34 2008 +0200
    80.3 @@ -0,0 +1,68 @@
    80.4 +application.title=currency
    80.5 +application.vendor=apidesign.org
    80.6 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8
    80.7 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
    80.8 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=default
    80.9 +build.classes.dir=${build.dir}/classes
   80.10 +build.classes.excludes=**/*.java,**/*.form
   80.11 +# This directory is removed when the project is cleaned:
   80.12 +build.dir=build
   80.13 +build.generated.dir=${build.dir}/generated
   80.14 +# Only compile against the classpath explicitly listed here:
   80.15 +build.sysclasspath=ignore
   80.16 +build.test.classes.dir=${build.dir}/test/classes
   80.17 +build.test.results.dir=${build.dir}/test/results
   80.18 +debug.classpath=\
   80.19 +    ${run.classpath}
   80.20 +debug.test.classpath=\
   80.21 +    ${run.test.classpath}
   80.22 +# This directory is removed when the project is cleaned:
   80.23 +dist.dir=dist
   80.24 +dist.jar=${dist.dir}/currency.jar
   80.25 +dist.javadoc.dir=${dist.dir}/javadoc
   80.26 +excludes=
   80.27 +file.reference.junit-4.4.jar=../../libs/junit-4.4.jar
   80.28 +file.reference.src-apifest08=..
   80.29 +includes=**
   80.30 +jar.compress=false
   80.31 +javac.classpath=
   80.32 +# Space-separated list of extra javac options
   80.33 +javac.compilerargs=-Xlint:unchecked
   80.34 +javac.deprecation=false
   80.35 +javac.source=1.5
   80.36 +javac.target=1.5
   80.37 +javac.test.classpath=\
   80.38 +    ${javac.classpath}:\
   80.39 +    ${build.classes.dir}:\
   80.40 +    ${file.reference.junit-4.4.jar}
   80.41 +javadoc.additionalparam=
   80.42 +javadoc.author=false
   80.43 +javadoc.encoding=
   80.44 +javadoc.noindex=false
   80.45 +javadoc.nonavbar=false
   80.46 +javadoc.notree=false
   80.47 +javadoc.private=false
   80.48 +javadoc.splitindex=true
   80.49 +javadoc.use=true
   80.50 +javadoc.version=false
   80.51 +javadoc.windowtitle=
   80.52 +jnlp.codebase.type=local
   80.53 +jnlp.codebase.url=file:/home/jarda/src/apifest08/currency/dist
   80.54 +jnlp.descriptor=application
   80.55 +jnlp.enabled=false
   80.56 +jnlp.offline-allowed=false
   80.57 +jnlp.signed=false
   80.58 +meta.inf.dir=${src.dir}/META-INF
   80.59 +platform.active=default_platform
   80.60 +run.classpath=\
   80.61 +    ${javac.classpath}:\
   80.62 +    ${build.classes.dir}
   80.63 +# Space-separated list of JVM arguments used when running the project
   80.64 +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
   80.65 +# or test-sys-prop.name=value to set system properties for unit tests):
   80.66 +run.jvmargs=
   80.67 +run.test.classpath=\
   80.68 +    ${javac.test.classpath}:\
   80.69 +    ${build.test.classes.dir}
   80.70 +src.dir=src
   80.71 +test.src.dir=test
    81.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    81.2 +++ b/task4/solution13/nbproject/project.xml	Sat Oct 18 07:47:34 2008 +0200
    81.3 @@ -0,0 +1,16 @@
    81.4 +<?xml version="1.0" encoding="UTF-8"?>
    81.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
    81.6 +    <type>org.netbeans.modules.java.j2seproject</type>
    81.7 +    <configuration>
    81.8 +        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
    81.9 +            <name>Currency Convertor Solution 13</name>
   81.10 +            <minimum-ant-version>1.6.5</minimum-ant-version>
   81.11 +            <source-roots>
   81.12 +                <root id="src.dir"/>
   81.13 +            </source-roots>
   81.14 +            <test-roots>
   81.15 +                <root id="test.src.dir"/>
   81.16 +            </test-roots>
   81.17 +        </data>
   81.18 +    </configuration>
   81.19 +</project>
    82.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    82.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/ConversionNotSupportedException.java	Sat Oct 18 07:47:34 2008 +0200
    82.3 @@ -0,0 +1,85 @@
    82.4 +package org.apidesign.apifest08.currency;
    82.5 +
    82.6 +/**
    82.7 + * Conversion not suported exception. This expecption may optionaly describe which conversion was required and failed.
    82.8 + * Required conversion can be found in {@link #getFromCurrecyCode() } and {@link #getToCurrecyCode() }.
    82.9 + * 
   82.10 + * @author arnostvalicek
   82.11 + * @since version2
   82.12 + */
   82.13 +public class ConversionNotSupportedException extends ConvertorException {
   82.14 +    String from;
   82.15 +    String to;
   82.16 +    boolean reversed;
   82.17 +    
   82.18 +    public ConversionNotSupportedException() {
   82.19 +        super();
   82.20 +    }
   82.21 +
   82.22 +    public ConversionNotSupportedException(String message) {
   82.23 +        super(message);
   82.24 +    }
   82.25 +
   82.26 +    public ConversionNotSupportedException(String message, Throwable cause) {
   82.27 +        super(message, cause);
   82.28 +    }
   82.29 +
   82.30 +    public ConversionNotSupportedException(Throwable cause) {
   82.31 +        super(cause);
   82.32 +    }
   82.33 +    
   82.34 +    /**
   82.35 +     * Create exception witd additional information about currencies which are not supported in coversion.
   82.36 +     * @param from Code of source currency.
   82.37 +     * @param to Code of target currency.
   82.38 +     * @param twoWay Set to <code>false</code> if <em>From-&gt;To</em> is not supported.
   82.39 +     *               Set to <code>true</code> if both ways <em>From-&gt;To</em> and <em>To->From</em> conversions are not supported.
   82.40 +     *               
   82.41 +     */
   82.42 +    public ConversionNotSupportedException(String from, String to, boolean twoWay) {
   82.43 +        this.from = from;
   82.44 +        this.to = to;
   82.45 +        this.reversed = true;
   82.46 +    }
   82.47 +
   82.48 +    @Override
   82.49 +    public String toString() {
   82.50 +        if (from!=null && to !=null) {
   82.51 +          if (reversed) {
   82.52 +             return "Neither conversion nor reverted conversion from " + from + " to " + to + " is supported,"; 
   82.53 +          }  else {
   82.54 +             return "Conversion from " + from + " to " + to + " is not supported,"; 
   82.55 +          }
   82.56 +        } else {
   82.57 +            return super.toString();
   82.58 +        }
   82.59 +    }
   82.60 +    
   82.61 +    /**
   82.62 +     * Returns code of source currency. This value may be null.
   82.63 +     * @return Returns code of source currency.
   82.64 +     */
   82.65 +    public String getFromCurrecyCode() {
   82.66 +        return from;
   82.67 +    }
   82.68 +
   82.69 +    /**
   82.70 +     * Returns code of target currency. This value may be null.
   82.71 +     * @return Returns code of target currency.
   82.72 +     */    public String getToCurrecyCode() {
   82.73 +        return to;
   82.74 +    }
   82.75 +     
   82.76 +    /**
   82.77 +     * Returns if one way or two way conversion is not supported.
   82.78 +     * 
   82.79 +     * Value <code>false</code> means one way conversion is not supported. 
   82.80 +     * Value <code>true</code> means that neither direct nor back conversion is not supported.
   82.81 +     * 
   82.82 +     * @return Returs <code>false</code> for one way conversion, <code>true</code> for two way conversion.
   82.83 +     */ 
   82.84 +    public boolean getTwoWayConversion() {
   82.85 +        return reversed;
   82.86 +    }
   82.87 +
   82.88 +}
    83.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    83.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/ConversionResult.java	Sat Oct 18 07:47:34 2008 +0200
    83.3 @@ -0,0 +1,56 @@
    83.4 +
    83.5 +package org.apidesign.apifest08.currency;
    83.6 +
    83.7 +import java.math.BigDecimal;
    83.8 +
    83.9 +/**
   83.10 + * Result of currency conversion. Holds converted value and remainder.
   83.11 + * <p>
   83.12 + * <em>Converted</em> describes value converted to <em>target</em> currenty. <em>Remainder</em> describes
   83.13 + * how much from original <em>amount</em> was not possible to convert. Convertor never loses any (small) money
   83.14 + * in conversion error (rounding), but instead of rounding is converts only as much as possible and keeps rest as remainder.
   83.15 + * 
   83.16 + * @author arnostvalicek
   83.17 + */
   83.18 +public class ConversionResult {
   83.19 +    private BigDecimal converted;
   83.20 +    private BigDecimal remainder;
   83.21 +    
   83.22 +    /**
   83.23 +     * Get converted value.
   83.24 +     * @return Returns converted value.
   83.25 +     */
   83.26 +    public BigDecimal getConverted() {
   83.27 +        return converted;
   83.28 +    }
   83.29 +
   83.30 +    void setConverted(BigDecimal converted) {
   83.31 +        this.converted = converted;
   83.32 +    }
   83.33 +
   83.34 +    
   83.35 +    /**
   83.36 +     * Get remainder of conversion. Remainder is set if part of converted amount which can't be converted 
   83.37 +     * because this target currency precision can't handle small numbers. Remainder value is in <em>from currency</em>
   83.38 +     * <p>
   83.39 +     * Converter never loses any precision in conversion. Remainer describes how much of amount can't be converted. 
   83.40 +     * If we substract <em>remainder</em> from <em>amount</em> we will be able to get exact conversion.
   83.41 +     * 
   83.42 +     * @return Returns remainder of conversion.
   83.43 +     */
   83.44 +    public BigDecimal getRemainder() {
   83.45 +        return remainder;
   83.46 +    }
   83.47 +
   83.48 +    void setRemainder(BigDecimal remainder) {
   83.49 +        this.remainder = remainder;
   83.50 +    }
   83.51 +
   83.52 +    @Override
   83.53 +    public String toString() {
   83.54 +        return getClass().getSimpleName()+": converter:"+getConverted()+", remainder:"+getRemainder();
   83.55 +    }
   83.56 +    
   83.57 +    
   83.58 +
   83.59 +}
    84.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    84.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/Convertor.java	Sat Oct 18 07:47:34 2008 +0200
    84.3 @@ -0,0 +1,335 @@
    84.4 +package org.apidesign.apifest08.currency;
    84.5 +
    84.6 +import java.math.BigDecimal;
    84.7 +import java.math.MathContext;
    84.8 +import java.math.RoundingMode;
    84.9 +import java.util.Date;
   84.10 +
   84.11 +/** Convertor able to convert amount from one currency to other currency.
   84.12 + * <p>
   84.13 + * Conversion method are:
   84.14 + * <ul>
   84.15 + * <li>{@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal)} - convert
   84.16 + * using exchange rate specified in exchange rate provide. This method is not try 
   84.17 + * use reverted excahnage rate. 
   84.18 + * <li>{@link #convertWithReversibleRates(ConvertorCurrency, ConvertorCurrency, BigDecimal)} - 
   84.19 + * convert using exchange rate specified in exchange rate provide. This method
   84.20 + * is not trying to use reverted exchange rate.
   84.21 + * </ul>
   84.22 + * 
   84.23 + * Exchange rate is provided by {@link ExchangeRateProvider}.
   84.24 + */
   84.25 +public class Convertor {
   84.26 +    private Convertor[] convertors;
   84.27 +    private IDateProviderEngine dateProvider;
   84.28 +    boolean remainderAllowed = true; //if false, remained is not allowed (should be true ideally, but can't handle it now)
   84.29 +    private ExchangeRateProvider exchangeRateProvider;
   84.30 +    private Date fromDate;
   84.31 +    private Date toDate;
   84.32 +
   84.33 +    /** Create new <code>Convertor</code> as merge of provided convertors. Merged convertor will use
   84.34 +     * provided convertors to convert between currencies.
   84.35 +     * <p>
   84.36 +     * Only one should be able to provide conversion between currencies. If more than one convertos
   84.37 +     * are able to convert currency, one of conversions will be used (it is not defined which).
   84.38 +     * 
   84.39 +     * @since version 2
   84.40 +     * @param convertors Convertor used to create merge-convertor.
   84.41 +     * @return Returns new convertor instance.
   84.42 +     */
   84.43 +    public static Convertor createConvertorAsMerge(Convertor[] convertors) {
   84.44 +        for (int i=0;i<convertors.length;i++) {
   84.45 +            if (convertors[i]==null) {
   84.46 +                throw new NullPointerException("Convertor at index "+i+" can't be null");
   84.47 +            }
   84.48 +        }
   84.49 +        return new Convertor(convertors);
   84.50 +    }
   84.51 +    
   84.52 +    
   84.53 +    /** Create simle convertor.
   84.54 +     */
   84.55 +    private Convertor() {
   84.56 +        setDateProvider(DateProvider.createCurrentDateProvider());
   84.57 +        this.convertors=new Convertor[0];
   84.58 +    }
   84.59 +    
   84.60 +    /** Create merge convertor.
   84.61 +     */            
   84.62 +    private Convertor(Convertor[] convertors) {
   84.63 +        this();
   84.64 +        this.convertors = convertors;       
   84.65 +    }
   84.66 +    
   84.67 +    /**
   84.68 +     * Create new <code>Convertor</code> using <code>ExchangeRateProvider</code>.
   84.69 +     * 
   84.70 +     * @param exchangeRateProvider {@link ExchangeRateProvider} used to get exchange rate.
   84.71 +     * 
   84.72 +     * @return Returns <code>Convertor</code> which can be used to convert money.
   84.73 +     * @since version1
   84.74 +     */
   84.75 +    public static Convertor createConvertor(ExchangeRateProvider exchangeRateProvider) {
   84.76 +        Convertor c = new Convertor();
   84.77 +
   84.78 +        c.exchangeRateProvider = exchangeRateProvider;
   84.79 +        return c;
   84.80 +    }
   84.81 +    
   84.82 +    /**
   84.83 +     * Convert <code>amount</code> from <code>fromCurrency</code> to <code>toCurrency</code> as specified
   84.84 +     * in <code>ExchangeRateProvider</code>.
   84.85 +     * 
   84.86 +     * @param amount Amount which should be converted. Can't be negative value (can be zero or positive).
   84.87 +     * @return Return <code>ConversionResult</code> which holds conversion result.
   84.88 +     * @since version1
   84.89 +     * @deprecated since version2. Use {@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal) } - explicitly specify conversion currencies.
   84.90 +     */
   84.91 +    public ConversionResult convert(BigDecimal amount) {
   84.92 +        return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, false,false,null);
   84.93 +    }
   84.94 +    
   84.95 +    /**
   84.96 +     * Convert <code>amount</code> from <code>toCurrency</code> to <code>fromCurrency</code> as specified
   84.97 +     * in <code>ExchangeRateProvider</code>. This is <em>reverted</em> order than suggested by  names of currency fields in <code>ExchangeRate</code>.
   84.98 +     * 
   84.99 +     * @param amount Amount which should be converted. Can't be negative value (can be zero or positive).
  84.100 +     * @return Return <code>ConversionResult</code> which holds conversion result.
  84.101 +     * @since version1
  84.102 +     * @deprecated since version2. Use {@link #convert(ConvertorCurrency, ConvertorCurrency, BigDecimal) } - explicitly specify conversion currencies.
  84.103 +     */
  84.104 +    public ConversionResult convertBack(BigDecimal amount) {
  84.105 +        return convertValue(exchangeRateProvider.getFromCurrency(), exchangeRateProvider.getToCurrency(),amount, true,false,null);
  84.106 +    }
  84.107 +
  84.108 +    private ConversionResult convertUsingSimpleConvertor(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, boolean reversibleExRate, BigDecimal amount, boolean convertBack,Date date) throws ConversionNotSupportedException, RuntimeException {
  84.109 +        if (date == null) {
  84.110 +            date = dateProvider.getCurrentDate();
  84.111 +        }
  84.112 +
  84.113 +        ExchangeRate rate;
  84.114 +        if (reversibleExRate) {
  84.115 +            rate = exchangeRateProvider.getReversibleExchangeRate(fromCurrency, toCurrency, date);
  84.116 +        } else {
  84.117 +            rate = exchangeRateProvider.getExchangeRate(fromCurrency, toCurrency, date);
  84.118 +        }
  84.119 +        if (rate == null) {
  84.120 +            return null;
  84.121 +        }
  84.122 +        ConversionResult result = new ConversionResult();
  84.123 +        
  84.124 +        int fromFranctionDigits = fromCurrency.getDefaultFractionDigits();
  84.125 +        int toFractionDigits = toCurrency.getDefaultFractionDigits();
  84.126 +        
  84.127 +        int usedFractionDigits = Math.max(fromFranctionDigits, toFractionDigits);
  84.128 +
  84.129 +        //if (toFractionDigits != 2) {
  84.130 +        //    throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + toCurrency + " has " + toFractionDigits + " defaultFractionDigits");
  84.131 +        //}
  84.132 +        //if (fromFranctionDigits != 2) {
  84.133 +        //    throw new ConvertorException("Can't process currency with defaultFractionDigits!=2, " + fromCurrency + " has " + fromFranctionDigits + " defaultFractionDigits");
  84.134 +        //}
  84.135 +
  84.136 +        if (amount.signum() == -1) {
  84.137 +            throw new RuntimeException("Can convert only non-negative value, current value is " + amount);
  84.138 +        }
  84.139 +
  84.140 +
  84.141 +        MathContext context = new MathContext(0, RoundingMode.DOWN);
  84.142 +
  84.143 +        BigDecimal from;
  84.144 +        BigDecimal to;
  84.145 +        if (convertBack) {
  84.146 +            //converting in reverted way
  84.147 +            to = rate.getFromValue();
  84.148 +            from = rate.getToValue();
  84.149 +        } else {
  84.150 +            //converting in normal way
  84.151 +            from = rate.getFromValue();
  84.152 +            to = rate.getToValue();
  84.153 +        }
  84.154 +
  84.155 +        BigDecimal amountCent = amount.movePointRight(usedFractionDigits);
  84.156 +
  84.157 +        final BigDecimal multiplied = amountCent.multiply(to, context);
  84.158 +        BigDecimal[] division = multiplied.divideAndRemainder(from, context);
  84.159 +
  84.160 +        if (!remainderAllowed && !(BigDecimal.ZERO.equals(division[1]))) {
  84.161 +            throw new RuntimeException("Remained is not allowed - remaining amount is " + division[1] + " cents");
  84.162 +        } else {
  84.163 +            result.setRemainder(BigDecimal.ZERO.setScale(fromFranctionDigits));
  84.164 +        }
  84.165 +
  84.166 +        BigDecimal converted = division[0].movePointLeft(usedFractionDigits);
  84.167 +        converted = converted.setScale(toFractionDigits,RoundingMode.DOWN); //XXX ugly
  84.168 +        result.setConverted(converted);
  84.169 +        BigDecimal[] convertedInBase = converted.multiply(from).divideAndRemainder(to);
  84.170 +        BigDecimal x2 = convertedInBase[0].add(convertedInBase[1]);
  84.171 +        BigDecimal remainder = amount.subtract(x2);
  84.172 +        result.setRemainder(remainder);
  84.173 +        //System.out.println("ConvertedInBase="+Arrays.asList(convertedInBase)+":"+x2 +" ("+amount+") ["+converted+"] <"+from+","+to+"> ->"+remainder);
  84.174 +        return result;
  84.175 +    }
  84.176 +    
  84.177 +
  84.178 +    private ConversionResult convertValue(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency,BigDecimal amount, boolean convertBack,boolean reversibleExRate,Date date) throws RuntimeException {
  84.179 +        //result.setRemainder(...);
  84.180 +        if (!dateIsInLimit(date)) {
  84.181 +            return null;
  84.182 +        }        
  84.183 +        if (convertors.length==0) {
  84.184 +            return convertUsingSimpleConvertor(fromCurrency, toCurrency, reversibleExRate, amount, convertBack,date);
  84.185 +        } else {
  84.186 +            ConversionResult result = null;
  84.187 +            for (int i = 0;i<convertors.length;i++) {
  84.188 +                Convertor subConvertor = convertors[i];
  84.189 +                result = subConvertor.convertValue(fromCurrency, toCurrency, amount, convertBack, reversibleExRate,date);
  84.190 +                if (result!=null) {
  84.191 +                    break;
  84.192 +                }
  84.193 +            }
  84.194 +            return result;
  84.195 +        }
  84.196 +    }
  84.197 +    
  84.198 +    /**
  84.199 +     * Convert <code>value</code> from <code>fromCurrency</code> to <code>toCurrency</code>.
  84.200 +     * <p>
  84.201 +     * Exchange rate is provided by exchange rate provider which was specified when Convertor was created.
  84.202 +     * This method is using only exchange rate from->to and not trying to use reverted excange rate to->from.
  84.203 +     * <p>
  84.204 +     * This method is using date from <code>IDateProviderEngine</code> to get exchange rate (see {@link #getDateProvider() }.
  84.205 +     * 
  84.206 +     * @param fromCurrency Source currency to convert from.
  84.207 +     * @param toCurrency Target currency to convert to.
  84.208 +     * @param value Value in source currency which should be converted.
  84.209 +     * @return Return conversion result.
  84.210 +     * @since version2
  84.211 +     * @throws ConversionNotSupportedException If conversion from <code>fromCurrency</code> to <code>toCurrency</code> is not supported.
  84.212 +     */
  84.213 +    public ConversionResult convert(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value) {
  84.214 +        Date defaultDate = dateProvider.getCurrentDate();
  84.215 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,false,defaultDate);
  84.216 +        if (result==null) {
  84.217 +            //throw new ConversionNotSupportedException("Conversion from " + fromCurrency + " to " + toCurrency + " is not supported");
  84.218 +            throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),false);
  84.219 +        }
  84.220 +        return result;
  84.221 +    }
  84.222 +    /**
  84.223 +     * Same as {@link #convert(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)} but using provided rate instead of
  84.224 +     * <code>IDateProviderEngine</code>.
  84.225 +     * 
  84.226 +     * @param fromCurrency Source currency to convert from.
  84.227 +     * @param toCurrency Target currency to convert to.
  84.228 +     * @param value Value in source currency which should be converted.
  84.229 +     * @return Return conversion result.
  84.230 +     * @param date Conversion date
  84.231 +     * @return Return conversion result.
  84.232 +     */
  84.233 +    public ConversionResult convert(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value, Date date) {
  84.234 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,false,date);
  84.235 +        if (result==null) {
  84.236 +            throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),false);
  84.237 +        }
  84.238 +        return result;
  84.239 +    }    
  84.240 +    
  84.241 +    /**
  84.242 +     * Convert <code>value</code> from <code>fromCurrency</code> to <code>toCurrency</code>.
  84.243 +     * Exchange rate is provided by exchange rate provider which was specified when Convertor was created.
  84.244 +     * <p>
  84.245 +     *  This method is using only exchange rate from->to and if not found, it is trying to use reverted excange rate to->from.
  84.246 +     * <p>
  84.247 +     * This method is using date from <code>IDateProviderEngine</code> to get exchange rate (see {@link #getDateProvider() }.
  84.248 +     * 
  84.249 +     * @param fromCurrency Source currency to convert from.
  84.250 +     * @param toCurrency Target currency to convert to.
  84.251 +     * @param value Value in source currency which should be converted.
  84.252 +     * @return Return conversion result.
  84.253 +     * @since version2
  84.254 +     * @throws ConversionNotSupportedException If conversion from <code>fromCurrency</code> to <code>toCurrency</code> 
  84.255 +     *         is not supported and neither conversion from <code>toCurrency</code> to <code>fromCurrency</code> is not supported.
  84.256 +     */
  84.257 +    public ConversionResult convertWithReversibleRates(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value) {
  84.258 +        Date defaultDate = dateProvider.getCurrentDate();
  84.259 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,true,defaultDate);
  84.260 +        if (result==null) {
  84.261 +            //throw new ConversionNotSupportedException("Neither onversion nor reverted conversion from " + fromCurrency + " to " + toCurrency + "  is not supported,");
  84.262 +            throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),true);
  84.263 +        }
  84.264 +        return result;
  84.265 +    }
  84.266 +    
  84.267 +    /**
  84.268 +     * Same as {@link #convertWithReversibleRates(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)} but using provided rate instead of
  84.269 +     * <code>IDateProviderEngine</code>.
  84.270 +     * 
  84.271 +     * @param fromCurrency Source currency to convert from.
  84.272 +     * @param toCurrency Target currency to convert to.
  84.273 +     * @param value Value in source currency which should be converted.
  84.274 +     * @return Return conversion result.
  84.275 +     * @param date Conversion date
  84.276 +     * @return Return conversion result.
  84.277 +     */
  84.278 +    public ConversionResult convertWithReversibleRates(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, BigDecimal value, Date date) {
  84.279 +        ConversionResult result = convertValue(fromCurrency, toCurrency, value, false,true,date);
  84.280 +        if (result==null) {
  84.281 +            throw new ConversionNotSupportedException(fromCurrency.getCurrencyCode(),toCurrency.getCurrencyCode(),true);
  84.282 +        }
  84.283 +        return result;
  84.284 +    }
  84.285 +    
  84.286 +    private boolean dateIsInLimit(Date date) {
  84.287 +        boolean result;
  84.288 +//        if (date==null) {
  84.289 +//          result = true;  
  84.290 +//        } else 
  84.291 +        if (fromDate == null && toDate == null) {
  84.292 +            result = true;
  84.293 +        } else if (fromDate.getTime()<=date.getTime() && date.getTime()<=toDate.getTime()) {
  84.294 +            result = true;
  84.295 +        } else {
  84.296 +            result = false;
  84.297 +        }
  84.298 +        
  84.299 +        return result;
  84.300 +    }
  84.301 +    
  84.302 +    
  84.303 +    public void limitAllowedDates(Date from, Date till) {
  84.304 +        if (from==null) {
  84.305 +            throw new NullPointerException("from Date can't be null");
  84.306 +        }
  84.307 +        if (till==null) {
  84.308 +            throw new NullPointerException("till Date can't be null");
  84.309 +        }
  84.310 +        if (from.getTime()>till.getTime()) {
  84.311 +            throw new IllegalArgumentException("From date "+from+" must be before tii date "+till);
  84.312 +        }
  84.313 +        this.fromDate = from;
  84.314 +        this.toDate = till;
  84.315 +    }
  84.316 +    
  84.317 +
  84.318 +    /** Return current date provider.
  84.319 +     * @return Returns current date provider.
  84.320 +     */
  84.321 +    public IDateProviderEngine getDateProvider() {
  84.322 +        return dateProvider;
  84.323 +    }
  84.324 +
  84.325 +    
  84.326 +    /**
  84.327 +     * Set date provider. Date provider is used to get "current date". Current date 
  84.328 +     * is used by methods {@link #convert(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)} and  
  84.329 +     * {@link #convertWithReversibleRates(ConvertorCurrency, ConvertorCurrency, java.math.BigDecimal)}.
  84.330 +     * 
  84.331 +     * @param dateProvider Date provider which should be used by Convertor.
  84.332 +     */
  84.333 +    public void setDateProvider(IDateProviderEngine dateProvider) {
  84.334 +        this.dateProvider = dateProvider;
  84.335 +    }
  84.336 +    
  84.337 +    
  84.338 +}
    85.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    85.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/ConvertorCurrency.java	Sat Oct 18 07:47:34 2008 +0200
    85.3 @@ -0,0 +1,97 @@
    85.4 +
    85.5 +package org.apidesign.apifest08.currency;
    85.6 +
    85.7 +import java.util.Currency;
    85.8 +
    85.9 +/**
   85.10 + * Desription of currency. 
   85.11 + * 
   85.12 + * Java has similar class {@link java.util.Currency}, but original class is not flexible
   85.13 + * enough, we use our own implementation of currency.
   85.14 + *
   85.15 + * @author arnostvalicek
   85.16 + */
   85.17 +public class ConvertorCurrency {
   85.18 +    private String currencyCode;
   85.19 +    private int fractionDigits;
   85.20 +
   85.21 +
   85.22 +    /**
   85.23 +     * Static method providing instance of <code>ConvertorCurrency</code> based on currency code.
   85.24 +     * <p>
   85.25 +     * <code>currencyCode</code> is one of codes supported by Java class {@link java.util.Currency}.
   85.26 +     * 
   85.27 +     * @param currencyCode Code of required currency.
   85.28 +     * @return Returns required <code>ConvertorCurrency</code>
   85.29 +     */
   85.30 +    public static ConvertorCurrency getInstance(String currencyCode) {
   85.31 +        ConvertorCurrency convertorCurrency = new ConvertorCurrency();
   85.32 +        Currency currency = Currency.getInstance(currencyCode);
   85.33 +        convertorCurrency.setCurrencyCode(currency.getCurrencyCode());
   85.34 +        convertorCurrency.setDefaultFractionDigits(currency.getDefaultFractionDigits());
   85.35 +        return convertorCurrency;
   85.36 +    }
   85.37 +    
   85.38 +    /**
   85.39 +     * Static method providing instance of <code>ConvertorCurrency</code> base of currency code.
   85.40 +     * 
   85.41 +     * @param currencyCode Code of required currency.
   85.42 +     * @param fractionDigits Number of fraction digits for currency.
   85.43 +     * @return Returns required <code>ConvertorCurrency</code>
   85.44 +     * @since version4
   85.45 +     */
   85.46 +    public static ConvertorCurrency getInstance(String currencyCode, int fractionDigits) {
   85.47 +        ConvertorCurrency convertorCurrency = new ConvertorCurrency();
   85.48 +        convertorCurrency.setCurrencyCode(currencyCode);
   85.49 +        convertorCurrency.setDefaultFractionDigits(fractionDigits);
   85.50 +        return convertorCurrency;
   85.51 +    }
   85.52 +
   85.53 +    /**
   85.54 +     * Gets the default number of fraction digits used with this currency. For example, the default number of fraction digits for the Euro is 2, while for the Japanese Yen it's 0.
   85.55 +     * @return Returns the default number of fraction digits used with this currency.
   85.56 +     */
   85.57 +    public int getDefaultFractionDigits() {
   85.58 +        return fractionDigits;
   85.59 +    }
   85.60 +    
   85.61 +    private void setDefaultFractionDigits(int value) {
   85.62 +        this.fractionDigits = value;
   85.63 +    }
   85.64 +    
   85.65 +    /**
   85.66 +     * Get currency code.
   85.67 +     * @return Returns currency code.
   85.68 +     */
   85.69 +    public String getCurrencyCode() {
   85.70 +        return currencyCode;
   85.71 +    }
   85.72 +    
   85.73 +    private void setCurrencyCode(String value) {
   85.74 +        currencyCode = value;
   85.75 +    }
   85.76 +
   85.77 +    @Override
   85.78 +    public boolean equals(Object obj) {
   85.79 +        boolean result;
   85.80 +        if (obj instanceof ConvertorCurrency) {
   85.81 +            ConvertorCurrency that = (ConvertorCurrency) obj;
   85.82 +            result = getCurrencyCode().equals(that.getCurrencyCode())  && getDefaultFractionDigits()==that.getDefaultFractionDigits();
   85.83 +        } else {
   85.84 +            result = false;
   85.85 +        }
   85.86 +        return result;
   85.87 +    }
   85.88 +
   85.89 +    @Override
   85.90 +    public int hashCode() {
   85.91 +        return currencyCode==null ? 47/*??*/ : currencyCode.hashCode();
   85.92 +    }
   85.93 +        
   85.94 +
   85.95 +    @Override
   85.96 +    public String toString() {
   85.97 +        return  "ConvertorCurrency[" + (currencyCode != null ? currencyCode : "NO-BASE-CURRENCY")+"]";
   85.98 +    }
   85.99 +   
  85.100 +}
  85.101 \ No newline at end of file
    86.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    86.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/ConvertorException.java	Sat Oct 18 07:47:34 2008 +0200
    86.3 @@ -0,0 +1,25 @@
    86.4 +package org.apidesign.apifest08.currency;
    86.5 +
    86.6 +/**
    86.7 + * Common Convertor exception.
    86.8 + * 
    86.9 + * @author arnostvalicek
   86.10 + */
   86.11 +public class ConvertorException extends RuntimeException {
   86.12 +
   86.13 +    public ConvertorException(Throwable cause) {
   86.14 +        super(cause);
   86.15 +    }
   86.16 +
   86.17 +    public ConvertorException(String message, Throwable cause) {
   86.18 +        super(message, cause);
   86.19 +    }
   86.20 +
   86.21 +    public ConvertorException(String message) {
   86.22 +        super(message);
   86.23 +    }
   86.24 +
   86.25 +    public ConvertorException() {
   86.26 +    }
   86.27 +  
   86.28 +}
    87.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    87.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/DateProvider.java	Sat Oct 18 07:47:34 2008 +0200
    87.3 @@ -0,0 +1,36 @@
    87.4 +package org.apidesign.apifest08.currency;
    87.5 +
    87.6 +import java.util.Date;
    87.7 +
    87.8 +/**
    87.9 + * Provider for current date.
   87.10 + * 
   87.11 + * @author arnostvalicek
   87.12 + * @since version4.
   87.13 + */
   87.14 +public class DateProvider {
   87.15 +    private DateProvider() {
   87.16 +        
   87.17 +    }
   87.18 +    
   87.19 +    /**
   87.20 +     * Create date provider based on current system time.
   87.21 +     * @see     java.lang.System#currentTimeMillis()
   87.22 +     */    
   87.23 +    public static IDateProviderEngine createCurrentDateProvider() {
   87.24 +        return new CurrentDateProvider();
   87.25 +    }
   87.26 +    
   87.27 +    /**
   87.28 +     * Date provider based on current system time.
   87.29 +     * @see     java.lang.System#currentTimeMillis()
   87.30 +     */
   87.31 +    private static class CurrentDateProvider implements IDateProviderEngine {
   87.32 +
   87.33 +        public Date getCurrentDate() {
   87.34 +            return new Date();
   87.35 +        }
   87.36 +        
   87.37 +    }
   87.38 +
   87.39 +}
    88.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    88.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/ExchangeRate.java	Sat Oct 18 07:47:34 2008 +0200
    88.3 @@ -0,0 +1,68 @@
    88.4 +
    88.5 +package org.apidesign.apifest08.currency;
    88.6 +
    88.7 +import java.math.BigDecimal;
    88.8 +
    88.9 +/**
   88.10 + * Exchange rate value. Contains <code>from</code> and <code>to</code> value.
   88.11 + * 
   88.12 + * @author arnostvalicek
   88.13 + */
   88.14 +public class ExchangeRate {
   88.15 +    private BigDecimal numberFor;
   88.16 +    private BigDecimal numberGet;
   88.17 +
   88.18 +    /**
   88.19 +     * Constructor for new exchange rate holding two values - <em>from value</em> and <em>to value</em>
   88.20 +     * @param fromValue Exchange rate <em>from value</em>
   88.21 +     * @param toValue Exchange rate <em>to value</em>
   88.22 +     */
   88.23 +    public  ExchangeRate(BigDecimal fromValue, BigDecimal toValue) {
   88.24 +        this.numberFor = fromValue;
   88.25 +        this.numberGet = toValue;
   88.26 +    }
   88.27 +    
   88.28 +    /**
   88.29 +     * Create new instance of <code>ExchangeRate</code> based on provided exchange rate, but swapping its
   88.30 +     * <em>from</em> and <em>to</em> value.
   88.31 +     * <p>
   88.32 +     * Provided exchange rate is not chaged, this method returns different instance describing reverted exchange rate.
   88.33 +     * 
   88.34 +     * @param rate Exchange rate which describes rate to be reverted.
   88.35 +     * @return Instance of reverted rate.
   88.36 +     */
   88.37 +    public static ExchangeRate createRevertedRate(ExchangeRate rate) {
   88.38 +        ExchangeRate reverted = new ExchangeRate(rate.getToValue(), rate.getFromValue());
   88.39 +        return reverted;
   88.40 +    }
   88.41 +
   88.42 +    @Override
   88.43 +    public String toString() {
   88.44 +        return "for "+numberFor+" recieve "+numberGet+" @"+getClass().getName();
   88.45 +    }
   88.46 +    
   88.47 +    /**
   88.48 +     * Return exchange rate <em>from</em> value stored in this object.
   88.49 +     * @return Returns <em>from</em> value for this exchange rate.
   88.50 +     */
   88.51 +    public BigDecimal getFromValue() {
   88.52 +        return numberFor;
   88.53 +    }
   88.54 +    
   88.55 +    /**
   88.56 +     * Return exchange rate <em>to</em> value stored in this object.
   88.57 +     * @return Returns <em>to</em> value for this exchange rate.
   88.58 +     */
   88.59 +    public BigDecimal getToValue() {
   88.60 +        return numberGet;
   88.61 +    }
   88.62 +    
   88.63 +    
   88.64 +//    public ExchangeRate createExchangeRate(BigDecimal forValue, BigDecimal getValue) {
   88.65 +//        ExchangeRate rate = new ExchangeRate(forValue, getValue);
   88.66 +//        return rate;
   88.67 +//    }
   88.68 +    
   88.69 +    
   88.70 +    
   88.71 +}
    89.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    89.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/ExchangeRateProvider.java	Sat Oct 18 07:47:34 2008 +0200
    89.3 @@ -0,0 +1,318 @@
    89.4 +package org.apidesign.apifest08.currency;
    89.5 +
    89.6 +import java.math.BigDecimal;
    89.7 +import java.util.Date;
    89.8 +import java.util.HashMap;
    89.9 +import java.util.Map;
   89.10 +
   89.11 +/**
   89.12 + * Exchange rate provider. Provides  exchange rate for two currencies (method {@link #getExchangeRate(ConvertorCurrency, ConvertorCurrency)} ).
   89.13 + * <p>
   89.14 + * Several method can be created to create <code>ExchangeRateProvider</code>:
   89.15 + * <ul>
   89.16 + * <li>{@link #createExchangeRateProvider() } - create <em>simple</em> exchange rate provider using fixed echange rate.
   89.17 + * <li>{@link #createExchangeRateProvider(IExchangeRateEngine) } - create exchange rate provider using custom {@link IExchangeRateEngine}.
   89.18 + * <li>{@link #createDateExchangeRateProvider(IDateExchangeRateEngine) } - create exchange rate provider using custom {@link IDateExchangeRateEngine}.
   89.19 + * </ul>
   89.20 + * <p>
   89.21 + * Date dependend exchange rate to be implemented.
   89.22 + * 
   89.23 + * @author arnostvalicek
   89.24 + */
   89.25 +public class ExchangeRateProvider {
   89.26 +
   89.27 +    IExchangeRateEngine exrateEngine;
   89.28 +    IDateExchangeRateEngine dateExrateEngine;
   89.29 +    
   89.30 +    /**
   89.31 +     * Simple constructor for <code>ExchangeRateProviderM</code> which can provide fixed exchange rate.
   89.32 +     * 
   89.33 +     * Describes conversion <em>from ONE</em> to <em>to ONE</em> currency.
   89.34 +     * 
   89.35 +     * @param fromValue From value. BigDecimal value, precision should be set to currency precision.
   89.36 +     * @param fromCurrency From currency.
   89.37 +     * @param toValue To value. BigDecimal value, precision should be set to currency precision.
   89.38 +     * @param toCurrency To currency.
   89.39 +     * @deprecated deprecated since task2. Use {@link #createExchangeRateProvider() } instead of this constructor.
   89.40 +     */
   89.41 +    public ExchangeRateProvider(BigDecimal fromValue, ConvertorCurrency fromCurrency, BigDecimal toValue, ConvertorCurrency toCurrency) {
   89.42 +        this.exrateEngine = FixedOneExchangeRateEngine.createEngine(fromValue, fromCurrency, toValue, toCurrency);
   89.43 +    }
   89.44 +
   89.45 +    private ExchangeRateProvider() {
   89.46 +    }
   89.47 +
   89.48 +    /**
   89.49 +     * Static method to create new exchange rate provider. This exchange rate provider does not contain
   89.50 +     * any exchange rates (this is difference to public constructor).
   89.51 +     * @return New <code>ExchangeRateProvider</code>
   89.52 +     */
   89.53 +    public static ExchangeRateProvider createExchangeRateProvider() {
   89.54 +        ExchangeRateProvider provider = new ExchangeRateProvider();
   89.55 +        provider.exrateEngine = FixedExchangeRateEngine.createEngine();
   89.56 +        return provider;
   89.57 +    }
   89.58 +
   89.59 +    /**
   89.60 +     * Static method to create exchange rate provider which is using provided <code>IExchangeRateEngine</code>. This exchange rate provider is using
   89.61 +     * <code>IExchangeRateEngine</code> to get actual exchange rate.
   89.62 +     * @param exchangeRateEngine <code>IExchangeRateEngine</code> used to get exchange rate.
   89.63 +     * @return Returns instance of <code>ExchangeRateProvider</code>
   89.64 +     */
   89.65 +    public static ExchangeRateProvider createExchangeRateProvider(IExchangeRateEngine exchangeRateEngine) {
   89.66 +        ExchangeRateProvider provider = new ExchangeRateProvider();
   89.67 +        provider.exrateEngine = exchangeRateEngine;
   89.68 +        return provider;
   89.69 +    }
   89.70 +    
   89.71 +    /**
   89.72 +     * Static method to create exchange rate provider which is using provided <code>IExIDateExchangeRateEnginechangeRateEngine</code>. This exchange rate provider is using
   89.73 +     * <code>IExchangeRateEngine</code> to get actual exchange rate.
   89.74 +     * @param exchangeRateEngine <code>IDateExchangeRateEngine</code> used to get exchange rate.
   89.75 +     * @return Returns instance of <code>ExchangeRateProvider</code>
   89.76 +     */
   89.77 +    public static ExchangeRateProvider createDateExchangeRateProvider(IDateExchangeRateEngine exchangeRateEngine) {
   89.78 +        ExchangeRateProvider provider = new ExchangeRateProvider();
   89.79 +        provider.dateExrateEngine = exchangeRateEngine;
   89.80 +        return provider;
   89.81 +    }
   89.82 +
   89.83 +    /**
   89.84 +     * Add new exchange rate to <code></code> to this <em>simple</em> exchange rate provider.
   89.85 +     * <p>
   89.86 +     * Example of specifiing conversion rate: 100 SKK == 80 CZK:<br>
   89.87 +     * <code>addFixedCurencyRate(ConvertorCurrency.getInstance("SKK"), new BigDecimal(100), ConvertorCurrency.getInstance("CZK"),  new BigDecimal(80));</code>
   89.88 +     * <p>
   89.89 +     * This method may be used <em>only</em> when <code>ExchangeRateProvider</code> is created using {@link #createExchangeRateProvider() } - creating exchange rate provider without external <code>IExchangeRateEngine</code>.
   89.90 +     * 
   89.91 +     * @param fromCurrency Source currency.
   89.92 +     * @param fromValue Valye for from currency.
   89.93 +     * @param toCurrency Target currency.
   89.94 +     * @param toValue Value for target currency.
   89.95 +     * @throws IllegalStateException Throws exception when adding fixed currency rate to exchange rate provider which is not created using {@link #createExchangeRateProvider() }.
   89.96 +     */
   89.97 +    public synchronized void addFixedCurencyRate(ConvertorCurrency fromCurrency, BigDecimal fromValue, ConvertorCurrency toCurrency, BigDecimal toValue) {
   89.98 +        if (exrateEngine instanceof FixedExchangeRateEngine) {
   89.99 +            ((FixedExchangeRateEngine) exrateEngine).addFixedCurencyRate(fromCurrency, fromValue, toCurrency, toValue);
  89.100 +        } else {
  89.101 +            throw new IllegalStateException("Cuurency rate can be added only to ExchangeRateProvider created with FixedExchangeRateEngine - using method createExchangeRateProvider()");
  89.102 +        }
  89.103 +    }
  89.104 +
  89.105 +    /**
  89.106 +     * Get fixed exange rate for currencies (from->to).
  89.107 +     * @return Returns exchange rate.
  89.108 +     * @deprecated deprecated since task2. Use {@link #getExchangeRate(ConvertorCurrency, ConvertorCurrency) }
  89.109 +     */
  89.110 +    public ExchangeRate getExchangeRate() {
  89.111 +        //works only for FixedExchangeRateEngine
  89.112 +        if (exrateEngine instanceof FixedOneExchangeRateEngine) {
  89.113 +            FixedOneExchangeRateEngine engine = (FixedOneExchangeRateEngine) exrateEngine;
  89.114 +            return new ExchangeRate(engine.getFromValue(), engine.getToValue());
  89.115 +        } else {
  89.116 +            throw new IllegalStateException("Method supported only for MinimalFixedExchangeRateEngine. This method is deprecated");
  89.117 +        }
  89.118 +
  89.119 +    }
  89.120 +
  89.121 +    /**
  89.122 +     * Get fixed exange rate for currencies (from->to).
  89.123 +     * @param fromCurrency Source currency.
  89.124 +     * @param toCurrency Target currency.
  89.125 +     * @return Returns exchange rate or <code>null</code> if exchange rate not found.
  89.126 +     */
  89.127 +    public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
  89.128 +        return getExchangeRateImpl(fromCurrency, toCurrency,null);
  89.129 +    }
  89.130 +    
  89.131 +    /**
  89.132 +     * Get exange rate for currencies (from->to) for date.
  89.133 +     * @param fromCurrency Source currency.
  89.134 +     * @param toCurrency Target currency.
  89.135 +     * @param date Conversion date.
  89.136 +     * @return Returns exchange rate or <code>null</code> if exchange rate not found.
  89.137 +     */
  89.138 +    public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, Date date) {
  89.139 +        return getExchangeRateImpl(fromCurrency, toCurrency,date);
  89.140 +    }
  89.141 +
  89.142 +    /**
  89.143 +     * Get fixed exange rate for currencies (from->to) or reversed exchange rate (to->from).
  89.144 +     * @param fromCurrency Source currency.
  89.145 +     * @param toCurrency Target currency.
  89.146 +     * @return Returns exchange rate or <code>null</code> if exchange rate not found.
  89.147 +     */
  89.148 +    public ExchangeRate getReversibleExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
  89.149 +        return getReversibleExrateImpl(fromCurrency, toCurrency,null);
  89.150 +    }
  89.151 +    
  89.152 +    /**
  89.153 +     * Get exange rate for currencies (from->to) or reversed exchange rate (to->from) for date.
  89.154 +     * @param fromCurrency Source currency.
  89.155 +     * @param toCurrency Target currency.
  89.156 +     * @param date Conversion date.
  89.157 +     * @return Returns exchange rate or <code>null</code> if exchange rate not found.
  89.158 +     */
  89.159 +    public ExchangeRate getReversibleExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, Date date) {
  89.160 +        return getReversibleExrateImpl(fromCurrency, toCurrency, date);
  89.161 +    }
  89.162 +
  89.163 +    /**
  89.164 +     * Get exchange rate for currencies (from->to) based on provided date.
  89.165 +     * @param date Date for which exchange rate should be provided.
  89.166 +     * @return Returns exchange rate 
  89.167 +     * @deprecated deprecated since task2. No real implementation in version2.
  89.168 +     */
  89.169 +    public ExchangeRate getExchangeRate(Date date) {
  89.170 +        //works only for FixedExchangeRateEngine
  89.171 +        if (exrateEngine instanceof FixedOneExchangeRateEngine) {
  89.172 +            FixedOneExchangeRateEngine engine = (FixedOneExchangeRateEngine) exrateEngine;
  89.173 +            return new ExchangeRate(engine.getFromValue(), engine.getToValue());
  89.174 +        } else {
  89.175 +            throw new IllegalStateException("Method supported only for FixedOneExchangeRateEngine. This method is deprecated");
  89.176 +        }
  89.177 +    }
  89.178 +
  89.179 +    ConvertorCurrency getFromCurrency() {
  89.180 +        if (exrateEngine instanceof FixedOneExchangeRateEngine) {
  89.181 +            FixedOneExchangeRateEngine engine = (FixedOneExchangeRateEngine) exrateEngine;
  89.182 +            return engine.getFromCurrency();
  89.183 +        } else {
  89.184 +            throw new IllegalStateException("Method supported only for FixedOneExchangeRateEngine. This method is deprecated");
  89.185 +        }
  89.186 +    }
  89.187 +
  89.188 +    ConvertorCurrency getToCurrency() {
  89.189 +        if (exrateEngine instanceof FixedOneExchangeRateEngine) {
  89.190 +            FixedOneExchangeRateEngine engine = (FixedOneExchangeRateEngine) exrateEngine;
  89.191 +            return engine.getToCurrency();
  89.192 +        } else {
  89.193 +            throw new IllegalStateException("Method supported only for FixedOneExchangeRateEngine. This method is deprecated");
  89.194 +        }
  89.195 +    }
  89.196 +    
  89.197 +
  89.198 +    private ExchangeRate getReversibleExrateImpl(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency,Date date) {
  89.199 +        ExchangeRate rate = getExchangeRateImpl(fromCurrency, toCurrency, date);
  89.200 +        if (rate == null) {
  89.201 +            ExchangeRate revertedRate = getExchangeRateImpl(toCurrency, fromCurrency, date);
  89.202 +            if (revertedRate != null) {
  89.203 +                rate = ExchangeRate.createRevertedRate(revertedRate);
  89.204 +            }
  89.205 +        }
  89.206 +        return rate;
  89.207 +    }    
  89.208 +
  89.209 +    private ExchangeRate getExchangeRateImpl(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency,Date date) {
  89.210 +        ExchangeRate result;
  89.211 +        if (exrateEngine!=null) {
  89.212 +            result = exrateEngine.getExchangeRate(fromCurrency, toCurrency);
  89.213 +        } else if (dateExrateEngine!=null) {
  89.214 +            result = dateExrateEngine.getExchangeRate(fromCurrency, toCurrency, date);
  89.215 +        } else {
  89.216 +            throw new IllegalStateException("No exchange rate engine provided");
  89.217 +        }
  89.218 +        return result;
  89.219 +    }
  89.220 +
  89.221 +    private static class FixedOneExchangeRateEngine implements IExchangeRateEngine {
  89.222 +
  89.223 +        BigDecimal fromValue;
  89.224 +        ConvertorCurrency fromCurrency;
  89.225 +        BigDecimal toValue;
  89.226 +        ConvertorCurrency toCurrency;
  89.227 +
  89.228 +        private FixedOneExchangeRateEngine(BigDecimal fromValue, ConvertorCurrency fromCurrency, BigDecimal toValue, ConvertorCurrency toCurrency) {
  89.229 +            this.fromValue = fromValue;
  89.230 +            this.toValue = toValue;
  89.231 +            this.fromCurrency = fromCurrency;
  89.232 +            this.toCurrency = toCurrency;
  89.233 +        }
  89.234 +
  89.235 +        /**
  89.236 +         * Create IExchangeRateEngine conveting from <code>fromCurrency</code> to <code>toCurrency</code>
  89.237 +         * with echange rate <code>fromValue:toValue</code>
  89.238 +         * @param fromValue
  89.239 +         * @param fromCurrency
  89.240 +         * @param toValue
  89.241 +         * @param toCurrency
  89.242 +         * @return Returns instance of <code>FixedOneExchangeRateEngine</code>.
  89.243 +         */
  89.244 +        private static IExchangeRateEngine createEngine(BigDecimal fromValue, ConvertorCurrency fromCurrency, BigDecimal toValue, ConvertorCurrency toCurrency) {
  89.245 +            return new FixedOneExchangeRateEngine(fromValue, fromCurrency, toValue, toCurrency);
  89.246 +        }
  89.247 +
  89.248 +        private BigDecimal getFromValue() {
  89.249 +            return fromValue;
  89.250 +        }
  89.251 +
  89.252 +        private BigDecimal getToValue() {
  89.253 +            return toValue;
  89.254 +        }
  89.255 +
  89.256 +        private ConvertorCurrency getFromCurrency() {
  89.257 +            return fromCurrency;
  89.258 +        }
  89.259 +
  89.260 +        private ConvertorCurrency getToCurrency() {
  89.261 +            return toCurrency;
  89.262 +        }
  89.263 +
  89.264 +        public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
  89.265 +            if (!fromCurrency.equals(this.fromCurrency)) {
  89.266 +                return null;
  89.267 +            }
  89.268 +            if (!toCurrency.equals(this.toCurrency)) {
  89.269 +                return null;
  89.270 +            }
  89.271 +            return new ExchangeRate(fromValue, toValue);
  89.272 +        }
  89.273 +    }
  89.274 +
  89.275 +    /**
  89.276 +     * Exchange rate engine able to hold several fixed exchange rates.
  89.277 +     * 
  89.278 +     * @author arnostvalicek
  89.279 +     */
  89.280 +    private static class FixedExchangeRateEngine implements IExchangeRateEngine {
  89.281 +
  89.282 +        private Map<ConvertorCurrency, Map<ConvertorCurrency, ExchangeRate>> exchangeRateMap = new HashMap<ConvertorCurrency, Map<ConvertorCurrency, ExchangeRate>>();
  89.283 +
  89.284 +        private FixedExchangeRateEngine() {
  89.285 +        }
  89.286 +
  89.287 +        /**
  89.288 +         * Create instance of <code>FixedExchangeRateEngine</code>.
  89.289 +         * @return Returns instance of <code>FixedExchangeRateEngine</code>.
  89.290 +         */
  89.291 +        public static IExchangeRateEngine createEngine() {
  89.292 +            return new FixedExchangeRateEngine();
  89.293 +        }
  89.294 +
  89.295 +        public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
  89.296 +            Map<ConvertorCurrency,ExchangeRate> map2 = exchangeRateMap.get(fromCurrency);
  89.297 +            if (map2 == null) {
  89.298 +                return null;
  89.299 +            }
  89.300 +            ExchangeRate result = map2.get(toCurrency);
  89.301 +            return result;
  89.302 +        }
  89.303 +
  89.304 +        public synchronized void addFixedCurencyRate(ConvertorCurrency fromCurrency, BigDecimal fromValue, ConvertorCurrency toCurrency, BigDecimal toValue) {
  89.305 +            if (fromValue == null) {
  89.306 +                throw new NullPointerException("fromValue can't be null");
  89.307 +            }
  89.308 +            if (toValue == null) {
  89.309 +                throw new NullPointerException("toValue can't be null");
  89.310 +            }
  89.311 +            Map<ConvertorCurrency,ExchangeRate> map2 = exchangeRateMap.get(fromCurrency);
  89.312 +            if (map2 == null) {
  89.313 +                map2 = new HashMap<ConvertorCurrency,ExchangeRate>();
  89.314 +                exchangeRateMap.put(fromCurrency, map2);
  89.315 +            }
  89.316 +
  89.317 +            ExchangeRate rate = new ExchangeRate(fromValue, toValue);
  89.318 +            map2.put(toCurrency, rate);
  89.319 +        }
  89.320 +    }
  89.321 +}
    90.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    90.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/IDateExchangeRateEngine.java	Sat Oct 18 07:47:34 2008 +0200
    90.3 @@ -0,0 +1,21 @@
    90.4 +package org.apidesign.apifest08.currency;
    90.5 +
    90.6 +import java.util.Date;
    90.7 +
    90.8 +/**
    90.9 + * Interface for exchange rate engine using dates.
   90.10 + * 
   90.11 + * @author arnostvalicek
   90.12 + */
   90.13 +public interface IDateExchangeRateEngine {
   90.14 +    /**
   90.15 +     * Get exchange rate for conversion from <code>fromCurrency</code> to <code>toCurrency</code> at <code>date</code>.
   90.16 +     * 
   90.17 +     * @param fromCurrency From currency.
   90.18 +     * @param toCurrency To currency.
   90.19 +     * @param date Conversion date.
   90.20 +     * @return Returns <code>ExchangeRate</code> if exchange rate is known or <code>null</code> if exchanger rate is not known.
   90.21 +     */
   90.22 +    public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, Date date);
   90.23 +
   90.24 +}
    91.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    91.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/IDateProviderEngine.java	Sat Oct 18 07:47:34 2008 +0200
    91.3 @@ -0,0 +1,19 @@
    91.4 +package org.apidesign.apifest08.currency;
    91.5 +
    91.6 +import java.util.Date;
    91.7 +
    91.8 +/**
    91.9 + * Date provider - provides current date.
   91.10 + * @see DateProvider#createCurrentDateProvider() 
   91.11 + * @see Convertor#setDateProvider(IDateProviderEngine) 
   91.12 + * @author arnostvalicek
   91.13 + * @since version4
   91.14 + */
   91.15 +public interface IDateProviderEngine {
   91.16 +    /**
   91.17 +     * Get current date.
   91.18 +     * @return Return current date.
   91.19 +     */
   91.20 +    public Date getCurrentDate();
   91.21 +
   91.22 +}
    92.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    92.2 +++ b/task4/solution13/src/org/apidesign/apifest08/currency/IExchangeRateEngine.java	Sat Oct 18 07:47:34 2008 +0200
    92.3 @@ -0,0 +1,18 @@
    92.4 +package org.apidesign.apifest08.currency;
    92.5 +
    92.6 +/**
    92.7 + * Interface for exchange rate engine.
    92.8 + * 
    92.9 + * @author arnostvalicek
   92.10 + */
   92.11 +public interface IExchangeRateEngine {
   92.12 +    
   92.13 +    /**
   92.14 +     * Get exchange rate for conversion from <code>fromCurrency</code> to <code>toCurrency</code>.
   92.15 +     * 
   92.16 +     * @param fromCurrency From currency.
   92.17 +     * @param toCurrency To currency.
   92.18 +     * @return Returns <code>ExchangeRate</code> if exchange rate is known or <code>null</code> if exchanger rate is not known.
   92.19 +     */
   92.20 +    public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency);
   92.21 +}
    93.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    93.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/BouncingExchangeRateEngine.java	Sat Oct 18 07:47:34 2008 +0200
    93.3 @@ -0,0 +1,79 @@
    93.4 +package org.apidesign.apifest08.test;
    93.5 +
    93.6 +import java.math.BigDecimal;
    93.7 +import org.apidesign.apifest08.currency.ConvertorCurrency;
    93.8 +import org.apidesign.apifest08.currency.ExchangeRate;
    93.9 +import org.apidesign.apifest08.currency.IExchangeRateEngine;
   93.10 +
   93.11 +/** Exchange rate engine which is periodicaly changing echange rate.
   93.12 + *  See <a href="http://wiki.apidesign.org/wiki/APIFest08:Task3">http://wiki.apidesign.org/wiki/APIFest08:Task3</a> for further description.
   93.13 + *
   93.14 + * @author arnostvalicek
   93.15 + */
   93.16 +public class BouncingExchangeRateEngine implements IExchangeRateEngine {
   93.17 +    
   93.18 +    ConvertorCurrency fromCurrency;
   93.19 +    BigDecimal fromValue;
   93.20 +    ConvertorCurrency toCurrency;
   93.21 +    BigDecimal toValueStart;
   93.22 +    BigDecimal toValueStop;
   93.23 +    BigDecimal step;
   93.24 +    
   93.25 +    BigDecimal toValue;
   93.26 +
   93.27 +    
   93.28 +    
   93.29 +    
   93.30 +    private BouncingExchangeRateEngine(ConvertorCurrency fromCurrency, BigDecimal fromValue, ConvertorCurrency toCurrency, BigDecimal toValueStart, BigDecimal toValueStop, BigDecimal step) {
   93.31 +        this.fromCurrency = fromCurrency;
   93.32 +        this.fromValue = fromValue;
   93.33 +        this.toCurrency = toCurrency;
   93.34 +        this.toValueStart = toValueStart;
   93.35 +        this.toValueStop = toValueStop;
   93.36 +        this.step = step;
   93.37 +        this.toValue = toValueStart;
   93.38 +    }
   93.39 +
   93.40 +
   93.41 +    public static IExchangeRateEngine create(ConvertorCurrency fromCurrency, BigDecimal fromValue, ConvertorCurrency toCurrency, BigDecimal toValueStart, BigDecimal toValueStop, BigDecimal step) {
   93.42 +        return new BouncingExchangeRateEngine(fromCurrency, fromValue, toCurrency, toValueStart, toValueStop, step);
   93.43 +    }
   93.44 +
   93.45 +    public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency) {
   93.46 +        if (!fromCurrency.equals(this.fromCurrency)) {
   93.47 +            return null;
   93.48 +        }
   93.49 +        if (!toCurrency.equals(this.toCurrency)) {
   93.50 +            return null;
   93.51 +        }
   93.52 +        
   93.53 +        ExchangeRate result = new ExchangeRate(fromValue, toValue);
   93.54 +        
   93.55 +        toValue = toValue.add(step);
   93.56 +        
   93.57 +//        if (step.signum()==1 && toValueStop.compareTo(toValue)==0) {
   93.58 +//            System.out.println("A");
   93.59 +//            toValue=toValueStop;
   93.60 +//            step = step.negate();
   93.61 +//            BigDecimal x = toValueStart;
   93.62 +//            toValueStart=toValueStop;
   93.63 +//            toValueStop=x;
   93.64 +//        } else if (step.signum()==-1 && toValueStop.compareTo(toValue)==0) {
   93.65 +//            System.out.println("B");
   93.66 +//            toValue=toValueStop;
   93.67 +//            step = step.negate();
   93.68 +//            BigDecimal x = toValueStart;
   93.69 +//            toValueStart=toValueStop;
   93.70 +//            toValueStop=x;
   93.71 +//        }
   93.72 +        if (toValueStop.compareTo(toValue)==0) {
   93.73 +            toValue=toValueStop;
   93.74 +            step = step.negate();
   93.75 +            BigDecimal x = toValueStart;
   93.76 +            toValueStart=toValueStop;
   93.77 +            toValueStop=x;
   93.78 +        }        
   93.79 +        return result;
   93.80 +    }
   93.81 +
   93.82 +}
    94.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    94.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/ConvertorWithDateExchangeRateTest.java	Sat Oct 18 07:47:34 2008 +0200
    94.3 @@ -0,0 +1,109 @@
    94.4 +/*
    94.5 + * To change this template, choose Tools | Templates
    94.6 + * and open the template in the editor.
    94.7 + */
    94.8 +package org.apidesign.apifest08.test;
    94.9 +
   94.10 +import java.math.BigDecimal;
   94.11 +import java.text.ParseException;
   94.12 +import java.text.SimpleDateFormat;
   94.13 +import java.util.Date;
   94.14 +import junit.framework.TestCase;
   94.15 +import org.apidesign.apifest08.currency.ConversionResult;
   94.16 +import org.apidesign.apifest08.currency.Convertor;
   94.17 +import org.apidesign.apifest08.currency.ConvertorCurrency;
   94.18 +import org.apidesign.apifest08.currency.ExchangeRate;
   94.19 +import org.apidesign.apifest08.currency.ExchangeRateProvider;
   94.20 +import org.apidesign.apifest08.currency.IDateExchangeRateEngine;
   94.21 +import org.apidesign.apifest08.currency.IDateProviderEngine;
   94.22 +
   94.23 +/**
   94.24 + *
   94.25 + * @author arnostvalicek
   94.26 + */
   94.27 +public class ConvertorWithDateExchangeRateTest extends TestCase {
   94.28 +
   94.29 +    private static ConvertorCurrency CZK = ConvertorCurrency.getInstance("CZK");
   94.30 +    private static ConvertorCurrency SKK = ConvertorCurrency.getInstance("SKK");
   94.31 +    private static ConvertorCurrency USD = ConvertorCurrency.getInstance("USD");
   94.32 +    private SimpleDateFormat df;
   94.33 +
   94.34 +    @Override
   94.35 +    protected void setUp() throws Exception {
   94.36 +        super.setUp();
   94.37 +        df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
   94.38 +    }
   94.39 +
   94.40 +    @Override
   94.41 +    protected void tearDown() throws Exception {
   94.42 +        super.tearDown();
   94.43 +        df = null;
   94.44 +    }
   94.45 +
   94.46 +    private IDateExchangeRateEngine createDateEngine() {
   94.47 +        IDateExchangeRateEngine engine = new IDateExchangeRateEngine() {
   94.48 +
   94.49 +            public ExchangeRate getExchangeRate(ConvertorCurrency fromCurrency, ConvertorCurrency toCurrency, Date date) {
   94.50 +                int day = date.getDay();
   94.51 +                if (day >= 1 && day <= 5) {
   94.52 +                    //weekday
   94.53 +                    return new ExchangeRate(new BigDecimal(1), new BigDecimal(2));
   94.54 +                } else {
   94.55 +                    //weekend
   94.56 +                    return new ExchangeRate(new BigDecimal(1), new BigDecimal(3));
   94.57 +                }
   94.58 +            }
   94.59 +        };
   94.60 +        return engine;
   94.61 +    }
   94.62 +
   94.63 +    private Convertor createConvertor() {
   94.64 +        ExchangeRateProvider exchangeRateProvider = ExchangeRateProvider.createDateExchangeRateProvider(createDateEngine());
   94.65 +        Convertor c = Convertor.createConvertor(exchangeRateProvider);
   94.66 +        return c;
   94.67 +    }
   94.68 +
   94.69 +    public void testConvertuUsingDifferentRatesForDays() throws ParseException {
   94.70 +        Convertor c = createConvertor();
   94.71 +        {
   94.72 +            Date d1 = df.parse("2008-10-15 9:00 GMT");  //Wednesday
   94.73 +            ConversionResult resultWeekday = c.convert(CZK, SKK, new BigDecimal("2.3"), d1);
   94.74 +            assertEquals("Weekday conversion", new BigDecimal("4.60"), resultWeekday.getConverted());
   94.75 +        }
   94.76 +        {
   94.77 +            Date d1 = df.parse("2008-10-18 9:00 GMT");  //Saturday
   94.78 +            ConversionResult resultWeekday = c.convert(CZK, SKK, new BigDecimal("2.3"), d1);
   94.79 +            assertEquals("Weekday conversion", new BigDecimal("6.90"), resultWeekday.getConverted());
   94.80 +        }
   94.81 +    }
   94.82 +
   94.83 +    public void testConvertUsingDifferenDefaultDay() throws ParseException {
   94.84 +        class MyDateProvider implements IDateProviderEngine {
   94.85 +
   94.86 +            private Date date;
   94.87 +
   94.88 +            void setDate(Date date) {
   94.89 +                this.date = date;
   94.90 +            }
   94.91 +
   94.92 +            public Date getCurrentDate() {
   94.93 +                return date;
   94.94 +            }
   94.95 +        }
   94.96 +
   94.97 +        Convertor c = createConvertor();
   94.98 +        MyDateProvider dp = new MyDateProvider();
   94.99 +        c.setDateProvider(dp);
  94.100 +
  94.101 +        {
  94.102 +            dp.setDate(df.parse("2008-10-15 9:00 GMT")); //Wednesday
  94.103 +            ConversionResult resultWeekday = c.convert(CZK, SKK, new BigDecimal("2.3"));
  94.104 +            assertEquals("Weekday conversion", new BigDecimal("4.60"), resultWeekday.getConverted());
  94.105 +        }
  94.106 +        {
  94.107 +            dp.setDate(df.parse("2008-10-18 9:00 GMT"));  //Saturday
  94.108 +            ConversionResult resultWeekday = c.convert(CZK, SKK, new BigDecimal("2.3"));
  94.109 +            assertEquals("Weekday conversion", new BigDecimal("6.90"), resultWeekday.getConverted());
  94.110 +        }
  94.111 +    }
  94.112 +}
    95.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    95.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/RemainderTest.java	Sat Oct 18 07:47:34 2008 +0200
    95.3 @@ -0,0 +1,80 @@
    95.4 +/*
    95.5 + * To change this template, choose Tools | Templates
    95.6 + * and open the template in the editor.
    95.7 + */
    95.8 +
    95.9 +package org.apidesign.apifest08.test;
   95.10 +
   95.11 +import java.math.BigDecimal;
   95.12 +import java.text.SimpleDateFormat;
   95.13 +import junit.framework.TestCase;
   95.14 +import org.apidesign.apifest08.currency.ConversionResult;
   95.15 +import org.apidesign.apifest08.currency.Convertor;
   95.16 +import org.apidesign.apifest08.currency.ConvertorCurrency;
   95.17 +import org.apidesign.apifest08.currency.ExchangeRateProvider;
   95.18 +
   95.19 +/**
   95.20 + *
   95.21 + * @author arnostvalicek
   95.22 + */
   95.23 +public class RemainderTest extends TestCase {
   95.24 +    private static ConvertorCurrency CZK = ConvertorCurrency.getInstance("CZK");
   95.25 +    private static ConvertorCurrency SKK = ConvertorCurrency.getInstance("SKK");
   95.26 +    private static ConvertorCurrency USD = ConvertorCurrency.getInstance("USD");
   95.27 +    private SimpleDateFormat df;
   95.28 +    
   95.29 +    @Override
   95.30 +    protected void setUp() throws Exception {
   95.31 +        super.setUp();
   95.32 +        df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
   95.33 +        
   95.34 +    }
   95.35 +
   95.36 +    @Override
   95.37 +    protected void tearDown() throws Exception {
   95.38 +        super.tearDown();
   95.39 +    }
   95.40 +    
   95.41 +    public void testRemainder1() {
   95.42 +        Convertor c = Task2Test.createUsdToSkkConvertor();
   95.43 +        {
   95.44 +            final BigDecimal convertedValue = new BigDecimal("12.34");
   95.45 +            ConversionResult result = c.convertWithReversibleRates(SKK,USD,convertedValue);
   95.46 +            //System.out.println("Result = "+result);
   95.47 +            assertEquals("Converted", new BigDecimal("0.61"),result.getConverted());
   95.48 +            assertEquals("Remainder", new BigDecimal("0.14"),result.getRemainder());
   95.49 +            
   95.50 +            ConversionResult resultBack = c.convertWithReversibleRates(USD, SKK, result.getConverted());
   95.51 +            assertEquals("Conversion back", convertedValue, resultBack.getConverted().add(result.getRemainder()));
   95.52 +            
   95.53 +        }
   95.54 +        {
   95.55 +            ConversionResult result = c.convertWithReversibleRates(SKK,USD,new BigDecimal("20.00"));
   95.56 +            //System.out.println("Result = "+result);
   95.57 +        }
   95.58 +    
   95.59 +        {
   95.60 +            ConversionResult result = c.convertWithReversibleRates(USD,SKK,new BigDecimal("1.00"));
   95.61 +            //System.out.println("Result = "+result);
   95.62 +        }
   95.63 +        
   95.64 +    }
   95.65 +    
   95.66 +    public void testRemainderStripCents() {
   95.67 +        ConvertorCurrency CZK_cents=ConvertorCurrency.getInstance("CZK", 2);
   95.68 +        ConvertorCurrency CZK_nocents=ConvertorCurrency.getInstance("CZK", 0);
   95.69 +        
   95.70 +        
   95.71 +        ExchangeRateProvider exchangeRateProvider = ExchangeRateProvider.createExchangeRateProvider();
   95.72 +        exchangeRateProvider.addFixedCurencyRate(CZK_cents, new BigDecimal(1), CZK_nocents, new BigDecimal(1));
   95.73 +        
   95.74 +        Convertor c = Convertor.createConvertor(exchangeRateProvider);
   95.75 +        
   95.76 +        ConversionResult result = c.convertWithReversibleRates(CZK_cents, CZK_nocents, new BigDecimal("2.34"));
   95.77 +        assertEquals("Converted",new BigDecimal("2"),result.getConverted());
   95.78 +        assertEquals("Remainder",new BigDecimal("0.34"),result.getRemainder());
   95.79 +    }
   95.80 +    
   95.81 +    
   95.82 +
   95.83 +}
    96.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    96.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/Task1Test.java	Sat Oct 18 07:47:34 2008 +0200
    96.3 @@ -0,0 +1,223 @@
    96.4 +package org.apidesign.apifest08.test;
    96.5 +
    96.6 +import java.math.BigDecimal;
    96.7 +import junit.framework.TestCase;
    96.8 +import org.apidesign.apifest08.currency.ConversionResult;
    96.9 +import org.apidesign.apifest08.currency.Convertor;
   96.10 +import org.apidesign.apifest08.currency.ConvertorCurrency;
   96.11 +import org.apidesign.apifest08.currency.ExchangeRateProvider;
   96.12 +import org.apidesign.apifest08.currency.ConversionNotSupportedException;
   96.13 +
   96.14 +/** Finish the Convertor API, and then write bodies of methods inside
   96.15 + * of this class to match the given tasks. To fullfil your task, use the
   96.16 + * API define in the <code>org.apidesign.apifest08.currency</code> package.
   96.17 + * Do not you reflection, or other hacks as your code
   96.18 + * shall run without any runtime permissions.
   96.19 + */
   96.20 +public class Task1Test extends TestCase {
   96.21 +    public Task1Test(String testName) {
   96.22 +        super(testName);
   96.23 +    }
   96.24 +
   96.25 +    @Override
   96.26 +    protected void setUp() throws Exception {
   96.27 +    }
   96.28 +
   96.29 +    @Override
   96.30 +    protected void tearDown() throws Exception {
   96.31 +    }
   96.32 +
   96.33 +    /** Create convertor that understands two currencies, CZK and
   96.34 +     *  USD. Make 1 USD == 17 CZK.
   96.35 +     *
   96.36 +     * Creation of the convertor shall not require subclassing of any class
   96.37 +     * or interface on the client side.
   96.38 +     *
   96.39 +     * @return prepared convertor ready for converting USD to CZK and CZK to USD
   96.40 +     */
   96.41 +    public static Convertor createCZKtoUSD() {
   96.42 +        ConvertorCurrency fromCurrency = ConvertorCurrency.getInstance("CZK");
   96.43 +        ConvertorCurrency toCurrency = ConvertorCurrency.getInstance("USD");
   96.44 +        ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(17), fromCurrency, new BigDecimal(1), toCurrency);
   96.45 +        
   96.46 +        return Convertor.createConvertor(exchangeRateProvider);
   96.47 +    }
   96.48 +
   96.49 +    /** Create convertor that understands two currencies, CZK and
   96.50 +     *  SKK. Make 100 SKK == 80 CZK.
   96.51 +     *
   96.52 +     * Creation of the convertor shall not require subclassing of any class
   96.53 +     * or interface on the client side.
   96.54 +     * 
   96.55 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
   96.56 +     */
   96.57 +    public static Convertor createSKKtoCZK() {
   96.58 +        ConvertorCurrency fromCurrency = ConvertorCurrency.getInstance("SKK");
   96.59 +        ConvertorCurrency toCurrency = ConvertorCurrency.getInstance("CZK");
   96.60 +        ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(100), fromCurrency, new BigDecimal(80), toCurrency);
   96.61 +        
   96.62 +        return Convertor.createConvertor(exchangeRateProvider);
   96.63 +    }
   96.64 +    
   96.65 +    
   96.66 +    public static Convertor createCZKtoYEN() {
   96.67 +        ConvertorCurrency fromCurrency = ConvertorCurrency.getInstance("CZK");
   96.68 +        ConvertorCurrency toCurrency = ConvertorCurrency.getInstance("JPY");
   96.69 +        ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(1), fromCurrency, new BigDecimal(1), toCurrency);
   96.70 +        
   96.71 +        return Convertor.createConvertor(exchangeRateProvider);
   96.72 +    }
   96.73 +    
   96.74 +    /** Use the convertor from <code>createCZKtoUSD</code> method and do few conversions
   96.75 +     * with it.
   96.76 +     */
   96.77 +    public void testCurrencyCZKUSD() throws Exception {
   96.78 +        Convertor convertCzkUsd = createCZKtoUSD();
   96.79 +
   96.80 +        {
   96.81 +            // convert $1 to CZK using c:
   96.82 +            ConversionResult result = convertCzkUsd.convertBack(new BigDecimal(1));
   96.83 +            assertEquals("Result is 17 CZK", new BigDecimal("17.00"), result.getConverted());
   96.84 +            assertEquals("No Remainer", new BigDecimal("00.00").setScale(2), result.getRemainder());
   96.85 +        }
   96.86 +
   96.87 +        {
   96.88 +            // convert 17CKZ to $ using c:
   96.89 +            ConversionResult result = convertCzkUsd.convert(new BigDecimal(17));
   96.90 +            assertEquals("Result is 1 $", new BigDecimal("1.00"), result.getConverted());
   96.91 +            assertEquals("No Remainer", new BigDecimal("00.00").setScale(2), result.getRemainder());
   96.92 +        }
   96.93 +
   96.94 +        {
   96.95 +            // convert $5 to CZK using c:
   96.96 +            ConversionResult result = convertCzkUsd.convertBack(new BigDecimal(5));
   96.97 +            assertEquals("Result is 85 CZK", new BigDecimal("85.00"), result.getConverted());
   96.98 +            assertEquals("No Remainer", BigDecimal.ZERO.setScale(2), result.getRemainder());
   96.99 +        }
  96.100 +        
  96.101 +        {
  96.102 +            // convert $8 to CZK
  96.103 +            ConversionResult result = convertCzkUsd.convertBack(new BigDecimal(8));
  96.104 +            assertEquals("Result is 136 CZK", new BigDecimal("136.00"), result.getConverted());
  96.105 +            assertEquals("No Remainer", BigDecimal.ZERO.setScale(2), result.getRemainder());
  96.106 +        }
  96.107 +
  96.108 +        {
  96.109 +            // convert 1003CZK to USD
  96.110 +            ConversionResult result = convertCzkUsd.convert(new BigDecimal(1003));
  96.111 +            assertEquals("Result is 59 USD", new BigDecimal("59.00"), result.getConverted());
  96.112 +            assertEquals("No Remainer", BigDecimal.ZERO.setScale(2), result.getRemainder());
  96.113 +        }
  96.114 +    }
  96.115 +
  96.116 +    /** Use the convertor from <code>createSKKtoCZK</code> method and do few conversions
  96.117 +     * with it.
  96.118 +     */
  96.119 +    public void testCurrencySKKCZK() throws Exception {
  96.120 +        Convertor convertSkkCzk = createSKKtoCZK();
  96.121 +        {
  96.122 +            // convert 100SKK using c:
  96.123 +            ConversionResult result = convertSkkCzk.convert(new BigDecimal(100));
  96.124 +            assertEquals("Result is 80 CZK", new BigDecimal("80.00"), result.getConverted());
  96.125 +        }
  96.126 +        {
  96.127 +            // convert 80CZK using c:
  96.128 +            ConversionResult result = convertSkkCzk.convertBack(new BigDecimal(80));
  96.129 +            assertEquals("Result is 100 SKK", new BigDecimal("100.00"), result.getConverted());
  96.130 +        }
  96.131 +        
  96.132 +        {
  96.133 +            // convert 16CZK using c:
  96.134 +            ConversionResult result = convertSkkCzk.convertBack(new BigDecimal(16));
  96.135 +            assertEquals("Result is 20 SKK", new BigDecimal("20.00"), result.getConverted());
  96.136 +        }
  96.137 +
  96.138 +        {
  96.139 +            // convert 500SKK to CZK
  96.140 +            ConversionResult result = convertSkkCzk.convert(new BigDecimal(500));
  96.141 +            assertEquals("Result is 400 CZK", new BigDecimal("400.00"), result.getConverted());
  96.142 +            assertEquals("No Remainer", BigDecimal.ZERO.setScale(2), result.getRemainder());            
  96.143 +        }
  96.144 +        
  96.145 +        {
  96.146 +            // convert 501SKK to CZK
  96.147 +            ConversionResult result = convertSkkCzk.convert(new BigDecimal(501));
  96.148 +            assertEquals("Result is 400 CZK", new BigDecimal("400.80"), result.getConverted());
  96.149 +            assertEquals("No Remainer", BigDecimal.ZERO.setScale(2), result.getRemainder());
  96.150 +            
  96.151 +        }
  96.152 +    }
  96.153 +    
  96.154 +    /**
  96.155 +     * Convert SKK to CZK. Convertor can't convert whole amout (can't convert one SKK cent to CZK). Remaining
  96.156 +     * amount is stored in remainder result.
  96.157 +     * 
  96.158 +     * Test is currently failing, because implementation can't handle this case.
  96.159 +     */
  96.160 +//    public void testConvertSmallUnits_failing() {
  96.161 +//        Convertor convertSkkCzk = createSKKtoCZK();
  96.162 +//        {
  96.163 +//            // convert 501SKK to CZK
  96.164 +//            ConversionResult result = convertSkkCzk.convert(new BigDecimal("501.01"));
  96.165 +//            assertEquals("Result is 400 CZK", new BigDecimal("400.80"), result.getConverted());
  96.166 +//            assertEquals("No Remainer", new BigDecimal("0.01"), result.getRemainder());
  96.167 +//            
  96.168 +//        }
  96.169 +//        
  96.170 +//    }
  96.171 +    
  96.172 +    /**
  96.173 +     * Test converting from CZK to JPY. Remained has scale of CZK.
  96.174 +     * 
  96.175 +     * This test is currently failing, because converter implementation currently can't handle conversion from "cent" to "no-cent" currency.
  96.176 +     */
  96.177 +//    public void testConvertCzkToJpy_failing() {
  96.178 +//        Convertor convertSkkCzk = createCZKtoYEN();
  96.179 +//        {
  96.180 +//            // convert 501SKK to CZK
  96.181 +//            ConversionResult result = convertSkkCzk.convert(new BigDecimal("120.00"));
  96.182 +//            assertEquals("Result is 120 YEN", new BigDecimal("120"), result.getConverted());
  96.183 +//            assertEquals("No Remainer", new BigDecimal("0.00"), result.getRemainder());
  96.184 +//            
  96.185 +//        }
  96.186 +//    }
  96.187 +    
  96.188 +    /**
  96.189 +     * Test converting from JPY to CZK. Remained has scale of JPY.
  96.190 +     * 
  96.191 +     * This test is currently failing, because converter implementation currently can't handle conversion from "cent" to "no-cent" currency.
  96.192 +     */
  96.193 +//    public void testConvertJpyToCzk_failing() {
  96.194 +//        Convertor convertSkkCzk = createCZKtoYEN();
  96.195 +//        {
  96.196 +//            // convert 501SKK to CZK
  96.197 +//            ConversionResult result = convertSkkCzk.convert(new BigDecimal("120.00"));
  96.198 +//            assertEquals("Result is 120 YEN", new BigDecimal("120"), result.getConverted());
  96.199 +//            assertEquals("No Remainer", new BigDecimal("0"), result.getRemainder());
  96.200 +//            
  96.201 +//        }
  96.202 +//    }
  96.203 +    
  96.204 +    public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception {
  96.205 +            Convertor c = createCZKtoUSD();
  96.206 +            // convert $5 to SKK, the API shall say this is not possible
  96.207 +            try {
  96.208 +                c.convert(ConvertorCurrency.getInstance("USD"), ConvertorCurrency.getInstance("SKK"), new BigDecimal(5));
  96.209 +                fail();
  96.210 +            } catch (ConversionNotSupportedException e) {
  96.211 +                //expected error;
  96.212 +                assertEquals("Exception From USD", "USD",e.getFromCurrecyCode());
  96.213 +                assertEquals("Exception To SKK", "SKK",e.getToCurrecyCode());
  96.214 +            }
  96.215 +            // convert 500 SKK to CZK, the API shall say this is not possible
  96.216 +            try {
  96.217 +                c.convert(ConvertorCurrency.getInstance("SKK"), ConvertorCurrency.getInstance("CZK"), new BigDecimal(500));
  96.218 +                fail();
  96.219 +            } catch (ConversionNotSupportedException e) {
  96.220 +                assertEquals("Exception From USD", "SKK",e.getFromCurrecyCode());
  96.221 +                assertEquals("Exception To SKK", "CZK",e.getToCurrecyCode());            }
  96.222 +    }
  96.223 +    
  96.224 + 
  96.225 +}
  96.226 +
    97.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    97.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/Task2Test.java	Sat Oct 18 07:47:34 2008 +0200
    97.3 @@ -0,0 +1,136 @@
    97.4 +package org.apidesign.apifest08.test;
    97.5 +
    97.6 +import java.math.BigDecimal;
    97.7 +import junit.framework.TestCase;
    97.8 +import org.apidesign.apifest08.currency.Convertor;
    97.9 +import org.apidesign.apifest08.currency.ConvertorCurrency;
   97.10 +import org.apidesign.apifest08.currency.ExchangeRateProvider;
   97.11 +
   97.12 +/** There are many currencies around the world and many banks manipulate
   97.13 + * with more than one or two at the same time. As banks are usually the
   97.14 + * best paying clients, which is true even in case of your Convertor API,
   97.15 + * it is reasonable to listen to their requests.
   97.16 + * <p>
   97.17 + * The quest for today is to enhance your existing convertor API to hold
   97.18 + * information about many currencies and allow conversions between any of them.
   97.19 + * Also, as conversion rates for diferent currencies usually arise from various
   97.20 + * bank departments, there is another important need. There is a need to
   97.21 + * compose two convertors into one by merging all the information about
   97.22 + * currencies they know about.
   97.23 + */
   97.24 +public class Task2Test extends TestCase {
   97.25 +    private static ConvertorCurrency currencyCZK = ConvertorCurrency.getInstance("CZK");
   97.26 +    private static ConvertorCurrency currencySKK = ConvertorCurrency.getInstance("SKK");
   97.27 +    private static ConvertorCurrency currencyUSD = ConvertorCurrency.getInstance("USD");
   97.28 +  
   97.29 +    public Task2Test(String testName) {
   97.30 +        super(testName);
   97.31 +    }
   97.32 +
   97.33 +    @Override
   97.34 +    protected void setUp() throws Exception {
   97.35 +
   97.36 +    }
   97.37 +
   97.38 +    @Override
   97.39 +    protected void tearDown() throws Exception {
   97.40 +    }
   97.41 +    
   97.42 +    public static Convertor createUsdToSkkConvertor() {
   97.43 +        ConvertorCurrency fromCurrency = currencyUSD;
   97.44 +        ConvertorCurrency toCurrency = currencySKK;
   97.45 +        ExchangeRateProvider exchangeRateProvider = new ExchangeRateProvider(new BigDecimal(1), fromCurrency, new BigDecimal(20), toCurrency);
   97.46 +        
   97.47 +        return Convertor.createConvertor(exchangeRateProvider);
   97.48 +        
   97.49 +    }
   97.50 +
   97.51 +    // As in Task1Test, keep in mind, that there are three parts
   97.52 +    // of the whole system:
   97.53 +    // 1. there is someone who knows the current exchange rate
   97.54 +    // 2. there is someone who wants to do the conversion
   97.55 +    // 3. there is the API between 1. and 2. which allows them to communicate
   97.56 +    // 
   97.57 +    // Please backward compatibly enhance your existing API to support following
   97.58 +    // usecases:
   97.59 +    //
   97.60 +    
   97.61 +    /** Create convertor that understands two currencies, CZK and
   97.62 +     *  SKK. Make 100 SKK == 75 CZK. This is method for the group of users that
   97.63 +     *  knows the exchange rate, and needs to use the API to create objects
   97.64 +     *  with the exchange rate. Anyone shall be ready to call this method without
   97.65 +     *  any other method being called previously. The API itself shall know
   97.66 +     *  nothing about any rates, before this method is called.
   97.67 +     */
   97.68 +    public static Convertor createTripleConvertor() {
   97.69 +        ExchangeRateProvider exRateProvider = ExchangeRateProvider.createExchangeRateProvider();
   97.70 +        
   97.71 +        // Rates: 1USD = 15CZK
   97.72 +        exRateProvider.addFixedCurencyRate(currencyUSD, new BigDecimal(1),currencyCZK,  new BigDecimal(15));
   97.73 +        
   97.74 +        // Rates: 1USD = 20SKK
   97.75 +        exRateProvider.addFixedCurencyRate(currencyUSD, new BigDecimal(1), currencySKK,  new BigDecimal(20));
   97.76 +        
   97.77 +        // Rates: 75CZK = 100SKK
   97.78 +        exRateProvider.addFixedCurencyRate(currencyCZK, new BigDecimal(75), currencySKK,  new BigDecimal(100));
   97.79 +        
   97.80 +        Convertor c = Convertor.createConvertor(exRateProvider);
   97.81 +        
   97.82 +        return c;
   97.83 +    }
   97.84 +
   97.85 +    /** Define convertor that understands three currencies. Use it.
   97.86 +     */
   97.87 +    public void testConvertorForUSDandCZKandSKK() throws Exception {
   97.88 +        Convertor c = createTripleConvertor();
   97.89 +
   97.90 +        // convert $5 to CZK using c:
   97.91 +        assertEquals("Result is 75 CZK",new BigDecimal("75.00"),c.convertWithReversibleRates(currencyUSD, currencyCZK, new BigDecimal(5)).getConverted());
   97.92 +
   97.93 +
   97.94 +        // convert $5 to SKK using c:
   97.95 +        assertEquals("Result is 100 SKK",new BigDecimal("100.00"),c.convertWithReversibleRates(currencyUSD, currencySKK, new BigDecimal(5)).getConverted());
   97.96 +
   97.97 +        // convert 200SKK to CZK using c:
   97.98 +        assertEquals("Result is 150 CZK",new BigDecimal("150.00"),c.convertWithReversibleRates(currencySKK, currencyCZK, new BigDecimal(200)).getConverted());
   97.99 +
  97.100 +        // convert 200SKK to USK using c:
  97.101 +        // assertEquals("Result is 10 USD");
  97.102 +    }
  97.103 +
  97.104 +    /** Merge all currency rates of convertor 1 with convertor 2.
  97.105 +     * Implement this using your API, preferably this method just delegates
  97.106 +     * into some API method which does the actual work, without requiring
  97.107 +     * API clients to code anything complex.
  97.108 +     */
  97.109 +    public static Convertor merge(Convertor one, Convertor two) {
  97.110 +        return Convertor.createConvertorAsMerge(new Convertor[]{one, two});
  97.111 +    }
  97.112 +
  97.113 +    /** Join the convertors from previous task, Task1Test and show that it
  97.114 +     * can be used to do reasonable conversions.
  97.115 +     */
  97.116 +    public void testConvertorComposition() throws Exception {
  97.117 +        Convertor c = merge(
  97.118 +            Task1Test.createCZKtoUSD(),
  97.119 +            Task1Test.createSKKtoCZK()
  97.120 +        );
  97.121 +
  97.122 +        // convert $5 to CZK using c:
  97.123 +        assertEquals("Result is 85 CZK",new BigDecimal("85.00"),c.convertWithReversibleRates(currencyUSD, currencyCZK, new BigDecimal(5)).getConverted());
  97.124 +
  97.125 +        // convert $8 to CZK using c:
  97.126 +        // assertEquals("Result is 136 CZK");
  97.127 +        assertEquals("Result is 136 CZK",new BigDecimal("136.00"),c.convertWithReversibleRates(currencyUSD, currencyCZK, new BigDecimal(8)).getConverted());
  97.128 +
  97.129 +        // convert 1003CZK to USD using c:
  97.130 +        assertEquals("Result is 59 USD",new BigDecimal("59.00"),c.convertWithReversibleRates(currencyCZK, currencyUSD, new BigDecimal(1003)).getConverted());
  97.131 +
  97.132 +        // convert 16CZK using c:
  97.133 +        assertEquals("Result is 20 SKK",new BigDecimal("20.00"),c.convertWithReversibleRates(currencyCZK, currencySKK, new BigDecimal(16)).getConverted());
  97.134 +
  97.135 +        // convert 500SKK to CZK using c:
  97.136 +        assertEquals("Result is 400 CZK",new BigDecimal("400.00"),c.convertWithReversibleRates(currencySKK, currencyCZK, new BigDecimal(500)).getConverted());
  97.137 +
  97.138 +    }
  97.139 +}
    98.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    98.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/Task3Test.java	Sat Oct 18 07:47:34 2008 +0200
    98.3 @@ -0,0 +1,122 @@
    98.4 +package org.apidesign.apifest08.test;
    98.5 +
    98.6 +import java.math.BigDecimal;
    98.7 +import junit.framework.TestCase;
    98.8 +import org.apidesign.apifest08.currency.Convertor;
    98.9 +import org.apidesign.apifest08.currency.ConvertorCurrency;
   98.10 +import org.apidesign.apifest08.currency.ExchangeRateProvider;
   98.11 +import org.apidesign.apifest08.currency.IExchangeRateEngine;
   98.12 +
   98.13 +/** The exchange rates are not always the same. They are changing. Day by day,
   98.14 + * hour by hour, minute by minute. For every bank it is important to always
   98.15 + * have the actual exchange rate available in the system. That is why let's
   98.16 + * create a pluggable convertor that will always have up to date value of its
   98.17 + * exchange rate.
   98.18 + * <p>
   98.19 + * The quest for today is to allow 3rd party developer to write a convertor
   98.20 + * that adjusts its exchange rate everytime it is queried. This convertor is
   98.21 + * written by independent vendor, the vendor knows only your Convertor API,
   98.22 + * he does not know how the whole system looks and how the convertor is supposed
   98.23 + * to be used.
   98.24 + */
   98.25 +public class Task3Test extends TestCase {
   98.26 +    
   98.27 +    private static ConvertorCurrency currencyCZK = ConvertorCurrency.getInstance("CZK");
   98.28 +    private static ConvertorCurrency currencySKK = ConvertorCurrency.getInstance("SKK");
   98.29 +    private static ConvertorCurrency currencyUSD = ConvertorCurrency.getInstance("USD");
   98.30 +    
   98.31 +    public Task3Test(String testName) {
   98.32 +        super(testName);
   98.33 +    }
   98.34 +
   98.35 +    @Override
   98.36 +    protected void setUp() throws Exception {
   98.37 +    }
   98.38 +
   98.39 +    @Override
   98.40 +    protected void tearDown() throws Exception {
   98.41 +    }
   98.42 +
   98.43 +    // Backward compatibly enhance your existing API to support following
   98.44 +    // usecases:
   98.45 +    //
   98.46 +
   98.47 +
   98.48 +    /** Without knowing anything about the surrounding system, write an
   98.49 +     * implementation of convertor that will return different rates everytime
   98.50 +     * it is queried. Convert USD to CZK and vice versa. Start with the rate of
   98.51 +     * 1USD = 16CZK and adjust it in favor of CZK by 0.01 CZK with every query.
   98.52 +     * As soon as you reach 1USD = 15CZK adjust it by 0.01 CZK in favor of USD
   98.53 +     * until you reach 1USD = 16CZK
   98.54 +     *
   98.55 +     * @return new instance of "online" USD and CZK convertor starting with rate 1USD = 16CZK
   98.56 +     */
   98.57 +    public static Convertor createOnlineCZKUSDConvertor() {
   98.58 +        // initial rate: 1USD = 16CZK
   98.59 +        // 2nd query 1USD = 15.99CZK
   98.60 +        // 3rd query 1USD = 15.98CZK
   98.61 +        // until 1USD = 15.00CZK
   98.62 +        // then 1USD = 15.01CZK
   98.63 +        // then 1USD = 15.02CZK
   98.64 +        // and so on and on up to 1USD = 16CZK
   98.65 +        // and then another round to 15, etc.
   98.66 +        IExchangeRateEngine engine = BouncingExchangeRateEngine.create(
   98.67 +                currencyUSD,  new BigDecimal(1),
   98.68 +                currencyCZK, 
   98.69 +                new BigDecimal(16),new BigDecimal(15),
   98.70 +                new BigDecimal("-0.01"));
   98.71 +        ExchangeRateProvider exRateProvider = ExchangeRateProvider.createExchangeRateProvider(engine);
   98.72 +        Convertor convertor = Convertor.createConvertor(exRateProvider);
   98.73 +        return convertor;
   98.74 +    }
   98.75 +
   98.76 +    public void testFewQueriesForOnlineConvertor() {
   98.77 +        Convertor c = createOnlineCZKUSDConvertor();
   98.78 +        doFewQueriesForOnlineConvertor(c);
   98.79 +    }
   98.80 +
   98.81 +    static void doFewQueriesForOnlineConvertor(Convertor c) {
   98.82 +        // convert $5 to CZK using c:
   98.83 +        assertEquals("Result is 80 CZK", new BigDecimal("80.00"), c.convertWithReversibleRates(currencyUSD, currencyCZK, new BigDecimal(5)).getConverted());
   98.84 +
   98.85 +        // convert $8 to CZK using c:
   98.86 +        assertEquals("Result is 127.92 CZK", new BigDecimal("127.92"), c.convertWithReversibleRates(currencyUSD, currencyCZK, new BigDecimal(8)).getConverted());
   98.87 +
   98.88 +        // convert $1 to CZK using c:
   98.89 +        assertEquals("Result is 15.98 CZK", new BigDecimal("15.98"), c.convertWithReversibleRates(currencyUSD, currencyCZK, new BigDecimal(1)).getConverted());
   98.90 +
   98.91 +        // convert 15.97CZK to USD using c:
   98.92 +        assertEquals("Result is 15.98 CZK", new BigDecimal("1.00"), c.convertWithReversibleRates(currencyCZK, currencyUSD , new BigDecimal("15.97")).getConverted());
   98.93 +    }
   98.94 +
   98.95 +    /** Join the convertors and show they behave sane.
   98.96 +     */
   98.97 +    public void testOnlineConvertorComposition() throws Exception {
   98.98 +        Convertor c = Task2Test.merge(
   98.99 +            createOnlineCZKUSDConvertor(),
  98.100 +            Task1Test.createSKKtoCZK()
  98.101 +        );
  98.102 +
  98.103 +        // convert 16CZK to SKK using c:
  98.104 +        assertEquals("Result is 20 CZK", new BigDecimal("20.00"), c.convertWithReversibleRates(currencyCZK, currencySKK , new BigDecimal("16")).getConverted());
  98.105 +
  98.106 +        // convert 500SKK to CZK using c:
  98.107 +        assertEquals("Result is 400 CZK", new BigDecimal("400.00"), c.convertWithReversibleRates(currencySKK, currencyCZK , new BigDecimal("500")).getConverted());
  98.108 +
  98.109 +        doFewQueriesForOnlineConvertor(c);
  98.110 +    }
  98.111 +    
  98.112 +//    public void testBouncing() {
  98.113 +//        Convertor c=createOnlineCZKUSDConvertor();
  98.114 +//        
  98.115 +//        IExchangeRateEngine engine = BouncingExchangeRateEngine.create(
  98.116 +//                currencyUSD,  new BigDecimal(1),
  98.117 +//                currencyCZK, 
  98.118 +//                new BigDecimal("16.00"),new BigDecimal("15.00"),
  98.119 +//                new BigDecimal("-0.01"));        
  98.120 +//        
  98.121 +//        for (int i=0;i<300;i++) {
  98.122 +//            System.out.println(engine.getExchangeRate(currencyUSD, currencyCZK).getToValue());
  98.123 +//        }
  98.124 +//    }
  98.125 +}
    99.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    99.2 +++ b/task4/solution13/test/org/apidesign/apifest08/test/Task4Test.java	Sat Oct 18 07:47:34 2008 +0200
    99.3 @@ -0,0 +1,188 @@
    99.4 +package org.apidesign.apifest08.test;
    99.5 +
    99.6 +import java.math.BigDecimal;
    99.7 +import java.text.SimpleDateFormat;
    99.8 +import java.util.Date;
    99.9 +import junit.framework.TestCase;
   99.10 +import org.apidesign.apifest08.currency.ConversionNotSupportedException;
   99.11 +import org.apidesign.apifest08.currency.Convertor;
   99.12 +import org.apidesign.apifest08.currency.ConvertorCurrency;
   99.13 +import org.apidesign.apifest08.currency.ExchangeRateProvider;
   99.14 +
   99.15 +/** The exchange rates are not always the same. They are changing. However
   99.16 + * as in order to predict the future, one needs to understand own past. That is
   99.17 + * why it is important to know the exchange rate as it was at any time during
   99.18 + * the past.
   99.19 + * <p>
   99.20 + * Today's quest is to enhance the convertor API to deal with dates.
   99.21 + * One shall be able to convert a currency at any date. Each currencies rate shall
   99.22 + * be associated with a range between two Date objects. In order
   99.23 + * to keep compatibility with old API that knew nothing about dates, the
   99.24 + * rates associated then are applicable "for eternity". Any use of existing
   99.25 + * convert methods that do not accept a Date argument, uses the current
   99.26 + * System.currentTimeMillis() as default date.
   99.27 + */
   99.28 +public class Task4Test extends TestCase {
   99.29 +    private static ConvertorCurrency CZK = ConvertorCurrency.getInstance("CZK");
   99.30 +    private static ConvertorCurrency SKK = ConvertorCurrency.getInstance("SKK");
   99.31 +    private static ConvertorCurrency USD = ConvertorCurrency.getInstance("USD");
   99.32 +    private SimpleDateFormat df;
   99.33 +    
   99.34 +    
   99.35 +    public Task4Test(String testName) {
   99.36 +        super(testName);
   99.37 +    }
   99.38 +
   99.39 +    @Override
   99.40 +    protected void setUp() throws Exception {
   99.41 +        super.setUp();
   99.42 +        df = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
   99.43 +    }
   99.44 +
   99.45 +    @Override
   99.46 +    protected void tearDown() throws Exception {
   99.47 +        super.tearDown();
   99.48 +        df = null;
   99.49 +    }
   99.50 +
   99.51 +    // Backward compatibly enhance your existing API to support following
   99.52 +    // usecases:
   99.53 +    //
   99.54 +
   99.55 +    /** Takes a convertor with any rates associated and creates new convertor
   99.56 +     * that returns the same values as the old one for time between from to till.
   99.57 +     * Otherwise it returns no results. This is just a helper method that
   99.58 +     * shall call some real one in the API.
   99.59 +     * 
   99.60 +     * @param old existing convertor
   99.61 +     * @param from initial date (inclusive)
   99.62 +     * @param till final date (exclusive)
   99.63 +     * @return new convertor
   99.64 +     */
   99.65 +    public static Convertor limitTo(Convertor old, Date from, Date till) {
   99.66 +        Convertor c = Convertor.createConvertorAsMerge(new Convertor[]{old});
   99.67 +        c.limitAllowedDates(from, till);
   99.68 +        return c;
   99.69 +    }
   99.70 +
   99.71 +
   99.72 +    public void testCompositionOfLimitedConvertors() throws Exception {
   99.73 +        Date d1 = df.parse("2008-10-01 0:00 GMT");
   99.74 +        Date d2 = df.parse("2008-10-02 0:00 GMT");
   99.75 +        Date d3 = df.parse("2008-10-03 0:00 GMT");
   99.76 +        
   99.77 +        Convertor c = Task2Test.merge(
   99.78 +            limitTo(Task1Test.createCZKtoUSD(), d1, d2),
   99.79 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
   99.80 +        );
   99.81 +
   99.82 +        // convert $5 to CZK using c:
   99.83 +        // cannot convert as no rate is applicable to current date
   99.84 +        try {
   99.85 +          c.convert(USD, CZK, new BigDecimal(5));
   99.86 +          fail();        
   99.87 +        } catch (ConversionNotSupportedException e) {
   99.88 +            //exception expected
   99.89 +        }
   99.90 +
   99.91 +        // convert $8 to CZK using c:
   99.92 +        // cannot convert as no rate is applicable to current date
   99.93 +        try {
   99.94 +          c.convert(USD, CZK, new BigDecimal(8));
   99.95 +          fail();        
   99.96 +        } catch (ConversionNotSupportedException e) {
   99.97 +            //exception expected
   99.98 +        }
   99.99 +        
  99.100 +
  99.101 +        // convert 1003CZK to USD using c:
  99.102 +        // cannot convert as no rate is applicable to current date
  99.103 +        try {
  99.104 +          c.convert(CZK, USD, new BigDecimal(1003));
  99.105 +          fail();        
  99.106 +        } catch (ConversionNotSupportedException e) {
  99.107 +            //exception expected
  99.108 +        }
  99.109 +
  99.110 +        // convert 16CZK using c:
  99.111 +        // cannot convert as no rate is applicable to current date
  99.112 +        // ???
  99.113 +        try {
  99.114 +          c.convert(CZK, USD, new BigDecimal(16));
  99.115 +          fail();        
  99.116 +        } catch (ConversionNotSupportedException e) {
  99.117 +            //exception expected
  99.118 +        }
  99.119 +
  99.120 +        // convert 500SKK to CZK using c:
  99.121 +        // cannot convert as no rate is applicable to current date
  99.122 +        try {
  99.123 +          c.convert(SKK, CZK, new BigDecimal(500));
  99.124 +          fail();        
  99.125 +        } catch (ConversionNotSupportedException e) {
  99.126 +            //exception expected
  99.127 +        }
  99.128 +        
  99.129 +
  99.130 +        // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
  99.131 +        // assertEquals("Result is 85 CZK");
  99.132 +        assertEquals("Result is 85 CZK", new BigDecimal("85.00"), c.convertWithReversibleRates(USD, CZK , new BigDecimal("5"),df.parse("2008-10-01 6:00 GMT")).getConverted());        
  99.133 +
  99.134 +        // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
  99.135 +        // assertEquals("Result is 136 CZK");
  99.136 +        assertEquals("Result is 136 CZK", new BigDecimal("136.00"), c.convertWithReversibleRates(USD, CZK , new BigDecimal("8"),df.parse("2008-10-01 6:00 GMT")).getConverted());        
  99.137 +
  99.138 +        // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
  99.139 +        // assertEquals("Result is 59 USD");
  99.140 +        assertEquals("Result is 59 USD", new BigDecimal("59.00"), c.convertWithReversibleRates(CZK, USD , new BigDecimal("1003"),df.parse("2008-10-01 6:00 GMT")).getConverted());        
  99.141 +
  99.142 +        // convert 16CZK using c at 2008-10-02 9:00 GMT:
  99.143 +        // assertEquals("Result is 20 SKK");
  99.144 +        assertEquals("Result is 20 SKK", new BigDecimal("20.00"), c.convertWithReversibleRates(CZK, SKK , new BigDecimal("16"),df.parse("2008-10-02 9:00 GMT")).getConverted());        
  99.145 +
  99.146 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  99.147 +        // assertEquals("Result is 400 CZK");
  99.148 +        assertEquals("Result is 400 SKK", new BigDecimal("400.00"), c.convertWithReversibleRates(SKK, CZK , new BigDecimal("500"),df.parse("2008-10-02 9:00 GMT")).getConverted());        
  99.149 +
  99.150 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  99.151 +        // cannot convert as no rate is applicable to current date
  99.152 +        try {
  99.153 +          c.convertWithReversibleRates(SKK, CZK , new BigDecimal("500"),df.parse("2008-10-01 6:00 GMT")).getConverted();        
  99.154 +          fail();        
  99.155 +        } catch (ConversionNotSupportedException e) {
  99.156 +            //exception expected
  99.157 +        }
  99.158 +
  99.159 +    }
  99.160 +
  99.161 +    /** Create convertor that understands two currencies, CZK and
  99.162 +     *  SKK. Make 100 SKK == 90 CZK.
  99.163 +     *
  99.164 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
  99.165 +     */
  99.166 +    public static Convertor createSKKtoCZK2() {
  99.167 +        ExchangeRateProvider exchangeRateProvider = ExchangeRateProvider.createExchangeRateProvider();
  99.168 +        exchangeRateProvider.addFixedCurencyRate(SKK, new BigDecimal("100.00"), CZK, new BigDecimal("90.00"));
  99.169 +        Convertor c  = Convertor.createConvertor(exchangeRateProvider);
  99.170 +        return c;
  99.171 +    }
  99.172 +
  99.173 +    public void testDateConvetorWithTwoDifferentRates() throws Exception {
  99.174 +
  99.175 +        Date d1 = df.parse("2008-10-01 0:00 GMT");
  99.176 +        Date d2 = df.parse("2008-10-02 0:00 GMT");
  99.177 +        Date d3 = df.parse("2008-10-03 0:00 GMT");
  99.178 +
  99.179 +        Convertor c = Task2Test.merge(
  99.180 +            limitTo(createSKKtoCZK2(), d1, d2),
  99.181 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
  99.182 +        );
  99.183 +
  99.184 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
  99.185 +        assertEquals("Result is 400 CZK", new BigDecimal("400.00"), c.convertWithReversibleRates(SKK, CZK , new BigDecimal("500"),df.parse("2008-10-02 9:00 GMT")).getConverted());        
  99.186 +
  99.187 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
  99.188 +        assertEquals("Result is 450 CZK", new BigDecimal("450.00"), c.convertWithReversibleRates(SKK, CZK , new BigDecimal("500"),df.parse("2008-10-01 9:00 GMT")).getConverted());        
  99.189 +    }
  99.190 +
  99.191 +}
   100.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   100.2 +++ b/task4/solution14/build.xml	Sat Oct 18 07:47:34 2008 +0200
   100.3 @@ -0,0 +1,69 @@
   100.4 +<?xml version="1.0" encoding="UTF-8"?>
   100.5 +<!-- You may freely edit this file. See commented blocks below for -->
   100.6 +<!-- some examples of how to customize the build. -->
   100.7 +<!-- (If you delete it and reopen the project it will be recreated.) -->
   100.8 +<project name="currency" default="default" basedir=".">
   100.9 +    <description>Builds, tests, and runs the project.</description>
  100.10 +    <import file="nbproject/build-impl.xml"/>
  100.11 +    <!--
  100.12 +
  100.13 +    There exist several targets which are by default empty and which can be 
  100.14 +    used for execution of your tasks. These targets are usually executed 
  100.15 +    before and after some main targets. They are: 
  100.16 +
  100.17 +      -pre-init:                 called before initialization of project properties
  100.18 +      -post-init:                called after initialization of project properties
  100.19 +      -pre-compile:              called before javac compilation
  100.20 +      -post-compile:             called after javac compilation
  100.21 +      -pre-compile-single:       called before javac compilation of single file
  100.22 +      -post-compile-single:      called after javac compilation of single file
  100.23 +      -pre-compile-test:         called before javac compilation of JUnit tests
  100.24 +      -post-compile-test:        called after javac compilation of JUnit tests
  100.25 +      -pre-compile-test-single:  called before javac compilation of single JUnit test
  100.26 +      -post-compile-test-single: called after javac compilation of single JUunit test
  100.27 +      -pre-jar:                  called before JAR building
  100.28 +      -post-jar:                 called after JAR building
  100.29 +      -post-clean:               called after cleaning build products
  100.30 +
  100.31 +    (Targets beginning with '-' are not intended to be called on their own.)
  100.32 +
  100.33 +    Example of inserting an obfuscator after compilation could look like this:
  100.34 +
  100.35 +        <target name="-post-compile">
  100.36 +            <obfuscate>
  100.37 +                <fileset dir="${build.classes.dir}"/>
  100.38 +            </obfuscate>
  100.39 +        </target>
  100.40 +
  100.41 +    For list of available properties check the imported 
  100.42 +    nbproject/build-impl.xml file. 
  100.43 +
  100.44 +
  100.45 +    Another way to customize the build is by overriding existing main targets.
  100.46 +    The targets of interest are: 
  100.47 +
  100.48 +      -init-macrodef-javac:     defines macro for javac compilation
  100.49 +      -init-macrodef-junit:     defines macro for junit execution
  100.50 +      -init-macrodef-debug:     defines macro for class debugging
  100.51 +      -init-macrodef-java:      defines macro for class execution
  100.52 +      -do-jar-with-manifest:    JAR building (if you are using a manifest)
  100.53 +      -do-jar-without-manifest: JAR building (if you are not using a manifest)
  100.54 +      run:                      execution of project 
  100.55 +      -javadoc-build:           Javadoc generation
  100.56 +      test-report:              JUnit report generation
  100.57 +
  100.58 +    An example of overriding the target for project execution could look like this:
  100.59 +
  100.60 +        <target name="run" depends="currency-impl.jar">
  100.61 +            <exec dir="bin" executable="launcher.exe">
  100.62 +                <arg file="${dist.jar}"/>
  100.63 +            </exec>
  100.64 +        </target>
  100.65 +
  100.66 +    Notice that the overridden target depends on the jar target and not only on 
  100.67 +    the compile target as the regular run target does. Again, for a list of available 
  100.68 +    properties which you can use, check the target you are overriding in the
  100.69 +    nbproject/build-impl.xml file. 
  100.70 +
  100.71 +    -->
  100.72 +</project>
   101.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   101.2 +++ b/task4/solution14/nbproject/build-impl.xml	Sat Oct 18 07:47:34 2008 +0200
   101.3 @@ -0,0 +1,642 @@
   101.4 +<?xml version="1.0" encoding="UTF-8"?>
   101.5 +<!--
   101.6 +*** GENERATED FROM project.xml - DO NOT EDIT  ***
   101.7 +***         EDIT ../build.xml INSTEAD         ***
   101.8 +
   101.9 +For the purpose of easier reading the script
  101.10 +is divided into following sections:
  101.11 +
  101.12 +  - initialization
  101.13 +  - compilation
  101.14 +  - jar
  101.15 +  - execution
  101.16 +  - debugging
  101.17 +  - javadoc
  101.18 +  - junit compilation
  101.19 +  - junit execution
  101.20 +  - junit debugging
  101.21 +  - applet
  101.22 +  - cleanup
  101.23 +
  101.24 +        -->
  101.25 +<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="Currency_Convertor_Solution_14-impl">
  101.26 +    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
  101.27 +    <!-- 
  101.28 +                ======================
  101.29 +                INITIALIZATION SECTION 
  101.30 +                ======================
  101.31 +            -->
  101.32 +    <target name="-pre-init">
  101.33 +        <!-- Empty placeholder for easier customization. -->
  101.34 +        <!-- You can override this target in the ../build.xml file. -->
  101.35 +    </target>
  101.36 +    <target depends="-pre-init" name="-init-private">
  101.37 +        <property file="nbproject/private/config.properties"/>
  101.38 +        <property file="nbproject/private/configs/${config}.properties"/>
  101.39 +        <property file="nbproject/private/private.properties"/>
  101.40 +    </target>
  101.41 +    <target depends="-pre-init,-init-private" name="-init-user">
  101.42 +        <property file="${user.properties.file}"/>
  101.43 +        <!-- The two properties below are usually overridden -->
  101.44 +        <!-- by the active platform. Just a fallback. -->
  101.45 +        <property name="default.javac.source" value="1.4"/>
  101.46 +        <property name="default.javac.target" value="1.4"/>
  101.47 +    </target>
  101.48 +    <target depends="-pre-init,-init-private,-init-user" name="-init-project">
  101.49 +        <property file="nbproject/configs/${config}.properties"/>
  101.50 +        <property file="nbproject/project.properties"/>
  101.51 +    </target>
  101.52 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
  101.53 +        <available file="${manifest.file}" property="manifest.available"/>
  101.54 +        <condition property="manifest.available+main.class">
  101.55 +            <and>
  101.56 +                <isset property="manifest.available"/>
  101.57 +                <isset property="main.class"/>
  101.58 +                <not>
  101.59 +                    <equals arg1="${main.class}" arg2="" trim="true"/>
  101.60 +                </not>
  101.61 +            </and>
  101.62 +        </condition>
  101.63 +        <condition property="manifest.available+main.class+mkdist.available">
  101.64 +            <and>
  101.65 +                <istrue value="${manifest.available+main.class}"/>
  101.66 +                <isset property="libs.CopyLibs.classpath"/>
  101.67 +            </and>
  101.68 +        </condition>
  101.69 +        <condition property="have.tests">
  101.70 +            <or>
  101.71 +                <available file="${test.src.dir}"/>
  101.72 +            </or>
  101.73 +        </condition>
  101.74 +        <condition property="have.sources">
  101.75 +            <or>
  101.76 +                <available file="${src.dir}"/>
  101.77 +            </or>
  101.78 +        </condition>
  101.79 +        <condition property="netbeans.home+have.tests">
  101.80 +            <and>
  101.81 +                <isset property="netbeans.home"/>
  101.82 +                <isset property="have.tests"/>
  101.83 +            </and>
  101.84 +        </condition>
  101.85 +        <condition property="no.javadoc.preview">
  101.86 +            <and>
  101.87 +                <isset property="javadoc.preview"/>
  101.88 +                <isfalse value="${javadoc.preview}"/>
  101.89 +            </and>
  101.90 +        </condition>
  101.91 +        <property name="run.jvmargs" value=""/>
  101.92 +        <property name="javac.compilerargs" value=""/>
  101.93 +        <property name="work.dir" value="${basedir}"/>
  101.94 +        <condition property="no.deps">
  101.95 +            <and>
  101.96 +                <istrue value="${no.dependencies}"/>
  101.97 +            </and>
  101.98 +        </condition>
  101.99 +        <property name="javac.debug" value="true"/>
 101.100 +        <property name="javadoc.preview" value="true"/>
 101.101 +        <property name="application.args" value=""/>
 101.102 +        <property name="source.encoding" value="${file.encoding}"/>
 101.103 +        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
 101.104 +            <and>
 101.105 +                <isset property="javadoc.encoding"/>
 101.106 +                <not>
 101.107 +                    <equals arg1="${javadoc.encoding}" arg2=""/>
 101.108 +                </not>
 101.109 +            </and>
 101.110 +        </condition>
 101.111 +        <property name="javadoc.encoding.used" value="${source.encoding}"/>
 101.112 +        <property name="includes" value="**"/>
 101.113 +        <property name="excludes" value=""/>
 101.114 +        <property name="do.depend" value="false"/>
 101.115 +        <condition property="do.depend.true">
 101.116 +            <istrue value="${do.depend}"/>
 101.117 +        </condition>
 101.118 +        <condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
 101.119 +            <and>
 101.120 +                <isset property="jaxws.endorsed.dir"/>
 101.121 +                <available file="nbproject/jaxws-build.xml"/>
 101.122 +            </and>
 101.123 +        </condition>
 101.124 +    </target>
 101.125 +    <target name="-post-init">
 101.126 +        <!-- Empty placeholder for easier customization. -->
 101.127 +        <!-- You can override this target in the ../build.xml file. -->
 101.128 +    </target>
 101.129 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
 101.130 +        <fail unless="src.dir">Must set src.dir</fail>
 101.131 +        <fail unless="test.src.dir">Must set test.src.dir</fail>
 101.132 +        <fail unless="build.dir">Must set build.dir</fail>
 101.133 +        <fail unless="dist.dir">Must set dist.dir</fail>
 101.134 +        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
 101.135 +        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
 101.136 +        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
 101.137 +        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
 101.138 +        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
 101.139 +        <fail unless="dist.jar">Must set dist.jar</fail>
 101.140 +    </target>
 101.141 +    <target name="-init-macrodef-property">
 101.142 +        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
 101.143 +            <attribute name="name"/>
 101.144 +            <attribute name="value"/>
 101.145 +            <sequential>
 101.146 +                <property name="@{name}" value="${@{value}}"/>
 101.147 +            </sequential>
 101.148 +        </macrodef>
 101.149 +    </target>
 101.150 +    <target name="-init-macrodef-javac">
 101.151 +        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
 101.152 +            <attribute default="${src.dir}" name="srcdir"/>
 101.153 +            <attribute default="${build.classes.dir}" name="destdir"/>
 101.154 +            <attribute default="${javac.classpath}" name="classpath"/>
 101.155 +            <attribute default="${includes}" name="includes"/>
 101.156 +            <attribute default="${excludes}" name="excludes"/>
 101.157 +            <attribute default="${javac.debug}" name="debug"/>
 101.158 +            <attribute default="" name="sourcepath"/>
 101.159 +            <element name="customize" optional="true"/>
 101.160 +            <sequential>
 101.161 +                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
 101.162 +                    <classpath>
 101.163 +                        <path path="@{classpath}"/>
 101.164 +                    </classpath>
 101.165 +                    <compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
 101.166 +                    <customize/>
 101.167 +                </javac>
 101.168 +            </sequential>
 101.169 +        </macrodef>
 101.170 +        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
 101.171 +            <attribute default="${src.dir}" name="srcdir"/>
 101.172 +            <attribute default="${build.classes.dir}" name="destdir"/>
 101.173 +            <attribute default="${javac.classpath}" name="classpath"/>
 101.174 +            <sequential>
 101.175 +                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
 101.176 +                    <classpath>
 101.177 +                        <path path="@{classpath}"/>
 101.178 +                    </classpath>
 101.179 +                </depend>
 101.180 +            </sequential>
 101.181 +        </macrodef>
 101.182 +        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
 101.183 +            <attribute default="${build.classes.dir}" name="destdir"/>
 101.184 +            <sequential>
 101.185 +                <fail unless="javac.includes">Must set javac.includes</fail>
 101.186 +                <pathconvert pathsep="," property="javac.includes.binary">
 101.187 +                    <path>
 101.188 +                        <filelist dir="@{destdir}" files="${javac.includes}"/>
 101.189 +                    </path>
 101.190 +                    <globmapper from="*.java" to="*.class"/>
 101.191 +                </pathconvert>
 101.192 +                <delete>
 101.193 +                    <files includes="${javac.includes.binary}"/>
 101.194 +                </delete>
 101.195 +            </sequential>
 101.196 +        </macrodef>
 101.197 +    </target>
 101.198 +    <target name="-init-macrodef-junit">
 101.199 +        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
 101.200 +            <attribute default="${includes}" name="includes"/>
 101.201 +            <attribute default="${excludes}" name="excludes"/>
 101.202 +            <attribute default="**" name="testincludes"/>
 101.203 +            <sequential>
 101.204 +                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
 101.205 +                    <batchtest todir="${build.test.results.dir}">
 101.206 +                        <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
 101.207 +                            <filename name="@{testincludes}"/>
 101.208 +                        </fileset>
 101.209 +                    </batchtest>
 101.210 +                    <classpath>
 101.211 +                        <path path="${run.test.classpath}"/>
 101.212 +                    </classpath>
 101.213 +                    <syspropertyset>
 101.214 +                        <propertyref prefix="test-sys-prop."/>
 101.215 +                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
 101.216 +                    </syspropertyset>
 101.217 +                    <formatter type="brief" usefile="false"/>
 101.218 +                    <formatter type="xml"/>
 101.219 +                    <jvmarg line="${run.jvmargs}"/>
 101.220 +                </junit>
 101.221 +            </sequential>
 101.222 +        </macrodef>
 101.223 +    </target>
 101.224 +    <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
 101.225 +        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
 101.226 +            <attribute default="${main.class}" name="name"/>
 101.227 +            <attribute default="${debug.classpath}" name="classpath"/>
 101.228 +            <attribute default="" name="stopclassname"/>
 101.229 +            <sequential>
 101.230 +                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
 101.231 +                    <classpath>
 101.232 +                        <path path="@{classpath}"/>
 101.233 +                    </classpath>
 101.234 +                </nbjpdastart>
 101.235 +            </sequential>
 101.236 +        </macrodef>
 101.237 +        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
 101.238 +            <attribute default="${build.classes.dir}" name="dir"/>
 101.239 +            <sequential>
 101.240 +                <nbjpdareload>
 101.241 +                    <fileset dir="@{dir}" includes="${fix.classes}">
 101.242 +                        <include name="${fix.includes}*.class"/>
 101.243 +                    </fileset>
 101.244 +                </nbjpdareload>
 101.245 +            </sequential>
 101.246 +        </macrodef>
 101.247 +    </target>
 101.248 +    <target name="-init-debug-args">
 101.249 +        <property name="version-output" value="java version &quot;${ant.java.version}"/>
 101.250 +        <condition property="have-jdk-older-than-1.4">
 101.251 +            <or>
 101.252 +                <contains string="${version-output}" substring="java version &quot;1.0"/>
 101.253 +                <contains string="${version-output}" substring="java version &quot;1.1"/>
 101.254 +                <contains string="${version-output}" substring="java version &quot;1.2"/>
 101.255 +                <contains string="${version-output}" substring="java version &quot;1.3"/>
 101.256 +            </or>
 101.257 +        </condition>
 101.258 +        <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
 101.259 +            <istrue value="${have-jdk-older-than-1.4}"/>
 101.260 +        </condition>
 101.261 +        <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
 101.262 +            <os family="windows"/>
 101.263 +        </condition>
 101.264 +        <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
 101.265 +            <isset property="debug.transport"/>
 101.266 +        </condition>
 101.267 +    </target>
 101.268 +    <target depends="-init-debug-args" name="-init-macrodef-debug">
 101.269 +        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
 101.270 +            <attribute default="${main.class}" name="classname"/>
 101.271 +            <attribute default="${debug.classpath}" name="classpath"/>
 101.272 +            <element name="customize" optional="true"/>
 101.273 +            <sequential>
 101.274 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
 101.275 +                    <jvmarg line="${debug-args-line}"/>
 101.276 +                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
 101.277 +                    <jvmarg line="${run.jvmargs}"/>
 101.278 +                    <classpath>
 101.279 +                        <path path="@{classpath}"/>
 101.280 +                    </classpath>
 101.281 +                    <syspropertyset>
 101.282 +                        <propertyref prefix="run-sys-prop."/>
 101.283 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
 101.284 +                    </syspropertyset>
 101.285 +                    <customize/>
 101.286 +                </java>
 101.287 +            </sequential>
 101.288 +        </macrodef>
 101.289 +    </target>
 101.290 +    <target name="-init-macrodef-java">
 101.291 +        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
 101.292 +            <attribute default="${main.class}" name="classname"/>
 101.293 +            <element name="customize" optional="true"/>
 101.294 +            <sequential>
 101.295 +                <java classname="@{classname}" dir="${work.dir}" fork="true">
 101.296 +                    <jvmarg line="${run.jvmargs}"/>
 101.297 +                    <classpath>
 101.298 +                        <path path="${run.classpath}"/>
 101.299 +                    </classpath>
 101.300 +                    <syspropertyset>
 101.301 +                        <propertyref prefix="run-sys-prop."/>
 101.302 +                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
 101.303 +                    </syspropertyset>
 101.304 +                    <customize/>
 101.305 +                </java>
 101.306 +            </sequential>
 101.307 +        </macrodef>
 101.308 +    </target>
 101.309 +    <target name="-init-presetdef-jar">
 101.310 +        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
 101.311 +            <jar compress="${jar.compress}" jarfile="${dist.jar}">
 101.312 +                <j2seproject1:fileset dir="${build.classes.dir}"/>
 101.313 +            </jar>
 101.314 +        </presetdef>
 101.315 +    </target>
 101.316 +    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
 101.317 +    <!--
 101.318 +                ===================
 101.319 +                COMPILATION SECTION
 101.320 +                ===================
 101.321 +            -->
 101.322 +    <target depends="init" name="deps-jar" unless="no.deps"/>
 101.323 +    <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
 101.324 +    <target depends="init" name="-check-automatic-build">
 101.325 +        <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
 101.326 +    </target>
 101.327 +    <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
 101.328 +        <antcall target="clean"/>
 101.329 +    </target>
 101.330 +    <target depends="init,deps-jar" name="-pre-pre-compile">
 101.331 +        <mkdir dir="${build.classes.dir}"/>
 101.332 +    </target>
 101.333 +    <target name="-pre-compile">
 101.334 +        <!-- Empty placeholder for easier customization. -->
 101.335 +        <!-- You can override this target in the ../build.xml file. -->
 101.336 +    </target>
 101.337 +    <target if="do.depend.true" name="-compile-depend">
 101.338 +        <j2seproject3:depend/>
 101.339 +    </target>
 101.340 +    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
 101.341 +        <j2seproject3:javac/>
 101.342 +        <copy todir="${build.classes.dir}">
 101.343 +            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
 101.344 +        </copy>
 101.345 +    </target>
 101.346 +    <target name="-post-compile">
 101.347 +        <!-- Empty placeholder for easier customization. -->
 101.348 +        <!-- You can override this target in the ../build.xml file. -->
 101.349 +    </target>
 101.350 +    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
 101.351 +    <target name="-pre-compile-single">
 101.352 +        <!-- Empty placeholder for easier customization. -->
 101.353 +        <!-- You can override this target in the ../build.xml file. -->
 101.354 +    </target>
 101.355 +    <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
 101.356 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
 101.357 +        <j2seproject3:force-recompile/>
 101.358 +        <j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
 101.359 +    </target>
 101.360 +    <target name="-post-compile-single">
 101.361 +        <!-- Empty placeholder for easier customization. -->
 101.362 +        <!-- You can override this target in the ../build.xml file. -->
 101.363 +    </target>
 101.364 +    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
 101.365 +    <!--
 101.366 +                ====================
 101.367 +                JAR BUILDING SECTION
 101.368 +                ====================
 101.369 +            -->
 101.370 +    <target depends="init" name="-pre-pre-jar">
 101.371 +        <dirname file="${dist.jar}" property="dist.jar.dir"/>
 101.372 +        <mkdir dir="${dist.jar.dir}"/>
 101.373 +    </target>
 101.374 +    <target name="-pre-jar">
 101.375 +        <!-- Empty placeholder for easier customization. -->
 101.376 +        <!-- You can override this target in the ../build.xml file. -->
 101.377 +    </target>
 101.378 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
 101.379 +        <j2seproject1:jar/>
 101.380 +    </target>
 101.381 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
 101.382 +        <j2seproject1:jar manifest="${manifest.file}"/>
 101.383 +    </target>
 101.384 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
 101.385 +        <j2seproject1:jar manifest="${manifest.file}">
 101.386 +            <j2seproject1:manifest>
 101.387 +                <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
 101.388 +            </j2seproject1:manifest>
 101.389 +        </j2seproject1:jar>
 101.390 +        <echo>To run this application from the command line without Ant, try:</echo>
 101.391 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
 101.392 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
 101.393 +        <pathconvert property="run.classpath.with.dist.jar">
 101.394 +            <path path="${run.classpath}"/>
 101.395 +            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
 101.396 +        </pathconvert>
 101.397 +        <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
 101.398 +    </target>
 101.399 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
 101.400 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
 101.401 +        <pathconvert property="run.classpath.without.build.classes.dir">
 101.402 +            <path path="${run.classpath}"/>
 101.403 +            <map from="${build.classes.dir.resolved}" to=""/>
 101.404 +        </pathconvert>
 101.405 +        <pathconvert pathsep=" " property="jar.classpath">
 101.406 +            <path path="${run.classpath.without.build.classes.dir}"/>
 101.407 +            <chainedmapper>
 101.408 +                <flattenmapper/>
 101.409 +                <globmapper from="*" to="lib/*"/>
 101.410 +            </chainedmapper>
 101.411 +        </pathconvert>
 101.412 +        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
 101.413 +        <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
 101.414 +            <fileset dir="${build.classes.dir}"/>
 101.415 +            <manifest>
 101.416 +                <attribute name="Main-Class" value="${main.class}"/>
 101.417 +                <attribute name="Class-Path" value="${jar.classpath}"/>
 101.418 +            </manifest>
 101.419 +        </copylibs>
 101.420 +        <echo>To run this application from the command line without Ant, try:</echo>
 101.421 +        <property location="${dist.jar}" name="dist.jar.resolved"/>
 101.422 +        <echo>java -jar "${dist.jar.resolved}"</echo>
 101.423 +    </target>
 101.424 +    <target name="-post-jar">
 101.425 +        <!-- Empty placeholder for easier customization. -->
 101.426 +        <!-- You can override this target in the ../build.xml file. -->
 101.427 +    </target>
 101.428 +    <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
 101.429 +    <!--
 101.430 +                =================
 101.431 +                EXECUTION SECTION
 101.432 +                =================
 101.433 +            -->
 101.434 +    <target depends="init,compile" description="Run a main class." name="run">
 101.435 +        <j2seproject1:java>
 101.436 +            <customize>
 101.437 +                <arg line="${application.args}"/>
 101.438 +            </customize>
 101.439 +        </j2seproject1:java>
 101.440 +    </target>
 101.441 +    <target name="-do-not-recompile">
 101.442 +        <property name="javac.includes.binary" value=""/>
 101.443 +    </target>
 101.444 +    <target depends="init,-do-not-recompile,compile-single" name="run-single">
 101.445 +        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
 101.446 +        <j2seproject1:java classname="${run.class}"/>
 101.447 +    </target>
 101.448 +    <!--
 101.449 +                =================
 101.450 +                DEBUGGING SECTION
 101.451 +                =================
 101.452 +            -->
 101.453 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger">
 101.454 +        <j2seproject1:nbjpdastart name="${debug.class}"/>
 101.455 +    </target>
 101.456 +    <target depends="init,compile" name="-debug-start-debuggee">
 101.457 +        <j2seproject3:debug>
 101.458 +            <customize>
 101.459 +                <arg line="${application.args}"/>
 101.460 +            </customize>
 101.461 +        </j2seproject3:debug>
 101.462 +    </target>
 101.463 +    <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
 101.464 +    <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
 101.465 +        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
 101.466 +    </target>
 101.467 +    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
 101.468 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
 101.469 +        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
 101.470 +        <j2seproject3:debug classname="${debug.class}"/>
 101.471 +    </target>
 101.472 +    <target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
 101.473 +    <target depends="init" name="-pre-debug-fix">
 101.474 +        <fail unless="fix.includes">Must set fix.includes</fail>
 101.475 +        <property name="javac.includes" value="${fix.includes}.java"/>
 101.476 +    </target>
 101.477 +    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
 101.478 +        <j2seproject1:nbjpdareload/>
 101.479 +    </target>
 101.480 +    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
 101.481 +    <!--
 101.482 +                ===============
 101.483 +                JAVADOC SECTION
 101.484 +                ===============
 101.485 +            -->
 101.486 +    <target depends="init" name="-javadoc-build">
 101.487 +        <mkdir dir="${dist.javadoc.dir}"/>
 101.488 +        <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
 101.489 +            <classpath>
 101.490 +                <path path="${javac.classpath}"/>
 101.491 +            </classpath>
 101.492 +            <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
 101.493 +                <filename name="**/*.java"/>
 101.494 +            </fileset>
 101.495 +        </javadoc>
 101.496 +    </target>
 101.497 +    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
 101.498 +        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
 101.499 +    </target>
 101.500 +    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
 101.501 +    <!--
 101.502 +                =========================
 101.503 +                JUNIT COMPILATION SECTION
 101.504 +                =========================
 101.505 +            -->
 101.506 +    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
 101.507 +        <mkdir dir="${build.test.classes.dir}"/>
 101.508 +    </target>
 101.509 +    <target name="-pre-compile-test">
 101.510 +        <!-- Empty placeholder for easier customization. -->
 101.511 +        <!-- You can override this target in the ../build.xml file. -->
 101.512 +    </target>
 101.513 +    <target if="do.depend.true" name="-compile-test-depend">
 101.514 +        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
 101.515 +    </target>
 101.516 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
 101.517 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
 101.518 +        <copy todir="${build.test.classes.dir}">
 101.519 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
 101.520 +        </copy>
 101.521 +    </target>
 101.522 +    <target name="-post-compile-test">
 101.523 +        <!-- Empty placeholder for easier customization. -->
 101.524 +        <!-- You can override this target in the ../build.xml file. -->
 101.525 +    </target>
 101.526 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
 101.527 +    <target name="-pre-compile-test-single">
 101.528 +        <!-- Empty placeholder for easier customization. -->
 101.529 +        <!-- You can override this target in the ../build.xml file. -->
 101.530 +    </target>
 101.531 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
 101.532 +        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
 101.533 +        <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
 101.534 +        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
 101.535 +        <copy todir="${build.test.classes.dir}">
 101.536 +            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
 101.537 +        </copy>
 101.538 +    </target>
 101.539 +    <target name="-post-compile-test-single">
 101.540 +        <!-- Empty placeholder for easier customization. -->
 101.541 +        <!-- You can override this target in the ../build.xml file. -->
 101.542 +    </target>
 101.543 +    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
 101.544 +    <!--
 101.545 +                =======================
 101.546 +                JUNIT EXECUTION SECTION
 101.547 +                =======================
 101.548 +            -->
 101.549 +    <target depends="init" if="have.tests" name="-pre-test-run">
 101.550 +        <mkdir dir="${build.test.results.dir}"/>
 101.551 +    </target>
 101.552 +    <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
 101.553 +        <j2seproject3:junit testincludes="**/*Test.java"/>
 101.554 +    </target>
 101.555 +    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
 101.556 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
 101.557 +    </target>
 101.558 +    <target depends="init" if="have.tests" name="test-report"/>
 101.559 +    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
 101.560 +    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
 101.561 +    <target depends="init" if="have.tests" name="-pre-test-run-single">
 101.562 +        <mkdir dir="${build.test.results.dir}"/>
 101.563 +    </target>
 101.564 +    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
 101.565 +        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
 101.566 +        <j2seproject3:junit excludes="" includes="${test.includes}"/>
 101.567 +    </target>
 101.568 +    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
 101.569 +        <fail if="tests.failed">Some tests failed; see details above.</fail>
 101.570 +    </target>
 101.571 +    <target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
 101.572 +    <!--
 101.573 +                =======================
 101.574 +                JUNIT DEBUGGING SECTION
 101.575 +                =======================
 101.576 +            -->
 101.577 +    <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
 101.578 +        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
 101.579 +        <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
 101.580 +        <delete file="${test.report.file}"/>
 101.581 +        <mkdir dir="${build.test.results.dir}"/>
 101.582 +        <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
 101.583 +            <customize>
 101.584 +                <syspropertyset>
 101.585 +                    <propertyref prefix="test-sys-prop."/>
 101.586 +                    <mapper from="test-sys-prop.*" to="*" type="glob"/>
 101.587 +                </syspropertyset>
 101.588 +                <arg value="${test.class}"/>
 101.589 +                <arg value="showoutput=true"/>
 101.590 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
 101.591 +                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
 101.592 +            </customize>
 101.593 +        </j2seproject3:debug>
 101.594 +    </target>
 101.595 +    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
 101.596 +        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
 101.597 +    </target>
 101.598 +    <target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
 101.599 +    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
 101.600 +        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
 101.601 +    </target>
 101.602 +    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
 101.603 +    <!--
 101.604 +                =========================
 101.605 +                APPLET EXECUTION SECTION
 101.606 +                =========================
 101.607 +            -->
 101.608 +    <target depends="init,compile-single" name="run-applet">
 101.609 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
 101.610 +        <j2seproject1:java classname="sun.applet.AppletViewer">
 101.611 +            <customize>
 101.612 +                <arg value="${applet.url}"/>
 101.613 +            </customize>
 101.614 +        </j2seproject1:java>
 101.615 +    </target>
 101.616 +    <!--
 101.617 +                =========================
 101.618 +                APPLET DEBUGGING  SECTION
 101.619 +                =========================
 101.620 +            -->
 101.621 +    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
 101.622 +        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
 101.623 +        <j2seproject3:debug classname="sun.applet.AppletViewer">
 101.624 +            <customize>
 101.625 +                <arg value="${applet.url}"/>
 101.626 +            </customize>
 101.627 +        </j2seproject3:debug>
 101.628 +    </target>
 101.629 +    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
 101.630 +    <!--
 101.631 +                ===============
 101.632 +                CLEANUP SECTION
 101.633 +                ===============
 101.634 +            -->
 101.635 +    <target depends="init" name="deps-clean" unless="no.deps"/>
 101.636 +    <target depends="init" name="-do-clean">
 101.637 +        <delete dir="${build.dir}"/>
 101.638 +        <delete dir="${dist.dir}"/>
 101.639 +    </target>
 101.640 +    <target name="-post-clean">
 101.641 +        <!-- Empty placeholder for easier customization. -->
 101.642 +        <!-- You can override this target in the ../build.xml file. -->
 101.643 +    </target>
 101.644 +    <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
 101.645 +</project>
   102.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   102.2 +++ b/task4/solution14/nbproject/genfiles.properties	Sat Oct 18 07:47:34 2008 +0200
   102.3 @@ -0,0 +1,8 @@
   102.4 +build.xml.data.CRC32=2ab820eb
   102.5 +build.xml.script.CRC32=58a52595
   102.6 +build.xml.stylesheet.CRC32=be360661
   102.7 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
   102.8 +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
   102.9 +nbproject/build-impl.xml.data.CRC32=1d0fd5f2
  102.10 +nbproject/build-impl.xml.script.CRC32=951643da
  102.11 +nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5
   103.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   103.2 +++ b/task4/solution14/nbproject/project.properties	Sat Oct 18 07:47:34 2008 +0200
   103.3 @@ -0,0 +1,68 @@
   103.4 +application.title=currency
   103.5 +application.vendor=apidesign.org
   103.6 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8
   103.7 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
   103.8 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=default
   103.9 +build.classes.dir=${build.dir}/classes
  103.10 +build.classes.excludes=**/*.java,**/*.form
  103.11 +# This directory is removed when the project is cleaned:
  103.12 +build.dir=build
  103.13 +build.generated.dir=${build.dir}/generated
  103.14 +# Only compile against the classpath explicitly listed here:
  103.15 +build.sysclasspath=ignore
  103.16 +build.test.classes.dir=${build.dir}/test/classes
  103.17 +build.test.results.dir=${build.dir}/test/results
  103.18 +debug.classpath=\
  103.19 +    ${run.classpath}
  103.20 +debug.test.classpath=\
  103.21 +    ${run.test.classpath}
  103.22 +# This directory is removed when the project is cleaned:
  103.23 +dist.dir=dist
  103.24 +dist.jar=${dist.dir}/currency.jar
  103.25 +dist.javadoc.dir=${dist.dir}/javadoc
  103.26 +excludes=
  103.27 +file.reference.junit-4.4.jar=../../libs/junit-4.4.jar
  103.28 +file.reference.src-apifest08=..
  103.29 +includes=**
  103.30 +jar.compress=false
  103.31 +javac.classpath=
  103.32 +# Space-separated list of extra javac options
  103.33 +javac.compilerargs=
  103.34 +javac.deprecation=false
  103.35 +javac.source=1.5
  103.36 +javac.target=1.5
  103.37 +javac.test.classpath=\
  103.38 +    ${javac.classpath}:\
  103.39 +    ${build.classes.dir}:\
  103.40 +    ${file.reference.junit-4.4.jar}
  103.41 +javadoc.additionalparam=
  103.42 +javadoc.author=false
  103.43 +javadoc.encoding=
  103.44 +javadoc.noindex=false
  103.45 +javadoc.nonavbar=false
  103.46 +javadoc.notree=false
  103.47 +javadoc.private=false
  103.48 +javadoc.splitindex=true
  103.49 +javadoc.use=true
  103.50 +javadoc.version=false
  103.51 +javadoc.windowtitle=
  103.52 +jnlp.codebase.type=local
  103.53 +jnlp.codebase.url=file:/home/jarda/src/apifest08/currency/dist
  103.54 +jnlp.descriptor=application
  103.55 +jnlp.enabled=false
  103.56 +jnlp.offline-allowed=false
  103.57 +jnlp.signed=false
  103.58 +meta.inf.dir=${src.dir}/META-INF
  103.59 +platform.active=default_platform
  103.60 +run.classpath=\
  103.61 +    ${javac.classpath}:\
  103.62 +    ${build.classes.dir}
  103.63 +# Space-separated list of JVM arguments used when running the project
  103.64 +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
  103.65 +# or test-sys-prop.name=value to set system properties for unit tests):
  103.66 +run.jvmargs=
  103.67 +run.test.classpath=\
  103.68 +    ${javac.test.classpath}:\
  103.69 +    ${build.test.classes.dir}
  103.70 +src.dir=src
  103.71 +test.src.dir=test
   104.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   104.2 +++ b/task4/solution14/nbproject/project.xml	Sat Oct 18 07:47:34 2008 +0200
   104.3 @@ -0,0 +1,16 @@
   104.4 +<?xml version="1.0" encoding="UTF-8"?>
   104.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
   104.6 +    <type>org.netbeans.modules.java.j2seproject</type>
   104.7 +    <configuration>
   104.8 +        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
   104.9 +            <name>Currency Convertor Solution 14</name>
  104.10 +            <minimum-ant-version>1.6.5</minimum-ant-version>
  104.11 +            <source-roots>
  104.12 +                <root id="src.dir"/>
  104.13 +            </source-roots>
  104.14 +            <test-roots>
  104.15 +                <root id="test.src.dir"/>
  104.16 +            </test-roots>
  104.17 +        </data>
  104.18 +    </configuration>
  104.19 +</project>
   105.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   105.2 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/Convertor.java	Sat Oct 18 07:47:34 2008 +0200
   105.3 @@ -0,0 +1,286 @@
   105.4 +package org.apidesign.apifest08.currency;
   105.5 +
   105.6 +import java.util.ArrayList;
   105.7 +import java.util.Collection;
   105.8 +import java.util.Collections;
   105.9 +import java.util.Date;
  105.10 +import java.util.HashSet;
  105.11 +import java.util.List;
  105.12 +import java.util.Set;
  105.13 +
  105.14 +/** This is the skeleton class for your API. You need to make it public, so
  105.15 + * it is accessible to your client code (currently in Task1Test.java) file.
  105.16 + * <p>
  105.17 + * Feel free to create additional classes or rename this one, just keep all
  105.18 + * the API and its implementation in this package. Do not spread it outside
  105.19 + * to other packages.
  105.20 + */
  105.21 +public final class Convertor {
  105.22 +    //version 1 fields
  105.23 +
  105.24 +    private String currency1 = null;
  105.25 +    private String currency2 = null;
  105.26 +    private Rate rate = null;
  105.27 +
  105.28 +    //version 2 field
  105.29 +    private List<TimeLimitedCurrencyRate> currencyRates = null;
  105.30 +
  105.31 +    //version - for compatible mode
  105.32 +    private int instanceVersion = 0; //compatible mode because of problem with empty currency and CZE -> CZE (1:2) rate
  105.33 +
  105.34 +    Convertor(String currency1, String currency2, Rate rate) {
  105.35 +        instanceVersion = 1;
  105.36 +        if ((currency1 == null) || (currency2 == null) || (rate == null)) {
  105.37 +            throw new IllegalArgumentException("All arguments have to be non-null.");
  105.38 +        }
  105.39 +        this.currency1 = currency1;
  105.40 +        this.currency2 = currency2;
  105.41 +        this.rate = rate;
  105.42 +    }
  105.43 +
  105.44 +    Convertor(final CurrencyRate ... currencyRate) {
  105.45 +        instanceVersion = 2;
  105.46 +
  105.47 +        if (currencyRate == null) {
  105.48 +            throw new IllegalArgumentException("Parameter cannot be null.");
  105.49 +        }
  105.50 +        if (currencyRate.length == 0) {
  105.51 +            throw new IllegalArgumentException("CurrencyRates cannot be empty.");
  105.52 +        }
  105.53 +        Set<Pair<String,String>> currencies = new HashSet<Pair<String,String>>();
  105.54 +        List<TimeLimitedCurrencyRate> curRates = new ArrayList<TimeLimitedCurrencyRate>();
  105.55 +        for (int i = 0; i < currencyRate.length; i++) {
  105.56 +            final CurrencyRate curRat = currencyRate[i];
  105.57 +            if (curRat == null) {
  105.58 +                throw new IllegalArgumentException("Parameter cannot be null.");
  105.59 +            }
  105.60 +            //check that currencyRate is not defined twice
  105.61 +            Pair<String, String> curPair= new Pair<String, String>(curRat.getCurrency1(), curRat.getCurrency2());
  105.62 +            if (currencies.contains(curPair)) {
  105.63 +                throw new IllegalArgumentException("Pair of currencies in a currency rate cannot be defined twice");
  105.64 +            }
  105.65 +            currencies.add(curPair);
  105.66 +
  105.67 +            if (curRat instanceof TimeLimitedCurrencyRate) {
  105.68 +                curRates.add((TimeLimitedCurrencyRate)curRat);
  105.69 +            } else {
  105.70 +                curRates.add(new TimeLimitedCurrencyRate() { //create delegate which implements TimeLimitedCurrencyRate
  105.71 +                    public long getFromTime() {
  105.72 +                        return Long.MIN_VALUE;
  105.73 +                    }
  105.74 +
  105.75 +                    public long getToTime() {
  105.76 +                        return Long.MAX_VALUE;
  105.77 +                    }
  105.78 +
  105.79 +                    public String getCurrency1() {
  105.80 +                        return curRat.getCurrency1();
  105.81 +                    }
  105.82 +
  105.83 +                    public String getCurrency2() {
  105.84 +                        return curRat.getCurrency2();
  105.85 +                    }
  105.86 +
  105.87 +                    public Rate getRate() {
  105.88 +                        return curRat.getRate();
  105.89 +                    }
  105.90 +                });
  105.91 +            }
  105.92 +        }
  105.93 +        this.currencyRates = Collections.unmodifiableList(curRates);
  105.94 +    }
  105.95 +
  105.96 +    Convertor(final int fakeParameter, final TimeLimitedCurrencyRate ... currencyRate) { //use fake parameter just to specify which constructor should be invoked
  105.97 +        instanceVersion = 3;
  105.98 +
  105.99 +        if (currencyRate == null) {
 105.100 +            throw new IllegalArgumentException("Parameter cannot be null.");
 105.101 +        }
 105.102 +        List<TimeLimitedCurrencyRate> curRates = new ArrayList<TimeLimitedCurrencyRate>();
 105.103 +        for (int i = 0; i < currencyRate.length; i++) {
 105.104 +            final TimeLimitedCurrencyRate curRat = currencyRate[i];
 105.105 +            if (curRat == null) {
 105.106 +                throw new IllegalArgumentException("Parameter cannot be null.");
 105.107 +            }
 105.108 +
 105.109 +            curRates.add(curRat);
 105.110 +        }
 105.111 +        this.currencyRates = Collections.unmodifiableList(curRates);
 105.112 +    }
 105.113 +
 105.114 +    public double convert(String fromCurrency, String toCurrency, int amount) {
 105.115 +        return convert(fromCurrency, toCurrency, amount, System.currentTimeMillis());
 105.116 +    }
 105.117 +
 105.118 +    public double convert(String fromCurrency, String toCurrency, int amount, Date date) {
 105.119 +        if (date == null) {
 105.120 +            throw new IllegalArgumentException("Date cannot be null");
 105.121 +        }
 105.122 +        return convert(fromCurrency, toCurrency, amount, date.getTime());
 105.123 +    }
 105.124 +
 105.125 +    public double convert(String fromCurrency, String toCurrency, int amount, long time) {
 105.126 +        if (instanceVersion == 1) {
 105.127 +            if ((fromCurrency == null) || (toCurrency == null)) {
 105.128 +                throw new IllegalArgumentException("All arguments have to be non-null.");
 105.129 +            }
 105.130 +
 105.131 +            if (currency1.equals(fromCurrency) && currency2.equals(toCurrency)) {
 105.132 +                return rate.convertAtoB(amount);
 105.133 +            } else if (currency2.equals(fromCurrency) && currency1.equals(toCurrency)) {
 105.134 +                return rate.convertBtoA(amount);
 105.135 +            } else {
 105.136 +                throw new IllegalArgumentException("Convertor " + this.toString() +
 105.137 +                        " cannot work with currencies " + fromCurrency + " and " + toCurrency + ".");
 105.138 +            }
 105.139 +        } else { //instanceVersion >= 2
 105.140 +            //find suitable convertor
 105.141 +           for (TimeLimitedCurrencyRate curRate : currencyRates) {
 105.142 +                if ((curRate.getCurrency1().equals(fromCurrency))&&
 105.143 +                        (curRate.getCurrency2().equals(toCurrency))&&
 105.144 +                        (curRate.getFromTime() <= time) &&
 105.145 +                        (curRate.getToTime() >= time))
 105.146 +                {
 105.147 +                    return curRate.getRate().convertAtoB(amount);
 105.148 +                }
 105.149 +            }
 105.150 +            //suitable convertor not found, try to find inverse convertor
 105.151 +            for (TimeLimitedCurrencyRate curRate : currencyRates) {
 105.152 +                if ((curRate.getCurrency2().equals(fromCurrency))&&
 105.153 +                        (curRate.getCurrency1().equals(toCurrency))&&
 105.154 +                        (curRate.getFromTime() <= time) &&
 105.155 +                        (curRate.getToTime() >= time))
 105.156 +                {
 105.157 +                    return curRate.getRate().convertBtoA(amount);
 105.158 +                }
 105.159 +            }
 105.160 +            //even inverse convertor not found
 105.161 +            throw new IllegalArgumentException("Cannot work with selected currencies.");
 105.162 +        }
 105.163 +    }
 105.164 +
 105.165 +    public double convert(String fromCurrency, String toCurrency, double amount) {
 105.166 +        return convert(fromCurrency, toCurrency, amount, System.currentTimeMillis());
 105.167 +    }
 105.168 +
 105.169 +    public double convert(String fromCurrency, String toCurrency, double amount, Date date) {
 105.170 +        if (date == null) {
 105.171 +            throw new IllegalArgumentException("Date cannot be null");
 105.172 +        }
 105.173 +        return convert(fromCurrency, toCurrency, amount, date.getTime());
 105.174 +    }
 105.175 +
 105.176 +    public double convert(String fromCurrency, String toCurrency, double amount, long time) {
 105.177 +        if (instanceVersion == 1) {
 105.178 +            if ((fromCurrency == null) || (toCurrency == null)) {
 105.179 +                throw new IllegalArgumentException("All arguments have to be non-null.");
 105.180 +            }
 105.181 +
 105.182 +            if (currency1.equals(fromCurrency) && currency2.equals(toCurrency)) {
 105.183 +                return rate.convertAtoB(amount);
 105.184 +            } else if (currency2.equals(fromCurrency) && currency1.equals(toCurrency)) {
 105.185 +                return rate.convertBtoA(amount);
 105.186 +            } else {
 105.187 +                throw new IllegalArgumentException("Convertor " + this.toString() +
 105.188 +                        " cannot work with currencies " + fromCurrency + " and " + toCurrency + ".");
 105.189 +            }
 105.190 +        } else { //instanceVersion >= 2
 105.191 +            //find suitable convertor
 105.192 +            for (TimeLimitedCurrencyRate curRate : currencyRates) {
 105.193 +                if ((curRate.getCurrency1().equals(fromCurrency))&&
 105.194 +                        (curRate.getCurrency2().equals(toCurrency))&&
 105.195 +                        (curRate.getFromTime() <= time) &&
 105.196 +                        (curRate.getToTime() >= time))
 105.197 +                {
 105.198 +                    return curRate.getRate().convertAtoB(amount);
 105.199 +                }
 105.200 +            }
 105.201 +            //suitable convertor not found, try to find inverse convertor
 105.202 +            for (TimeLimitedCurrencyRate curRate : currencyRates) {
 105.203 +                if ((curRate.getCurrency2().equals(fromCurrency))&&
 105.204 +                        (curRate.getCurrency1().equals(toCurrency))&&
 105.205 +                        (curRate.getFromTime() <= time) &&
 105.206 +                        (curRate.getToTime() >= time))
 105.207 +                {
 105.208 +                    return curRate.getRate().convertBtoA(amount);
 105.209 +                }
 105.210 +            }
 105.211 +            //even inverse convertor not found
 105.212 +            throw new IllegalArgumentException("Cannot work with selected currencies.");
 105.213 +        }
 105.214 +    }
 105.215 +
 105.216 +    /**
 105.217 +     * Returns currency rates. If instantiated with constructor from vesion 1
 105.218 +     * it creates new collection with one CurrencyRate.
 105.219 +     * Note, it can cause exception because of empty currencies or same currencies.
 105.220 +     */
 105.221 +    public Collection<CurrencyRate> getCurrencyRates() {
 105.222 +        if (instanceVersion == 1) {
 105.223 +            List<CurrencyRate> ret = new ArrayList<CurrencyRate>();
 105.224 +            ret.add(new CurrencyRateImpl(currency1, currency2, rate)); //here it checks that currency rate is not nonsense
 105.225 +            return Collections.unmodifiableCollection(ret);
 105.226 +        } else { //instanceVersion >= 2
 105.227 +            List<CurrencyRate> ret = new ArrayList<CurrencyRate>(currencyRates);
 105.228 +            return Collections.unmodifiableCollection(ret);
 105.229 +        }
 105.230 +    }
 105.231 +
 105.232 +    public Collection<TimeLimitedCurrencyRate> getTimeLimitedCurrencyRates() {
 105.233 +        if (instanceVersion == 1) {
 105.234 +            List<TimeLimitedCurrencyRate> ret = new ArrayList<TimeLimitedCurrencyRate>();
 105.235 +            ret.add(new CurrencyRateImpl(currency1, currency2, rate)); //here it checks that currency rate is not nonsense
 105.236 +            return Collections.unmodifiableCollection(ret);
 105.237 +        } else { //instanceVersion >= 2
 105.238 +            List<TimeLimitedCurrencyRate> ret = new ArrayList<TimeLimitedCurrencyRate>(currencyRates);
 105.239 +            return Collections.unmodifiableCollection(ret);
 105.240 +        }
 105.241 +    }
 105.242 +
 105.243 +    @Override
 105.244 +    public String toString() {
 105.245 +        if (instanceVersion == 1) {
 105.246 +            return currency1 + currency2;
 105.247 +        } else { //instanceVersion == 2
 105.248 +            return super.toString(); //better be compatible in future :-)
 105.249 +        }
 105.250 +    }
 105.251 +
 105.252 +    @Override
 105.253 +    public boolean equals(Object obj) {
 105.254 +        if (instanceVersion == 1) {
 105.255 +            if (obj == null) {
 105.256 +                return false;
 105.257 +            }
 105.258 +            if (getClass() != obj.getClass()) {
 105.259 +                return false;
 105.260 +            }
 105.261 +            final Convertor other = (Convertor) obj;
 105.262 +            if (this.currency1 != other.currency1 && (this.currency1 == null || !this.currency1.equals(other.currency1))) {
 105.263 +                return false;
 105.264 +            }
 105.265 +            if (this.currency2 != other.currency2 && (this.currency2 == null || !this.currency2.equals(other.currency2))) {
 105.266 +                return false;
 105.267 +            }
 105.268 +            if (this.rate != other.rate && (this.rate == null || !this.rate.equals(other.rate))) {
 105.269 +                return false;
 105.270 +            }
 105.271 +            return true;
 105.272 +        } else { //instanceVersion == 2
 105.273 +            return super.equals(obj); //better be compatible in future :-)
 105.274 +        }
 105.275 +    }
 105.276 +
 105.277 +    @Override
 105.278 +    public int hashCode() {
 105.279 +        if (instanceVersion == 1) {
 105.280 +            int hash = 5;
 105.281 +            hash = 67 * hash + (this.currency1 != null ? this.currency1.hashCode() : 0);
 105.282 +            hash = 67 * hash + (this.currency2 != null ? this.currency2.hashCode() : 0);
 105.283 +            hash = 67 * hash + (this.rate != null ? this.rate.hashCode() : 0);
 105.284 +        return hash;
 105.285 +        } else { //instanceVersion == 2
 105.286 +            return super.hashCode(); //better be compatible in future :-)
 105.287 +        }
 105.288 +    }
 105.289 +}
   106.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   106.2 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/ConvertorFactory.java	Sat Oct 18 07:47:34 2008 +0200
   106.3 @@ -0,0 +1,118 @@
   106.4 +
   106.5 +package org.apidesign.apifest08.currency;
   106.6 +
   106.7 +import java.util.ArrayList;
   106.8 +import java.util.Collection;
   106.9 +import java.util.Date;
  106.10 +import java.util.List;
  106.11 +
  106.12 +public final class ConvertorFactory {
  106.13 +
  106.14 +    //Singleton
  106.15 +    private static ConvertorFactory thisFactory = new ConvertorFactory();    
  106.16 +    private ConvertorFactory() {};    
  106.17 +    public static ConvertorFactory newInstance() { //ehm, mistake - it should be named getInstance
  106.18 +        return thisFactory;
  106.19 +    }        
  106.20 +    
  106.21 +    public Convertor createConvertor(String currency1, String currency2, Rate rate) {
  106.22 +        return new Convertor(currency1, currency2, rate);
  106.23 +    }
  106.24 +    
  106.25 +    public Convertor createConvertor(String currency1, String currency2, int amount1, int amount2) {
  106.26 +        return new Convertor(currency1, currency2, new Rate(amount1, amount2));
  106.27 +    }
  106.28 +
  106.29 +    public Convertor createConvertor(String currency1, String currency2, double amount1, double amount2) {
  106.30 +        return new Convertor(currency1, currency2, new Rate(amount1, amount2));
  106.31 +    }
  106.32 +    
  106.33 +    public Convertor createConvertor(String currency1, String currency2, double rate) {
  106.34 +        return new Convertor(currency1, currency2, new Rate(rate));
  106.35 +    }
  106.36 +
  106.37 +    public Convertor createConvertor(CurrencyRate currencyRate) {
  106.38 +        return new Convertor(currencyRate);
  106.39 +    }
  106.40 +
  106.41 +    public Convertor createConvertor(CurrencyRate ... currencyRates) {
  106.42 +        return new Convertor(currencyRates);
  106.43 +    }
  106.44 +
  106.45 +    public Convertor mergeConvertors(Convertor ... convertors) {
  106.46 +        if (convertors == null) {
  106.47 +            throw new IllegalArgumentException("Parameter cannot be null.");
  106.48 +        }
  106.49 +        if (convertors.length == 0) {
  106.50 +            throw new IllegalArgumentException("Convertors cannot be empty.");
  106.51 +        }
  106.52 +        List<CurrencyRate> currRates = new ArrayList<CurrencyRate>();
  106.53 +        List<Pair<String,String>> currPairs = new ArrayList<Pair<String,String>>();
  106.54 +        for (Convertor convertor : convertors) {
  106.55 +            if (convertor == null) {
  106.56 +                throw new IllegalArgumentException("Parameter cannot be null.");                
  106.57 +            }
  106.58 +            for (CurrencyRate currRate : convertor.getCurrencyRates()) {
  106.59 +                Pair<String,String> currPair = new Pair<String,String>(currRate.getCurrency1(), currRate.getCurrency2());
  106.60 +                if (currPairs.contains(currPair)) {
  106.61 +                    throw new IllegalArgumentException("Cannot merge - convertors contain same currency rates.");
  106.62 +                }
  106.63 +                currPairs.add(currPair);
  106.64 +                currRates.add(currRate);
  106.65 +            }
  106.66 +        }
  106.67 +        
  106.68 +        return new Convertor(currRates.toArray(new CurrencyRate[0]));
  106.69 +    }
  106.70 +
  106.71 +    public Convertor mergeConvertorsIgnoreEqualCurrencies(Convertor ... convertors) {
  106.72 +        if (convertors == null) {
  106.73 +            throw new IllegalArgumentException("Parameter cannot be null.");
  106.74 +        }
  106.75 +        if (convertors.length == 0) {
  106.76 +            throw new IllegalArgumentException("Convertors cannot be empty.");
  106.77 +        }
  106.78 +        List<TimeLimitedCurrencyRate> currRates = new ArrayList<TimeLimitedCurrencyRate>();
  106.79 +        for (Convertor convertor : convertors) {
  106.80 +            if (convertor == null) {
  106.81 +                throw new IllegalArgumentException("Parameter cannot be null.");
  106.82 +            }
  106.83 +            currRates.addAll(convertor.getTimeLimitedCurrencyRates());
  106.84 +        }
  106.85 +
  106.86 +        return new Convertor(4, currRates.toArray(new TimeLimitedCurrencyRate[0]));
  106.87 +    }
  106.88 +
  106.89 +    public Convertor limitConvertor(Convertor convertor, Date fromDate, Date toDate) {
  106.90 +        if ((convertor == null)||(fromDate == null)||(toDate == null)) {
  106.91 +            throw new IllegalArgumentException("Parameter cannot be null");
  106.92 +        }
  106.93 +        Collection<TimeLimitedCurrencyRate> timeLimitedCurrencyRates = convertor.getTimeLimitedCurrencyRates();
  106.94 +        List<TimeLimitedCurrencyRate> filteredRates = new ArrayList<TimeLimitedCurrencyRate>();
  106.95 +        for (final TimeLimitedCurrencyRate timeLimitedCurrencyRate : timeLimitedCurrencyRates) {
  106.96 +            final long newFrom = java.lang.Math.max(fromDate.getTime(), timeLimitedCurrencyRate.getFromTime());
  106.97 +            final long newTo = java.lang.Math.min(toDate.getTime(), timeLimitedCurrencyRate.getToTime());
  106.98 +            if (newTo >= newFrom) {
  106.99 +                filteredRates.add(new TimeLimitedCurrencyRate() { //create delegate
 106.100 +                    public long getFromTime() {
 106.101 +                        return newFrom;
 106.102 +                    }
 106.103 +                    public long getToTime() {
 106.104 +                        return newTo;
 106.105 +                    }
 106.106 +                    public String getCurrency1() {
 106.107 +                        return timeLimitedCurrencyRate.getCurrency1();
 106.108 +                    }
 106.109 +                    public String getCurrency2() {
 106.110 +                        return timeLimitedCurrencyRate.getCurrency2();
 106.111 +                    }
 106.112 +                    public Rate getRate() {
 106.113 +                        return timeLimitedCurrencyRate.getRate();
 106.114 +                    }
 106.115 +                });
 106.116 +            }
 106.117 +        }
 106.118 +        return new Convertor(4, filteredRates.toArray(new TimeLimitedCurrencyRate[0]));
 106.119 +    }
 106.120 +
 106.121 +}
   107.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   107.2 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/CurrencyRate.java	Sat Oct 18 07:47:34 2008 +0200
   107.3 @@ -0,0 +1,11 @@
   107.4 +package org.apidesign.apifest08.currency;
   107.5 +
   107.6 +/**
   107.7 + * This is interface for creating currency rates. The rate can be static or can change in time.
   107.8 + * Implement this interface to inform the Convertor about the actual exchange rate between two currencies.
   107.9 + */
  107.10 +public interface CurrencyRate {
  107.11 +    public String getCurrency1();
  107.12 +    public String getCurrency2();
  107.13 +    public Rate getRate();
  107.14 +}
   108.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   108.2 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/CurrencyRateFactory.java	Sat Oct 18 07:47:34 2008 +0200
   108.3 @@ -0,0 +1,37 @@
   108.4 +package org.apidesign.apifest08.currency;
   108.5 +
   108.6 +
   108.7 +public final class CurrencyRateFactory {
   108.8 +
   108.9 +    //Singleton
  108.10 +    private static CurrencyRateFactory thisFactory = new CurrencyRateFactory();
  108.11 +    private CurrencyRateFactory() {};
  108.12 +    public static CurrencyRateFactory getInstance() {
  108.13 +        return thisFactory;
  108.14 +    }
  108.15 +
  108.16 +    public CurrencyRate createCurrencyRate(final String currency1, final String currency2, final Rate rate) {
  108.17 +        return new CurrencyRateImpl(currency1, currency2, rate);
  108.18 +    }
  108.19 +    
  108.20 +    public CurrencyRate createCurrencyRate(final String currency1, final String currency2, int amount1, int amount2) {
  108.21 +        return new CurrencyRateImpl(currency1, currency2, new Rate(amount1, amount2));
  108.22 +    }
  108.23 +
  108.24 +    public CurrencyRate createCurrencyRate(final String currency1, final String currency2, double amount1, double amount2) {
  108.25 +        return new CurrencyRateImpl(currency1, currency2, new Rate(amount1, amount2));
  108.26 +    }
  108.27 +
  108.28 +    public TimeLimitedCurrencyRate createCurrencyRateTimeLimited(final String currency1, final String currency2, final Rate rate, long fromTime, long toTime) {
  108.29 +        return new CurrencyRateImpl(currency1, currency2, rate, fromTime, toTime);
  108.30 +    }
  108.31 +
  108.32 +    public TimeLimitedCurrencyRate createCurrencyRateTimeLimited(final String currency1, final String currency2, int amount1, int amount2, long fromTime, long toTime) {
  108.33 +        return new CurrencyRateImpl(currency1, currency2, new Rate(amount1, amount2), fromTime, toTime);
  108.34 +    }
  108.35 +
  108.36 +    public TimeLimitedCurrencyRate createCurrencyRateTimeLimited(final String currency1, final String currency2, double amount1, double amount2, long fromTime, long toTime) {
  108.37 +        return new CurrencyRateImpl(currency1, currency2, new Rate(amount1, amount2), fromTime, toTime);
  108.38 +    }
  108.39 +    
  108.40 +}
   109.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   109.2 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/CurrencyRateImpl.java	Sat Oct 18 07:47:34 2008 +0200
   109.3 @@ -0,0 +1,70 @@
   109.4 +
   109.5 +package org.apidesign.apifest08.currency;
   109.6 +
   109.7 +public final class CurrencyRateImpl implements CurrencyRate, TimeLimitedCurrencyRate {
   109.8 +    private String currency1;
   109.9 +    private String currency2;
  109.10 +    private Rate rate;
  109.11 +    private long fromTime;
  109.12 +    private long toTime;
  109.13 +    
  109.14 +    CurrencyRateImpl(final String currency1, final String currency2, final Rate rate) {
  109.15 +        if ((currency1 == null)||(currency2 == null) || (rate == null)) {
  109.16 +            throw new IllegalArgumentException("Argument cannot be null.");
  109.17 +        }
  109.18 +        if ("".equals(currency1) || "".equals(currency2)) {
  109.19 +            throw new IllegalArgumentException("Name of currency cannot be empty string");
  109.20 +        }
  109.21 +        if (currency1.equals(currency2)) {
  109.22 +            throw new IllegalArgumentException("Currencies in rate cannot be the same");
  109.23 +        }
  109.24 +       
  109.25 +        this.currency1 = currency1;
  109.26 +        this.currency2 = currency2;
  109.27 +        this.rate = rate;
  109.28 +        this.fromTime = Long.MIN_VALUE;
  109.29 +        this.toTime = Long.MAX_VALUE;
  109.30 +    }
  109.31 +
  109.32 +    CurrencyRateImpl(final String currency1, final String currency2, final Rate rate, final long fromTime, final long toTime) {
  109.33 +        if ((currency1 == null)||(currency2 == null) || (rate == null)) {
  109.34 +            throw new IllegalArgumentException("Argument cannot be null.");
  109.35 +        }
  109.36 +        if ("".equals(currency1) || "".equals(currency2)) {
  109.37 +            throw new IllegalArgumentException("Name of currency cannot be empty string");
  109.38 +        }
  109.39 +        if (currency1.equals(currency2)) {
  109.40 +            throw new IllegalArgumentException("Currencies in rate cannot be the same");
  109.41 +        }
  109.42 +        if (fromTime > toTime) {
  109.43 +            throw new IllegalArgumentException("Invalid time range");
  109.44 +        }
  109.45 +
  109.46 +        this.currency1 = currency1;
  109.47 +        this.currency2 = currency2;
  109.48 +        this.rate = rate;
  109.49 +        this.fromTime = fromTime;
  109.50 +        this.toTime = toTime;
  109.51 +    }
  109.52 +
  109.53 +    public String getCurrency1() {
  109.54 +        return currency1;
  109.55 +    }
  109.56 +            
  109.57 +    public String getCurrency2() {
  109.58 +        return currency2;
  109.59 +    }
  109.60 +
  109.61 +    public Rate getRate(){
  109.62 +        return rate;
  109.63 +    }
  109.64 +
  109.65 +    public long getFromTime() {
  109.66 +        return fromTime;
  109.67 +    }
  109.68 +
  109.69 +    public long getToTime() {
  109.70 +        return toTime;
  109.71 +    }
  109.72 +
  109.73 +}
   110.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   110.2 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/Pair.java	Sat Oct 18 07:47:34 2008 +0200
   110.3 @@ -0,0 +1,53 @@
   110.4 +
   110.5 +package org.apidesign.apifest08.currency;
   110.6 +
   110.7 +
   110.8 +public final class Pair<A,B> {
   110.9 +
  110.10 +    private final A first;
  110.11 +    private final B second;
  110.12 +
  110.13 +    public Pair(A first, B second) {
  110.14 +	this.first = first;
  110.15 +	this.second = second;
  110.16 +    }
  110.17 +
  110.18 +    public A getFirst() { return first; }
  110.19 +    public B getSecond() { return second; }
  110.20 +
  110.21 +    @Override
  110.22 +    public String toString() {
  110.23 +        return "(" + first + ", " + second + ")";
  110.24 +    }
  110.25 +
  110.26 +    private static boolean equals(Object x, Object y) {
  110.27 +	return (x == null && y == null) || (x != null && x.equals(y));
  110.28 +    }
  110.29 +
  110.30 +    @Override
  110.31 +    public int hashCode() {
  110.32 +        int hash = 5;
  110.33 +        hash = 59 * hash + (this.first != null ? this.first.hashCode() : 0);
  110.34 +        hash = 59 * hash + (this.second != null ? this.second.hashCode() : 0);
  110.35 +        return hash;
  110.36 +    }
  110.37 +
  110.38 +    @Override
  110.39 +    public boolean equals(Object obj) {
  110.40 +        if (obj == null) {
  110.41 +            return false;
  110.42 +        }
  110.43 +        if (getClass() != obj.getClass()) {
  110.44 +            return false;
  110.45 +        }
  110.46 +        final Pair<A, B> other = (Pair<A, B>) obj;
  110.47 +        if (this.first != other.first && (this.first == null || !this.first.equals(other.first))) {
  110.48 +            return false;
  110.49 +        }
  110.50 +        if (this.second != other.second && (this.second == null || !this.second.equals(other.second))) {
  110.51 +            return false;
  110.52 +        }
  110.53 +        return true;
  110.54 +    }
  110.55 +
  110.56 +}
   111.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   111.2 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/Rate.java	Sat Oct 18 07:47:34 2008 +0200
   111.3 @@ -0,0 +1,68 @@
   111.4 +
   111.5 +package org.apidesign.apifest08.currency;
   111.6 +
   111.7 +public final class Rate {
   111.8 +
   111.9 +    private double rate;
  111.10 +
  111.11 +    public Rate(int amountA, int amountB) {
  111.12 +        rate = amountA / (double)amountB;
  111.13 +        if (rate <= 0) {
  111.14 +            throw new IllegalArgumentException("Exchange rate must be positive.");
  111.15 +        }
  111.16 +    }
  111.17 +
  111.18 +    public Rate(double amountA, double amountB) {
  111.19 +        rate = amountA / amountB;
  111.20 +        if (rate <= 0) {
  111.21 +            throw new IllegalArgumentException("Exchange rate must be positive.");
  111.22 +        }
  111.23 +    }
  111.24 +    
  111.25 +    public Rate(double rate) {
  111.26 +        this.rate = rate;
  111.27 +        if (this.rate <= 0) {
  111.28 +            throw new IllegalArgumentException("Exchange rate must be positive.");
  111.29 +        }
  111.30 +    }
  111.31 +
  111.32 +    public double convertAtoB(int a) {
  111.33 +        return a / rate;
  111.34 +    }
  111.35 +
  111.36 +    public double convertAtoB(double a) {
  111.37 +        return a / rate;
  111.38 +    }
  111.39 +
  111.40 +    public double convertBtoA(int b) {
  111.41 +        return b * rate;
  111.42 +    }
  111.43 +
  111.44 +    public double convertBtoA(double b) {
  111.45 +        return b * rate;
  111.46 +    }
  111.47 +
  111.48 +
  111.49 +    @Override
  111.50 +    public boolean equals(Object obj) {
  111.51 +        if (obj == null) {
  111.52 +            return false;
  111.53 +        }
  111.54 +        if (getClass() != obj.getClass()) {
  111.55 +            return false;
  111.56 +        }
  111.57 +        final Rate other = (Rate) obj;
  111.58 +        return true;
  111.59 +    }
  111.60 +
  111.61 +    @Override
  111.62 +    public int hashCode() {
  111.63 +        int hash = 5;
  111.64 +        return hash;
  111.65 +    }
  111.66 +
  111.67 +    @Override
  111.68 +    public String toString() {
  111.69 +        return ""+rate;
  111.70 +    }
  111.71 +}
   112.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   112.2 +++ b/task4/solution14/src/org/apidesign/apifest08/currency/TimeLimitedCurrencyRate.java	Sat Oct 18 07:47:34 2008 +0200
   112.3 @@ -0,0 +1,9 @@
   112.4 +package org.apidesign.apifest08.currency;
   112.5 +
   112.6 +
   112.7 +public interface TimeLimitedCurrencyRate extends CurrencyRate {
   112.8 +
   112.9 +    public long getFromTime();
  112.10 +    public long getToTime();
  112.11 +
  112.12 +}
   113.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   113.2 +++ b/task4/solution14/test/org/apidesign/apifest08/test/Task1Test.java	Sat Oct 18 07:47:34 2008 +0200
   113.3 @@ -0,0 +1,144 @@
   113.4 +package org.apidesign.apifest08.test;
   113.5 +
   113.6 +import junit.framework.TestCase;
   113.7 +import org.apidesign.apifest08.currency.Convertor;
   113.8 +import org.apidesign.apifest08.currency.ConvertorFactory;
   113.9 +
  113.10 +/** Finish the Convertor API, and then write bodies of methods inside
  113.11 + * of this class to match the given tasks. To fullfil your task, use the
  113.12 + * API define in the <code>org.apidesign.apifest08.currency</code> package.
  113.13 + * Do not you reflection, or other hacks as your code
  113.14 + * shall run without any runtime permissions.
  113.15 + */
  113.16 +public class Task1Test extends TestCase {
  113.17 +    public Task1Test(String testName) {
  113.18 +        super(testName);
  113.19 +    }
  113.20 +
  113.21 +    @Override
  113.22 +    protected void setUp() throws Exception {
  113.23 +    }
  113.24 +
  113.25 +    @Override
  113.26 +    protected void tearDown() throws Exception {
  113.27 +    }
  113.28 +
  113.29 +    //
  113.30 +    // Imagine that there are three parts of the whole system:
  113.31 +    // 1. there is someone who knows the current exchange rate
  113.32 +    // 2. there is someone who wants to do the conversion
  113.33 +    // 3. there is the API between 1. and 2. which allows them to communicate
  113.34 +    // Please design such API
  113.35 +    //
  113.36 +
  113.37 +    /** Create convertor that understands two currencies, CZK and
  113.38 +     *  USD. Make 1 USD == 17 CZK. This is a method provided for #1 group -
  113.39 +     *  e.g. those that know the exchange rate. They somehow need to create
  113.40 +     *  the objects from the API and tell them the exchange rate. The API itself
  113.41 +     *  knows nothing about any rates, before the createCZKtoUSD method is called.
  113.42 +     *
  113.43 +     * Creation of the convertor shall not require subclassing of any class
  113.44 +     * or interface on the client side.
  113.45 +     *
  113.46 +     * @return prepared convertor ready for converting USD to CZK and CZK to USD
  113.47 +     */
  113.48 +    public static Convertor createCZKtoUSD() {
  113.49 +        return ConvertorFactory.newInstance().createConvertor("CZK", "USD", 17, 1);
  113.50 +    }
  113.51 +
  113.52 +    /** Create convertor that understands two currencies, CZK and
  113.53 +     *  SKK. Make 100 SKK == 80 CZK. Again this is method for the #1 group -
  113.54 +     *  it knows the exchange rate, and needs to use the API to create objects
  113.55 +     *  with the exchange rate. Anyone shall be ready to call this method without
  113.56 +     *  any other method being called previously. The API itself shall know
  113.57 +     *  nothing about any rates, before this method is called.
  113.58 +     *
  113.59 +     * Creation of the convertor shall not require subclassing of any class
  113.60 +     * or interface on the client side.
  113.61 +     * 
  113.62 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
  113.63 +     */
  113.64 +    public static Convertor createSKKtoCZK() {
  113.65 +        return ConvertorFactory.newInstance().createConvertor("SKK", "CZK", 100, 80);
  113.66 +    }
  113.67 +
  113.68 +    //
  113.69 +    // now the methods for group #2 follow:
  113.70 +    // this group knows nothing about exchange rates, but knows how to use
  113.71 +    // the API to do conversions. It somehow (by calling one of the factory
  113.72 +    // methods) gets objects from the API and uses them to do the conversions.
  113.73 +    //
  113.74 +    
  113.75 +    /** Use the convertor from <code>createCZKtoUSD</code> method and do few conversions
  113.76 +     * with it.
  113.77 +     */
  113.78 +    public void testCurrencyCZKUSD() throws Exception {
  113.79 +        Convertor c = createCZKtoUSD();        
  113.80 +        // convert $5 to CZK using c:        
  113.81 +        assertEquals("Result is 85 CZK", 85.0, c.convert("USD", "CZK", 5));
  113.82 +
  113.83 +        // convert $8 to CZK
  113.84 +        assertEquals("Result is 136 CZK", 136.0, c.convert("USD", "CZK", 8));
  113.85 +
  113.86 +        // convert 1003CZK to USD
  113.87 +        assertEquals("Result is 59 CZK", 59.0, c.convert("CZK", "USD", 1003));
  113.88 +    }
  113.89 +
  113.90 +    /** Use the convertor from <code>createSKKtoCZK</code> method and do few conversions
  113.91 +     * with it.
  113.92 +     */
  113.93 +    public void testCurrencySKKCZK() throws Exception {
  113.94 +        Convertor c = createSKKtoCZK();
  113.95 +        // convert 16CZK using c:
  113.96 +        // assertEquals("Result is 20 SKK");
  113.97 +        assertEquals("Result is 20 SKK", 20.0, c.convert("CZK", "SKK", 16));
  113.98 +
  113.99 +        // convert 500SKK to CZK
 113.100 +        // assertEquals("Result is 400 CZK");
 113.101 +        assertEquals("Result is 400 SKK", 400.0, c.convert("SKK", "CZK", 500));
 113.102 +    }
 113.103 +
 113.104 +    /** Verify that the CZK to USD convertor knows nothing about SKK.
 113.105 +     */
 113.106 +    public void testCannotConvertToSKKwithCZKUSDConvertor() throws Exception {
 113.107 +        Convertor c = createCZKtoUSD();
 113.108 +        // convert $5 to SKK, the API shall say this is not possible
 113.109 +        try {
 113.110 +            c.convert("USD", "SKK", 5);
 113.111 +            fail("Converting SKK with CZKUSD convertor is impossible");
 113.112 +        } catch (IllegalArgumentException e){
 113.113 +            //ok
 113.114 +        }
 113.115 +        
 113.116 +        // convert 500 SKK to CZK, the API shall say this is not possible
 113.117 +        try {
 113.118 +            c.convert("SKK", "CZK", 500);
 113.119 +            fail("Converting SKK with CZKUSD convertor is impossible");
 113.120 +        } catch (IllegalArgumentException e){
 113.121 +            //ok
 113.122 +        }
 113.123 +        
 113.124 +    }
 113.125 +
 113.126 +    /** Verify that the CZK to SKK convertor knows nothing about USD.
 113.127 +     */
 113.128 +    public void testCannotConvertToUSDwithCZKSKKConvertor() throws Exception {
 113.129 +        Convertor c = createSKKtoCZK();
 113.130 +        // convert $5 to SKK, the API shall say this is not possible
 113.131 +        try {
 113.132 +            c.convert("USD", "SKK", 5);
 113.133 +            fail("Converting SKK with SKKCZK convertor is impossible");
 113.134 +        } catch (IllegalArgumentException e){
 113.135 +            //ok
 113.136 +        }
 113.137 +        
 113.138 +        // convert 500 CZK to USD, the API shall say this is not possible
 113.139 +        try {
 113.140 +            c.convert("CZK", "USD", 500);
 113.141 +            fail("Converting USD with SKKCZK convertor is impossible");
 113.142 +        } catch (IllegalArgumentException e){
 113.143 +            //ok
 113.144 +        }
 113.145 +        
 113.146 +    }
 113.147 +}
   114.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   114.2 +++ b/task4/solution14/test/org/apidesign/apifest08/test/Task2Test.java	Sat Oct 18 07:47:34 2008 +0200
   114.3 @@ -0,0 +1,169 @@
   114.4 +package org.apidesign.apifest08.test;
   114.5 +
   114.6 +import junit.framework.TestCase;
   114.7 +import org.apidesign.apifest08.currency.Convertor;
   114.8 +import org.apidesign.apifest08.currency.ConvertorFactory;
   114.9 +import org.apidesign.apifest08.currency.CurrencyRate;
  114.10 +import org.apidesign.apifest08.currency.CurrencyRateFactory;
  114.11 +
  114.12 +/** There are many currencies around the world and many banks manipulate
  114.13 + * with more than one or two at the same time. As banks are usually the
  114.14 + * best paying clients, which is true even in case of your Convertor API,
  114.15 + * it is reasonable to listen to their requests.
  114.16 + * <p>
  114.17 + * The quest for today is to enhance your existing convertor API to hold
  114.18 + * information about many currencies and allow conversions between any of them.
  114.19 + * Also, as conversion rates for diferent currencies usually arise from various
  114.20 + * bank departments, there is another important need. There is a need to
  114.21 + * compose two convertors into one by merging all the information about
  114.22 + * currencies they know about.
  114.23 + */
  114.24 +public class Task2Test extends TestCase {
  114.25 +    public Task2Test(String testName) {
  114.26 +        super(testName);
  114.27 +    }
  114.28 +
  114.29 +    @Override
  114.30 +    protected void setUp() throws Exception {
  114.31 +    }
  114.32 +
  114.33 +    @Override
  114.34 +    protected void tearDown() throws Exception {
  114.35 +    }
  114.36 +
  114.37 +    // As in Task1Test, keep in mind, that there are three parts
  114.38 +    // of the whole system:
  114.39 +    // 1. there is someone who knows the current exchange rate
  114.40 +    // 2. there is someone who wants to do the conversion
  114.41 +    // 3. there is the API between 1. and 2. which allows them to communicate
  114.42 +    // 
  114.43 +    // Please backward compatibly enhance your existing API to support following
  114.44 +    // usecases:
  114.45 +    //
  114.46 +    
  114.47 +    /** Create convertor that understands two currencies, CZK and
  114.48 +     *  SKK. Make 100 SKK == 75 CZK. This is method for the group of users that
  114.49 +     *  knows the exchange rate, and needs to use the API to create objects
  114.50 +     *  with the exchange rate. Anyone shall be ready to call this method without
  114.51 +     *  any other method being called previously. The API itself shall know
  114.52 +     *  nothing about any rates, before this method is called.
  114.53 +     */
  114.54 +    public static Convertor createTripleConvertor() {
  114.55 +        // Rates: 1USD = 15CZK
  114.56 +        // Rates: 1USD = 20SKK
  114.57 +        // Rates: 75CZK = 100SKK
  114.58 +        CurrencyRate usdCzk = CurrencyRateFactory.getInstance().createCurrencyRate("USD", "CZK", 1, 15);
  114.59 +        CurrencyRate usdSkk = CurrencyRateFactory.getInstance().createCurrencyRate("USD", "SKK", 1, 20);
  114.60 +        CurrencyRate czkSkk = CurrencyRateFactory.getInstance().createCurrencyRate("CZK", "SKK", 75, 100);
  114.61 +        return ConvertorFactory.newInstance().createConvertor(usdCzk, usdSkk, czkSkk);        
  114.62 +    }
  114.63 +
  114.64 +    /** Define convertor that understands three currencies. Use it.
  114.65 +     */
  114.66 +    public void testConvertorForUSDandCZKandSKK() throws Exception {
  114.67 +        Convertor c = createTripleConvertor();
  114.68 +
  114.69 +        // convert $5 to CZK using c:
  114.70 +        // assertEquals("Result is 75 CZK");
  114.71 +        assertEquals("Result is 75 CZK", 75.0, c.convert("USD", "CZK", 5));
  114.72 +        
  114.73 +        // convert $5 to SKK using c:
  114.74 +        // assertEquals("Result is 100 SKK");
  114.75 +        assertEquals("Result is 100 SKK", 100.0, c.convert("USD", "SKK", 5));
  114.76 +
  114.77 +        // convert 200SKK to CZK using c:
  114.78 +        // assertEquals("Result is 150 CZK");
  114.79 +        assertEquals("Result is 150 CZK", 150.0, c.convert("SKK", "CZK", 200));
  114.80 +
  114.81 +        // convert 200SKK to USD using c:
  114.82 +        // assertEquals("Result is 10 USD");
  114.83 +        assertEquals("Result is 10 USD", 10.0, c.convert("SKK", "USD", 200));
  114.84 +    }
  114.85 +
  114.86 +    /** Merge all currency rates of convertor 1 with convertor 2.
  114.87 +     * Implement this using your API, preferably this method just delegates
  114.88 +     * into some API method which does the actual work, without requiring
  114.89 +     * API clients to code anything complex.
  114.90 +     */
  114.91 +    public static Convertor merge(Convertor one, Convertor two) {
  114.92 +        return ConvertorFactory.newInstance().mergeConvertorsIgnoreEqualCurrencies(one,two);
  114.93 +    }
  114.94 +
  114.95 +    /** Join the convertors from previous task, Task1Test and show that it
  114.96 +     * can be used to do reasonable conversions.
  114.97 +     */
  114.98 +    public void testConvertorComposition() throws Exception {
  114.99 +        Convertor c = merge(
 114.100 +            Task1Test.createCZKtoUSD(),
 114.101 +            Task1Test.createSKKtoCZK()
 114.102 +        );
 114.103 +
 114.104 +        // convert $5 to CZK using c:
 114.105 +        // assertEquals("Result is 85 CZK");
 114.106 +        assertEquals("Result is 85 CZK", 85.0, c.convert("USD", "CZK", 5));
 114.107 +
 114.108 +        // convert $8 to CZK using c:
 114.109 +        // assertEquals("Result is 136 CZK");
 114.110 +        assertEquals("Result is 136 CZK", 136.0, c.convert("USD", "CZK", 8));
 114.111 +
 114.112 +        // convert 1003CZK to USD using c:
 114.113 +        // assertEquals("Result is 59 USD");
 114.114 +        assertEquals("Result is 59 USD", 59.0, c.convert("CZK", "USD", 1003));
 114.115 +
 114.116 +        // convert 16CZK using c:
 114.117 +        // assertEquals("Result is 20 SKK");
 114.118 +        assertEquals("Result is 20 SKK", 20.0, c.convert("CZK", "SKK", 16));
 114.119 +
 114.120 +        // convert 500SKK to CZK using c:
 114.121 +        // assertEquals("Result is 400 CZK");
 114.122 +        assertEquals("Result is 400 CZK", 400.0, c.convert("SKK", "CZK", 500));
 114.123 +
 114.124 +        //test exceptions
 114.125 +        Convertor one = Task1Test.createCZKtoUSD();
 114.126 +        Convertor two = Task1Test.createSKKtoCZK();
 114.127 +        Convertor three = Task1Test.createSKKtoCZK();
 114.128 +        try {
 114.129 +            ConvertorFactory.newInstance().mergeConvertors(one,two,three);
 114.130 +            fail();
 114.131 +        } catch (IllegalArgumentException e) {
 114.132 +            //ok
 114.133 +        }
 114.134 +
 114.135 +        //test exceptions
 114.136 +        try {
 114.137 +            ConvertorFactory.newInstance().mergeConvertors(c, two);
 114.138 +            fail();
 114.139 +        } catch (IllegalArgumentException e) {
 114.140 +            //ok
 114.141 +        }
 114.142 +        
 114.143 +        //try convertors from version 1
 114.144 +        Convertor v1one = ConvertorFactory.newInstance().createConvertor("CZE", "CZE", 1, 2);        
 114.145 +        assertEquals("CZE->CZE 1:2 10 expects 20", 20.0, v1one.convert("CZE", "CZE", 10));
 114.146 +        try {
 114.147 +            ConvertorFactory.newInstance().mergeConvertors(v1one, two);
 114.148 +            fail();
 114.149 +        } catch (IllegalArgumentException e) {
 114.150 +            //ok
 114.151 +        }
 114.152 +
 114.153 +        Convertor v1two = ConvertorFactory.newInstance().createConvertor("EUR", "", 1, 2);
 114.154 +        assertEquals("EUR->'' 1:2 10 expects 20", 20.0, v1two.convert("EUR", "", 10));
 114.155 +        try {
 114.156 +            ConvertorFactory.newInstance().mergeConvertors(v1two, two);
 114.157 +            fail();
 114.158 +        } catch (IllegalArgumentException e) {
 114.159 +            //ok
 114.160 +        }
 114.161 +
 114.162 +        Convertor v1three = ConvertorFactory.newInstance().createConvertor("EUR", "", 1, 2);
 114.163 +        assertEquals("''->EUR 1:2 10 expects 5", 5.0, v1three.convert("", "EUR", 10));
 114.164 +        try {
 114.165 +            ConvertorFactory.newInstance().mergeConvertors(v1three, two);
 114.166 +            fail();
 114.167 +        } catch (IllegalArgumentException e) {
 114.168 +            //ok
 114.169 +        }
 114.170 +        
 114.171 +    }
 114.172 +}
   115.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   115.2 +++ b/task4/solution14/test/org/apidesign/apifest08/test/Task3Test.java	Sat Oct 18 07:47:34 2008 +0200
   115.3 @@ -0,0 +1,135 @@
   115.4 +package org.apidesign.apifest08.test;
   115.5 +
   115.6 +import junit.framework.TestCase;
   115.7 +import org.apidesign.apifest08.currency.Convertor;
   115.8 +import org.apidesign.apifest08.currency.ConvertorFactory;
   115.9 +import org.apidesign.apifest08.currency.CurrencyRate;
  115.10 +import org.apidesign.apifest08.currency.Rate;
  115.11 +
  115.12 +/** The exchange rates are not always the same. They are changing. Day by day,
  115.13 + * hour by hour, minute by minute. For every bank it is important to always
  115.14 + * have the actual exchange rate available in the system. That is why let's
  115.15 + * create a pluggable convertor that will always have up to date value of its
  115.16 + * exchange rate.
  115.17 + * <p>
  115.18 + * The quest for today is to allow 3rd party developer to write a convertor
  115.19 + * that adjusts its exchange rate everytime it is queried. This convertor is
  115.20 + * written by independent vendor, the vendor knows only your Convertor API,
  115.21 + * he does not know how the whole system looks and how the convertor is supposed
  115.22 + * to be used.
  115.23 + */
  115.24 +public class Task3Test extends TestCase {
  115.25 +    public Task3Test(String testName) {
  115.26 +        super(testName);
  115.27 +    }
  115.28 +
  115.29 +    @Override
  115.30 +    protected void setUp() throws Exception {
  115.31 +    }
  115.32 +
  115.33 +    @Override
  115.34 +    protected void tearDown() throws Exception {
  115.35 +    }
  115.36 +
  115.37 +    // Backward compatibly enhance your existing API to support following
  115.38 +    // usecases:
  115.39 +    //
  115.40 +
  115.41 +
  115.42 +    /** Without knowing anything about the surrounding system, write an
  115.43 +     * implementation of convertor that will return different rates everytime
  115.44 +     * it is queried. Convert USD to CZK and vice versa. Start with the rate of
  115.45 +     * 1USD = 16CZK and adjust it in favor of CZK by 0.01 CZK with every query.
  115.46 +     * As soon as you reach 1USD = 15CZK adjust it by 0.01 CZK in favor of USD
  115.47 +     * until you reach 1USD = 16CZK
  115.48 +     *
  115.49 +     * @return new instance of "online" USD and CZK convertor starting with rate 1USD = 16CZK
  115.50 +     */
  115.51 +    public static Convertor createOnlineCZKUSDConvertor() {
  115.52 +        // initial rate: 1USD = 16CZK
  115.53 +        // 2nd query 1USD = 15.99CZK
  115.54 +        // 3rd query 1USD = 15.98CZK
  115.55 +        // until 1USD = 15.00CZK
  115.56 +        // then 1USD = 15.01CZK
  115.57 +        // then 1USD = 15.02CZK
  115.58 +        // and so on and on up to 1USD = 16CZK
  115.59 +        // and then another round to 15, etc.
  115.60 +        CurrencyRate onlineCurrencyRate = new CurrencyRate() {
  115.61 +            private int usdAmount = 100;
  115.62 +            private int czkAmount = 1600;
  115.63 +            private boolean up = false;
  115.64 +            public String getCurrency1() {
  115.65 +                return "USD";
  115.66 +            }
  115.67 +
  115.68 +            public String getCurrency2() {
  115.69 +                return "CZK";
  115.70 +            }
  115.71 +
  115.72 +            public Rate getRate() { //return Rate according to online status
  115.73 +                Rate rate = new Rate(usdAmount, czkAmount);
  115.74 +                if (up) {
  115.75 +                    if (czkAmount < 1600) {
  115.76 +                        czkAmount++;
  115.77 +                    } else {
  115.78 +                        up = false;
  115.79 +                        czkAmount--;
  115.80 +                    }
  115.81 +                } else { //down
  115.82 +                    if (czkAmount > 1500) {
  115.83 +                        czkAmount--;
  115.84 +                    } else {
  115.85 +                        up = true;
  115.86 +                        czkAmount++;
  115.87 +                    }
  115.88 +                }
  115.89 +                return rate;
  115.90 +            }
  115.91 +        };
  115.92 +
  115.93 +        return ConvertorFactory.newInstance().createConvertor(onlineCurrencyRate);
  115.94 +    }
  115.95 +
  115.96 +    public void testFewQueriesForOnlineConvertor() {
  115.97 +        Convertor c = createOnlineCZKUSDConvertor();
  115.98 +        doFewQueriesForOnlineConvertor(c);
  115.99 +    }
 115.100 +
 115.101 +    static void doFewQueriesForOnlineConvertor(Convertor c) {
 115.102 +        // convert $5 to CZK using c:
 115.103 +        //assertEquals("Result is 80 CZK");
 115.104 +        assertEquals("Result is 80 CZK", 80.0, c.convert("USD", "CZK", 5), 0.001);
 115.105 +
 115.106 +        // convert $8 to CZK using c:
 115.107 +        //assertEquals("Result is 127.92 CZK");
 115.108 +        assertEquals("Result is 127.92 CZK", 127.92, c.convert("USD", "CZK", 8), 0.001);
 115.109 +
 115.110 +        // convert $1 to CZK using c:
 115.111 +        //assertEquals("Result is 15.98 CZK");
 115.112 +        assertEquals("Result is 15.98 CZK", 15.98, c.convert("USD", "CZK", 1), 0.001);
 115.113 +
 115.114 +        // convert 15.97CZK to USD using c:
 115.115 +        //assertEquals("Result is 1$");
 115.116 +        assertEquals("Result is 1$", 1.0, c.convert("CZK", "USD", 15.97), 0.001);
 115.117 +        
 115.118 +    }
 115.119 +
 115.120 +    /** Join the convertors and show they behave sane.
 115.121 +     */
 115.122 +    public void testOnlineConvertorComposition() throws Exception {
 115.123 +        Convertor c = Task2Test.merge(
 115.124 +            createOnlineCZKUSDConvertor(),
 115.125 +            Task1Test.createSKKtoCZK()
 115.126 +        );
 115.127 +
 115.128 +        // convert 16CZK to SKK using c:
 115.129 +        // assertEquals("Result is 20 SKK");
 115.130 +        assertEquals("Result is 20 SKK", 20.0, c.convert("CZK", "SKK", 16), 0.001);
 115.131 +
 115.132 +        // convert 500SKK to CZK using c:
 115.133 +        // assertEquals("Result is 400 CZK");
 115.134 +        assertEquals("Result is 400 CZK", 400.0, c.convert("SKK", "CZK", 500), 0.001);
 115.135 +
 115.136 +        doFewQueriesForOnlineConvertor(c);
 115.137 +    }
 115.138 +}
   116.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   116.2 +++ b/task4/solution14/test/org/apidesign/apifest08/test/Task4Test.java	Sat Oct 18 07:47:34 2008 +0200
   116.3 @@ -0,0 +1,167 @@
   116.4 +package org.apidesign.apifest08.test;
   116.5 +
   116.6 +import java.util.Date;
   116.7 +import java.util.GregorianCalendar;
   116.8 +import junit.framework.TestCase;
   116.9 +import org.apidesign.apifest08.currency.Convertor;
  116.10 +import org.apidesign.apifest08.currency.ConvertorFactory;
  116.11 +
  116.12 +/** The exchange rates are not always the same. They are changing. However
  116.13 + * as in order to predict the future, one needs to understand own past. That is
  116.14 + * why it is important to know the exchange rate as it was at any time during
  116.15 + * the past.
  116.16 + * <p>
  116.17 + * Today's quest is to enhance the convertor API to deal with dates.
  116.18 + * One shall be able to convert a currency at any date. Each currencies rate shall
  116.19 + * be associated with a range between two Date objects. In order
  116.20 + * to keep compatibility with old API that knew nothing about dates, the
  116.21 + * rates associated then are applicable "for eternity". Any use of existing
  116.22 + * convert methods that do not accept a Date argument, uses the current
  116.23 + * System.currentTimeMillis() as default date.
  116.24 + */
  116.25 +public class Task4Test extends TestCase {
  116.26 +    public Task4Test(String testName) {
  116.27 +        super(testName);
  116.28 +    }
  116.29 +
  116.30 +    @Override
  116.31 +    protected void setUp() throws Exception {
  116.32 +    }
  116.33 +
  116.34 +    @Override
  116.35 +    protected void tearDown() throws Exception {
  116.36 +    }
  116.37 +
  116.38 +    // Backward compatibly enhance your existing API to support following
  116.39 +    // usecases:
  116.40 +    //
  116.41 +
  116.42 +    /** Takes a convertor with any rates associated and creates new convertor
  116.43 +     * that returns the same values as the old one for time between from to till.
  116.44 +     * Otherwise it returns no results. This is just a helper method that
  116.45 +     * shall call some real one in the API.
  116.46 +     * 
  116.47 +     * @param old existing convertor
  116.48 +     * @param from initial date (inclusive)
  116.49 +     * @param till final date (exclusive)
  116.50 +     * @return new convertor
  116.51 +     */
  116.52 +    public static Convertor limitTo(Convertor old, Date from, Date till) {
  116.53 +        return ConvertorFactory.newInstance().limitConvertor(old, from, till);
  116.54 +    }
  116.55 +
  116.56 +
  116.57 +    public void testCompositionOfLimitedConvertors() throws Exception {
  116.58 +        Date d1 = (new GregorianCalendar(2008, 9, 1, 0, 0)).getTime(); // 2008-10-01 0:00 GMT
  116.59 +        Date d2 = (new GregorianCalendar(2008, 9, 2, 0, 0)).getTime(); // 2008-10-02 0:00 GMT
  116.60 +        Date d3 = (new GregorianCalendar(2008, 9, 3, 0, 0)).getTime(); // 2008-10-03 0:00 GMT
  116.61 +        
  116.62 +        Convertor c = Task2Test.merge(
  116.63 +            limitTo(Task1Test.createCZKtoUSD(), d1, d2),
  116.64 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
  116.65 +        );
  116.66 +
  116.67 +        // convert $5 to CZK using c:
  116.68 +        // cannot convert as no rate is applicable to current date
  116.69 +        try {
  116.70 +            c.convert("USD", "CZK", 5);
  116.71 +            fail();
  116.72 +        } catch (IllegalArgumentException e) {
  116.73 +            //ok
  116.74 +        }
  116.75 +
  116.76 +        // convert $8 to CZK using c:
  116.77 +        // cannot convert as no rate is applicable to current date
  116.78 +        try {
  116.79 +            c.convert("USD", "CZK", 8);
  116.80 +            fail();
  116.81 +        } catch (IllegalArgumentException e) {
  116.82 +            //ok
  116.83 +        }
  116.84 +
  116.85 +        // convert 1003CZK to USD using c:
  116.86 +        // cannot convert as no rate is applicable to current date
  116.87 +        try {
  116.88 +            c.convert("CZK", "USD", 1003);
  116.89 +            fail();
  116.90 +        } catch (IllegalArgumentException e) {
  116.91 +            //ok
  116.92 +        }
  116.93 +
  116.94 +        // convert 16CZK using c:
  116.95 +        // cannot convert as no rate is applicable to current date
  116.96 +        try {
  116.97 +            c.convert("CZK", "USD", 16);
  116.98 +            fail();
  116.99 +        } catch (IllegalArgumentException e) {
 116.100 +            //ok
 116.101 +        }
 116.102 +
 116.103 +        // convert 500SKK to CZK using c:
 116.104 +        // cannot convert as no rate is applicable to current date
 116.105 +        try {
 116.106 +            c.convert("SKK", "CZK", 500);
 116.107 +            fail();
 116.108 +        } catch (IllegalArgumentException e) {
 116.109 +            //ok
 116.110 +        }
 116.111 +
 116.112 +        // convert $5 to CZK using c at 2008-10-01 6:00 GMT:
 116.113 +        // assertEquals("Result is 85 CZK");
 116.114 +        assertEquals("Result is 85 CZK", 85.0, c.convert("USD", "CZK", 5, (new GregorianCalendar(2008,9,1,6,0)).getTime()));
 116.115 +
 116.116 +        // convert $8 to CZK using c at 2008-10-01 6:00 GMT:
 116.117 +        // assertEquals("Result is 136 CZK");
 116.118 +        assertEquals("Result is 136 CZK", 136.0, c.convert("USD", "CZK", 8, (new GregorianCalendar(2008,9,1,6,0)).getTime()));
 116.119 +
 116.120 +        // convert 1003CZK to USD using c at 2008-10-01 6:00 GMT:
 116.121 +        // assertEquals("Result is 59 USD");
 116.122 +        assertEquals("Result is 59 USD", 59.0, c.convert("CZK", "USD", 1003.0, (new GregorianCalendar(2008, 9, 1, 6, 0)).getTime()));
 116.123 +
 116.124 +        // convert 16CZK using c at 2008-10-02 9:00 GMT:
 116.125 +        // assertEquals("Result is 20 SKK");
 116.126 +        assertEquals("Result is 20 SKK", 20.0, c.convert("CZK", "SKK", 16.0, (new GregorianCalendar(2008, 9, 2, 9, 0)).getTime()));
 116.127 +
 116.128 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
 116.129 +        // assertEquals("Result is 400 CZK");
 116.130 +        assertEquals("Result is 400 CZK", 400.0, c.convert("SKK", "CZK", 500.0, (new GregorianCalendar(2008, 9, 2, 9, 0)).getTime()));
 116.131 +
 116.132 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
 116.133 +        // cannot convert as no rate is applicable to current date
 116.134 +        try {
 116.135 +            c.convert("SKK", "CZK", 500.0, (new GregorianCalendar(2008, 9, 1, 6, 0)).getTime());
 116.136 +            fail();
 116.137 +        } catch (IllegalArgumentException e) {
 116.138 +            //ok
 116.139 +        }
 116.140 +    }
 116.141 +
 116.142 +    /** Create convertor that understands two currencies, CZK and
 116.143 +     *  SKK. Make 100 SKK == 90 CZK.
 116.144 +     *
 116.145 +     * @return prepared convertor ready for converting SKK to CZK and CZK to SKK
 116.146 +     */
 116.147 +    public static Convertor createSKKtoCZK2() {
 116.148 +        return ConvertorFactory.newInstance().createConvertor("SKK", "CZK", 100, 90);
 116.149 +    }
 116.150 +
 116.151 +    public void testDateConvetorWithTwoDifferentRates() throws Exception {
 116.152 +        Date d1 = (new GregorianCalendar(2008,9,1,0,0)).getTime(); // 2008-10-01 0:00 GMT
 116.153 +        Date d2 = (new GregorianCalendar(2008,9,2,0,0)).getTime(); // 2008-10-02 0:00 GMT
 116.154 +        Date d3 = (new GregorianCalendar(2008,9,6,0,0)).getTime(); // 2008-10-03 0:00 GMT
 116.155 +
 116.156 +        Convertor c = Task2Test.merge(
 116.157 +            limitTo(createSKKtoCZK2(), d1, d2),
 116.158 +            limitTo(Task1Test.createSKKtoCZK(), d2, d3)
 116.159 +        );
 116.160 +
 116.161 +        // convert 500SKK to CZK using c at 2008-10-02 9:00 GMT:
 116.162 +        // assertEquals("Result is 400 CZK");
 116.163 +        assertEquals("Result is 400 CZK", 400.0, c.convert("SKK", "CZK", 500.0, (new GregorianCalendar(2008, 9, 2, 9, 0)).getTime()));
 116.164 +
 116.165 +        // convert 500SKK to CZK using c at 2008-10-01 6:00 GMT:
 116.166 +        // assertEquals("Result is 450 CZK");
 116.167 +        assertEquals("Result is 450 CZK", 450.0, c.convert("SKK", "CZK", 500.0, (new GregorianCalendar(2008, 9, 1, 6, 0)).getTime()));
 116.168 +    }
 116.169 +
 116.170 +}