Top Description Inners Fields Constructors Methods
groovy.text

public Class StreamingTemplateEngine

extends TemplateEngine
Class Inheritance
Imports
edu.umd.cs.findbugs.annotations.SuppressFBWarnings, groovy.lang.Closure, .GroovyClassLoader, .GroovyCodeSource, .GroovyObject, .GroovyRuntimeException, .Writable, org.codehaus.groovy.control.CompilationFailedException, .ErrorCollector, .MultipleCompilationErrorsException, org.codehaus.groovy.control.messages.Message, .SyntaxErrorMessage, org.codehaus.groovy.runtime.StackTraceUtils, org.codehaus.groovy.syntax.SyntaxException, java.io.IOException, .LineNumberReader, .Reader, .StringReader, java.lang.reflect.InvocationTargetException, java.security.AccessController, .PrivilegedAction, java.util.ArrayList, .List, .Map, java.util.concurrent.atomic.AtomicInteger

Processes template source files substituting variables and expressions into placeholders in a template source text to produce the desired output using a closure based approach. This engine has equivalent functionality to the groovy.text.SimpleTemplateEngine but creates the template using writable closures making it more scalable for large templates.

Specifically this template engine can handle strings larger than 64k which still causes problems for the other groovy template engines.

The template engine uses JSP style <% %> script and <%= %> expression syntax or GString style expressions. The variable 'out' is bound to the writer that the template is being written to.

Frequently, the template source will be in a file but here is a simple example providing the template as a string:

def binding = [
  firstname : "Grace",
  lastname  : "Hopper",
  accepted  : true,
  title     : 'Groovy for COBOL programmers'
]
def text = '''\
Dear <% out.print firstname %> ${lastname},

We <% if (accepted) out.print 'are pleased' else out.print 'regret' %> \
to inform you that your paper entitled
'$title' was ${ accepted ? 'accepted' : 'rejected' }.

The conference committee.
'''

def template = new groovy.text.StreamingTemplateEngine().createTemplate(text)

print template.make(binding)
This example uses a mix of the JSP style and GString style placeholders but you can typically use just one style if you wish. Running this example will produce this output:

Dear Grace Hopper,

We are pleased to inform you that your paper entitled
'Groovy for COBOL programmers' was accepted.

The conference committee.

StreamingTemplateEngine as a servlet engine

The template engine can also be used as the engine for groovy.servlet.TemplateServlet by placing the following in your web.xml file (plus a corresponding servlet-mapping element):

<servlet>
  <servlet-name>StreamingTemplate</servlet-name>
  <servlet-class>groovy.servlet.TemplateServlet</servlet-class>
  <init-param>
    <param-name>template.engine</param-name>
    <param-value>groovy.text.StreamingTemplateEngine</param-value>
  </init-param>
</servlet>
In this case, your template source file should be HTML with the appropriate embedded placeholders.

Debugging Template Code

The template engine makes an effort to throw descriptive exceptions with context lines, ie:

 groovy.text.TemplateExecutionException: Template parse error at line 4:
          3: We <% if (accepted) out.print 'are pleased' else out.print 'regret' %> to inform you that your paper entitled
      --> 4: '$txitle' was ${ accepted ? 'accepted' : 'rejected' }.
          5:
at test.run(test.groovy:18)

Caused by: groovy.lang.MissingPropertyException: No such property: txitle for class: groovy.tmp.templates.StreamingTemplateScript1
... 1 more
and sanitize the exceptions to make things readable.

When the exceptions are not enough, it might sometimes be useful to view the actual script source generated by the template engine. This would conceptually be equivalent to viewing the .java file generated for a jsp page. The source is not currently very readable and until we get a built in groovy code pretty printer, we will probably continue to opt for compactness rather than readability.

With that being said, viewing the source might still have some value. For this reason the script source is accessible via the template.scriptSource property, i.e.:

    println template.scriptSource
In the above example.

Nested and Inner Type Summary

Modifier and TypeClass and Description
private static class
StreamingTemplateEngine.StreamingTemplate

The class used to implement the Template interface for the StreamingTemplateEngine

Field Summary

Modifier and TypeField and Description
private static AtomicInteger
private final ClassLoader
private static final String

Constructor Summary

AccessConstructor and Description
public
StreamingTemplateEngine()

Create a streaming template engine instance using the default class loader

public
StreamingTemplateEngine(ClassLoader
The class loader to use when parsing the template code.
parentLoader
)

Create a streaming template engine instance using a custom class loader

The custom loader is used when parsing the template code

