Catapult is able to render several document formats, such as simple text, JSON, and every XML-based format such as XHTML, RSS or SVG. In the page source you may use the Spring Expression Language (SpEL) for accessing data from the current request or the application. A SpEL expression within Catapult must be surrounded by the delimiters #{ and }. Thus a simple text page containing the following line

You came from #{header['referer']}

will output the text “You came from ” and the referer for that page, which stems from the bean named header. A comprehensive list of beans provided by Catapult can be found in the chapter TODO.

Templating

For all XML-based formats Catapult offers a powerful templating engine that allows re-use of page skeletons (e.g. for page layouts) and page partials. The templating engines makes heavily use of XML namespaces. The following three kinds of namespaces will be distinguished:

  • format namespaces, such as http://www.w3.org/1999/xhtml for XHTML – these will be copied to the output. However, they are important to support and extend the semantics of certain elements such as <form> elements, i.e. the XHTML namespace cannot simply be omitted.
  • Catapult namespaces for additional functionality, which are
    • the core namespace http://www.catapultframework.org/xml/core
    • the param namespace http://www.catapultframework.org/xml/param
    • the default-param namespace http://www.catapultframework.org/xml/default-param
  • template namespaces which identify directories for files that act as page templates – these consists of paths starting with a slash, for example simply / or /matches

We will shortly see all three namespace kinds in action. First start with a very simple example.

Let’s suppose you are going to create a page that defines the base layout for all pages of your web application. A very simple base XHTML page (named layout.html) could look like this:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>#{#title}</title>
  </head>
  <body>
    <div id="main" />
  </body>
</html>

There are two aspects to mention here: First, the page references a SpEL variable title within the title element, whose definition we will see in a minute. Second, it contains an empty div element having the ID main.

Now some other page may use this layout.html as its base in the following way:

<root:layout
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:root="/"
  title="Hello">
  <root:main>
    <h1>Hello, world!</h1>
  </root:main>
</root:layout>

Several things will happen here:

  • The template namespace prefix root is bound to the root directory / (note that there is no host part etc). The toplevel element root:layout therefore refers to the file layout.html in the root directory. The extension .html will derived from the extension of the current page.
  • The attribute title="Hello" assigns the string value “Hello” to the variable title. This way you can pass variable values to a template.
  • The element root:main (as a descendant of root:layout) overrides the contents of the element having the ID main

The resulting page rendered by Catapult is

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Hello</title>
  </head>
  <body>
    <div id="main">
      <h1>Hello, world!</h1>
    </div>
  </body>
</html>

The page referred to by the root:layout element doesn’t necessarily have to be the top-level element of a page. When used within a page the constructs behaves like an include. (Note: the underlying template mechanism is the same in both use cases.)

Here is a simple example. File logo.html:

<div xmlns="http://www.w3.org/1999/xhtml" class="logo">
  <span>The L<sub>o</sub>g<sup>o</sup></span>
</div>

This file then may be used in different pages (or even several times within the same page) by writing

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:root="/">
  ...
  <body>
    <root:logo />
  </body>
</html>

or (by combining the two templates)

<root:layout
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:root="/"
  title="Hello">
  <root:main>
    <h1>Hello, world!</h1>
    <root:logo />
  </root:main>
</root:layout>

Again you may pass variables to the included partial (simply by assigning attributes) or override parts of the partial by using the corresponding IDs.

There is the special element super in the catapult core namespace that inserts the overridden contents. A typical use case is

<html xmlns="http://www.w3.org/1999/xhtml">
  <head id="head">
    <sript type="text/javascript" href="main.js" />
  </head>
  ...
</html>

and then

<root:layout
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:c="http://www.catapultframework.org/xml/core"
  xmlns:root="/">
  <root:head>
    <c:super />
    <script type="text/javascript" href="user.js" />
  </root:head>
  ...
</root:layout>

It is possible to use cascaded templates. For example if the last code snippet resides in the file user.html then a third page address.html may use user.html as its base template by:

<root:user
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:c="http://www.catapultframework.org/xml/core"
  xmlns:root="/">
  <root:head>
    <c:super />
    <script type="text/javascript" href="address.js" />
  </root:head>
  ...
</root:user>

The rendered page will contain links to all three javascript files (i.e. main.js, user.js, and address.js). Of course it is possible to override elements from any of the base templates in the cascade.

In addition to template files Catapult provides so called local templates. This is especially useful if you have to use the same template multiple times within a single page only. In this case the definition of the template may be placed directly in the page that uses the template.

A local template has to be put inside a c:def element like this (the prefix c denotes the Catapult core namespace as usual):

<root:layout
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:c="http://www.catapultframework.org/xml/core"
  xmlns:root="/">
  <c:def name="logo">
    <span>The L<sub>o</sub>g<sup>o</sup></span>
  </c:def>
  ...
</root:layout>

The value logo of the name attribute defines the name of the local template. The c:def element itself doesn’t render anyhing, it is just a definition. To insert the template you have to use the name of the template in the core namespace (in this case c:logo) like this:

