StringTemplate Documentation

StringTemplate Documentation

Brought to you by that maniac that brings you ANTLR!

Terence Parr
University of San Francisco
parrt@cs.usfca.edu
Copyright 2003
http://www.antlr.org/stringtemplate/index.tml
(StringTemplate released under BSD License)

Version 1.0.3, Feb 5, 2003

Acknowledgements

Tom Burns (CEO jGuru.com) co-designed StringTemplate and listened to me think out loud incessantly. I would also like to thank Monty Zukowski for planting the template "meme" in my head back in the mid 1990's. Loring Craymer and Monty both are helping me hone these ideas for use in source-to-source language translation. Matthew Ford has done a huge amount of thinking about StringTemplate and has submitted numerous suggestions and patches.

Introduction

A StringTemplate is a "document" with holes in it where you can stick values. StringTemplate breaks up your template into chunks of text and attribute expressions, which are by default enclosed in dollar signs $attribute-expression$ (to make them easy to see in HTML files). StringTemplate ignores everything outside of attribute expressions, treating it as just text to spit out when you call StringTemplate.toString().

StringTemplate is not a "system" or "engine" or "server"; it's a library with two classes of interest: StringTemplate and StringTemplateGroup. You can directly create a StringTemplate in Java code or you can load a template from a file.

StringTemplate evolved over years developing the following sites:

with the strict principle of separation of logic from display in mind.

Creating Templates With Java Code

Here is a simple piece of Java that creates and uses a template on the fly:

StringTemplate query =
  new StringTemplate("SELECT $column$ FROM $table$;");
query.setAttribute("column", "name");
query.setAttribute("table", "User");

where StringTemplate considers anything in $...$ to be something it needs to pay attention to. By setting attributes, you are "pushing" values into the template for use when the template is printed out. The attribute values are set by referencing their names. Invoking query.toString() would yield

SELECT name FROM User;

You can set an attribute multiple times, which simply means that the attribute is multi-valued. For example, adding another setAttribute() call makes attribute column multi-valued:

StringTemplate query =
  new StringTemplate("SELECT $column$ FROM $table$;");
query.setAttribute("column", "name");
query.setAttribute("column", "email");
query.setAttribute("table", "User");

Invoking query.toString() would now yield

SELECT nameemail FROM User;

Ooops...there is no separator between the multiple values. If you want a comma, say, between the column names, then change the template to record that formatting information:

StringTemplate query =
  new StringTemplate("SELECT $column; separator=\",\"$ FROM $table$;");
query.setAttribute("column", "name");
query.setAttribute("column", "email");
query.setAttribute("table", "User");

Note that the right-hand-side of the separator specification in this case is a string literal; therefore, I have escaped the double-quotes as the template is specified in a Java string. In general, the right-hand-side can be any attribute expression. Invoking query.toString() would now yield

SELECT name,email FROM User;

Attributes can be any object at all. StringTemplate calls toString() on each object as it writes the template out. The separator is not used unless the attribute is multi-valued.

Loading Templates From Files

To load a template from the disk you must use a StringTemplateGroup that will manage all the templates you load, caching them so you do not waste time talking to the disk for each template fetch request (you can change it to not cache; see below). You may have multiple template groups. Here is a simple example that loads the previous SQL template from file /tmp/theQuery.st:

SELECT $column; separator=","$ FROM $table$;

The Java code creates a StringTemplateGroup called myGroup rooted at /tmp so that requests for template theQuery forces a load of file /tmp/theQuery.st.

StringTemplateGroup group =
  new StringTemplateGroup("myGroup", "/tmp");
StringTemplate query = group.getInstanceOf("theQuery");
query.setAttribute("column", "name");
query.setAttribute("column", "email");
query.setAttribute("table", "User");

If you have a directory hierarchy of templates such as file /tmp/jguru/bullet.st, you would reference them relative to the root; in this case, you would ask for template jguru/bullet().

Setting the Expression Delimiters

By default, expressions in a template are delimited by dollar signs: $...$. This works great for the most common case of HTML generation because the attribute expressions are clearly highlighted in the text. Sometimes, with other formats like SQL statement generation, you may want to change the template expression delimiters to avoid a conflict and to make the expressions stand out.