Method Summary

Modifier and TypeMethod and Description
public Template
createTemplate(final Reader reader)

Implements abstract groovy.text.TemplateEngine.createTemplate.

Creates a template instance using the template source from the provided Reader.

Inherited from groovy.text.TemplateEngine:
createTemplatecreateTemplatecreateTemplatecreateTemplatecreateTemplate

Field Detail

counterback to summary
private static AtomicInteger counter
parentLoaderback to summary
private final ClassLoader parentLoader
TEMPLATE_SCRIPT_PREFIXback to summary
private static final String TEMPLATE_SCRIPT_PREFIX

Constructor Detail

StreamingTemplateEngineback to summary
public StreamingTemplateEngine()

Create a streaming template engine instance using the default class loader

StreamingTemplateEngineback to summary
public StreamingTemplateEngine(ClassLoader parentLoader)

Create a streaming template engine instance using a custom class loader

The custom loader is used when parsing the template code

Parameters
parentLoader:ClassLoader

The class loader to use when parsing the template code.

Method Detail

createTemplateback to summary
public Template createTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException

Implements abstract groovy.text.TemplateEngine.createTemplate.

Creates a template instance using the template source from the provided Reader. The template can be applied repeatedly on different bindings to produce custom output. Technical detail
Under the hood the returned template is represented as a four argument closure where the three first arguments are curried in while generating the template.

In essence we start with a closure on the form:

  { parentClass, stringSectionList, binding, out ->
     //code generated by parsing the template data
  }
We then curry in the parentClass and stringSectionList arguments so that the StreamingTemplate instance returned from 'createTemplate' internally contains a template closure on the form:
  { binding, out ->
     //code generated by parsing the template data
  }
Calling template.make(binding), curries in the 'binding' argument:
  public Writable make(final Map map) {
    final Closure template = this.template.curry(new Object[]{map});
    return (Writable) template;
  }
This only leaves the 'out' argument unbound. The only method on the writable interface is writeTo(Writer out) so groovy rules about casting a closure to a one-method-interface apply and the above works. I.e. we return the now one argument closure as the Writable which can be serialized to System.out, a file, etc according to the Writable interface contract.
Annotations
@Override
See Also
groovy.text.TemplateEngine#createTemplate(java.io.Reader)
groovy.text back to summary

private Class StreamingTemplateEngine.StreamingTemplate

extends Object
implements Template
Class Inheritance
All Implemented Interfaces
groovy.text.Template

The class used to implement the Template interface for the StreamingTemplateEngine

Nested and Inner Type Summary

Modifier and TypeClass and Description
private static class
private static class
private static class
StreamingTemplateEngine.StreamingTemplate.StringSection

A StringSection represent a section in the template source with only string data (i.e. no branching, GString references, etc).

Field Summary

Modifier and TypeField and Description
public static final StackTraceElement[]
private static final StreamingTemplateEngine.StreamingTemplate.FinishedReadingException
private int
private int
private static final String
SCRIPT_HEAD

The 'header' we use for the resulting groovy script source.

private static final String
SCRIPT_TAIL

The 'footer' we use for the resulting groovy script source

pack-priv String
pack-priv final Closure<V>
private StringBuilder
pack-priv boolean

Constructor Summary

AccessConstructor and Description
pack-priv
StreamingTemplate(final Reader
A reader into the template source data
source
,
final ClassLoader
A class loader we use
parentLoader
)

Turn the template into a writable Closure.

Method Summary

Modifier and TypeMethod and Description
private void
private void
private static void
clear(StringBuilder lookAhead)

private Closure<V>
public void
private StreamingTemplateEngine.StreamingTemplate.StringSection
private void
finishStringSection(List<StreamingTemplateEngine.StreamingTemplate.StringSection>
The list of string sections. The current section gets added to this section.
sections
,
StreamingTemplateEngine.StreamingTemplate.StringSection
The current string section.
currentSection
,
StringBuilder
Template expressions
templateExpressions
,
StreamingTemplateEngine.StreamingTemplate.Position
The last read position in the source template stream.
lastSourcePosition
,
StreamingTemplateEngine.StreamingTemplate.Position
The last written to position in the target script stream.
targetPosition
)

Called to handle the ending of a string section.

private String
getErrorContext(int actualLine)

private int
private void
public Writable
public Writable
make(final Map<K, V> map)

Implements groovy.text.Template.make.

private String
private RuntimeException
private void
private String
padLeft(String s, int len)

