Expression options

There are 5 expression options at the moment:

  • separator. Specify text to be emitted between multiple values emitted for a single expression. For example, given a list of names, <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?.
  • format. Used in conjunction with the 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).
    This option is very effective for locale changes and for choosing the display characteristics of an object in the template rather than encode.
    Each template may have a renderer for each object type or can default to the group's renderer or the super group's renderer if the group doesn't have one. See Object rendering#Format Option.
  • null. Emit a special value for each null element. For example, given values=9,6,null,2,null
    $values; null="-1", separator=", "$
    
    emits:
    9, 6, -1, 2, -1
    
    See Expressions
  • wrap. Tell ST that it is okay to wrapped lines to get too long. The 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.
  • anchor. Line up all wrapped lines with left edge of expression when wrapping. Default is anchor="true" (any non-null value means anchor). 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=" ")+"&nbsp;"$</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.

Java examples

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

Python examples

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.