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.

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 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 a special element with the local name super 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:root="/">
  <root:head>
    <root: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:root="/">
  <root:head>
    <root: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.

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 ns:super inserts the overridden content of the current 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('varname')
  • The subexpression iterable must evaluate to an array, a collection, or a map.
  • The string varname defines the name of the loop variable.

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.

The loop variable possesses some additional properties:

  • 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')}"
      class="#{#h.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.


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/core. 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)

Note: The variable declaration actually becomes active once the attribute has been evaluted. This means you could use the variable value already within other (unprefixed) attributes. So the following is perfectly valid:

<div p:var="#{....}" class="#{#var}">

Catapult will evaluate attributes in the param namespace always before attributes in no namespace. But never ever try to define multiple variables at once that refer each other. So the behavior of the following declarations is undefined:

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

Because the order of attributes in an XML start tag is undefined according to the XML specification (and in fact the XML parser will report them in any order), Catapult will evaluate the variable declarations in any order. So it might be that in fact p:var will be evaluated first and p:foo afterwards. Since undefined variables simply evaluate to null you don’t even get necessarily an error in such cases.

Also: Don’t use dependent c:if / c:foreach together with variable declarations in the same tag. The order of their evaluation is as well undefined.