private void
parseDollarCurlyIdentifier(final Reader
The reader reading from the template source
reader
,
final StringBuilder
The target groovy script source we write to
target
,
final StreamingTemplateEngine.StreamingTemplate.Position
The reader position in the source stream
sourcePosition
,
final StreamingTemplateEngine.StreamingTemplate.Position
The writer position in the target stream
targetPosition
)

Parses a dollar curly preceded identifier of the type '${bird}' in the following template example:

Alice why is a ${bird} like a writing desk
private int

Returns:

true if a valid dollar preceded identifier was found, false otherwise. More specifically, returns true if the first character after the dollar sign is a valid java identifier. Note that the dollar curly syntax is handled by another method.
parseDollarIdentifier
(int
The first letter of the potential identifier, 'b' in the above example
c
,
final Reader
The reader reading from the template source
reader
,
final StringBuilder
The target groovy script source we write to
target
,
final StreamingTemplateEngine.StreamingTemplate.Position
The reader position in the source stream
sourcePosition
,
final StreamingTemplateEngine.StreamingTemplate.Position
The writer position in the target stream
targetPosition
)

Parses a non curly dollar preceded identifier of the type '$bird' in the following template example:

Alice why is a $bird like a writing desk
private void
parseExpression(final Reader reader, final StringBuilder target, final StreamingTemplateEngine.StreamingTemplate.Position sourcePosition, final StreamingTemplateEngine.StreamingTemplate.Position targetPosition)

Parse a <%= ....

private void
parseSection(final int pendingC, final Reader reader, final StringBuilder target, final StreamingTemplateEngine.StreamingTemplate.Position sourcePosition, final StreamingTemplateEngine.StreamingTemplate.Position targetPosition)

Parse a <% ....

private int
private int
private int
read(final Reader reader)

private void
Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait

Field Detail

EMPTY_STACKTRACEback to summary
public static final StackTraceElement[] EMPTY_STACKTRACE
finishedReadingExceptionback to summary
private static final StreamingTemplateEngine.StreamingTemplate.FinishedReadingException finishedReadingException
indexback to summary
private int index
lastReadback to summary
private int lastRead
SCRIPT_HEADback to summary
private static final String SCRIPT_HEAD

The 'header' we use for the resulting groovy script source.

SCRIPT_TAILback to summary
private static final String SCRIPT_TAIL

The 'footer' we use for the resulting groovy script source

scriptSourceback to summary
pack-priv String scriptSource
templateback to summary
pack-priv final Closure<V> template
templateSourceback to summary
private StringBuilder templateSource
useLastReadback to summary
pack-priv boolean useLastRead

Constructor Detail

StreamingTemplateback to summary
pack-priv StreamingTemplate(final Reader source, final ClassLoader parentLoader) throws CompilationFailedException, ClassNotFoundException, IOException

Turn the template into a writable Closure. When executed the closure evaluates all the code embedded in the template and then writes a GString containing the fixed and variable items to the writer passed as a parameter

For example:

'<%= "test" %> of expr and <% test = 1 %>${test} script.'
would compile into:
{ out -> out << "${"test"} of expr and "; test = 1 ; out << "${test} script."}.asWritable()
Parameters
source:Reader

A reader into the template source data

parentLoader:ClassLoader

A class loader we use

Method Detail

appendback to summary
private void append(final StringBuilder target, StreamingTemplateEngine.StreamingTemplate.Position targetPosition, char c)
appendback to summary
private void append(final StringBuilder target, StreamingTemplateEngine.StreamingTemplate.Position targetPosition, String s)
clearback to summary
private static void clear(StringBuilder lookAhead)
createTemplateClosureback to summary
private Closure<V> createTemplateClosure(List<StreamingTemplateEngine.StreamingTemplate.StringSection> sections, final ClassLoader parentLoader, StringBuilder target) throws ClassNotFoundException
errorback to summary
public void error(int index, List<StreamingTemplateEngine.StreamingTemplate.StringSection> sections, Throwable e) throws Throwable
findPrecedingSectionback to summary
private StreamingTemplateEngine.StreamingTemplate.StringSection findPrecedingSection(StreamingTemplateEngine.StreamingTemplate.Position p, List<StreamingTemplateEngine.StreamingTemplate.StringSection> sections)
finishStringSectionback to summary
private void finishStringSection(List<StreamingTemplateEngine.StreamingTemplate.StringSection> sections, StreamingTemplateEngine.StreamingTemplate.StringSection currentSection, StringBuilder templateExpressions, StreamingTemplateEngine.StreamingTemplate.Position lastSourcePosition, StreamingTemplateEngine.StreamingTemplate.Position targetPosition)