<root:layout
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:c="http://www.catapultframework.org/xml/core"
  xmlns:root="/">
  <c:def name="logo">
    <span>The L<sub>o</sub>g<sup>o</sup></span>
  </c:def>
  <root:main>
    <c:logo />
    ... <!-- further content -->
    <c:logo />
  </root:main>
</root:layout>

In the resulting page each of the two c:logo elements will be replaced with the contents of the c:def element. Of course, as with other templates, you can pass parameters to a local template by using attributes.

Summary

  • A ns:name element will be replaced with the contents of a file name.ext that has the same extension ext as the referencing file and that resides in the directory specified by the namespace for the prefix ns.
  • Any (non-prefixed) attribute for such an element defines a variable for usage in the referenced file and that has the attribute name as the variable name and the attribute value as the variable value.
  • The children of ns:name may be elements in the same namespace of the form ns:id1, ns:id2, etc. The contents of each of these elements replaces the contents of a corresponding element in the template file (name.ext) whose ID is the local name of the referring element (i.e. ns:id1 replaces the contents of the element having id="id1").
  • The special element c:super inserts the overridden content of the current element.
  • Local templates can be defined using the c:def element.

Conditions

For the conditional rendering of elements in an XHTML or XML page Catapult provides the attribute if in Catapult’s core namespace http://www.catapultframework.org/xml/core. The value of the attribute must evaluate to a boolean that determines whether the element will be rendered or not.

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:c="http://www.catapultframework.org/xml/core">
...
  <div c:if="#{#game.over}">
    Game over!
  </div>
...

If the expression #game.over evaluates to false then the div element will not be rendered.

Note that a null will have the same effect as a false. However, when using boolean operators then neither of the operands can be null. This means

<div c:if="#{#bar}">Bar: #{#bar}</div>
<div c:if="#{#baz}">Baz: #{#baz}</div>

will render nothing if both of the variables bar and baz don’t have a value. In contrast

<div c:if="#{#bar and #baz}">Both present: #{#bar} and #{#baz}</div>

will cause an exception in this case.

Since there is no c:else attribute, the else case has to be be expressed by using an c:if with the negated expression.

<div c:if="#{#game.over}">
  Game over!
</div>
<div c:if="#{!#game.over}">
  Your turn!
</div>

The double evaluation of a complex or expensive expression can be prevented by defining a local variable (see Local variables).

For attributes it is far easier to prevent their output on an rendered XML element. The simple rule is that an attribute whose value is null will not be rendered. For example

<div class="#{#answer != 42 ? 'error' : ''}>

will render an empty class attribute if the variable answer has the value 42, whereas

<div class="#{#answer != 42 ? 'error' : null}>

will render no class attribute at all in this case.

Loops

For the repeated display of elements (for example for tables and lists) Catapult offers the attribute foreach in the core namespace http://www.catapultframework.org/xml/core. The value of the foreach attribute is typically an expression of the form

iterable.iterator('loopvar' [, 'statusvar'])
  • The subexpression iterable must evaluate to an array, a collection, or a map.
  • The string loopvar defines the name of the loop variable.
  • The optional string statusvar defines the name of an additional status variable for the the loop.

The element that holds the foreach attribute will be repeatedly rendered for each item in iterable. Within the element the current item is stored in the loop variable.

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:c="http://www.catapultframework.org/xml/core">
...
  <ul>
    <li c:foreach="#{#user.betSlip.matches.iterator('match')}">
      #{#match.goalsA} : #{#match.goalsB}
    </li>
  </ul>
...

This example renders a list of all guessed match results of the user.

<table>
  <tr c:foreach="#{header.iterator('h')}">
    <td>#{#h.key}</td>
    <td>#{#h.value}</td>
  </tr>
</table>

This example renders a table containing the HTTP header values of the current request. Since header is a map, the loop variable h contains a java.util.Map.Entry value in each iteration. By accessing its properties key and value we can display the map contents.

By using the status variable we can access additional properties of the loop:

  • index returns the index (number) of the current iteration, 0 based.
  • count returns the index (number) of the current iteration, 1 based. This means count = index + 1.
  • first returns true for the first iteration and false otherwise.
  • last returns true for the last iteration and false otherwise.
  • even returns true for those iteraton where index is even.
  • odd returns true for those iteraton where index is odd.
  • cycle(arg1, arg2, ...) returns one element after the other from the cycle during the iteration.

The following example illustrates the behavior of the cycle function:

<table>
  <tr c:foreach="#{header.iterator('h', 'status')}"
      class="#{#status.cycle('red', 'green', 'blue')}">
    <td>#{#h.key}</td>
    <td>#{#h.value}</td>
  </tr>
</table>

Here the class values red, green, and blue will be used consecutively.

Additional blocks

