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.
def binding = [ firstname : "Grace", lastname : "Hopper", accepted : true, title : 'Groovy for COBOL programmers' ] def text = '''\ DearThis 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:<%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)
Dear Grace Hopper, We are pleased to inform you that your paper entitled 'Groovy for COBOL programmers' was accepted. The conference committee.
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.
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.
| Modifier and Type | Class and Description |
|---|---|
| private static class | StreamingTemplateEngine.
The class used to implement the Template interface for the StreamingTemplateEngine |
| Modifier and Type | Field and Description |
|---|---|
| private static AtomicInteger | |
| private final ClassLoader | |
| private static final String |
| Access | Constructor and Description |
|---|---|
| public | |
| 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 |
| Modifier and Type | Method and Description |
|---|---|
| public Template | createTemplate(final Reader reader)
Implements abstract groovy. Creates a template instance using the template source from the provided Reader. |
| counter | back to summary |
|---|---|
| private static AtomicInteger counter | |
| parentLoader | back to summary |
|---|---|
| private final ClassLoader parentLoader | |
| TEMPLATE_SCRIPT_PREFIX | back to summary |
|---|---|
| private static final String TEMPLATE_SCRIPT_PREFIX | |
| StreamingTemplateEngine | back to summary |
|---|---|
| public StreamingTemplateEngine() Create a streaming template engine instance using the default class loader | |
| StreamingTemplateEngine | back 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
| |
| createTemplate | back to summary |
|---|---|
| public Template createTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException Implements abstract groovy. 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
{ parentClass, stringSectionList, binding, out
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
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.
| |
| Modifier and Type | Class and Description |
|---|---|
| private static class | |
| private static class | |
| private static class | StreamingTemplateEngine.
A StringSection represent a section in the template source with only string data (i.e. no branching, GString references, etc). |
| Modifier and Type | Field and Description |
|---|---|
| public static final StackTraceElement[] | |
| private static final StreamingTemplateEngine. | |
| 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 | |
| private StringBuilder | |
| pack-priv boolean |
| Access | Constructor 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. |
| Modifier and Type | Method and Description |
|---|---|
| private void | append(final StringBuilder target, StreamingTemplateEngine.
|
| private void | append(final StringBuilder target, StreamingTemplateEngine.
|
| private static void | |
| private Closure | createTemplateClosure(List<StreamingTemplateEngine.
|
| public void | error(int index, List<StreamingTemplateEngine.
|
| private StreamingTemplateEngine. | |
| private void | finishStringSection(List<StreamingTemplateEngine.
The list of string sections. The current section gets added to this section. sections,The current string section. currentSection,Template expressions templateExpressions, StreamingTemplateEngine.The last read position in the source template stream. lastSourcePosition,The last written to position in the target script stream. targetPositionCalled to handle the ending of a string section. |
| private String | |
| private int | |
| private void | handleEscaping(final Reader source, final StreamingTemplateEngine.
|
| public Writable | |
| public Writable | |
| private String | |
| private RuntimeException | |
| private void | |
| private String | |
| 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.The reader position in the source stream sourcePosition,The writer position in the target stream targetPositionParses 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.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.The reader position in the source stream sourcePosition,The writer position in the target stream targetPositionParses 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.
Parse a <%= .... |
| private void | parseSection(final int pendingC, final Reader reader, final StringBuilder target, final StreamingTemplateEngine.
Parse a <% .... |
| private int | read(final Reader reader, StreamingTemplateEngine.
|
| private int | |
| private int | |
| private void | readAndAppend(Reader reader, StringBuilder target, StreamingTemplateEngine.
|
| EMPTY_STACKTRACE | back to summary |
|---|---|
| public static final StackTraceElement[] EMPTY_STACKTRACE | |
| finishedReadingException | back to summary |
|---|---|
| private static final StreamingTemplateEngine. | |
| index | back to summary |
|---|---|
| private int index | |
| lastRead | back to summary |
|---|---|
| private int lastRead | |
| SCRIPT_HEAD | back to summary |
|---|---|
| private static final String SCRIPT_HEAD The 'header' we use for the resulting groovy script source. | |
| SCRIPT_TAIL | back to summary |
|---|---|
| private static final String SCRIPT_TAIL The 'footer' we use for the resulting groovy script source | |
| scriptSource | back to summary |
|---|---|
| pack-priv String scriptSource | |
| template | back to summary |
|---|---|
| pack-priv final Closure<V> template | |
| templateSource | back to summary |
|---|---|
| private StringBuilder templateSource | |
| useLastRead | back to summary |
|---|---|
| pack-priv boolean useLastRead | |
| StreamingTemplate | back 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()
| |
| append | back to summary |
|---|---|
| private void append(final StringBuilder target, StreamingTemplateEngine. | |
| append | back to summary |
|---|---|
| private void append(final StringBuilder target, StreamingTemplateEngine. | |
| clear | back to summary |
|---|---|
| private static void clear(StringBuilder lookAhead) | |
| createTemplateClosure | back to summary |
|---|---|
| private Closure | |
| error | back to summary |
|---|---|
| public void error(int index, List<StreamingTemplateEngine. | |
| findPrecedingSection | back to summary |
|---|---|
| private StreamingTemplateEngine. | |
| finishStringSection | back to summary |
|---|---|
| private void finishStringSection(List<StreamingTemplateEngine. Called to handle the ending of a string section.
| |
| getErrorContext | back to summary |
|---|---|
| private String getErrorContext(int actualLine) throws IOException | |
| getLinesInSource | back to summary |
|---|---|
private int getLinesInSource() throws IOException
| |
| handleEscaping | back to summary |
|---|---|
| private void handleEscaping(final Reader source, final StreamingTemplateEngine. | |
| make | back to summary |
|---|---|
| public Writable make() Implements groovy.
| |
| make | back to summary |
|---|---|
| public Writable make(final Map<K, V> map) Implements groovy.
| |
| mangleExceptionMessage | back to summary |
|---|---|
| private String mangleExceptionMessage(String original, StreamingTemplateEngine. | |
| mangleMultipleCompilationErrorsException | back to summary |
|---|---|
| private RuntimeException mangleMultipleCompilationErrorsException(MultipleCompilationErrorsException e, List<StreamingTemplateEngine. | |
| offsetPositionFromSection | back to summary |
|---|---|
| private void offsetPositionFromSection(StreamingTemplateEngine. | |
| padLeft | back to summary |
|---|---|
| private String padLeft(String s, int len) | |
| parseDollarCurlyIdentifier | back to summary |
|---|---|
| private void parseDollarCurlyIdentifier(final Reader reader, final StringBuilder target, final StreamingTemplateEngine. 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
| |
| parseDollarIdentifier | back to summary |
|---|---|
| private int parseDollarIdentifier(int c, final Reader reader, final StringBuilder target, final StreamingTemplateEngine. Parses a non curly dollar preceded identifier of the type '$bird' in the following template example: Alice why is a $bird like a writing deskwhich 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
| |
| parseExpression | back to summary |
|---|---|
| private void parseExpression(final Reader reader, final StringBuilder target, final StreamingTemplateEngine. Parse a <%= .... %> expression | |
| parseSection | back to summary |
|---|---|
| private void parseSection(final int pendingC, final Reader reader, final StringBuilder target, final StreamingTemplateEngine. Parse a <% .... %> section if we are writing a GString close and append ';' then write the section as a statement | |
| read | back to summary |
|---|---|
| private int read(final Reader reader, StreamingTemplateEngine. | |
| read | back to summary |
|---|---|
| private int read(final Reader reader, StreamingTemplateEngine. | |
| read | back to summary |
|---|---|
| private int read(final Reader reader) throws IOException | |
| readAndAppend | back to summary |
|---|---|
| private void readAndAppend(Reader reader, StringBuilder target, StreamingTemplateEngine. | |
| Modifier and Type | Field and Description |
|---|---|
| private static final long |
| Access | Constructor and Description |
|---|---|
| private |
| serialVersionUID | back to summary |
|---|---|
| private static final long serialVersionUID | |
| FinishedReadingException | back to summary |
|---|---|
| private FinishedReadingException() | |
| Access | Constructor and Description |
|---|---|
| private | |
| private |
| Modifier and Type | Method and Description |
|---|---|
| private void | |
| public String |
| Position | back to summary |
|---|---|
| private Position(int row, int column) | |
| Position | back to summary |
|---|---|
| private Position(StreamingTemplateEngine. | |
| set | back to summary |
|---|---|
| private void set(StreamingTemplateEngine. | |
| toString | back to summary |
|---|---|
| public String toString() Overrides java. Doc from java. Returns a string representation of the object.
Satisfying this method's contract implies a non-
| |
Alice why is a $bird like a writing deskWould produce a string section "Alice why is a " followed by a dollar identifier expression followed by another string section " like a writing desk".
| Modifier and Type | Field and Description |
|---|---|
| pack-priv StringBuilder | |
| pack-priv StreamingTemplateEngine. | |
| pack-priv StreamingTemplateEngine. | |
| pack-priv StreamingTemplateEngine. |
| Access | Constructor and Description |
|---|---|
| private |
| Modifier and Type | Method and Description |
|---|---|
| public String |
| data | back to summary |
|---|---|
| pack-priv StringBuilder data | |
| firstSourcePosition | back to summary |
|---|---|
| pack-priv StreamingTemplateEngine. | |
| lastSourcePosition | back to summary |
|---|---|
| pack-priv StreamingTemplateEngine. | |
| lastTargetPosition | back to summary |
|---|---|
| pack-priv StreamingTemplateEngine. | |
| StringSection | back to summary |
|---|---|
| private StringSection(StreamingTemplateEngine. | |
| toString | back to summary |
|---|---|
| public String toString() Overrides java. Doc from java. Returns a string representation of the object.
Satisfying this method's contract implies a non- | |