Manual: Updated getting started example to use bean for latestProduct, and to use ftlh template extension.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/7dfa6642 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/7dfa6642 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/7dfa6642 Branch: refs/heads/2.3 Commit: 7dfa6642807583d26a52a8a27b6421e7cda0457c Parents: 56b5f6e Author: ddekany <[email protected]> Authored: Tue Dec 29 17:11:34 2015 +0100 Committer: ddekany <[email protected]> Committed: Tue Dec 29 17:11:34 2015 +0100 ---------------------------------------------------------------------- src/manual/en_US/book.xml | 154 +++++++++++++------ .../manual/GettingStartedExample.java | 49 ++++++ src/test/java/freemarker/manual/Product.java | 31 ++++ src/test/resources/freemarker/manual/test.ftlh | 10 ++ 4 files changed, 194 insertions(+), 50 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7dfa6642/src/manual/en_US/book.xml ---------------------------------------------------------------------- diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml index 092d18b..145eb3c 100644 --- a/src/manual/en_US/book.xml +++ b/src/manual/en_US/book.xml @@ -6288,10 +6288,10 @@ cfg.setLogTemplateExceptions(false);</programlisting> <listitem> <para>Use your custom bean class for hashes where the items correspond to the bean properties. For example the - <literal>price</literal> property - (<literal>getProperty()</literal>) of <literal>product</literal> - can be get as <literal>product.price</literal>. (The actions of - the beans can be exposed as well; see much later <link + <literal>price</literal> property (<literal>getPrice()</literal>) + of <literal>product</literal> can be get as + <literal>product.price</literal>. (The actions of the beans can be + exposed as well; see much later <link linkend="pgui_misc_beanwrapper">here</link>)</para> </listitem> </itemizedlist> @@ -6312,66 +6312,74 @@ cfg.setLogTemplateExceptions(false);</programlisting> <para>This Java code fragment that builds this data-model:</para> - <programlisting role="unspecified">// Create the root hash + <programlisting role="unspecified">// Create the root hash. We use a Map here, but it could be a JavaBean too. Map<String, Object> root = new HashMap<>(); -// Put string ``user'' into the root + +// Put string "user" into the root root.put("user", "Big Joe"); -// Create the hash for ``latestProduct'' -Map<String, Object> latest = new HashMap<>(); + +// Create the "latestProduct" hash. We use a JavaBean here, but it could be a Map too. +Product latest = new Product(); +latest.setUrl("products/greenmouse.html"); +latest.setName("green mouse"); // and put it into the root root.put("latestProduct", latest); -// put ``url'' and ``name'' into latest -latest.put("url", "products/greenmouse.html"); -latest.put("name", "green mouse");</programlisting> +</programlisting> - <para>In real applications, instead of <literal>Map</literal>-s, you - will often use application-specific classes that has + <para>As demonstrated above, for hashes (something that stores other + named items) you can use either a <literal>Map</literal> or any kind + of public class that has public <literal>get<replaceable>Xxx</replaceable></literal>/<literal>is<replaceable>Xxx</replaceable></literal> - methods as prescribed by the JavaBeans specification. Like you have a - class similar to this:</para> + methods as prescribed by the JavaBeans specification. Like the above + <literal>Product</literal> class could be something like:</para> - <programlisting role="unspecified">public class Product { + <programlisting role="unspecified">/** + * Product bean; note that it must be a public class! + */ +public class Product { private String url; private String name; - ... - + // As per the JavaBeans spec., this defines the "url" bean property + // It must be public! public String getUrl() { return url; } - + + public void setUrl(String url) { + this.url = url; + } + // As per the JavaBean spec., this defines the "name" bean property + // It must be public! public String getName() { return name; } - - ... - -}</programlisting> - - <para>and you add an instance of it to the data-model somehow like - this:</para> - <programlisting role="unspecified">Product latestProducts = getLatestProductFromDatabaseOrSomething(); -root.put("latestProduct", latestProduct);</programlisting> + public void setName(String name) { + this.name = name; + } - <para>The template will be the same as if - <literal>latestProduct</literal> was a <literal>Map</literal>, like - <literal>${latestProduct.name}</literal> works in both cases.</para> +}</programlisting> - <para>The root itself need not be a <literal>Map</literal> either. It - could be an object with <literal>getUser()</literal> and + <para>Regardless if <literal>latestProduct</literal> is a + <literal>Map</literal> that contains the <literal>"name"</literal> and + <literal>"url"</literal> keys, or it's a JavaBean as shown above, in + the template you can use <literal>${latestProduct.name}</literal> .The + root itself need not be a <literal>Map</literal> either. It could be + an object with <literal>getUser()</literal> and <literal>getLastestProduct()</literal> methods too.</para> <note> <para>The behavior described here only stands if the value of the <literal>object_wrapper</literal> configuration setting is something that's used in almost all real world setups anyway. Anything that - the <literal>ObjectWrapper</literal> wraps to be a hash can be used - as the root, and can be traversed in templates with the dot and - <literal>[]</literal> operators. Something that it doesn't wrap to - be a hash can't be used as the root or be traversed like + the <literal>ObjectWrapper</literal> wraps to be a hash (something + that implements the <literal>TemplateHashModel</literal> interface) + can be used as the root, and can be traversed in templates with the + dot and <literal>[]</literal> operators. Something that it doesn't + wrap to be a hash can't be used as the root or be traversed like that.</para> </note> </section> @@ -6392,21 +6400,21 @@ root.put("latestProduct", latestProduct);</programlisting> template instance you can get it with its <literal>getTemplate</literal> method. Store <link linkend="example.first">the example template</link> in the - <literal>test.ftl</literal> file of the <link + <literal>test.ftlh</literal> file of the <link linkend="pgui_quickstart_createconfiguration">earlier</link> set directory, then you can do this:</para> - <programlisting role="unspecified">Template temp = cfg.getTemplate("test.ftl");</programlisting> + <programlisting role="unspecified">Template temp = cfg.getTemplate("test.ftlh");</programlisting> <para>When you call this, it will create a <literal>Template</literal> - instance corresponds to <literal>test.ftl</literal>, by reading - <literal><replaceable>/where/you/store/templates/</replaceable>test.ftl</literal> + instance corresponds to <literal>test.ftlh</literal>, by reading + <literal><replaceable>/where/you/store/templates/</replaceable>test.ftlh</literal> and parsing (compile) it. The <literal>Template</literal> instance stores the template in the parsed form, and not as text.</para> <para><literal>Configuration</literal> caches <literal>Template</literal> instances, so when you get - <literal>test.ftl</literal> again, it probably won't read and parse + <literal>test.ftlh</literal> again, it probably won't read and parse the template file again, just returns the same <literal>Template</literal> instance as for the first time.</para> </section> @@ -6453,7 +6461,7 @@ temp.process(root, out);</programlisting> <para>Note that once you have obtained a <literal>Template</literal> instance, you can merge it with different data-models for unlimited times (<literal>Template</literal> instances are stateless). Also, the - <literal>test.ftl</literal> file is accessed only while the + <literal>test.ftlh</literal> file is accessed only while the <literal>Template</literal> instance is created, not when you call the process method.</para> </section> @@ -6480,7 +6488,7 @@ public class Test { Configuration cfg = new Configuration(Configuration.VERSION_2_3_24); cfg.setDirectoryForTemplateLoading(new File("<replaceable>/where/you/store/templates</replaceable>")); cfg.setDefaultEncoding("UTF-8"); - cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW); + cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); cfg.setLogTemplateExceptions(false); /* ------------------------------------------------------------------------ */ @@ -6489,13 +6497,13 @@ public class Test { /* Create a data-model */ Map root = new HashMap(); root.put("user", "Big Joe"); - Map latest = new HashMap(); + Product latest = new Product(); + latest.setUrl("products/greenmouse.html"); + latest.setName("green mouse"); root.put("latestProduct", latest); - latest.put("url", "products/greenmouse.html"); - latest.put("name", "green mouse"); /* Get the template (uses cache internally) */ - Template temp = cfg.getTemplate("test.ftl"); + Template temp = cfg.getTemplate("test.ftlh"); /* Merge data-model with template */ Writer out = new OutputStreamWriter(System.out); @@ -6509,6 +6517,52 @@ public class Test { <para>I have suppressed the exceptions for the sake of simplicity. Don't do it in real products.</para> </note> + + <para>For the sake completeness, here's the the Product class used in + the data model:</para> + + <programlisting role="unspecified">/** + * Product bean; note that it must be a public class! + */ +public class Product { + + private String url; + private String name; + + // As per the JavaBeans spec., this defines the "url" bean property + // It must be public! + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + // As per the JavaBean spec., this defines the "name" bean property + // It must be public! + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +}</programlisting> + + <para>and the template:</para> + + <programlisting role="template"><html> +<head> + <title>Welcome!</title> +</head> +<body> + <h1>Welcome ${user}!</h1> + <p>Our latest product: + <a href="${latestProduct.url}">${latestProduct.name}</a>! +</body> +</html></programlisting> </section> </chapter> @@ -8623,7 +8677,7 @@ cfg.setTemplateExceptionHandler(new MyTemplateExceptionHandler());</programlisti <para>will print this:</para> - <programlisting role="output">a[ERROR: Expression badVar is undefined on line 1, column 7 in test.ftl.]b</programlisting> + <programlisting role="output">a[ERROR: Expression badVar is undefined on line 1, column 7 in test.ftlh.]b</programlisting> <para>Note that the error occurred in the <literal>if</literal> start-tag (<literal><#if badVar></literal>), but the whole @@ -8667,7 +8721,7 @@ b <programlisting role="output">a Foo - [ERROR: Expression badVar is undefined on line 4, column 5 in test.ftl.] + [ERROR: Expression badVar is undefined on line 4, column 5 in test.ftlh.] Bar c</programlisting> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7dfa6642/src/test/java/freemarker/manual/GettingStartedExample.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/manual/GettingStartedExample.java b/src/test/java/freemarker/manual/GettingStartedExample.java new file mode 100644 index 0000000..3e54c6c --- /dev/null +++ b/src/test/java/freemarker/manual/GettingStartedExample.java @@ -0,0 +1,49 @@ +package freemarker.manual; + +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateExceptionHandler; + +public class GettingStartedExample { + + @Test + public void main() throws Exception { + /* ------------------------------------------------------------------------ */ + /* You should do this ONLY ONCE in the whole application life-cycle: */ + + /* Create and adjust the configuration singleton */ + Configuration cfg = new Configuration(Configuration.VERSION_2_3_24); + cfg.setClassForTemplateLoading(GettingStartedExample.class, ""); + cfg.setDefaultEncoding("UTF-8"); + cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + cfg.setLogTemplateExceptions(false); + + /* ------------------------------------------------------------------------ */ + /* You usually do these for MULTIPLE TIMES in the application life-cycle: */ + + /* Create a data-model */ + Map root = new HashMap(); + root.put("user", "Big Joe"); + Product latest = new Product(); + latest.setUrl("products/greenmouse.html"); + latest.setName("green mouse"); + root.put("latestProduct", latest); + + /* Get the template (uses cache internally) */ + Template temp = cfg.getTemplate("test.ftlh"); + + /* Merge data-model with template */ + Writer out = new OutputStreamWriter(System.out); + temp.process(root, out); + // Note: Depending on what `out` is, you may need to call `out.close()`. + // This is usually the case for file output, but not for servlet output. + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7dfa6642/src/test/java/freemarker/manual/Product.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/manual/Product.java b/src/test/java/freemarker/manual/Product.java new file mode 100644 index 0000000..67d2cc6 --- /dev/null +++ b/src/test/java/freemarker/manual/Product.java @@ -0,0 +1,31 @@ +package freemarker.manual; + +/** + * Product bean; note that it must be a public class! + */ +public class Product { + + private String url; + private String name; + + // As per the JavaBeans spec., this defines the "url" bean property + // It must be public! + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + // As per the JavaBean spec., this defines the "name" bean property + // It must be public! + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7dfa6642/src/test/resources/freemarker/manual/test.ftlh ---------------------------------------------------------------------- diff --git a/src/test/resources/freemarker/manual/test.ftlh b/src/test/resources/freemarker/manual/test.ftlh new file mode 100644 index 0000000..890b827 --- /dev/null +++ b/src/test/resources/freemarker/manual/test.ftlh @@ -0,0 +1,10 @@ +<html> +<head> + <title>Welcome!</title> +</head> +<body> + <h1>Welcome ${user}!</h1> + <p>Our latest product: + <a href="${latestProduct.url}">${latestProduct.name}</a>! +</body> +</html> \ No newline at end of file