Since both constructs for conditions (c:if) and loops (c:foreach) are XML attributes, they always require a single XML element that carries the control construct. For those cases when you don’t have a single element, the c:block element (also in Catapult’s core namespace http://www.catapultframework.org/xml/core) comes handy. The element itself won’t be rendered, its only purpose is to group other content for c:if or c:foreach as well as for defining local variables (see below).

The following example alters the HTTP headers example from above and outputs a definition list instead of a table:

<dl>
  <c:block c:foreach="#{header.iterator('h')}">
    <dt>#{#h.key}</dt>
    <dd>#{#h.value}</dd>
  </c:block>
</dl>

The c:block is necessary since the pair of dt and dd elements has to be repeated.

Default parameters

As we have seen above in the templating chapter, variables may be passed to templates by using an attribute in the template element. Catapult requires all variables to be assigned, thus it is not allowed to use a variable in a template without an assignment. So for example a typo in a variable name will be reported as an error during the rendering of the page.

Note: this has changed in Catapult 0.99. In versions prior to 0.99 referencing an unassigned variable simply evaluates to null. By setting the Catapult property catapult.renderer.ignore-undeclared-variables to true the previous behaviour can be reestablished.

However, to prevent passing all variable values via attributes, Catapult provides the http://www.catapultframework.org/xml/default-param namespace for defining default values for parameters. A default value of a parameter will be defined by using an attribute with the name of the parameter in the default-param namespace like this:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:d="http://www.catapultframework.org/xml/default-param"
      d:title="Hello, world!">
  <head>
    <title>#{#title}</title>
  </head>
  ...
</html>

The title variable will have the value “Hello, world!” unless it is passed explicitely to the template.

Local variables

Often it is useful to assign a complex SpEL expression to a local variable that will be used several times. Catapult enables element scoped variables by using the param namespace http://www.catapultframework.org/xml/param. Usually this namespace will be bound to the prefix p. Any attribute in the param namespace establishes a variable declaration. This variable can be used in the contents of the element.

Here is an example

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:c="http://www.catapultframework.org/xml/core"
      xmlns:p="http://www.catapultframework.org/xml/param">
...
  <div p:valid="#{#foo.hasValues() and (#bar.prop1 gt 100 or #bar.prop2 lt 50)}">
    <span c:if="#{#valid}">The data is valid</span>
    <span c:if="#{!#valid}">The data is not valid</span>
  </div>
</html>

(TODO: useful example)

While the order of attributes in XML is in general undefined (i.e. the order that is used in the input XML isn’t necessarily used in the output XML), Catapult will evaluate attributes in the core and param namespaces before any other attributes. The order is as follows:

  1. d:* (i.e. all default parameter declarations)
  2. c:foreach
  3. p:* (i.e. all local variable declarations)
  4. c:if
  5. all other attributes

This means, attributes that declare variables (c:foreach and p:*) will be evaluated before conditions (c:if). The following example snippets are legal attribute combinations:

<foo c:foreach="#{#collection.iterator('x')}"
     p:double="#{2 * #x}"
     bar="#{#double}"> ...

<foo c:foreach="#{#collection.iterator('x')}"
     c:if="#{#x != 42}"> ...

<foo p:name="#{#car?.owner?.name}"
     c:if="#{#name != null}"> ...

However, the following examples are illegal and will not work:

<foo c:if="{#collection != null}"
     c:foreach="#{#collection.iterator('i')}"> ...
<-- #collection.iterator gets called even if collection is null -->

<foo p:matches="#{#user.betSlip.matches}"
     c:foreach="#{#matches.iterator('m')}"> ...
<-- matches is null when c:foreach is evaluated -->

<foo c:if="#{#user != null}"
     p:matches="#{#user.betSlip.matches}"> ...
<-- #user.betSlip will be evaluated even if user is null -->

The solution to this kind of problems is usually to move the conflicting attributes to the next outer (parent) or inner (child) element. If you don’t find proper elements you can always add a c:block. The first of the illegal example above can be fixed by using

<c:block c:if="{#collection != null}">
  <foo c:foreach="#{#collection.iterator('i')}"> ...

Note: For multiple attributes in the param namespace the evaluation order is still undefined. This means: never declare multiple variables at once that refer each other. The behavior of the following declarations is undefined:

<div p:foo="#{13}" p:bar="#{#foo + 42}">

If you are lucky, bar will have the value 55 afterwards. But it might as well be that you are seeing an error because foo is null when p:bar is evaluated. The solution here is as well to put the variable declarations to different elements (for example to an additional c:block).

Comments

Catapult supports two kinds of comments within HTML resp. XML templates.

A simple comment of the form <-- comment contents ... --> will be evaluated and rendered. Thus the resulting page will contain the comment with all contained expressions evaluated. So this kind of comment is normally not appropriate for commenting out code, because the code will be rendered within the comment and invalid EL expressions might cause errors.

In contrast, Catapult comments of the form <--% comment contents ... %--> will be completely ignored by Catapult (note the additional percent characters next to the comment delimiters). EL expressions commented out this way won’t be evaluated by Catapult and won’t be seen by users in the resulting HTML source.