The start and stop strings can be anything you want and any size

Specify new delimiters when you create a StringTemplateGroup:

StringTemplateGroup group =
  new StringTemplateGroup("mysqltemplates", "/tmp", "<", ">");
StringTemplate query =
  new StringTemplate(group, "SELECT <column> FROM <table>;");
query.setAttribute("column", "name");
query.setAttribute("table", "User");

All templates created through the group or in anyway associated with the group will assume your new delimiters. It's smart to be consistent across all files of similar type such as "all HTML templates use $...$" and "all SQL templates use <...>".

Caching

By default templates are loaded from disk just once. During development, however, it is convenient to turn caching off. Also, you may want to turn off caching so that you can quickly update a running site. You can set a simple refresh interval using StringTemplateGroup.setRefreshInterval(...). When the interval is reached, all templates are thrown out. Set interval to 0 to refresh constantly (no caching). Set the interval to a huge number like Integer.MAX_INT to have no refreshing at all.

Functionality

Attribute References

Named attributes

The most common thing in a template beside plain text is a simple named attribute reference such as:

Your email: $email$

The template will look up the value of email and insert it into the output stream when you ask the template to print itself out. If email has no value, then it evaluates to the empty string and nothing is printed out for that attribute expression.

Property references

If a named attribute is an aggregate with a property (ala JavaBeans), you may reference that property using attribute.property. For example:

Your name: $person.name$
Your email: $person.email$

StringTemplate ignores the actual object type stored in attribute person and simply invokes getName() and getEmail() via reflection.

Because the type is ignored, you can pass in whatever existing Java aggregate (class) you have such as User or Person:

User u = database.lookupPerson("parrt@jguru.com");
st.setAttribute("person", u);

Or, if a suitable aggregate doesn't exist, you can make a connector or "glue" object and pass that in instead:

st.setAttribute("person", new Connector());

where Connector is defined as:

public class Connector {
    public String getName() { return "Terence"; }
    public String getEmail() { return "parrt@jguru.com"; }
}

The ability to reference aggregrate properties saves you the trouble of having to pull out the properties with Java code like this:

User u = database.lookupPerson("parrt@jguru.com");
st.setAttribute("name", u.getName());
st.setAttribute("email", u.getEmail());

and having template:

Your name: $name$
Your email: $email$

Warning: The latter is more widely applicable and totally decoupled from code and logic; i.e., it's "better" but much less convenient. Be very careful that the property methods do not have any side-effects like updating a counter or whatever. This breaks the rule of order of evaluation independence.

Template References

You may reference other templates to have them included just like the C language preprocessor #include construct behaves. For example, if you are building a web page (page.st) that has a search box, you might want the search box stored in a separate template file, say, searchbox.st. This has two advantages:

  1. You can reuse the template over and over (no cut/paste)
  2. You can change one template and all search boxes change on the whole site.

Using method call syntax, just reference the foreign template:

<html>
<body>
...
$searchbox()$
...
</body>
</html>

The invoking Java code would still just create the overall page and the enclosing page template would automatically create an instance of the referenced template and insert it:

StringTemplateGroup group =
  new StringTemplateGroup("webpages", "/usr/local/site/templates");
StringTemplate page = group.getInstanceOf("page");

If the template you want to reference, say searchbox, is in a subdirectory of the StringTemplateGroup root directory called misc, then you must reference the template as: misc/searchbox().

The included template may access attributes. How can you set the attribute of an included template? There are two ways: inheriting attributes and passing parameters.

Accessing Attributes Of Enclosing Template

Any included template can reference the attributes of the enclosing template instance. So if searchbox references an attribute called resource:

<form ...>
...
<input type=hidden name=resource value=$resource$>
...
</form>

you could set attribute resource in the enclosing template page:

StringTemplate page = group.getInstanceOf("page");
page.setAttribute("resource", "faqs");

This "inheritance" of attributes feature is particularly handy for setting generally useful attributes like siteFontTag.

Passing Parameters To Another Template

Another, more obvious, way to set the attributes of an included template is to pass in values as parameters. In fact, the syntax looks like a set of attribute assignments:

<html>
<body>
...
$searchbox(resource="faqs")$
...
</body>
</html>

where I am setting the attribute of the included searchbox to be the string literal "faqs".

