There are 5 expression options at the moment:
<names>
spits them out right next to each other. Using a separator can put a comma in between automatically: <names; separator=",">
. This is by far the most commonly used option. See How to construct separators?.AttributeRenderer
interface, which describes an object that knows how to format or otherwise render an object appropriately. The toString(Object,String)
method is used when the user uses the format
option: $o; format="f"$
. Renderers check the formatName and apply the appropriate formatting. If the format string passed to the renderer is not recognized, then it should simply call toString(Object)
.
$values; null="-1", separator=", "$ |
9, 6, -1, 2, -1 |
wrap
option may also take an argument but it's default is simply a \n string. You must specify an integer width using the toString(int)
method to get ST to actually wrap expressions modified with this option. For example, given a list of names and expression <names; wrap>
, a call to toString(72)
will emit the names until it surpasses 72 characters in with and then inserts a new line and begins emitting names again. Naturally this can be used in conjunction with the separator option. ST Never breaks in between a real element and the separator; the wrap occurs only after a separator. See Automatic line wrapping.The option values are all full expressions, which can include references to templates, anonymous templates, and so on. For example here is a separator that invokes another template:
<ul>$name; separator=bulletSeparator(foo=" ")+" "$</ul> |
The wrap and anchor options are implemented via the Output Filters. The others are handled during interpretation by ST. Well, the filters also are notified that a separator vs regular string is coming out to prevent newlines between real elements and separators.
Here is an example use of the format
option.
public void testRendererWithFormatAndList() throws Exception { StringTemplate st =new StringTemplate( "The names: <names; format=\"upper\">", AngleBracketTemplateLexer.class); st.setAttribute("names", "ter"); st.setAttribute("names", "tom"); st.setAttribute("names", "sriram"); st.registerRenderer(String.class, new StringRenderer()); String expecting = "The names: TERTOMSRIRAM"; String result = st.toString(); assertEquals(expecting, result); } |
The code registers a renderer for the String class. Without the format option, the toString(Object)
method is used to convert strings to the emitted text. With the option, the toString(Object, String)
method is invoked. Here is the renderer used in the example:
public class StringRenderer implements AttributeRenderer { public String toString(Object o) { return (String)o; } public String toString(Object o, String formatString) { if ( formatString.equals("upper") ) { return ((String)o).toUpperCase(); } return toString(o); } } |
The following code snippet is the same as the previous example except for the introduction of the separator option, which cleans up the output as you can see by the expecting
string:
public void testRendererWithFormatAndSeparator() throws Exception { StringTemplate st =new StringTemplate( "The names: <names; separator=\" and \", format=\"upper\">", AngleBracketTemplateLexer.class); st.setAttribute("names", "ter"); st.setAttribute("names", "tom"); st.setAttribute("names", "sriram"); st.registerRenderer(String.class, new StringRenderer()); String expecting = "The names: TER and TOM and SRIRAM"; String result = st.toString(); assertEquals(expecting, result); } |
If there are null elements in the list of names, you can specify a string to replace all of the null values using the null
option:
public void testRendererWithFormatAndSeparatorAndNull() throws Exception { StringTemplate st =new StringTemplate( "The names: <names; separator=\" and \", null=\"n/a\", format=\"upper\">", AngleBracketTemplateLexer.class); List names = new ArrayList(); names.add("ter"); names.add(null); names.add("sriram"); st.setAttribute("names", names); st.registerRenderer(String.class, new StringRenderer()); String expecting = "The names: TER and N/A and SRIRAM"; String result = st.toString(); assertEquals(expecting, result); } |
If you are constructing HTML documents you have to escape plain text strings so that <
or &
characters appear as literal text and do not act as HTML delimiters (thus opening a wide range of possible attacks if the text originated from user input).
import cgi import stringtemplate3 group = stringtemplate3.StringTemplateGroup( name="default", rootDir="path/to/templates/" ) class EscapeRenderer(stringtemplate3.AttributeRenderer): def toString(self, o, formatName=None): if formatName is None: # no formatting specified return str(o) if formatName == "escape": return cgi.escape(str(o)) else: raise ValueError("Unsupported format name") group.registerRenderer(str, EscapeRenderer()) st = group.getInstanceOf("blogEntry") st['comment'] = database.loadComment() # an instance with username, text, url, ... attributes |
Then you can use $comment.text; format="escape"$
in your templates whenever an attribute is not known to be save.