In fact builder pattern is combining mutable objects during creation with immutable result
authorJaroslav Tulach <jtulach@netbeans.org>
Tue, 25 Aug 2009 11:32:10 +0200
changeset 337d5b6a877e5a8
parent 336 219810ff3c72
child 338 2ed9d213455e
In fact builder pattern is combining mutable objects during creation with immutable result
samples/aserverinfo/nbproject/build-impl.xml
samples/aserverinfo/nbproject/genfiles.properties
samples/aserverinfo/nbproject/project.properties
samples/aserverinfo/src/org/apidesign/aserverinfo/builder/ServerInfo.java
samples/aserverinfo/test/org/apidesign/aserverinfo/test/BuilderFactoryTest.java
     1.1 --- a/samples/aserverinfo/nbproject/build-impl.xml	Sat Jun 20 16:06:19 2009 +0200
     1.2 +++ b/samples/aserverinfo/nbproject/build-impl.xml	Tue Aug 25 11:32:10 2009 +0200
     1.3 @@ -20,6 +20,13 @@
     1.4  
     1.5          -->
     1.6  <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="aserverinfo-impl">
     1.7 +    <fail message="Please build using Ant 1.7.1 or higher.">
     1.8 +        <condition>
     1.9 +            <not>
    1.10 +                <antversion atleast="1.7.1"/>
    1.11 +            </not>
    1.12 +        </condition>
    1.13 +    </fail>
    1.14      <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
    1.15      <!-- 
    1.16                  ======================
    1.17 @@ -152,10 +159,18 @@
    1.18              <attribute default="${includes}" name="includes"/>
    1.19              <attribute default="${excludes}" name="excludes"/>
    1.20              <attribute default="${javac.debug}" name="debug"/>
    1.21 -            <attribute default="" name="sourcepath"/>
    1.22 +            <attribute default="${empty.dir}" name="sourcepath"/>
    1.23 +            <attribute default="${empty.dir}" name="gensrcdir"/>
    1.24              <element name="customize" optional="true"/>
    1.25              <sequential>
    1.26 +                <property location="${build.dir}/empty" name="empty.dir"/>
    1.27 +                <mkdir dir="${empty.dir}"/>
    1.28                  <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}">
    1.29 +                    <src>
    1.30 +                        <dirset dir="@{gensrcdir}" erroronmissingdir="false">
    1.31 +                            <include name="*"/>
    1.32 +                        </dirset>
    1.33 +                    </src>
    1.34                      <classpath>
    1.35                          <path path="@{classpath}"/>
    1.36                      </classpath>
    1.37 @@ -271,6 +286,8 @@
    1.38                  <java classname="@{classname}" dir="${work.dir}" fork="true">
    1.39                      <jvmarg line="${debug-args-line}"/>
    1.40                      <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
    1.41 +                    <jvmarg value="-Dfile.encoding=${source.encoding}"/>
    1.42 +                    <redirector errorencoding="${source.encoding}" inputencoding="${source.encoding}" outputencoding="${source.encoding}"/>
    1.43                      <jvmarg line="${run.jvmargs}"/>
    1.44                      <classpath>
    1.45                          <path path="@{classpath}"/>
    1.46 @@ -291,6 +308,8 @@
    1.47              <element name="customize" optional="true"/>
    1.48              <sequential>
    1.49                  <java classname="@{classname}" dir="${work.dir}" fork="true">
    1.50 +                    <jvmarg value="-Dfile.encoding=${source.encoding}"/>
    1.51 +                    <redirector errorencoding="${source.encoding}" inputencoding="${source.encoding}" outputencoding="${source.encoding}"/>
    1.52                      <jvmarg line="${run.jvmargs}"/>
    1.53                      <classpath>
    1.54                          <path path="@{classpath}"/>
    1.55 @@ -317,7 +336,23 @@
    1.56                  COMPILATION SECTION
    1.57                  ===================
    1.58              -->
    1.59 -    <target depends="init" name="deps-jar" unless="no.deps"/>
    1.60 +    <target name="-deps-jar-init" unless="built-jar.properties">
    1.61 +        <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/>
    1.62 +        <delete file="${built-jar.properties}" quiet="true"/>
    1.63 +    </target>
    1.64 +    <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps">
    1.65 +        <mkdir dir="${build.dir}"/>
    1.66 +        <touch file="${built-jar.properties}" verbose="false"/>
    1.67 +        <property file="${built-jar.properties}" prefix="already.built.jar."/>
    1.68 +        <fail message="Cycle detected: aserverinfo was already built">
    1.69 +            <condition>
    1.70 +                <isset property="already.built.jar.${basedir}"/>
    1.71 +            </condition>
    1.72 +        </fail>
    1.73 +        <propertyfile file="${built-jar.properties}">
    1.74 +            <entry key="${basedir}" value=""/>
    1.75 +        </propertyfile>
    1.76 +    </target>
    1.77      <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
    1.78      <target depends="init" name="-check-automatic-build">
    1.79          <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
    1.80 @@ -333,10 +368,15 @@
    1.81          <!-- You can override this target in the ../build.xml file. -->
    1.82      </target>
    1.83      <target if="do.depend.true" name="-compile-depend">
    1.84 -        <j2seproject3:depend/>
    1.85 +        <pathconvert property="build.generated.subdirs">
    1.86 +            <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
    1.87 +                <include name="*"/>
    1.88 +            </dirset>
    1.89 +        </pathconvert>
    1.90 +        <j2seproject3:depend srcdir="${src.dir}:${build.generated.subdirs}"/>
    1.91      </target>
    1.92      <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
    1.93 -        <j2seproject3:javac/>
    1.94 +        <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
    1.95          <copy todir="${build.classes.dir}">
    1.96              <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
    1.97          </copy>
    1.98 @@ -353,7 +393,7 @@
    1.99      <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
   1.100          <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
   1.101          <j2seproject3:force-recompile/>
   1.102 -        <j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
   1.103 +        <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.dir}"/>
   1.104      </target>
   1.105      <target name="-post-compile-single">
   1.106          <!-- Empty placeholder for easier customization. -->
   1.107 @@ -419,11 +459,29 @@
   1.108          <property location="${dist.jar}" name="dist.jar.resolved"/>
   1.109          <echo>java -jar "${dist.jar.resolved}"</echo>
   1.110      </target>
   1.111 +    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="libs.CopyLibs.classpath" name="-do-jar-with-libraries-without-manifest" unless="manifest.available+main.class">
   1.112 +        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
   1.113 +        <pathconvert property="run.classpath.without.build.classes.dir">
   1.114 +            <path path="${run.classpath}"/>
   1.115 +            <map from="${build.classes.dir.resolved}" to=""/>
   1.116 +        </pathconvert>
   1.117 +        <pathconvert pathsep=" " property="jar.classpath">
   1.118 +            <path path="${run.classpath.without.build.classes.dir}"/>
   1.119 +            <chainedmapper>
   1.120 +                <flattenmapper/>
   1.121 +                <globmapper from="*" to="lib/*"/>
   1.122 +            </chainedmapper>
   1.123 +        </pathconvert>
   1.124 +        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
   1.125 +        <copylibs compress="${jar.compress}" jarfile="${dist.jar}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
   1.126 +            <fileset dir="${build.classes.dir}"/>
   1.127 +        </copylibs>
   1.128 +    </target>
   1.129      <target name="-post-jar">
   1.130          <!-- Empty placeholder for easier customization. -->
   1.131          <!-- You can override this target in the ../build.xml file. -->
   1.132      </target>
   1.133 -    <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"/>
   1.134 +    <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-do-jar-with-libraries-without-manifest,-post-jar" description="Build JAR." name="jar"/>
   1.135      <!--
   1.136                  =================
   1.137                  EXECUTION SECTION
   1.138 @@ -502,6 +560,9 @@
   1.139              <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
   1.140                  <filename name="**/*.java"/>
   1.141              </fileset>
   1.142 +            <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
   1.143 +                <include name="**/*.java"/>
   1.144 +            </fileset>
   1.145          </javadoc>
   1.146      </target>
   1.147      <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
   1.148 @@ -563,7 +624,7 @@
   1.149          <j2seproject3:junit testincludes="**/*Test.java"/>
   1.150      </target>
   1.151      <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
   1.152 -        <fail if="tests.failed">Some tests failed; see details above.</fail>
   1.153 +        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
   1.154      </target>
   1.155      <target depends="init" if="have.tests" name="test-report"/>
   1.156      <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
   1.157 @@ -576,7 +637,7 @@
   1.158          <j2seproject3:junit excludes="" includes="${test.includes}"/>
   1.159      </target>
   1.160      <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
   1.161 -        <fail if="tests.failed">Some tests failed; see details above.</fail>
   1.162 +        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
   1.163      </target>
   1.164      <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"/>
   1.165      <!--
   1.166 @@ -642,7 +703,23 @@
   1.167                  CLEANUP SECTION
   1.168                  ===============
   1.169              -->
   1.170 -    <target depends="init" name="deps-clean" unless="no.deps"/>
   1.171 +    <target name="-deps-clean-init" unless="built-clean.properties">
   1.172 +        <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/>
   1.173 +        <delete file="${built-clean.properties}" quiet="true"/>
   1.174 +    </target>
   1.175 +    <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps">
   1.176 +        <mkdir dir="${build.dir}"/>
   1.177 +        <touch file="${built-clean.properties}" verbose="false"/>
   1.178 +        <property file="${built-clean.properties}" prefix="already.built.clean."/>
   1.179 +        <fail message="Cycle detected: aserverinfo was already built">
   1.180 +            <condition>
   1.181 +                <isset property="already.built.clean.${basedir}"/>
   1.182 +            </condition>
   1.183 +        </fail>
   1.184 +        <propertyfile file="${built-clean.properties}">
   1.185 +            <entry key="${basedir}" value=""/>
   1.186 +        </propertyfile>
   1.187 +    </target>
   1.188      <target depends="init" name="-do-clean">
   1.189          <delete dir="${build.dir}"/>
   1.190          <delete dir="${dist.dir}"/>
   1.191 @@ -652,4 +729,20 @@
   1.192          <!-- You can override this target in the ../build.xml file. -->
   1.193      </target>
   1.194      <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
   1.195 +    <target name="-check-call-dep">
   1.196 +        <property file="${call.built.properties}" prefix="already.built."/>
   1.197 +        <condition property="should.call.dep">
   1.198 +            <not>
   1.199 +                <isset property="already.built.${call.subproject}"/>
   1.200 +            </not>
   1.201 +        </condition>
   1.202 +    </target>
   1.203 +    <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep">
   1.204 +        <ant antfile="${call.script}" inheritall="false" target="${call.target}">
   1.205 +            <propertyset>
   1.206 +                <propertyref prefix="transfer."/>
   1.207 +                <mapper from="transfer.*" to="*" type="glob"/>
   1.208 +            </propertyset>
   1.209 +        </ant>
   1.210 +    </target>
   1.211  </project>
     2.1 --- a/samples/aserverinfo/nbproject/genfiles.properties	Sat Jun 20 16:06:19 2009 +0200
     2.2 +++ b/samples/aserverinfo/nbproject/genfiles.properties	Tue Aug 25 11:32:10 2009 +0200
     2.3 @@ -4,5 +4,5 @@
     2.4  # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
     2.5  # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
     2.6  nbproject/build-impl.xml.data.CRC32=c82747ea
     2.7 -nbproject/build-impl.xml.script.CRC32=1033418a
     2.8 -nbproject/build-impl.xml.stylesheet.CRC32=84d9cdb5
     2.9 +nbproject/build-impl.xml.script.CRC32=c7c23170
    2.10 +nbproject/build-impl.xml.stylesheet.CRC32=29e56e74@1.29.0.45
     3.1 --- a/samples/aserverinfo/nbproject/project.properties	Sat Jun 20 16:06:19 2009 +0200
     3.2 +++ b/samples/aserverinfo/nbproject/project.properties	Tue Aug 25 11:32:10 2009 +0200
     3.3 @@ -3,6 +3,7 @@
     3.4  # This directory is removed when the project is cleaned:
     3.5  build.dir=build
     3.6  build.generated.dir=${build.dir}/generated
     3.7 +build.generated.sources.dir=${build.dir}/generated-sources
     3.8  # Only compile against the classpath explicitly listed here:
     3.9  build.sysclasspath=ignore
    3.10  build.test.classes.dir=${build.dir}/test/classes
     4.1 --- a/samples/aserverinfo/src/org/apidesign/aserverinfo/builder/ServerInfo.java	Sat Jun 20 16:06:19 2009 +0200
     4.2 +++ b/samples/aserverinfo/src/org/apidesign/aserverinfo/builder/ServerInfo.java	Tue Aug 25 11:32:10 2009 +0200
     4.3 @@ -6,7 +6,7 @@
     4.4  import org.apidesign.aserverinfo.spi.URLProvider;
     4.5  
     4.6  /**
     4.7 - * Cumulative factory methods for the builder pattern
     4.8 + * Mutable "setter" methods for the builder pattern.
     4.9   *
    4.10   * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.11   */
    4.12 @@ -18,15 +18,21 @@
    4.13      }
    4.14  
    4.15      public final ServerInfo nameProvider(NameProvider np) {
    4.16 -        return new ServerInfo(np, this.url, this.reset, this.shutdown);
    4.17 +        this.name = np;
    4.18 +        return this;
    4.19      }
    4.20  
    4.21      public final ServerInfo urlProvider(URLProvider up) {
    4.22 -        return new ServerInfo(this.name, up, this.reset, this.shutdown);
    4.23 +        this.url = up;
    4.24 +        return this;
    4.25      }
    4.26 +    // BEGIN: aserverinfo.builder.setter
    4.27      public final ServerInfo reset(ResetHandler h) {
    4.28 -        return new ServerInfo(this.name, this.url, h, this.shutdown);
    4.29 +        this.reset = h;
    4.30 +        return this;
    4.31      }
    4.32 +    // END: aserverinfo.builder.setter
    4.33 +    
    4.34      /** All one needs to do when there is a need to add new
    4.35       * style of creation is to add new method for a builder.
    4.36       * @param handler
    4.37 @@ -34,7 +40,8 @@
    4.38       * @since 2.0
    4.39       */
    4.40      public final ServerInfo shutdown(ShutdownHandler handler) {
    4.41 -        return new ServerInfo(this.name, this.url, this.reset, handler);
    4.42 +        this.shutdown = handler;
    4.43 +        return this;
    4.44      }
    4.45      
    4.46      /** Creates the server connector based on current values in the 
    4.47 @@ -46,10 +53,10 @@
    4.48      }
    4.49      // FINISH: aserverinfo.builder.factory
    4.50  
    4.51 -    private final NameProvider name;
    4.52 -    private final URLProvider url;
    4.53 -    private final ResetHandler reset;
    4.54 -    private final ShutdownHandler shutdown;
    4.55 +    private NameProvider name;
    4.56 +    private URLProvider url;
    4.57 +    private ResetHandler reset;
    4.58 +    private ShutdownHandler shutdown;
    4.59  
    4.60      private ServerInfo(
    4.61          NameProvider name, URLProvider url,
     5.1 --- a/samples/aserverinfo/test/org/apidesign/aserverinfo/test/BuilderFactoryTest.java	Sat Jun 20 16:06:19 2009 +0200
     5.2 +++ b/samples/aserverinfo/test/org/apidesign/aserverinfo/test/BuilderFactoryTest.java	Tue Aug 25 11:32:10 2009 +0200
     5.3 @@ -32,16 +32,16 @@
     5.4          NameProvider np = p;
     5.5          URLProvider up = p;
     5.6          ResetHandler res = p;
     5.7 -        ServerConnector inf;
     5.8 +        ServerConnector connection;
     5.9          
    5.10          // BEGIN: ServerConnector.builder.creation
    5.11 -        inf = ServerInfo.empty()
    5.12 +        connection = ServerInfo.empty()
    5.13                  .nameProvider(np).urlProvider(up).reset(res).connect();
    5.14          // END: ServerConnector.builder.creation
    5.15          
    5.16 -        assertEquals("API Design Server", inf.getName());
    5.17 -        assertEquals("http://www.apidesign.org", inf.getURL().toExternalForm());
    5.18 -        inf.reset();
    5.19 +        assertEquals("API Design Server", connection.getName());
    5.20 +        assertEquals("http://www.apidesign.org", connection.getURL().toExternalForm());
    5.21 +        connection.reset();
    5.22          assertEquals("Once reset", 1, p.resets);
    5.23          
    5.24      }
    5.25 @@ -49,19 +49,19 @@
    5.26      @Test
    5.27      public void showVerboseUseOfBuilder() throws Exception {
    5.28          Prov prov = new Prov();
    5.29 -        ServerConnector info;
    5.30 +        ServerConnector connection;
    5.31          
    5.32          // BEGIN: ServerConnector.builder.creation.verbose
    5.33          ServerInfo empty = ServerInfo.empty();
    5.34          ServerInfo name = empty.nameProvider(prov);
    5.35          ServerInfo urlAndName = name.urlProvider(prov);
    5.36          ServerInfo all = urlAndName.reset(prov);
    5.37 -        info = all.connect();
    5.38 +        connection = all.connect();
    5.39          // END: ServerConnector.builder.creation.verbose
    5.40          
    5.41 -        assertEquals("API Design Server", info.getName());
    5.42 -        assertEquals("http://www.apidesign.org", info.getURL().toExternalForm());
    5.43 -        info.reset();
    5.44 +        assertEquals("API Design Server", connection.getName());
    5.45 +        assertEquals("http://www.apidesign.org", connection.getURL().toExternalForm());
    5.46 +        connection.reset();
    5.47          assertEquals("Once reset", 1, prov.resets);
    5.48          
    5.49      }