The right-hand-side of the assignment may be any expression such as an attribute reference or even a reference to another template like this:

$boldMe(item=copyrightNotice())$

Attribute Expressions

When setting parameters or testing IF conditionals, you may find it handy to use the plus "string concatenate" operator. For example, when building web pages, you will find it useful to create a template called link and then use it to generate HTML link tags; you may want to change the way every link looks on your site and it's convenient to have one place to change things. The template might look like:

<a href="$url$"><b>$title$</b></a>

Then in a page template you might reference:

...$link(url="http://www.jguru.com", title="jGuru")$...

or you could use attributes to set the link parameters:

...$link(url=person.homePage, title=person.name)$...

Sometimes you may want to compute the URL; usually it is enough to concatenate strings:

...$link(url="/faq/view?ID="+faqid, title=faqtitle)$...

where faqid and faqtitle are attributes you would have to set for the template that referenced link.

You may parenthesize any expression:

...$link(url=("/faq/view?ID="+faqid), title=faqtitle)$...

Template Application

Imagine a simple template called bold:

<b>$item$</b>

Just as with template link described above, you can reference it from a template by invoking it like a method call:

$bold(item=name)$

What if you want something bold and italicized? You could simply nest the template reference:

$bold(item=italics(item=name))$

where template italics is defined as:

<i>$item$</i>

using a different attribute with the same name, item; the attributes have different values just like you would expect if these template references where Java method calls and item were a local variable. Parameters and attribute references are scoped like a programming language.

Think about what you are really trying to say here. You want to say "make name italics and then make it bold", or "apply italics to the name and then apply bold." There is an "apply template" syntax that is a literal translation:

$name:italics():bold()$

where the templates are applied in the order specified from left to right. This is much more clear, particularly if you had three templates to apply:

$name:courierFont():italics():bold()$

For this syntax to work, however, the applied templates have to reference a standard attribute because you are not setting the attribute in a parameter assignment. In general for syntax expr:template(), an attribute called attr is set to the value of expr. So, the definition of bold (and analogously italics), would have to be:

<b>$attr$</b>

to pick up the value of name in our examples above.

Applying Templates To Multi-Valued Attributes

Where template application really shines though is when an attribute is multi-valued. One of the most common web page generation issues is making lists of items either as bullet lists or table rows etc... Applying a template to a multi-valued attribute means that you want the template applied to each of the values.

Consider a list of names (i.e., you set attribute names multiple times) that you want in a bullet list. If you have a template called listItem:

<li>$attr$</li>

then you can do this:

<ul>
$names:listItem()$
</ul>

and each name will appear as a bullet item. For example, if you set names to "Terence", "Tom", and "Jim", then you would see:

<ul>
<li>Terence</li>
<li>Tom</li>
<li>Jim</li>
</ul>

in the output.

Whenever you apply a template to an attribute or multi-valued attribute, the default attribute attr is set. Another attribute i (of type Integer) is also set to the value's index number starting from 0. For example, if you wanted to make your own style of numbered list, you could reference i to get the index:

$names:numberedListItem()$

where template numberedListItem is defined as:

$i+1$. $attr$<br>

Note the use of expression $i+1$ where StringTemplate overloads the plus operator to mean integer add. The result is an Integer object since i is an Integer and so is 1.

In this case, the output would be:

1. Terence<br>
2. Tom<br>
3. Jim<br>

If there is only one attribute value, then i will be 0.

Applying Multiple Templates To Multi-Valued Attributes

The result of applying a template to a multi-valued attribute is another multi-valued attribute containing the results of the application. You may apply another template to the results of the first template application, which comes in handy when you need to format the elements of a list before they go into the list. For example, to bold the elements of a list do the following (given the appropriate template definitions from above):

$names:bold():listItem()$

If you actually want to apply a template to the combined (string) result of a previous template application, enclose the previous application in parenthesis. The parenthesis will force immediate evaluation of the template application, resulting in a string. For example,

$(names:bold()):listItem()$

results in a single list item full of a bunch of bolded names. Without the parenthesis, you get a list of items that are bolded.

Applying Alternating Templates To Multi-Valued Attributes

