3.0 Release Notes

StringTemplate 3.0 Release Notes

Brought to you by that maniac that brings you the ANTLR parser generator!

Terence Parr
University of San Francisco
parrt at cs dot usfca dot edu
Copyright 2003-2006
http://www.stringtemplate.org (StringTemplate released under BSD License)

Version 3.0, September 6, 2006

3.0 fixes lots of bugs and adds some great new features including:

The features were added in response to my needs building ANTLR v3's code generator and from feedback by StringTemplate users.

3.0 should be a drop-in replacement for those using ST for websites and code generation with a few minor potential incompatibilities as noted below. Also see the change list. The biggest issue is that now angle brackets are the default delimiter for StringTemplate groups.

Enhancements

  • Added interfaces. See unit tests and http://www.cs.usfca.edu/~parrt/papers/ST.pdf.
    group Java implements ANTLRCoreTarget;
    rule(...) ::= "..."
    ...
    
    You can say "optional template(args);" also. Uses group loader to find interfaces.
  • Added CommonGroupLoader so you can reference groups/interfaces now in group files etc...
    StringTemplateGroup.registerGroupLoader(new CommonGroupLoader(dir,errorListener));
    
    The dir is a string that can be "dir1:dir2:dir3" etc... These are interpreted as relative paths to be used with CLASSPATH to locate groups. E.g., If you pass in "org/antlr/codegen/templates" and ask to load group "foo" it will try to load via classpath as "org/antlr/codegen/templates/foo". Another, PathGroupLoader, is a brain dead loader that looks only in the directory(ies) you specify in the ctor.
  • Added group inheritance notation:
    group subG : superG;
    
    Method setSuperGroup(name) now does something useful it invokes the loadGroup() method of the group loader.
  • added amazing line wrap facility.* Updated StringTemplateWriter to support line wrapping:
  • map still iterates over values if you say <aMap> but it is now shorthand for <aMap.values>. Similarly <aMap.keys> walks over the keys. You can do stuff like: <aMap.keys:{k| <k> maps to <aMap.(k)>}>.
  • added length(attribute) thanks to Kay Roepke. For now it does not work on strings; it works on attributes so length("foo") is 1 meaning 1 attribute. Nulls are counted in lists so a list of 300 nulls is length 300. If you don't want to count nulls, use
    length(strip(list)).
  • added strip(attribute) that returns an iterator that skips any null values.
    strip(x)=x when x is a single-valued attribute. Added StripIterator to handle this.
  • added ability to show start/stop of template with <name>...</name>. emitTemplateStartDebugString/emitTemplateStopDebugString and doNotEmitDebugStringsForTemplate(tempaltename)
  • Added null=expr option on expressions. For null values in iterated attributes and single attributes that are null, use this value instead of skipping. For single valued attributes like <name; null="n/a">. It's a shorthand for
    <if(name)><name><else>n/a<endif>
    
    For iterated values
    <values; null="0", separator=",">
    
    you get 0 for null list values. Works for template application
    like this also:
    <values:{v| <v>}; null="0">
    
    This does not replace empty strings like "" as they are not null.
  • added build.xml ANT file (ick)
  • {} and "" work as arguments now to templates
  • note in doc that map strings are now templates and that <<...>> works too. default or others can have empty values (implying no value) or use "key" but not in template; it's a keyword. Also, default must be at end now (and only 1). Default value is empty as before. To return null, use "default :" at end. Can use empty values too:
    {"float":}, {"int":"0"}, ...
  • added i0 which is like i except indexed from 0 not 1.
  • Added StringTemplate.getDependencyGraph() to get a list of n->m edges where template n contains template m. Also made convenient getDOTForDependencyGraph(). You get a template back that lets you reset node shape, fontsize, width, height attributes. Use removeAttribute before setting so you are sure you only get one value.
  • Added StringTemplate.toStructureString() to help discover nested structure.
  • added toString to Aggregate so that referencing foo in isolation prints something useful when you have the following code: setAttribute("foo.{x,y}", ...).
  • Added default lexer mechanism for groups so you can set once:
    public static void registerDefaultLexer(Class lexerClass)
  • Improved error when property ref causes the error internally. Now shows that exception rather than invocation target exception.
  • Added STG.getInstanceOf(name,attributes)

Backward incompatibilities

