Examples

Examples

Java

You should look at org.antlr.stringtemplate.test.TestStringTemplate.java, which contains many tests.

C#

You should look at Antlr.Stringtemplate.Tests.TestStringTemplate in the StringTemplateTests project, which contains many tests.

Python

You should look at stringtemplate.test.TestStringTemplate, which contains many 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:

Java

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; }
}

C#

public class User {
    string name;
    int age;
    public User(string name, int age) {
        this.name = name;
        this.age = age;
    }
    public string Name { get { return name;} }
    public int Age     { get { return age; } }
}

Python

class User(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def getName(self):
        return self.name
    def getAge(self):
        return self.age

Our database is just a static list:

Java

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

C#

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

Python

users = [ User("Boris", 39), \
          User("Natasha", 31),
          User("Jorge", 25),
          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>$it.name$</td><td>$it.age$</td>
  </tr>
}$
</table>

Again, it is the default attribute passed to a template when you apply that template to an attribute or attributes. it.name gets the name property, if it exists, from the it object. That is, StringTemplate uses reflection to call the Name property, then the get_Name() method then others including 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.

Java

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"; }
}

C#

public class UserListPage : SamplePage {
    /** This "controller" pulls from "model" and pushes to "view" */
    public void generateBody(StringTemplate bodyST) {
        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"; }
}

Python

class UserListPage(SamplePage):
    ## This "controller" pulls from "model" and pushes to "view"
    #
    def generateBody(self, bodyST):
        self.list = users      # normally pull from database
        # filter list if you want here (not in template)
        bodyST["users"] = self.list

    def getTitle(self):
        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>$it.name$</td><td>$it.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)$

Internationalization and localization

StringTemplate provides a simple and effective method for localizing web pages. The goal is to alter a page based upon the locale; that is, page strings or other content must change depending on a locale. This article not only illustrates how to make a pages change text depending on locale, it shows how the same site may easily have two different skins (site "looks").

This technique works well in practice for real sites. Schoolloop.com is a case in point. Click on the link that says "en espanol" to flip the site into Spanish mode. The exact same templates are used; all strings are pulled from a serious of resource bundles. There is no duplication of pages to change the strings.

Multiple skins

First let's look at multiple skins in order to show how templates are loaded for this example.

Multiple site looks are organized into their own directories. A StringTemplateGroup object rooted at that directory will load templates directly from there. In my example, I made two skins, blue and red. Here is how the group is loaded:

Java

String skin="blue";
ClassLoader cl = Thread.currentThread().getContextClassLoader();
// get a template group rooted at appropriate skin
String absoluteSkinRootDirectoryName = cl.getResource(skin).getFile();
StringTemplateGroup templates =
    new StringTemplateGroup("test", absoluteSkinRootDirectoryName);

C#

string skin="blue";
// get a template group rooted at appropriate skin
// we are using a path relative to our application assembly's location
string absoluteSkinRootDirectoryName =
    Path.Combine(new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).Parent.FullName, skin);
StringTemplateGroup templates =
    new StringTemplateGroup("test", absoluteSkinRootDirectoryName);

Then, when you ask for an instance of a page, it pulls from whichever directory skin is set to:

Java

StringTemplate page1ST = templates.getInstanceOf("page1");

C#

StringTemplate page1ST = templates.GetInstanceOf("page1");

Here is page 1 in the blue skin:

<html>
<title>$strings.page1_title$</title>
<body>
<font color=blue>
<p>$strings.intro$

<p>$strings.page1_content$
</font>
</body>
</html>

and here is page 2:

<html>
<title>$strings.page2_title$</title>
<body>
<font color=blue>
<p>$strings.page2_content$
</font>
</body>
</html>

For the red here is page 1:

<html>
<title>$strings.page1_title$</title>
<body>
<font color=red>
<h1>$strings.page1_title$</h1>

<p>$strings.intro$
<hr>
<p>$strings.page1_content$
</font>
</body>
</html>

and page 2:

<html>
<title>$strings.page2_title$</title>
<body>
<font color=red>
<h1>$strings.page2_title$</h1>
<hr>
<p>$strings.page2_content$
</font>
</body>
</html>

The thing to note is that there is no text, just formatting in these page templates. Those strings from the strings attribute are used for all text that could change per locale.

Localizing template strings

For a more, please see http://www.cs.usfca.edu/~parrt/papers/i18n.pdf.

Once the code knows how to load templates, the locale must dictate which strings are displayed. The templates clearly have attribute references pulling from an object instance labelled strings (it could be a hash table).