When generating lists of things, you often need to change the color or other formatting instructions depending on the list position. For example, you might want to alternate the color of the background for the elements of a list. The easiest and most natural way to specify this is with an alternating list of templates to apply to an expression of the form: $expr:t1(),t2(),...,tN()$. To make an alternating list of blue and green names, you might say:

$names:blueListItem(),greenListItem()$

where presumably blueListItem template is an HTML <table> or something that lets you change background color. names[0] would get blueListItem() applied to it, names[1] would get greenListItem(), and names[2] would get blueListItem(), etc...

If names is single-valued, then blueListItem() is applied and that's it.

Applying Anonymous Templates

Some templates are so simple or so unlikely to be reused that it seems a waste of time making a separate template file and then referencing it. StringTemplate provides anonymous subtemplates to handle this case. The templates are anonymous in the sense that they are not named; they are directly applied in a single instance.

For example, to show a name list do the following:

<ul>
$names:{<li>$attr$</li>}$
</ul>

where anything enclosed in curlies is an anonymous subtemplate if, of course, it's within an attribute expression. Note that in the subtemplate, I must enclose the attr reference in the template expression delimiters. You have started a new template exactly like the surrounding template and you must distinguish between text and attribute expressions.

You can apply multiple templates very conveniently. Here is the bold list of names again with anonymous templates:

<ul>
$names:{<b>$attr$</b>}:{<li>$attr$</li>}$
</ul>

The output would look like:

<ul>
<li><b>Terence</b></li>
<li><b>Tom</b></li>
<li><b>Jim</b></li>
</ul>

Anonymous templates work on single-valued attributes as well.

Conditionally Included Subtemplates (IF statements)

There are many situations when you want to conditionally include some text or another template. StringTemplate provides simple IF-statements to let you specify conditional includes. For example, in a dynamic web page you usually want a slightly different look depending on whether or not the viewer is "logged in" or not. Without a conditional include, you would need two templates: page_logged_in and page_logged_out. You can use a single page definition with if(expr) attribute actions instead:

<html>
...
<body>
$if(member)$
$gutter/top_gutter_logged_in()$
$endif$
$if(!member)$
$gutter/top_gutter_logged_out()$
$endif$
...
</body>
</html>

where template top_gutter_logged_in is located in the gutter subdirectory of my StringTemplateGroup.

Notice that I use the inverted expression to simulate an ELSE clause. A lack of ELSE clause was done deliberately for consistency reasons so all attribute actions or expressions evaluate to a value not a choice of values. ELSE clauses are pretty hard to represent in HTML-rendering editors as well.

Template Inheritance

Recall that a StringTemplateGroup is a collection of related templates such as all templates associated with the look of a web site. If you want to design a similar look for that site (such as for premium users), you don't really want to cut-n-paste the original template files for use in the new look. Changes to the original will not be propogated to the new look.

A better approach is to use template inheritance; that is, have one StringTemplateGroup derive from another. This lets you define a new group as it differs from an existing group. For example, imagine a "supergroup" has a bold template definition:

StringTemplateGroup supergroup = new StringTemplateGroup("super");
StringTemplate bold = supergroup.defineTemplate("bold", "<b>$attr$</b>");

Now define a "subgroup" that derives from the "supergroup":

StringTemplateGroup subgroup = new StringTemplateGroup("sub");
subgroup.setSuperGroup(supergroup);

If a template in the subgroup references bold, there would normally be a problem as it's undefined in the subgroup. Because of inheritance, via use of method setSuperGroup(), bold is found in the supergroup:

StringTemplate duh = new StringTemplate(subgroup, "$name:bold()$");
duh.setAttribute("name", "Terence");
System.out.println(duh);
// expecting "<b>Terence</b>";

Template visualization

Sometimes you use or define templates improperly. Either you set an attribute that is not used or forget to set one or reference the wrong template etc... I have made a toy visualization tool via that shows both the attributes and the way StringTemplate breaks up your template into chunks. It properly handles StringTemplate objects as attributes and other nested structures. Here is the way to launch a Swing frame to view your template:

StringTemplate st = ...;
StringTemplateTreeView viz =
    new StringTemplateTreeView("sample",st);
viz.setVisible(true);

