Template engines for Apache Groovy including SimpleTemplateEngine, StreamingTemplateEngine, XmlTemplateEngine, and MarkupTemplateEngine
—
The MarkupTemplateEngine provides the most advanced template processing capabilities, leveraging StreamingMarkupBuilder for highly optimized XML/XHTML generation. It includes compile-time type checking, extensive configuration options, template caching, and a rich base template class for markup generation.
Advanced template engine with type checking, configuration, and optimization features.
/**
* Advanced template engine leveraging StreamingMarkupBuilder for optimized XML/XHTML generation.
* Includes compile-time type checking, template caching, and extensive configuration options.
*/
public class MarkupTemplateEngine extends TemplateEngine {
/**
* Creates a new MarkupTemplateEngine with default configuration
*/
public MarkupTemplateEngine();
/**
* Creates a new MarkupTemplateEngine with custom configuration
* @param config TemplateConfiguration for engine settings
*/
public MarkupTemplateEngine(TemplateConfiguration config);
/**
* Creates a new MarkupTemplateEngine with custom class loader and configuration
* @param parentLoader ClassLoader for template compilation
* @param config TemplateConfiguration for engine settings
*/
public MarkupTemplateEngine(ClassLoader parentLoader, TemplateConfiguration config);
/**
* Creates a new MarkupTemplateEngine with full customization
* @param parentLoader ClassLoader for template compilation
* @param config TemplateConfiguration for engine settings
* @param resolver TemplateResolver for resolving template resources
*/
public MarkupTemplateEngine(ClassLoader parentLoader, TemplateConfiguration config, TemplateResolver resolver);
}Comprehensive configuration options for markup template generation.
/**
* Configuration options for MarkupTemplateEngine
*/
public class TemplateConfiguration {
/**
* Creates a new TemplateConfiguration with default settings
*/
public TemplateConfiguration();
/**
* Creates a copy of an existing TemplateConfiguration
* @param that configuration to copy
*/
public TemplateConfiguration(TemplateConfiguration that);
// XML Declaration Configuration
public String getDeclarationEncoding();
public void setDeclarationEncoding(String declarationEncoding);
// Element Formatting
public boolean isExpandEmptyElements();
public void setExpandEmptyElements(boolean expandEmptyElements);
// Attribute Quoting
public boolean isUseDoubleQuotes();
public void setUseDoubleQuotes(boolean useDoubleQuotes);
// Line Handling
public String getNewLineString();
public void setNewLineString(String newLineString);
// Content Processing
public boolean isAutoEscape();
public void setAutoEscape(boolean autoEscape);
// Indentation Control
public boolean isAutoIndent();
public void setAutoIndent(boolean autoIndent);
public String getAutoIndentString();
public void setAutoIndentString(String autoIndentString);
// Newline Management
public boolean isAutoNewLine();
public void setAutoNewLine(boolean autoNewLine);
// Template Base Class
public Class<? extends BaseTemplate> getBaseTemplateClass();
public void setBaseTemplateClass(Class<? extends BaseTemplate> baseTemplateClass);
// Internationalization
public Locale getLocale();
public void setLocale(Locale locale);
// Performance
public boolean isCacheTemplates();
public void setCacheTemplates(boolean cacheTemplates);
}Abstract base class providing utility methods for markup generation.
/**
* Base class for all markup templates providing utility methods for markup generation.
* Thread-safe when using distinct instances per thread/document.
*/
public abstract class BaseTemplate implements Writable {
/**
* Constructor for base template
* @param templateEngine the markup template engine
* @param model data model for template
* @param modelTypes type information for model properties
* @param configuration template configuration
*/
public BaseTemplate(MarkupTemplateEngine templateEngine, Map model, Map<String,String> modelTypes, TemplateConfiguration configuration);
/**
* Get the template data model
* @return Map containing template data
*/
public Map getModel();
/**
* Execute the template logic
* @return result of template execution
*/
public abstract Object run();
/**
* Render object with XML escaping for safe output
* @param obj object to render
* @return this template instance for chaining
* @throws IOException if output fails
*/
public BaseTemplate yield(Object obj) throws IOException;
/**
* Render object without escaping (raw output)
* @param obj object to render unescaped
* @return this template instance for chaining
* @throws IOException if output fails
*/
public BaseTemplate yieldUnescaped(Object obj) throws IOException;
/**
* Create string representation of closure output
* @param cl closure to execute and capture output
* @return string representation of closure output
* @throws IOException if output fails
*/
public String stringOf(Closure cl) throws IOException;
/**
* Render XML comment
* @param cs comment content
* @return this template instance for chaining
* @throws IOException if output fails
*/
public BaseTemplate comment(Object cs) throws IOException;
/**
* Render XML declaration
* @return this template instance for chaining
* @throws IOException if output fails
*/
public BaseTemplate xmlDeclaration() throws IOException;
/**
* Render processing instruction
* @param attrs attributes for processing instruction
* @return this template instance for chaining
* @throws IOException if output fails
*/
public BaseTemplate pi(Map<?, ?> attrs) throws IOException;
/**
* Handle dynamic method calls for tag generation
* @param tagName name of the tag
* @param args tag arguments
* @return tag output
* @throws IOException if output fails
*/
public Object methodMissing(String tagName, Object args) throws IOException;
/**
* Include another Groovy template
* @param templatePath path to template to include
* @throws IOException if template loading or rendering fails
* @throws ClassNotFoundException if template class cannot be found
*/
public void includeGroovy(String templatePath) throws IOException, ClassNotFoundException;
/**
* Include template with escaped output
* @param templatePath path to template to include
* @throws IOException if template loading or rendering fails
*/
public void includeEscaped(String templatePath) throws IOException;
/**
* Include template with unescaped output
* @param templatePath path to template to include
* @throws IOException if template loading or rendering fails
*/
public void includeUnescaped(String templatePath) throws IOException;
/**
* Try to escape content if auto-escape is enabled
* @param contents content to potentially escape
* @return escaped or original content
*/
public Object tryEscape(Object contents);
/**
* Get the output writer
* @return the writer for template output
*/
public Writer getOut();
/**
* Write a newline to output
* @throws IOException if write fails
*/
public void newLine() throws IOException;
/**
* Create template fragment from inline template text
* @param model data model for fragment
* @param templateText template source text
* @return fragment output
* @throws IOException if rendering fails
* @throws ClassNotFoundException if template class cannot be found
*/
public Object fragment(Map model, String templateText) throws IOException, ClassNotFoundException;
/**
* Apply layout template
* @param model data model for layout
* @param templateName name of layout template
* @return layout output
* @throws IOException if rendering fails
* @throws ClassNotFoundException if template class cannot be found
*/
public Object layout(Map model, String templateName) throws IOException, ClassNotFoundException;
/**
* Apply layout template with optional model inheritance
* @param model data model for layout
* @param templateName name of layout template
* @param inheritModel whether to inherit current model
* @return layout output
* @throws IOException if rendering fails
* @throws ClassNotFoundException if template class cannot be found
*/
public Object layout(Map model, String templateName, boolean inheritModel) throws IOException, ClassNotFoundException;
/**
* Define content section for layout
* @param cl closure defining content
* @return content closure
*/
public Closure contents(Closure cl);
}Usage Examples:
import groovy.text.markup.MarkupTemplateEngine;
import groovy.text.markup.TemplateConfiguration;
import groovy.text.Template;
import java.util.Map;
import java.util.HashMap;
import java.util.Locale;
// Basic usage with default configuration
MarkupTemplateEngine engine = new MarkupTemplateEngine();
// Advanced configuration
TemplateConfiguration config = new TemplateConfiguration();
config.setAutoIndent(true);
config.setAutoIndentString(" "); // 4 spaces
config.setAutoNewLine(true);
config.setAutoEscape(true);
config.setUseDoubleQuotes(true);
config.setExpandEmptyElements(false);
config.setDeclarationEncoding("UTF-8");
config.setLocale(Locale.ENGLISH);
MarkupTemplateEngine configuredEngine = new MarkupTemplateEngine(config);
// Markup template using Groovy markup DSL
String template = """
html {
head {
title(pageTitle)
meta(charset: 'UTF-8')
}
body {
h1(pageTitle)
div(class: 'content') {
p("Welcome, \${user.name}!")
ul {
items.each { item ->
li(item.name)
}
}
}
}
}
""";
Template compiledTemplate = engine.createTemplate(template);
Map<String, Object> model = new HashMap<>();
model.put("pageTitle", "My Page");
Map<String, String> user = new HashMap<>();
user.put("name", "John Doe");
model.put("user", user);
List<Map<String, String>> items = Arrays.asList(
Collections.singletonMap("name", "Item 1"),
Collections.singletonMap("name", "Item 2"),
Collections.singletonMap("name", "Item 3")
);
model.put("items", items);
String result = compiledTemplate.make(model).toString();MarkupTemplateEngine uses a Groovy-based DSL for generating markup:
// Template syntax
html {
head {
title('My Page')
}
body {
h1('Welcome')
p('This is a paragraph')
}
}// Elements with attributes
div(class: 'container', id: 'main') {
img(src: '/images/logo.png', alt: 'Logo')
a(href: 'https://example.com', target: '_blank', 'Click here')
}
// Empty elements
input(type: 'text', name: 'username', placeholder: 'Enter username')
br()
hr()// Using variables from model
h1(pageTitle)
p("Welcome, ${user.name}!")
// Conditional content
if (user.isAdmin) {
div(class: 'admin-panel') {
p('Admin controls')
}
}// Iterating over collections
ul {
items.each { item ->
li {
a(href: item.url, item.title)
}
}
}
// With index
ol {
products.eachWithIndex { product, index ->
li("${index + 1}. ${product.name} - \$${product.price}")
}
}TemplateConfiguration config = new TemplateConfiguration();
config.setAutoIndent(true);
config.setAutoIndentString(" "); // 2 spaces (default)
// config.setAutoIndentString("\t"); // Use tabs
// config.setAutoIndentString(" "); // 4 spacesconfig.setAutoEscape(true); // Automatically escape content for XML safety// Control empty element format
config.setExpandEmptyElements(false); // <br/> (default)
config.setExpandEmptyElements(true); // <br></br>
// Control attribute quotes
config.setUseDoubleQuotes(false); // class='container' (default)
config.setUseDoubleQuotes(true); // class="container"config.setAutoNewLine(true); // Automatically add newlines
config.setNewLineString("\n"); // Unix line endings (default)
config.setNewLineString("\r\n"); // Windows line endingsconfig.setDeclarationEncoding("UTF-8"); // Adds <?xml version="1.0" encoding="UTF-8"?>MarkupTemplateEngine supports template caching for improved performance:
TemplateConfiguration config = new TemplateConfiguration();
config.setCacheTemplates(true); // Enable caching (default)
MarkupTemplateEngine engine = new MarkupTemplateEngine(config);
// Templates are automatically cached and reused
Template template1 = engine.createTemplate(templateSource);
Template template2 = engine.createTemplate(templateSource); // Returns cached versionExtend BaseTemplate to add custom utility methods:
public class MyCustomTemplate extends BaseTemplate {
public MyCustomTemplate(MarkupTemplateEngine engine, Map model,
Map<String,String> modelTypes, TemplateConfiguration config) {
super(engine, model, modelTypes, config);
}
// Custom helper method
public MyCustomTemplate formatCurrency(double amount) throws IOException {
yield(String.format("$%.2f", amount));
return this;
}
// Custom helper for dates
public MyCustomTemplate formatDate(Date date) throws IOException {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
yield(formatter.format(date));
return this;
}
}
// Configure engine to use custom base template
TemplateConfiguration config = new TemplateConfiguration();
config.setBaseTemplateClass(MyCustomTemplate.class);
MarkupTemplateEngine engine = new MarkupTemplateEngine(config);Interface for custom template path resolution.
/**
* Interface for resolving template paths to URLs
*/
public interface TemplateResolver {
/**
* Configure the resolver with class loader and template configuration
* @param templateClassLoader class loader for template compilation
* @param configuration template configuration settings
*/
void configure(ClassLoader templateClassLoader, TemplateConfiguration configuration);
/**
* Resolve template path to URL
* @param templatePath path to resolve
* @return URL for the template resource
* @throws IOException if template cannot be found or accessed
*/
URL resolveTemplate(String templatePath) throws IOException;
}Built-in Resolvers:
/**
* Default template resolver that searches classpath
*/
public static class DefaultTemplateResolver implements TemplateResolver {
public DefaultTemplateResolver();
public void configure(ClassLoader templateClassLoader, TemplateConfiguration configuration);
public URL resolveTemplate(String templatePath) throws IOException;
}
/**
* Caching template resolver for improved performance
*/
public static class CachingTemplateResolver extends DefaultTemplateResolver {
public CachingTemplateResolver(Map<String, URL> cache);
public CachingTemplateResolver();
public void configure(ClassLoader templateClassLoader, TemplateConfiguration configuration);
public URL resolveTemplate(String templatePath) throws IOException;
}Usage Example:
public class MyTemplateResolver implements TemplateResolver {
@Override
public void configure(ClassLoader templateClassLoader, TemplateConfiguration configuration) {
// Initialize resolver with classloader and configuration
}
@Override
public URL resolveTemplate(String templatePath) throws IOException {
// Custom logic for resolving template paths
return getClass().getResource("/templates/" + templatePath);
}
}
MyTemplateResolver resolver = new MyTemplateResolver();
MarkupTemplateEngine engine = new MarkupTemplateEngine(
getClass().getClassLoader(),
new TemplateConfiguration(),
resolver
);MarkupTemplateEngine includes compile-time type checking:
// Templates are type-checked at compile time
String template = """
html {
body {
// This would cause a compile-time error if 'user' is not defined in model
p("Hello \${user.name}")
}
}
""";
// Type information can be provided for better checking
Map<String, String> modelTypes = new HashMap<>();
modelTypes.put("user", "User"); // Specify that 'user' is of type 'User'Utility class for template path resolution with locale support.
/**
* Represents a template resource with optional locale support
*/
public static class TemplateResource {
/**
* Parse a template path into a TemplateResource
* @param fullPath the full template path
* @return parsed TemplateResource
*/
public static TemplateResource parse(String fullPath);
/**
* Create variant with specific locale
* @param locale locale to use
* @return TemplateResource with locale
*/
public TemplateResource withLocale(String locale);
/**
* Check if this resource has a locale
* @return true if locale is specified
*/
public boolean hasLocale();
/**
* String representation of the resource path
* @return string representation
*/
public String toString();
}/**
* Enumeration for different template inclusion types
*/
public enum IncludeType {
template("includeGroovy"),
escaped("includeEscaped"),
unescaped("includeUnescaped");
/**
* Get the method name for this include type
* @return method name
*/
public String getMethodName();
}Writer that provides automatic indentation support for template output.
/**
* Writer that delegates to another writer and provides indentation support
*/
public class DelegatingIndentWriter extends Writer {
public static final String SPACES = " ";
public static final String TAB = "\t";
/**
* Create indent writer with default spaces indentation
* @param delegate writer to delegate to
*/
public DelegatingIndentWriter(Writer delegate);
/**
* Create indent writer with custom indentation
* @param delegate writer to delegate to
* @param indentString string to use for indentation
*/
public DelegatingIndentWriter(Writer delegate, String indentString);
/**
* Increase indentation level
* @return new indentation level
*/
public int next();
/**
* Decrease indentation level
* @return new indentation level
*/
public int previous();
/**
* Write current indentation to output
* @throws IOException if write fails
*/
public void writeIndent() throws IOException;
// Writer methods (inherited)
public void write(int c) throws IOException;
public void write(char[] cbuf) throws IOException;
public void write(char[] cbuf, int off, int len) throws IOException;
public void write(String str) throws IOException;
public void write(String str, int off, int len) throws IOException;
public Writer append(CharSequence csq) throws IOException;
public Writer append(CharSequence csq, int start, int end) throws IOException;
public Writer append(char c) throws IOException;
public void flush() throws IOException;
public void close() throws IOException;
}MarkupTemplateEngine provides comprehensive error handling:
try {
MarkupTemplateEngine engine = new MarkupTemplateEngine();
Template template = engine.createTemplate(templateSource);
String result = template.make(model).toString();
} catch (CompilationFailedException e) {
// Template compilation failed due to syntax errors
System.err.println("Compilation error: " + e.getMessage());
} catch (ClassNotFoundException e) {
// Missing dependencies or custom base template class
System.err.println("Class not found: " + e.getMessage());
} catch (IOException e) {
// I/O error reading template source
System.err.println("I/O error: " + e.getMessage());
} catch (Exception e) {
// Runtime errors during template execution
System.err.println("Runtime error: " + e.getMessage());
}Install with Tessl CLI
npx tessl i tessl/maven-org-apache-groovy--groovy-templates