StringTemplate 2.2 Release Notes
StringTemplate 2.2 Release Notes
Brought to you by that maniac that brings you the
ANTLR parser generator!
Terence Parr
University of San Francisco
parrt@cs.usfca.edu
Copyright 2003-2005
http://www.stringtemplate.org
(StringTemplate released under BSD License)
Version 2.2, August 5, 2005
2.2 fixes a few bugs, but mainly 2.2 adds lots of great new features.
The features were added in response to my needs building ANTLR v3's
code generator and from feedback by StringTemplate users.
2.2 should be a drop-in replacement for those using StringTemplate
for websites and code generation with a few minor potential
incompatibilities.
Enhancements
- You can define arguments for anonymous templates now, which is much
nicer that using it all the time:
$names:{n| <b>$n$</b><br>}; separator=","$
- added parallel attribute iteration where you can apply a template to
multiple lists of values; works for anonymous templates only:
$names,phones:{n,p | $n$: $p$}$
An error is generated if you have too many args for the number of
parallel lists. Iteration proceeds while at least one of the
attributes (names or phones, in this case) has values.
- added "[a,b,c,...]" list operator to create multi-valued attributes,
yielding single, longer multi-valued attribute. $[mine,yours]$ is a
new list with both elements; all of mine first then all of yours.
- any template invocation assumes sole formal argument name if just
one formal argument defined in target template. For example, if you
do $bold(name)$ and bold has one formal argument, then it gets the
value of name. This works also for template application:
test(names) ::= "<names:bold(),italics()>"
bold(x) ::= "*<x>*"
italics(y) ::= "_<y>_"
Note: The binding of a value to the name of a sole formal argument is
done dynamically so that indirect template invocation and lazy
evaluation stuff works properly. For example (templateName)(value)
should work for different templates with differently-named (but sole)
formal arguments. See unit test
testInvokeIndirectTemplateWithSingleFormalArgs().
- Added operators to get first, rest, last attribute elements; e.g.,
first(users) yields the first value of users. rest(users)
returns all elements of users except the first; it returns nothing
if users is single-valued. This introduces a function-like syntax,
which necessitated the "assign to sole formal arg" functionality
above that supports things like bold(name). You can combine
operations to say things like first(rest(names)) to get second
element. Can say first([mine,yours]) to get the first of a combined
list. These operators work on any iterable object. [backward
incompatible if you used first,last,tail as an attribute or template
name]
- Added maps to StringTemplate groups. For example,
typeInitMap ::= ["int"="0", "float"="0.0", default="null"]
then within a template you can refer to them <typeInitMap.int>,
which returns "0". Those strings are actually templates, but I can't
really think of a use for that just yet. ;) If your type name is an
attribute not a constant like int, then use
<typeInitMap.(typeName)>. The maps are defined in the group's scope
and are visible if no attribute hides them. For example, if you
define a formal argument called typeInitMap in template foo then
foo cannot see the map defined in the group (though you could pass
it in, which would be the point). If a name is not an attribute and
it's not in the group's maps table, then the super group is consulted
etc... You may not redefine a map and it may not have the same name
as a template in that group. The default value is used if you use a
key as a property that doesn't exist. For example <typeInitMap.foo>
returns "null".
- Added renderers per template and group. Templates are inherited
from super group. New Interface AttributeRenderer defines how an
object is rendered to String. Here is a renderer that
renders Calendar date objects tersely.
public class DateRenderer implements AttributeRenderer {
public String toString(Object o) {
SimpleDateFormat f = new SimpleDateFormat("yyyy.MM.dd");
return f.format(((Calendar)o).getTime());
}
}
...
StringTemplate st =new StringTemplate(
"date: <created>",
AngleBracketTemplateLexer.class);
st.setAttribute("created", new GregorianCalendar(2005, 07-1, 05));
st.registerRenderer(GregorianCalendar.class, new DateRenderer());
String expecting = "date: 2005.07.05";
You can set the renderer for a class either at the group level or the
individual template level. Thanks to Anton Keks for his suggestion and sample
implementation.
- template arguments can be anonymous templates now or simple strings.
From the bug list:
Template inclusion expressions won't accept a nested template
as an argument. I'd like to be able to write <foo(bar={...})>, which
would mean the same thing as <foo(bar="":{...})>.
Now you can do <foo(bar={<firstName> <lastName>)>}.
- added default values (strings or anonymous templates like explicit
template arguments) for formal arguments like
bold(x="empty") ::= ...
Note: because of lazy evaluation semantics, default value templates may refer
to argument values. Everything is evaluated after arg values are set.
This works for invoked templates and templates you create with code.
- when calling another template, y, with formal arguments from within
a template, x, none of the
x parameters are visible to y because the formal parameters force you to
define values. This prevents surprises and makes it easy to ensure a
a 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
<y()> erases all of x's parameters. Now, use <y(...)> as a
syntax to indicate y should inherit all values. <y(name="foo", ...)>
would set one arg, but the others are inherited whereas
<y(name="foo")> only has name set; others are empty. You can set
manually with
StringTemplate.setPassThroughAttributes().
- When a property or argument is not found, you get a better error. I show the
template context (nested tree of templates); e.g.,
no such attribute: decisionNumber in template context
[outputFile lexer cyclicDFA cyclicDFAState cyclicDFAEdge lookaheadTest]
- added ability to use indirect property names. $user.(propName)$
evaluates (propName) to find the name of the property and then looks
it up in user object whereas $user.foo$ looks for literally property
foo in user object.
Bug Fixes
- Thanks to Joe Soroka, you can now access public members of anonymous
inner classes; Java doesn't normally allow even with public keyword.
Bizarre. The following unit test works now:
StringTemplate st =
new StringTemplate("$x.foo$:$x.bar$");
Object o = new Object() {
public int foo = 9;
public int getBar() { return 34; }
};
st.setAttribute("x", o);
String expecting = "9:34";
assertEqual(st.toString(), expecting);
- fields that were arrays didn't get treated as iterable objects
- When you invoke foo(x=y), x must be an attribute of foo (well if you
have formal args defined that is) and y is evaluated within the
context of the surrounding template; more precisely, it's evaluated in
the context of the actual template invocation (which can have
predefined attributes like attr, it, and i). It's weird, but
foo(x=x) also makes sense. See new unit test
testArgEvaluationContext(). This was not working before, though I
thought it was!
- Collections, Maps, and Iterators that are non-null but have no elements
return false in conditionals; e.g., $if(users)$ is false if users is
an empty list.
- To define an empty template, you had to write <<>>; "" didn't work.
- Previous release (2.1) notes forgot to mention that I added field access
to public object fields now instead of via just accessors.