Tutorial

Introduction

SimpleTemplate generates text using templates you provide. A template is plain text interspersed with template directives. While the rest of the text is emitted as it is into the output, SimpleTemplate processes the template directives and replaces the directives with data from the model.

TemplateReader, Template and Scope

For reading a template, you use a TemplateReader. You can create a TemplateReader by either using the constructor or using one of the static helper functions.
TemplateReader reader = new TemplateReader(url, startToken, endToken);
TemplateReader reader = TemplateReader.fromFile(fileName);
TemplateReader reader = TemplateReader.fromFile(fileName, startToken, endToken);
TemplateReader reader = TemplateReader.fromString(s);
TemplateReader reader = TemplateReader.fromString(s, startToken, endToken);
TemplateReader reader = TemplateReader.fromResource(resource);
TemplateReader reader = TemplateReader.fromResource(resource, startToken, endToken);

Example:

TemplateReader reader = TemplateReader.fromString(“Hello $greeting$”);
In the first form, url refers to a java.lang.URL. All other methods invoke this constructor. By default, SimpleTemplate uses “$” as directive separator. You can override this by using one of the methods that accepts startToken and endToken parameters.
The ‘include’ SimpleTemplate directive uses the given resource or file as the base for finding included templates. In case of a TemplateReader that is constructed using one of fromString variants, the working directory is used as the base instead.
Once you have a TemplateReader, use readTemplate() method to parse and extract a Template from the input.
Template template.readTemplate();
Once you have a template, you need to pass the java model and get the results. You use a Scope object to pass the model details and apply method to get the results. You can put any number of objects in a scope using different names. Using the same name twice, replaces the previous object with a later one.
Scope scope = new Scope();
scope.put(“greeting”, “World”) ;
String result = template.apply(scope);
The apply method processes the template and returns the result as a String. The result will contain “Hello World”.

Template Syntax

SimpleTemplate input is just plain text interspersed with template directives. Any number of model variables can be passed to the template through the scope object.

Contexts and Data Types

SimpleTemplate operates in two contexts. When you start reading a file it is in Text context. In Text context all text input is emitted into the output. SimpleTemplate moves into Template context when the stream reaches one of the template directives (i.e one of $identifier, $if, etc.). In the Template context, SimpleTemplate reads text as tokens and can identify integers, booleans (true/false) and strings. Each of these data types, themselves, is a template. You can switch into a block context using ${}$ block. See Block Statements.

Attribute Access

The simplest directive in a template is referencing to a model attribute. You can refer to an attribute reference such as name and email from scope:
Name is $name$ and email is $email$
The $name$ and $email$ in the template will be replaced with the return values from the toString method of the objects put in the scope as “name” and “email”.
You can also access nested attributes.
Address address = new Address();
address.setName(“John Doe”);
address.setEmail(“john.doe@example.com”);
scope.put(“address”, address);
With scope setup with a composite object like address:
Name is $address.name$ and email is $address.email$
gives a result “Name is John Doe and email is john.doe@example.com”. SimpleTemplate just emits an empty string for the attributes that are null. When an attribute needs to be resolved, SimpleTemplate looks for methods that start with 'get', 'is' and '' it that order. For example, address.email returns the value of if it exists. If it does not exist
When an attribute (nested or otherwise) refers to a java.util.Collection, SimpleTemplate handles it differently. SimpleTemplate emits a concatenation of toString values of each item separated by st_list_separator. Also each list item is prefixed with st_list_prefix and suffixed with st_list_suffix.
With scope setup as:
String[] flowers = new String[] { “Rose”, “Jasmine”, “Lily” } ;
scope.put(“list”, flowers);
The following script:
$set st_list_separator to “--”
$set st_list_prefix to “<flower:”
$set st_list_suffix to “/>”
$list$
gives a result “<flower:Rose/>--<flower:Jasmine/>--<flower:Lily/>”. The st_list_prefix and st_list_suffix defaults to empty strings and st_list_separator defaults to “,”.

Indexed Access

You can access items from a Collection, Array or a Map using indexed access. Setting up scope as follows,
String[] flowers = new String[] { “Rose”, “Jasmine”, “Lily” } ;
scope.put(“flowers”, flowers);
the following template
$flowers[0]$
gives a result "Rose". Similarly, with a map set into scope:
Map<String, String> capitals = new HashMap<String, String();
capitals.put("India", "New Delhi");
capitals.put("United States", "Washington");
capitals.put("Canada", "Ottawa");
scope.put(“capitals”, capitals);
the following template
$capitals["United States"]$
gives a result "Washington". You can even use an expression as an index. For accessing items from a Collection, the expression should be resolved into an integer. For example, with scope setup as
Map<String, String> capitals = new HashMap<String, String();
capitals.put("India", "New Delhi");
capitals.put("United States", "Washington");
capitals.put("Canada", "Ottawa");
scope.put(“capitals”, capitals);
scope.put("mycountry", "India");
the following template
$capitals[mycountry]$
gives a result "New Delhi". You get the same result even you use a template instruction as an index. So,
$capitals[$mycountry$]$
also gives a result "New Delhi".

Looping

You can loop through a Map or a Collection with SimpleTemplate looping directive.
List of flowers available:
$flowers {
$index1$: $it$
}$