Java

Assuming that there are a few different strings and that they are stored in a Java properties file called [language-code].strings where [language-code] is the two-letter language code. Here is the en.strings file:

intro=Welcome to my test page for internationalization with StringTemplate
page1_title=Page 1 testing I18N
page2_title=Page 2 testing I18N
page1_content=This is page 1's simple page content
page2_content=This is page 2's simple page content

and here is the fr.strings file:

intro=Bienvenue sur la page de test d'internationalisation avec StringTemplate
page1_title=Page 1 test de I18N
page2_title=Page 2 test de I18N
page1_content=Le contenu de la page 1
page2_content=Le contenu de la page 2

To load these per the current locale is pretty easy:

// use Locale to get 2 letter country code for this computer
Locale locale = Locale.getDefault();
String defaultLanguage = locale.getLanguage();
// allow them to override language from argument on command line
String language = defaultLanguage;
if ( args.length>0 ) {
    language = args[0];
}
// load strings from a properties files like en.strings
URL stringsFile = cl.getResource(language+".strings");
if ( stringsFile==null ) {
    System.err.println("can't find strings for language: "+language);
    return;
}
Properties strings = new Properties();
InputStream is = stringsFile.openStream();
strings.load(is);

The strings properties object is just a Hashtable so you can directly pass to StringTemplate templates as an attribute:

StringTemplate page1ST = templates.getInstanceOf("page1");
page1ST.setAttribute("strings", strings);

To generate the page, just say:

System.out.println(page1ST);

C#

For this example, I'm assuming that I have a few different strings and that I'm storing them in resource files called Content.Strings.resx. The langauge specific alternatives are named Content.Strings[language-code].resx where language-code is the two-letter language code. Here are the name/value entries stored in Content.Strings.resx:

intro=Welcome to my test page for internationalization with StringTemplate
page1_title=Page 1 testing I18N
page2_title=Page 2 testing I18N
page1_content=This is page 1's simple page content
page2_content=This is page 2's simple page content

and here are the entries in the Content.Strings.fr.resx file:

intro=Bienvenue sur la page de test d'internationalisation avec StringTemplate
page1_title=Page 1 test de I18N
page2_title=Page 2 test de I18N
page1_content=Le contenu de la page 1
page2_content=Le contenu de la page 2

To load these per the current locale is pretty easy:

// allow them to override language from argument on command line
String language = defaultLanguage;
if ( args.length>0 ) {
    language = args[0];
}
Thread.CurrentThread.CurrentUICulture = new CultureInfo(language);
ResourceManager resMgr =
    new ResourceManager("ST.Examples.i18n.Content.Strings", typeof(ResourceWrapper).Assembly);
ResourceWrapper strings = new ResourceWrapper(resMgr);

The strings properties object is just a wrapper around the ResourceManager so you can directly pass to StringTemplate templates as an attribute:

Here's are the relevant bits of the wrapper class:

class ResourceWrapper
{
	ResourceManager mgr;
	public ResourceWrapper(ResourceManager mgr) { this.mgr	= mgr; }

	public string intro           { get { return mgr.GetString("intro"); } }
	.......
	.......
	public string page2_content   { get { return mgr.GetString("page2_content"); } }
}

And here's how this is passed directly to the StringTemplate:

StringTemplate page1ST = templates.GetInstanceOf("page1");
page1ST.SetAttribute("strings", strings);

To generate the page, just say:

Console.Out.WriteLine(page1ST);

The output will be (for the en locale):

<html>
<title>Page 1 testing I18N</title>
<body>
<font color=blue>
<p>Welcome to my test page for internationalization with
StringTemplate

<p>This is page 1's simple page content
</font>
</body>
</html>

If I change the locale to fr then without changes templates, the following is generated:

<html>
<title>Page 1 test de I18N</title>
<body>
<font color=blue>
<p>

<p>Le contenu de la page 1
</font>
</body>
</html>

Source and compilation

Java

Here is the code including the strings and skins:

C#

Here is the code including the strings and skins:

You naturally need ANTLR too; get it here.

You can compile this example like this:

nant -t:net-1.1

Python

Here is the code including the strings and skins:

Summary

In summary, the key element to demonstrate here is that you do not have to duplicate all of your templates to change what they say. You can leave the formatting alone and, with a simple hashtable pulled from a data file, push in the proper strings per the locale. I also took the opportunity to show off just how easy it is to make multiple site skins.