The following changes were worth making despite causing some backward compatibilities for some users.

  • <...> is now default expression delimiter for group files.
  • I had to make a more clear distinction between empty and null. Null means there is nothing there whereas empty means the value is there but renders to an empty string. IF conditionals now evaluate to empty if condition is false.
  • Changed how separators are generated. Now I generate a separator for any non-null value in the list even if that value is a conditional that evaluates to false. Iterated empty values always get a separator. Note that empty is not the same thing as missing. "" is not missing and hence will get a separator.

This made the ASTExpr.write separator computation much simpler and allowed me to properly handle the new "null" option.

Bug Fixes

  • allow different source of classloader:
    InputStream is = cl.getResourceAsStream(fileName);
    if ( is==null ) \{
    	cl = ErrorManager.class.getClassLoader();
    	is = cl.getResourceAsStream(fileName);
    }
    
  • fixed bug where separator did not appear in lists of stuff with lots of null entries.
  • made static maps in STG synchronized, also synchronized the look up/def methods for templates in STG.
  • removed reflection property lookup; too complex for value. profiling indicates it's a small cost. No thread synch issues either now.
  • template evaluation for anonymous templates did not properly look up overridden templates. The group for anonymous templates was not reset to be the current enclosing template's group (remember we can dynamically set the superGroup).
  • Signature changed to use AttributeRenderer:
    public void registerRenderer(Class attributeClassType,
                                 AttributeRenderer renderer)
    
  • If expr in <(expr):template()> evaluated to nothing, the template was still invoked.
  • Couldn't handle List<int[]>. Nested int[] was not iteratable.
  • Couldn't gen \ in a template. \<value> in a template escaped the <.
    <value> printed both slashes. Fixed.
  • Couldn't use
    \{
    inside of a {...} anonymous template.
  • Fixed: you could not have template expressions, just simple expressions in indirect template expressions like
    $data:("foo":a())()$}}
    . I decided not to allow IF expressions inside.
  • now throws exception when dots are in template names or attribute names
  • don't set "it" nor "attr" default attributes if there is a parameter to an anonymous block like <names:{n | ...}>
  • Bug fix. If you passed in a list and then another element, it added it to the list you passed in! Now, I make a (shallow) copy of the list.
  • Bug fix. If you passed an element then a list to an attr and I think it didn't flatten out properly!
  • Couldn't escape in template group. \<< failed as did ...} for anonymous templates.
  • When creating an aggregate list, couldn't have spaces in {...} such as "folders.{a, b}".
  • Embedded templates such as {...} anonymous templates couldn't see the renderers for enclosing templates. Easy to set one renderer in root template for, say, Date and forget about it. If none found for that class in containment hierarchy, then group hierarchy is checked.
  • using nativegroup to create instances now in ST.getInstanceOf. Needed so that super.foo works properly. THe new instance's group will point at creating group so polymorphism works properly.
  • auto defined attribute i was not defined for <a,b: {...}> case. ooops.
  • You couldn't have '=' in a string if preceded by '+' like foo+"ick=".
  • Fixed bug where an expr on the first line that yields no output left a blank line.
  • There was a bug in StringTemplateGroup.isDefined that it always returned true.
  • If you iterated multiple empty attributes, it iterated once with null values. Bizarre. Fixed.
  • Bug in polymorphism when an overridden template called its super which called another template which was also overridden; didn't call the overridden method. Fixed getInstanceOf() so that it always sets the proper group so polymorphic template lookup also starts at the right group.
  • Test testLazyEvalOfSuperInApplySuperTemplateRef was wrong...The "super." prefix is (like in Java, ...) a scope override and is always evaluated relative to the template in which it was defined not the template in which it is evaluate!
  • Improving error messages to be more specific when you get a parser error. I'm including more context info and hopefully the file within a group file the error occurs.
  • The defaultGroup is now public so you can know StringTemplate's default group when you see it:
    	public static StringTemplateGroup defaultGroup =
    		new StringTemplateGroup("defaultGroup", ".");
    
  • Template polymorphism bug: wouldn't work for references in IF clauses!
  • Any Map instance is now allowed as a "map" attribute not just Hashtable, HashMap.
  • NullPtrExc if you registered a renderer and sent an object as an attribute of another type!
  • The tree viewer didn't work; class cast problem with Hashtable vs HashMap.