Called to handle the ending of a string section.

Parameters
sections:List<StreamingTemplateEngine.StreamingTemplate.StringSection>

The list of string sections. The current section gets added to this section.

currentSection:StreamingTemplateEngine.StreamingTemplate.StringSection

The current string section.

templateExpressions:StringBuilder

Template expressions

lastSourcePosition:StreamingTemplateEngine.StreamingTemplate.Position

The last read position in the source template stream.

targetPosition:StreamingTemplateEngine.StreamingTemplate.Position

The last written to position in the target script stream.

getErrorContextback to summary
private String getErrorContext(int actualLine) throws IOException
getLinesInSourceback to summary
private int getLinesInSource() throws IOException
Annotations
@SuppressFBWarnings:SR_NOT_CHECKED
justification:safe to ignore return value of skip from reader backed by StringReader
handleEscapingback to summary
private void handleEscaping(final Reader source, final StreamingTemplateEngine.StreamingTemplate.Position sourcePosition, final StreamingTemplateEngine.StreamingTemplate.StringSection currentSection, final StringBuilder lookAhead) throws IOException, FinishedReadingException
makeback to summary
public Writable make()

Implements groovy.text.Template.make.

Annotations
@Override
makeback to summary
public Writable make(final Map<K, V> map)

Implements groovy.text.Template.make.

Annotations
@Override
mangleExceptionMessageback to summary
private String mangleExceptionMessage(String original, StreamingTemplateEngine.StreamingTemplate.Position p)
mangleMultipleCompilationErrorsExceptionback to summary
private RuntimeException mangleMultipleCompilationErrorsException(MultipleCompilationErrorsException e, List<StreamingTemplateEngine.StreamingTemplate.StringSection> sections)
offsetPositionFromSectionback to summary
private void offsetPositionFromSection(StreamingTemplateEngine.StreamingTemplate.Position p, StreamingTemplateEngine.StreamingTemplate.StringSection s)
padLeftback to summary
private String padLeft(String s, int len)
parseDollarCurlyIdentifierback to summary
private void parseDollarCurlyIdentifier(final Reader reader, final StringBuilder target, final StreamingTemplateEngine.StreamingTemplate.Position sourcePosition, final StreamingTemplateEngine.StreamingTemplate.Position targetPosition) throws IOException, FinishedReadingException

Parses a dollar curly preceded identifier of the type '${bird}' in the following template example:

Alice why is a ${bird} like a writing desk
which would produce the following template data:
out << "Alice why is a ";
out << """${bird}""";
out << " like a writing desk";
This method is given the 'b' in 'bird' in argument c, checks if it is a valid java identifier start (we assume groovy did not mangle the java identifier rules). If so it proceeds to parse characters from the input until it encounters a non-java-identifier character. At that point
Parameters
reader:Reader

The reader reading from the template source

target:StringBuilder

The target groovy script source we write to

sourcePosition:StreamingTemplateEngine.StreamingTemplate.Position

The reader position in the source stream

targetPosition:StreamingTemplateEngine.StreamingTemplate.Position

The writer position in the target stream

parseDollarIdentifierback to summary
private int parseDollarIdentifier(int c, final Reader reader, final StringBuilder target, final StreamingTemplateEngine.StreamingTemplate.Position sourcePosition, final StreamingTemplateEngine.StreamingTemplate.Position targetPosition) throws IOException, FinishedReadingException

Parses a non curly dollar preceded identifier of the type '$bird' in the following template example:

Alice why is a $bird like a writing desk
which would produce the following template data:
out << "Alice why is a ";
out << bird;
out << " like a writing desk";
This method is given the 'b' in 'bird' in argument c, checks if it is a valid java identifier start (we assume groovy did not mangle the java identifier rules). If so it proceeds to parse characters from the input until it encounters a non-java-identifier character. At that point
Parameters
c:int

The first letter of the potential identifier, 'b' in the above example

reader:Reader

The reader reading from the template source

target:StringBuilder

The target groovy script source we write to

sourcePosition:StreamingTemplateEngine.StreamingTemplate.Position

The reader position in the source stream

targetPosition:StreamingTemplateEngine.StreamingTemplate.Position

The writer position in the target stream

Returns:int

true if a valid dollar preceded identifier was found, false otherwise. More specifically, returns true if the first character after the dollar sign is a valid java identifier. Note that the dollar curly syntax is handled by another method.