$capitals {
Capital of $key$ is $value$
}$
Withing the scope of the looping over a Collection or Array, SimpleTemplate sets index0 to the current item index (starting with 0), index1 to the current item index (starting with 1) and it to the item itself. For Maps, key is set to key of the current entry and value is set to the value.

You can also loop through a Map using the values or keySet (though it is nothing to do with SimpleTemplate).
Known Capitals: $capitals.keySet { $it$ }

Introducing Aliases

Sometimes, you need to access attributes that are deeply nested in the model. You can introduce aliases into the current scope and reduce the amount of typing you need to do as well as make the template code look good. SimpleTemplate adds the aliases it, index0, index1, key and value in the looping context. You can introduce your own aliases using set and with directives to SimpleTemplate.

Suppose we have a AddressBook object which has a list of AddressBookEntrys. Each AddressBookEntry further has a list of PhoneNumbers each with a type and a number. For iterating through all the phone numbers in an AddressBook we might write:
$addressBook.addresses {
Name: $it.firstName$ $it.lastName$
$it.phoneNumbers {
$it.type$ phone: $it.number$
}$
}$

The same thing can also be written (using with):
$addressBook.addresses {
$with it {
Name: $firstName$ $lastName$
$phoneNumbers {
$with it {
$type$ phone: $number$
}$
}$
}$
}$
In this academic example the amount of the template text for the example with with is more. However, in real world where you need to work with deeply nested java objects, the with directive saves time and makes the template code read cleaner.

We can also use set directive to create an alias. The same code above written using set :

$addressBook.addresses {
$set address to it
Name: $address.firstName$ $address.lastName$
$address.phoneNumbers {
$with it {
$type$ phone: $number$
}$
}$
}$

Conditionals

There will be times in when you want to conditionally emit text. For those cases SimpleTemplate supports if-else and if constructs. Most expressions can be used as conditions to test in a if statement. An object that evaluates to null is false. A Collection, Map, Array and String returns false if it is empty. All other cases the expression evaluates to be true.

Using conditionals you can check whether a Collection is empty before you process it:
This address book belongs to: $addressBook.belongsTo$
$ifelse $addressBook.addresses {
$addressBook.addresses {
$set address to it
Name: $address.firstName$ $address.lastName$
$address.phoneNumbers {
$with it {
$type$ phone: $number$
}$
}$
}$
}
{
"No address book does not have any entries"
}$

Block Statements

SimpleTemplate supports two types block statements. You have already seen one of them - the looping constructs and conditional examples use {}$ blocks. The other type of block statement starts with ${ and ends with }$. The difference between these two kinds of block statements is the way they are evaluated. The {}$ block is evaluated as text and it escapes into the template mode when it sees any one of the template directives. The ${ block is evaluated in template syntax. When SimpleTemplate reads a template it starts evaluating in text mode.
For examples,
${ "Hello World" }$
evaluates to Hello World where as,
${ {"Hello World"}$ }$
evaluates to "Hello World" (Note the double quotes around the text). You can use {}$ blocks only in a template context because in Text context the { will be considered as text.

Defining and Invoking Methods

When you are defining templates, there are times the same template code is repeated over and over again. You can use methods to reduce the repetition and make the template code clearer.

For example,
$printAddressDetails (address) {
<address>
<firstname>$address.firstName$</firstname>
<lastname>$address.lastName$</lastname>
<email>$address.email$</email>
</address>
}$
Defines a method that takes one parameter - address. You can invoke this method by:
$:printAddressDetails(address)$
You can also invoke this method by,
$address:printAddressDetails()$
When you invoke the method using the first form the evaluated value of the expression before : is passed to the method as the first parameter.
Your methods can take any number of parameters. SimpleTemplate matches the number of parameters and throws an exception if they do not match. You can also chain multiple methods - each one taking the return value of the previous one. So,
$italics(s) ${ "<i>" $s$ "</i>" }$
$bold(s) ${ "<b>" $s$ "</b>" }$

${ $set hello to "Hello" $hello:bold():italics()$ }$
Gives an out put of "<i><b>Hello</b></i>".

Reusing templates using include

The ultimate reuse is however, to use a complete template or methods in another template. SimpleTemplate provides a inclusion mechanism to achieve this.

File: address.st

$printAddressDetails (address) {
<address>
<firstname>$address.firstName$</firstname>
<lastname>$address.lastName$</lastname>
<email>$address.email$</email>
</address>
}$

File: template.st

$include "address.st"
$addressBook.addresses {
$it:printAddressDetails()$
}$
With both the files in the same directory, processing template.st includes the definition of printAddressDetails method and the result will contain the address details.

Next Steps

This concludes this tutorial. The downloads includes the examples that are discussed in this tutorial in the examples directory along with a build file. You can find help and support on SimpleTemplate google groups.

Just one more thing...

When developing SimpleTemplate I started with a class heirarchy that contained a TemplateElement, CompositeTemplateElement, SimpleTemplateElement etc. In the end, clubbing all of them together made better sense. So everything in SimpleTemplate is a TemplateElement - either it is a block statement or an if statement. And any of them can be used any where. Play around - it is fun to find things that work and that do not work (because the Parser imposes restrictions).