Jamon Tutorial - 9. Template Inheritance: Composing Template Output
Jamon template inheritance allows a template to declare that it can include the output of a child template that extends it. (There are other benefits of inheritence as well, which will be explained in the next tutorial section.)The benefits of template inclusion are obvious: a containing template, for example, can set up the header, footer, and navigation bar of a web page while the included template can provide the body of the page. Template inheritance makes this inclusion easy: a parent template can set up the page layout while the child template simply declares that it extends the parent in order to have its output included by the parent.
For a template to declare that it supports inheritance it must specify the tag
<%abstract>
Then, where the parent template wants to include the output of a derived, or child, template, it must specify the tag
<&* CHILD &>
For example, the template InheritanceParent.jamon
, shown below, declares the
<& *CHILD &>
where it wants to include the
child template's output. The parent template takes care of the
HTML header as well as the page's header banner while the child
template simply fills in the body.
<%abstract> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Jamon Documentation</title> </head> <body> <%doc> could include a navigation bar here </%doc> <h1>Jamon</h1> <h3>a typed template engine for Java</h3> <hr> <& *CHILD &> <%doc> could also include a footer here</%doc> </body> </html>
The child template declares that it extends the parent template by including the tag
<%extends InheritanceParent>
If the parent template is in a different directory, then the extends
tag must specify the path to the parent template.
<%extends ../InheritanceParent>
The example template below,
InheritanceChild.jamon
extends InheritanceTemplate
and provides subject text that is included in the output of the parent template.
<%extends InheritanceParent> <h2>Inheritance Documentation</h2> To declare that a template extends a parent template, it must include the <code><%extends></code> tag.This template would be invoked from Java with the class
InheritanceSimpleTut9.java
import java.io.OutputStreamWriter; public class InheritanceSimpleTut9 { public static void main(String[] args) throws Exception { new InheritanceChild().render(new OutputStreamWriter(System.out)); } }The output from the template is
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Jamon Documentation</title> </head> <body> <h1>Jamon</h1> <h3>a typed template engine for Java</h3> <hr> <h2>Inheritance Documentation</h2> To declare that a template extends a parent template, it must include the <code><%extends></code> tag. </body> </html>
Inheritance with template arguments
Both the parent and child templates may take arguments. To specify arguments, the parent and child templates simply include a<%args>
... </%args>
tag.
By default, arguments in the parent template are not visible to
the child template.
They can be made visible to code in the child
template by declaring them in <%xargs>
... </%xargs>
blocks.
For example, consider the parent template
InheritanceParentWArgs.jamon
. It defines arguments
userAccount
and title
.
<%abstract> <%args> User userAccount; String title = ""; </%args> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title><% title %></title> </head> <body> <%doc> could include a navigation bar here </%doc> <h1>Jamon</h1> <h3>a typed template engine for Java</h3> <hr> <%if userAccount.isAuthorized() %> <& *CHILD &> <%else> <em>Not authorized to view this page.</em> </%if> <%doc> could also include a footer here</%doc> </body> </html>The child template can only access these arguments by including
<%xargs> userAccount; title = "special of the week"; </%xargs>Notice that only the name of the arguments should be listed. This is because the types of the arguments are already specified in the parent class. Also note, however, that any optional arguments can have their default values redefined in the child template.
A complete example of a child template,
InheritanceChildWArgs.jamon
, is below.
<%extends InheritanceParentWArgs> <%args> String special; </%args> <%xargs> userAccount; title = "special of the week"; </%xargs> <h2>Weekly Special</h2> <% userAccount == null ? "You" : (userAccount.getName() + ", you") %>\ qualify for the special of the week! <blink><% special %></blink>
Invoking a derived template
The two choices for invoking a derived template are-
the Java code can call the
render()
method on the derived template and provide all the arguments that are required by the child template as well as all arguments required by the parent template, or - the code may also call the
makeParentRenderer()
method on the derived template, provide the arguments required by the child template, and then call render on theInheritanceParentWArgs.ParentRenderer
object returned bymakeParentRenderer()
method and provide the arguments required by the parent template.
The class User.java
implements
invoking the child template through the second approach described above.
import java.io.OutputStreamWriter; public class User { private String name; private boolean authorized; public User(String p_name, boolean p_authorized) { name = p_name; authorized = p_authorized; } public String getName() { return name; } public boolean isAuthorized() { return authorized; } public static void main(String[] args) throws Exception { User[] users = new User[2]; users[0] = new User("John Public", false); users[1] = new User("John Rockerfeller", true); for (int i=0; i<users.length; i++) { // call render() on the parent template and provide arguments processUser(args.length == 0 ? null : args[0]) .setTitle("Jamon News") // set optional argument in parent .render(new OutputStreamWriter(System.out), users[i]); } } private static InheritanceParentWArgs.ParentRenderer processUser(String templateName) { // the default template, the special of the week if (templateName == null || "special".equalsIgnoreCase(templateName)) { return new InheritanceChildWArgs() .makeParentRenderer("Jamon free this week only!"); } else { // process other derived templates here return null; } } }
The output from the template is
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Jamon News</title> </head> <body> <h1>Jamon</h1> <h3>a typed template engine for Java</h3> <hr> <em>Not authorized to view this page.</em> </body> </html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Jamon News</title> </head> <body> <h1>Jamon</h1> <h3>a typed template engine for Java</h3> <hr> <h2>Weekly Special</h2> John Rockerfeller, you qualify for the special of the week! <blink>Jamon free this week only!</blink> </body> </html>