Exceptions
FinishedReadingException:
If we encountered the end of the source stream.
parseExpressionback to summary
private void parseExpression(final Reader reader, final StringBuilder target, final StreamingTemplateEngine.StreamingTemplate.Position sourcePosition, final StreamingTemplateEngine.StreamingTemplate.Position targetPosition) throws IOException, FinishedReadingException

Parse a <%= .... %> expression

parseSectionback to summary
private void parseSection(final int pendingC, final Reader reader, final StringBuilder target, final StreamingTemplateEngine.StreamingTemplate.Position sourcePosition, final StreamingTemplateEngine.StreamingTemplate.Position targetPosition) throws IOException, FinishedReadingException

Parse a <% .... %> section if we are writing a GString close and append ';' then write the section as a statement

readback to summary
private int read(final Reader reader, StreamingTemplateEngine.StreamingTemplate.Position position, StringBuilder lookAhead) throws IOException, FinishedReadingException
readback to summary
private int read(final Reader reader, StreamingTemplateEngine.StreamingTemplate.Position position) throws IOException, FinishedReadingException
readback to summary
private int read(final Reader reader) throws IOException
readAndAppendback to summary
private void readAndAppend(Reader reader, StringBuilder target, StreamingTemplateEngine.StreamingTemplate.Position sourcePosition, StreamingTemplateEngine.StreamingTemplate.Position targetPosition) throws IOException, FinishedReadingException
groovy.text back to summary

private Class StreamingTemplateEngine.StreamingTemplate.FinishedReadingException

extends Exception
Class Inheritance

Field Summary

Modifier and TypeField and Description
private static final long

Constructor Summary

AccessConstructor and Description
private

Field Detail

serialVersionUIDback to summary
private static final long serialVersionUID

Hides java.lang.Exception.serialVersionUID.

Constructor Detail

FinishedReadingExceptionback to summary
private FinishedReadingException()
groovy.text back to summary

private final Class StreamingTemplateEngine.StreamingTemplate.Position

extends Object
Class Inheritance
  • java.lang.Object
  • groovy.text.StreamingTemplateEngine.StreamingTemplate.Position

Field Summary

Modifier and TypeField and Description
public int
public int

Constructor Summary

AccessConstructor and Description
private
Position(int row, int column)

private

Method Summary

Modifier and TypeMethod and Description
private void
public String
toString()

Overrides java.lang.Object.toString.

Returns a string representation of the object.

Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAllwaitwaitwait

Field Detail

columnback to summary
public int column
rowback to summary
public int row

Constructor Detail

Positionback to summary
private Position(int row, int column)
Positionback to summary
private Position(StreamingTemplateEngine.StreamingTemplate.Position p)

Method Detail

setback to summary
private void set(StreamingTemplateEngine.StreamingTemplate.Position p)
toStringback to summary
public String toString()

Overrides java.lang.Object.toString.

Doc from java.lang.Object.toString.

Returns a string representation of the object. Satisfying this method's contract implies a non-null result must be returned.

Returns:String

a string representation of the object

groovy.text back to summary

private final Class StreamingTemplateEngine.StreamingTemplate.StringSection

extends Object
Class Inheritance
  • java.lang.Object
  • groovy.text.StreamingTemplateEngine.StreamingTemplate.StringSection

A StringSection represent a section in the template source with only string data (i.e. no branching, GString references, etc). As an example, the following template string:
Alice why is a $bird like a writing desk
Would produce a string section "Alice why is a " followed by a dollar identifier expression followed by another string section " like a writing desk".

Constructor Summary

AccessConstructor and Description
private

Method Summary

Modifier and TypeMethod and Description
public String
toString()

Overrides java.lang.Object.toString.

Returns a string representation of the object.

Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAllwaitwaitwait

Field Detail

databack to summary
pack-priv StringBuilder data
firstSourcePositionback to summary
pack-priv StreamingTemplateEngine.StreamingTemplate.Position firstSourcePosition
lastSourcePositionback to summary
pack-priv StreamingTemplateEngine.StreamingTemplate.Position lastSourcePosition
lastTargetPositionback to summary
pack-priv StreamingTemplateEngine.StreamingTemplate.Position lastTargetPosition

Constructor Detail

StringSectionback to summary
private StringSection(StreamingTemplateEngine.StreamingTemplate.Position firstSourcePosition)

Method Detail

toStringback to summary
public String toString()

Overrides java.lang.Object.toString.

Doc from java.lang.Object.toString.

Returns a string representation of the object. Satisfying this method's contract implies a non-null result must be returned.

Returns:String

a string representation of the object

Annotations
@Override