Here is a snapshot (note that due some weird font thing on my os x box, the first <html> text chunk is missing in the display.) :( The display is associated with the fill-a-table example below.

Functionality Summary

Syntax Description
<attribute> Evaluates to the value of attribute if it exists else empty string.
<multi-valued-attribute> Concatenation of toString() invoked on each element. If multi-valued-attribute is missing this evaluates to the empty string.
<multi-valued-attribute; separator=expr> Concatenation of toString() invoked on each element separated by expr.
<template(argument-list)> Include template. The argument-list is a list of attribute assignments where each assignment is of the form attribute=expr.
<(expr)(argument-list)> Include template whose name is computed via expr. The argument-list is a list of attribute assignments where each assignment is of the form attribute=expr. Example $(name)()$ looks up name's value and uses that as template name.
<attribute:template(argument-list)> Apply template to attribute. The optional argument-list is evaluated before application so that you can set attributes within template. The default attribute attr is set to the value of attribute. Example: $data:bold() applies bold() to data's value.
<attribute:(expr)(argument-list)> Apply template, whose name is computed from expr, to attribute. The optional argument-list is evaluated before application so that you can set attributes within template. The default attribute attr is set to the value of attribute. Example $data:(name)()$ looks up name's value and uses that as template name to apply to data.
<multi-valued-attribute:template(argument-list)> Concatenate the results of applying template to each element of multi-valued-attribute. The optional argument-list is evaluated before each application so that you can set attributes within template. The default attribute attr is set to the value of each element in turn.
<multi-valued-attribute:template1(argument-list): ... :templateN(argument-list)> Apply multiple templates in order from left to right. The result of a template application upon a multi-valued attribute is another multi-valued attribute. The overall expression evaluates to the concatenation of all elements of the final multi-valued attribute resulting from templateN's application.
<multi-valued-attribute:{anonymous-template}> Concatenate the results of applying a template with no name to each element of multi-valued-attribute.
<multi-valued-attribute:t1(),t2(),...,tN()> Apply an alternating list of templates.
<if(attribute-expr)>subtemplate<endif> If attribute-expr has a value, include subtemplate.
<if(!attribute-expr)>subtemplate<endif> If attribute-expr has no value, include subtemplate.
<if(conditional-expr1)>
...
<if(conditional-expr2)>subtemplate<endif>
...
<endif>
Nested IF constructs. If both conditional-expr1 and conditional-expr2 are true, include subtemplate. If conditional-expr1 is true, then the text surrounding the nested IF is included.

Examples

You should look at org.antlr.stringtemplate.test.TestStringTemplate.java, which contains a few dozen tests.

Fill-a-Table Example

The manner in which a template engine handles filling an HTML table with data often provides good insight into its programming and design strategy. It illustrates the interaction of the model and view via the controller. Using StringTemplate, the view may not access the model directly; rather the view is the passive recipient of data from the model.

First, imagine we have objects of type User that we will pull from a simulated database:

public static class User {
    String name;
    int age;
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() { return name; }
    public int getAge() { return age; }
}

Our database is just a static list:

static User[] users = new User[] {
    new User("Boris", 39),
    new User("Natasha", 31),
    new User("Jorge", 25),
    new User("Vladimir", 28)
};

Here is my simple overall page design template, page.st:

<html>
<head>
<title>$title$</title>
</head>
<body>
<h1>$title$</h1>

$body$

</body>
</html>

The body attribute of page.st will be set to the following template users.inline.st by my web server infrastructure (part of the controller):

<table border=1>
$users:{
  <tr>
    <td>$attr.name$</td><td>$attr.age$</td>
  </tr>
}$
</table>

Again, attr is the default attribute passed to a template when you apply that template to an attribute or attributes. attr.name gets the name property, if it exists, from the attr object ala JavaBeans. That is, StringTemplate uses reflection to call the getName() method on the incoming object. By using reflection, I avoid a type dependence between model and view.

Now, imagine the server and templates are set up to format data. My page definition is part of the controller that pulls data from the model (the database) and pushes into the view (the template). That is all the page definition should do--interpret the data and set some attributes in the view. The view only formats data and does no interpretation.

public static class UserListPage extends SamplePage {
    /** This "controller" pulls from "model" and pushes to "view" */
    public void generateBody(StringTemplate bodyST) throws Exception {
        User[] list = users; // normally pull from database
        // filter list if you want here (not in template)
        bodyST.setAttribute("users", list);
    }

    public String getTitle() { return "User List"; }
}

Notice that the controller and model have no HTML in them at all and that the template has no code with side-effects or logic that can break the model-view separation. If you wanted to only see users with age < 30, you would filter the list in generateBody() rather than alter your template. The template only displays information once the controller pulls the right data from the model.

Pushing factorization further, you could make a row.st component in order to reuse the table row HTML:

<tr>
  <td>$attr.name$</td><td>$attr.age$</td>
</tr>

Then the user list template reduces to the more readable:

<table border=1>
$users:row()$
</table>

Naturally, you could go one step further and make another component for the entire table (putting it in file table.st):

<table border=1>
$elements:row()$
</table>

then the body template would simply be:

$table(elements=users)$

A Common Syntax Question

Why have a template like:

Check this faq entry: $link(url="/faq/view?ID="+id, title="A FAQ")$.

instead of

Check this faq entry: $link(url="/faq/view?ID="+$id$, title="A FAQ")$.

using $id$ instead of id for that attribute reference? The simplest answer is that you are already within the scope of an attribute expression between the $...$ and hence StringTemplate knows that id must be a reference to an attribute by the grammar alone. The template delimiters mark the beginning and end of what StringTemplate cares about.

Another way to look at it is the following. Surely syntax (i) makes the most sense:

(i) SELECT $col+blort$ FROM $table$

(ii) SELECT $$col$+$blort$$ FROM $table$

(iii) SELECT $col$+$blort$ FROM $table$

Syntax (ii) is rather verbose and redundant wouldn't you say? Syntax (iii) doesn't even work because the + is outside the realm of StringTemplate delimiters.

Complete Grammar

A StringTemplate is "compiled" to a list of text chunks and attribute actions (expressions or conditional includes). Anything outside of the StringTemplate start/stop tags (defaults to $...$) is ignored. The ANTLR grammar inside the attribute action is:

action
    :   templatesExpr (SEMI optionList)?
    |   "if" LPAREN ifCondition RPAREN
    ;

optionList
    :   "separator" ASSIGN expr
    ;

ifCondition
    :   ifAtom
    |   NOT ifAtom
    ;

ifAtom
    :   expr
    ;

expr:   atom (PLUS atom)*
    ;

atom:   attribute
    |   (templateInclude)=>templateInclude // (see past parens to arglist)
    |   LPAREN expr RPAREN  // regular old parens
    ;

templatesExpr
    :   expr ( COLON template (COMMA template)* )*
    ;

nonAlternatingTemplateExpr
    :   expr ( COLON template )*
    ;

template
    :   (   namedTemplate       // foo()
        |   anonymousTemplate   // {foo}
        )
    ;

namedTemplate
    :   ID argList
    ;

anonymousTemplate
    :   t:ANONYMOUS_TEMPLATE
    ;

attribute
    :   ID
    |   objPropertyRef
    |   STRING
    |   INT
    ;

templateInclude
    :   ID argList
    ;

objPropertyRef
    :   ID (DOT^ ID)+
    ;

argList
    :   LPAREN RPAREN
    |   LPAREN argumentAssignment (COMMA argumentAssignment)* RPAREN
    ;

argumentAssignment
    :   ID ASSIGN nonAlternatingTemplateExpr
    ;

An identifier is:

ID  :	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|'/')*
    ;

Strings are double-quoted with optional embedded escaped characters:

STRING
    :   '"' (ESC_CHAR | ~'"')* '"'
    ;

Anonymous templates are enclosed in curlies:

ANONYMOUS_TEMPLATE
    :   '{' (ESC_CHAR | ~'}')* '}'
    ;

The allowed escapes are:

ESC_CHAR
    :   '\\'
        (   'n'
        |   'r'
        |   't'
        |   'b'
        |   'f'
        |   '"'
        |   '\\'
        )
     ;

The other unspecified operators are:

COMMA  : ',' ;
DOT    : '.' ;
ASSIGN : '=' ;
COLON  : ':' ;
PLUS   : '+' ;
SEMI   : ';' ;
NOT    : '!' ;
LPAREN : '(' ;
RPAREN : ')' ;