Table of Contents for Expressions
Expressions
Attribute References
Named attributes
The most common thing in a template besides plain text is a simple named attribute reference such as:
Your email: $email$
If the attribute is multi-value such as an instance of a list, the elements are emitted without separator one after the other. If there are null values in the list, these are ignored by default. Given template $values$ with attribute values=9,6,null,2,null then the output would be:
962
$values; separator=", "$
9, 6, 2
$values; null="-1", separator=", "$
9, 6, -1, 2, -1
Property references
If a named attribute is an aggregate with a property or a simple data field, you may reference that property using attribute.property. For example:
Your name: $person.name$ Your email: $person.email$
| Java |
|
|---|---|
| C# |
|
| Python |
|
An exception is thrown if that property is not defined on the target object.
Because the type is ignored, you can pass in whatever existing aggregate (class) you have such as User or Person:
| Java | User u = database.lookupPerson("parrt@jguru.com"); st.setAttribute("person", u); |
|---|---|
| C# | User u = database.LookupPerson("parrt@jguru.com"); st.SetAttribute("person", u); |
| Python | u = database.lookupPerson("parrt@jguru.com") st["person"] = u |
Or, if a suitable aggregate doesn't exist, you can make a connector or "glue" object and pass that in instead:
| Java | st.setAttribute("person", new Connector()); |
|---|---|
| C# | st.SetAttribute("person", new Connector()); |
| Python | st["person"] = Connector()
|
where Connector is defined as:
| Java | public class Connector { public String getName() { return "Terence"; } public String getEmail() { return "parrt@jguru.com"; } } |
|---|---|
| C# | public class Connector { public string Name { get {return "Terence";} } public string Email { get { return "parrt@jguru.com";} } } |
| Python | class Connector(object):
def getName(self):
return "Terence"
def getEmail(self):
return "parrt@jguru.com"
|
The ability to reference aggregrate properties saves you the trouble of having to pull out the properties with code like this:
| Java | User u = database.lookupPerson("parrt@jguru.com"); st.setAttribute("name", u.getName()); st.setAttribute("email", u.getEmail()); |
|---|---|
| C# | User u = database.lookupPerson("parrt@jguru.com"); st.SetAttribute("name", u.Name); st.SetAttribute("email", u.Email); |
| Python | u = database.lookupPerson("parrt@jguru.com") st["name"] = u.getName() st["email"] = u.getEmail() |
and having template:
Your name: $name$ Your email: $email$
| 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. |
Indirect property names
Sometimes the property name is itself variable, in which case you need to use indirect property access notation:
$person.(propertyName)$
propertyName may actually be an expression instead of a simple attribute name.
Map key/value pair access
| Java | You may pass in instances of any object that implements the Map interface. Rather than creating an aggregate object (though automatic aggregate creation is discussed in the next section) you can pass in a HashMap that has keys referencable within templates. For example, StringTemplate a = new StringTemplate("$user.name$, $user.phone$"); HashMap user = new HashMap(); user.put("name", "Terence"); user.put("phone", "none-of-your-business"); a.setAttribute("user", user); String results = a.toString(); yields a result of "Terence, none-of-your-business". |
|---|---|
| C# | You may pass in instances of type Hashtable and ListDictionary but cannot pass in objects implementing the IDictionary interface because that would allow all sorts of wacky stuff like database access. Rather than creating an aggregate object (though automatic aggregate creation is discussed in the next section) you can pass in a Hashtable that has keys referencable within templates. For example, StringTemplate a = new StringTemplate("$user.name$, $user.phone$"); Hashtable user = new Hashtable(); user.Add("name", "Terence"); user.Add("phone", "none-of-your-business"); a.SetAttribute("user", user); string results = a.ToString(); yields a result of "Terence, none-of-your-business". |
| Python | You may pass in instances of type dict. Rather than creating an aggregate object (though automatic aggregate creation is discussed in the next section) you can pass in a dict that has keys referencable within templates. For example, a = stringtemplate3.StringTemplate("$user.name$, $user.phone$") user = {} user["name"] = "Terence" user["phone"] = "none-of-your-business" a["user"] = user results = str(a) yields a result of "Terence, none-of-your-business". |
StringTemplate interprets Map objects to have two predefined properties: keys and values that yield a list of all keys and the list of all values, respectively. When applying a template to a map, StringTemplate iterates over the values so that <aMap> is a shorthand for <aMap.values>. Similarly <aMap.keys> walks over the keys. You can list all of the elements in a map like this:
<aMap.keys:{k| <k> maps to <aMap.(k)>}>.
Difficult property names
Some property names cause parse errors because of clashes with built in keywords or because they do not match the rules for IDs as used by String Template. These difficult property names can be dealt with by quoting the property name in combination with the indirect property construct:
$person.("first")$ --- Build in keyword
$person.("1")$ --- non ID conforment name
| Be careful that the keys are the appropriate type. If person keys are Integer, $person.("1")$ won't work as Strings are never Integers. |
Automatic aggregate creation
Creating one-off data aggregates is a pain, you have to define a new class just to associate two pieces of data. StringTemplate makes it easy to group data during setAttribute() calls. You may pass in an aggregrate attribute name to setAttribute() with the data to aggregate:
| Java | StringTemplate st = new StringTemplate("$items:{$it.(\"last\")$, $it.(\"first\")$\n}$"); st.setAttribute("items.{first,last}", "John", "Smith"); st.setAttribute("items.{first,last}", "Baron", "Von Munchhausen"); String expecting = "Smith, John\n" + "Von Munchhausen, Baron\n"; |
|---|---|
| C# | StringTemplate st = new StringTemplate("$items:{$it.(\"last\")$, $it.(\"first\")$\n}$"); st.SetAttribute("items.{first,last}", "John", "Smith"); st.SetAttribute("items.{first,last}", "Baron", "Von Munchhausen"); string expecting = "Smith, John\n" + "Von Munchhausen, Baron\n"; |
| Python | st = stringtemplate3.StringTemplate("$items:{$it.(\"last\")$, $it.(\"first\")$\n}$") st.setAttribute("items.{first,last}", "John", "Smith") st.setAttribute("items.{first,last}", "Baron", "Von Munchhausen") expecting = \ "Smith, John\n" + \ "Von Munchhausen, Baron\n" |
Note that the template, st, expects the items to be aggregates with properties first and last. By using attribute name
items.{first,last}
The various overloads of the setAttribute() method can handle from 1 to 5 arguments. The C# version uses variable-length argument list (using params keyword).
List construction
As of v2.2, you may combine multiple attributes into a single multi-valued attribute in a syntax similar to the group map feature. Concatenate attributes by placing them in square brackets in a comma-separated list. For example,
$[mine,yours]$
Naturally you may combine the list construction with template application:
$[mine,yours]:{ v | ...}$
$mine,yours:{ x,y | ...}$
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:
- You can reuse the template over and over (no cut/paste)
- 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>
| Java | StringTemplateGroup group = new StringTemplateGroup("webpages", "/usr/local/site/templates"); StringTemplate page = group.getInstanceOf("page"); |
|---|---|
| C# | StringTemplateGroup group = new StringTemplateGroup("webpages", "C:/Inetpub/wwwroot/site/templates"); StringTemplate page = group.GetInstanceOf("page"); |
| Python | group = stringtemplate3.StringTemplateGroup("webpages", "/usr/local/site/templates") 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>
| Java | StringTemplate page = group.getInstanceOf("page"); page.setAttribute("resource", "faqs"); |
|---|---|
| C# | StringTemplate page = group.GetInstanceOf("page"); page.SetAttribute("resource", "faqs"); |
| Python | page = group.getInstanceOf("page") page["resource"] = "faqs" |
This "inheritance" (dynamic scoping really) of attributes feature is particularly handy for setting generally useful attributes like siteFontTag in the outermost body template and being able to reference it in any nested template in the body.
Passing Parameters To Another Template
Another, more obvious, way to set the attributes of an included template is to pass in values as parameters, making them look like C macro invocations rather than includes. The syntax looks like a set of attribute assignments:
<html> <body> ... $searchbox(resource="faqs")$ ... </body> </html>
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())$
$bold(it={$firstName$ $lastName$})$
If you are using StringTemplate groups, then you have formal parameters and for those templates with a sole formal argument, you can pass just an expression instead of doing an assignment to the argument name. For example, if you do $bold(name)$ and bold has one formal argument called item, then item gets the value of name just as if you had said {$bold(item=name)$}.
Allowing enclosing attributes to pass through
When template x calls template y, the formal arguments of y hide any x arguments of the same because the formal parameters force you to define values. This prevents surprises and makes it easy to ensure any parameter value is empty unless you specifically set it for that template. The problem is that you need to factor templates sometimes and want to refine behavior with a subclass or just invoke another shared template but invoking y as <y()> hides all of x's parameters with the same name. Use <y(...)> syntax to indicate y should inherit all values even those with the same name. <y(name="foo", ...)> would set one arg, but the others are inherited whereas <y(name="foo")> only has name set; all other arguments of template y are empty. You can set manually with:
| Java | StringTemplate.setPassThroughAttributes() |
|---|---|
| C# | StringTemplate.SetPassThroughAttributes() |
| Python | st.passThroughAttributes = True |
Argument evaluation scope
The right-hand-side of the argument assignments are evaluated within the scope of the enclosing template whereas the left-hand-side attribute name is the name of an attribute in the target template. Template invocations like $bold(item=item)$ actually make sense because the item on the right is evaluated in a different scope.
Attribute operators
StringTemplate provides a number of operators that you can apply to attributes to get a new view of that data: first, rest, last, length, strip.
Sometimes you need to treat the first or last element of multi-valued attribute differently than the others. For example, if you have a list of integers in an attribute and you need to generate code to sum those numbers, you could start like this:
<numbers:{ n | sum += <n>;}>
int sum = 0;
<numbers:{ n | sum += <n>;}>
<if(numbers)>int sum = 0;<endif> <numbers:{ n | sum += <n>;}>
<first(numbers):{ n | int sum = <n>;}>
<rest(numbers):{ n | sum += <n>;}>
The other operator available to you is last, which naturally results in the last value of a multi-valued attribute. Now we have trunc also which returns all but the last value.
Special cases:
- operations on empty attributes yields an empty value
- rest of a single-valued attribute yields an empty value
- tail of a single-valued attribute yields the same as first, the attribute value
You may find it handy to use another operator sometimes: plus "string concatenate". operator. For example, you may want to compute an argument to a template using a literal and an attribute:
...$link(url="/faq/view?ID="+faqid, title=faqtitle)$...
the template that referenced link.
| Terence says I'm a little uncomfortable with this concatenation operation. Please use a template instead ...$link(url={/faq/view?ID=$faqid$}, title=faqtitle)$...
|
int data[$length(x)$] = { $x; separator=", "$ };
int data[3] = { 5, 2, 9 };
int data[$length(strip(x))] = { $x; separator=", "$ };
Template Application
Imagine a simple template called bold:
<b>$item$</b>
$bold(item=name)$
$bold(item=italics(item=name))$
<i>$item$</i>
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()$
$name:courierFont():italics():bold()$
<b>$it$</b>
As of 2.2 StringTemplate, you can avoid using it as a default parameter by using formal arguments. For expression $x:y()$, StringTemplate will assign the value of x to it and any sole formal argument of y. For example, if y is:
y(item) ::= "_$item$_"
If the attribute to which you are applying a template is null (i.e., missing), then the application is not done as there is no work to do. Optionally, you can specify what string template should display when the attribute is null a using the null option:
$name:bold(); null="n/a"$
$if(name)$$name:bold()$$else$n/a$endif$
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>$it$</li>
<ul> $names:listItem()$ </ul>
<ul> <li>Terence</li> <li>Tom</li> <li>Kunle</li> </ul>
Whenever you apply a template to an attribute or multi-valued attribute, the default attribute it is set. Similarly, attributes i and i0 (since v3.0) of type integer are set to the value's index number starting from 1 (i0 starts 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()$
$i$. $it$<br>
1. Terence<br> 2. Tom<br> 3. Kunle<br>
$i0$. $it$<br>
0. Terence<br> 1. Tom<br> 2. Kunle<br>
numberedListItem(item) ::= "$i$. $item$<br>"
$names:bold(); null="n/a"$
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()$
$(names:bold()):listItem()$
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()$
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>$it$</li>}$
</ul>
You can apply multiple templates very conveniently. Here is the bold list of names again with anonymous templates:
<ul>
$names:{<b>$it$</b>}:{<li>$it$</li>}$
</ul>
<ul> <li><b>Terence</b></li> <li><b>Tom</b></li> <li><b>Kunle</b></li> </ul>
As of 2.2, you may define formal arguments on anonymous templates even if you are not using StringTemplate groups. This syntax is borrowed from Smalltalk though it is identical in function to lambda of Python. Use a comma-separated list of argument names followed by the '|' "pipe" symbol. Any single whitespace character immediately following the pipe is ignored. The following example bolds the names in a list using an argument to avoid the monotonous use of it:
<ul>
$names:{ n | <b>$n$</b>}$
</ul>
Anonymous template application to multiple attributes
In some cases, the model may present data to the view as separate columns of data rather than as a single list of objects, such as multi-valued attributes names and phones rather than a single users multi-valued attribute. As of 2.2, you may iterate over multiple attributes:
$names,phones:{ n,p | $n$: $p$}$
Indirect template references
Sometimes the name of the template you would like to include is itself a variable. So, rather than using "<item:format()>" you want the name of the template, format, to be a variable rather than a literal. Just enclose the template name in parenthesis to indicate you want the immediate value of that attribute and then add () like a normal template invocation and you get "<item:(someFormat)()>", which means "look up attribute someFormat and use its value as a template name; appy to item." This deliberately looks similar to the C function call indirection through a function pointer (e.g., "(*fp)()" where fp is a pointer to a function). A better way to look at it though is that the (someFormat) implies immediately evaluate someFormat and use as the template name.
Usually this "variable template" situation occurs when you have a list of items to format and each element may require a different template. Rather than have the controller code create a bunch of instances, one could consider it better to have StringTemplate do the creation--the controller just names what format to use.
If StringTemplate did not have a map definition, you could simulate its functionality. Consider generating a list of C# declarations that are initialized to 0, false, null, etc... You could define a template for int, Object, Array, etc... declarations and then pass in an aggregate object that has the variable declaration object and the format. In a template group file you might have:
group Java;
file(variables,methods) ::= <<
<variables:{ v | <v.decl:(v.format)()>}; separator="\n">
<methods>
\>>
intdecl(decl) ::= "int <decl.name> = 0;"
intarray(decl) ::= "int[] <decl.name> = null;"
| Java | StringTemplateGroup group =
new StringTemplateGroup(new StringReader(templates),
AngleBracketTemplateLexer.class);
StringTemplate f = group.getInstanceOf("file");
f.setAttribute("variables.{decl,format}", new Decl("i","int"), "intdecl");
f.setAttribute("variables.{decl,format}", new Decl("a","int-array"), "intarray");
System.out.println("f="+f);
String expecting = ""+newline+newline;
|
|---|---|
| C# | StringTemplateGroup group =
new StringTemplateGroup(new StringReader(templates),
typeof(AngleBracketTemplateLexer));
StringTemplate f = group.GetInstanceOf("file");
f.setAttribute("variables.{decl,format}", new Decl("i","int"), "intdecl");
f.setAttribute("variables.{decl,format}", new Decl("a","int-array"), "intarray");
Console.Out.WriteLine("f="+f);
string expecting = ""+newline+newline;
|
| Python | group = stringtemplate3.StringTemplateGroup(file=StringIO(templates), lexer="angle-bracket") f = group.getInstanceOf("file") f.setAttribute("variables.{decl,format}", Decl("i","int"), "intdecl") f.setAttribute("variables.{decl,format}", Decl("a","int-array"), "intarray") print "f =", f expecting = ""+os.linesep |
For this simple unit test, the following dummy decl class is used:
| Java | public static class Decl { String name; String type; public Decl(String name, String type) {this.name=name; this.type=type;} public String getName() {return name;} public String getType() {return type;} } |
|---|---|
| C# | public class Decl { string name; string type; public Decl(string name, string type) {this.name=name; this.type=type;} public string Name { get {return name;} } public string Type { get {return type;} } } |
| Python | class Decl(object):
def __init__(self, name, type_):
self.name = name
self.type = type_
def getName(self):
return self.name
def getType(self):
return self.type
|
The value of f.ToString() is:
int i = 0; int[] a = null;