CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-codehaus-groovy--groovy-templates

Groovy templating engines providing JSP-style scripting, GString expressions, markup builders, and streaming template capabilities for generating dynamic content from templates with variable substitution and control flow

Pending
Overview
Eval results
Files

markup-template-engine.mddocs/

Markup Template Engine

The MarkupTemplateEngine is an advanced template engine that leverages Groovy's StreamingMarkupBuilder to generate XML/XHTML with type safety, compile-time checking, template inheritance, and sophisticated layout support. It uses a Groovy DSL rather than JSP-style syntax.

API

class MarkupTemplateEngine extends TemplateEngine {
    MarkupTemplateEngine();
    MarkupTemplateEngine(TemplateConfiguration config);
    MarkupTemplateEngine(ClassLoader parentLoader, TemplateConfiguration config);
    MarkupTemplateEngine(ClassLoader parentLoader, TemplateConfiguration config, TemplateResolver resolver);
    
    Template createTemplate(Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException;
}

Configuration

The MarkupTemplateEngine uses a comprehensive configuration system for controlling output formatting and behavior.

class TemplateConfiguration {
    TemplateConfiguration();
    TemplateConfiguration(TemplateConfiguration that);
    
    // XML/HTML output configuration
    void setDeclarationEncoding(String declarationEncoding);
    String getDeclarationEncoding();
    void setExpandEmptyElements(boolean expandEmptyElements);
    boolean isExpandEmptyElements();
    void setUseDoubleQuotes(boolean useDoubleQuotes);
    boolean isUseDoubleQuotes();
    void setNewLineString(String newLineString);
    String getNewLineString();
    
    // Content processing configuration
    void setAutoEscape(boolean autoEscape);
    boolean isAutoEscape();
    void setAutoIndent(boolean autoIndent);
    boolean isAutoIndent();
    void setAutoIndentString(String autoIndentString);
    String getAutoIndentString();
    void setAutoNewLine(boolean autoNewLine);
    boolean isAutoNewLine();
    
    // Template system configuration
    void setBaseTemplateClass(Class<? extends BaseTemplate> baseTemplateClass);
    Class<? extends BaseTemplate> getBaseTemplateClass();
    void setLocale(Locale locale);
    Locale getLocale();
    void setCacheTemplates(boolean cacheTemplates);
    boolean isCacheTemplates();
}

Template Syntax

Unlike other template engines, MarkupTemplateEngine uses a Groovy DSL syntax based on method calls and closures:

html {
    head {
        title "Page Title"
        meta(charset: 'UTF-8')
    }
    body {
        h1 "Welcome ${userName}"
        div(class: 'content') {
            p "This is content"
            ul {
                items.each { item ->
                    li item.name
                }
            }
        }
    }
}

Usage Examples

Basic HTML Generation

import groovy.text.markup.MarkupTemplateEngine;
import groovy.text.markup.TemplateConfiguration;
import groovy.text.Template;
import java.util.HashMap;
import java.util.Map;

MarkupTemplateEngine engine = new MarkupTemplateEngine();

String templateText = """
html {
    head {
        title title
        meta(charset: 'UTF-8')
    }
    body {
        h1 "Welcome \${userName}"
        div(class: 'content') {
            p message
            if (showList) {
                ul {
                    items.each { item ->
                        li item
                    }
                }
            }
        }
    }
}
""";

Template template = engine.createTemplate(templateText);

Map<String, Object> binding = new HashMap<>();
binding.put("title", "My Page");
binding.put("userName", "Alice");
binding.put("message", "Hello world!");
binding.put("showList", true);
binding.put("items", Arrays.asList("Item 1", "Item 2", "Item 3"));

String result = template.make(binding).toString();

Configuration Options

TemplateConfiguration config = new TemplateConfiguration();

// XML declaration and formatting
config.setDeclarationEncoding("UTF-8");
config.setExpandEmptyElements(false); // <br/> instead of <br></br>
config.setUseDoubleQuotes(true); // Use " instead of '
config.setNewLineString("\n");

// Auto-formatting
config.setAutoEscape(true); // Escape HTML entities automatically
config.setAutoIndent(true); // Automatic indentation
config.setAutoIndentString("  "); // Two spaces for indentation
config.setAutoNewLine(true); // Automatic newlines

// Template system
config.setLocale(Locale.ENGLISH);
config.setCacheTemplates(true); // Cache compiled templates

MarkupTemplateEngine engine = new MarkupTemplateEngine(config);

Custom Base Template Class

// Custom base template with helper methods
public class MyBaseTemplate extends BaseTemplate {
    public String formatCurrency(double amount) {
        return String.format("$%.2f", amount);
    }
    
    public void renderUserCard(String name, String email) {
        div(Map.of("class", "user-card"), () -> {
            h3(name);
            p(email);
        });
    }
}

TemplateConfiguration config = new TemplateConfiguration();
config.setBaseTemplateClass(MyBaseTemplate.class);

MarkupTemplateEngine engine = new MarkupTemplateEngine(config);

Template with Custom Methods

String templateText = """
html {
    head {
        title 'E-commerce Site'
    }
    body {
        h1 'Products'
        div(class: 'products') {
            products.each { product ->
                renderProductCard(product)
            }
        }
        div(class: 'total') {
            p "Total: \${formatCurrency(total)}"
        }
    }
}
""";

XML Generation with Namespaces

String xmlTemplateText = """
yieldUnescaped '<?xml version="1.0" encoding="UTF-8"?>'
soap('http://schemas.xmlsoap.org/soap/envelope/') {
    'soap:Envelope' {
        'soap:Header' {
            if (authToken) {
                auth(xmlns: 'http://example.com/auth') {
                    token authToken
                }
            }
        }
        'soap:Body' {
            request(xmlns: 'http://example.com/service') {
                operation operation
                parameters {
                    params.each { key, value ->
                        parameter(name: key, value)
                    }
                }
            }
        }
    }
}
""";

Advanced Layout and Includes

// Main template using layout
String templateText = """
Map layoutModel = [title: 'Page Title']
layout(layoutModel, 'main.tpl')

// Content for the layout
html {
    body {
        h2 'Page Content'
        p message
        includeGroovy('fragments/sidebar.tpl')
    }
}
""";

// Layout template (main.tpl)
String layoutText = """
html {
    head {
        title title
        link(rel: 'stylesheet', href: '/css/style.css')
    }
    body {
        header {
            nav {
                // Navigation content
            }
        }
        main {
            bodyContents()
        }
        footer {
            p "© 2024 My Company"
        }
    }
}
""";

BaseTemplate API

The BaseTemplate class provides utility methods for template development:

abstract class BaseTemplate {
    // Content generation
    BaseTemplate yieldUnescaped(Object obj) throws IOException;
    BaseTemplate yield(Object obj) throws IOException;
    String stringOf(Closure cl) throws IOException;
    Object tryEscape(Object contents);
    Writer getOut();
    
    // Template composition and layouts
    Object layout(Map model, String templateName) throws IOException, ClassNotFoundException;
    Object layout(Map model, String templateName, boolean inheritModel) throws IOException, ClassNotFoundException;
    Object fragment(Map model, String templateText) throws IOException, ClassNotFoundException;
    Closure contents(Closure cl);
    
    // Template includes
    void includeGroovy(String templatePath) throws IOException, ClassNotFoundException;
    void includeEscaped(String templatePath) throws IOException;
    void includeUnescaped(String templatePath) throws IOException;
    
    // HTML/XML utilities
    BaseTemplate comment(Object cs) throws IOException;
    BaseTemplate xmlDeclaration() throws IOException;
    BaseTemplate pi(Map<?, ?> attrs) throws IOException;
    void newLine() throws IOException;
    
    // Model access
    Map getModel();
    
    // Abstract method to implement
    abstract Object run();
}

Type Safety and Compile-Time Checking

The MarkupTemplateEngine provides compile-time type checking when used with appropriate type annotations:

// Template with type hints
@groovy.transform.TypeChecked
template = """
html {
    head {
        title ((String) title).toUpperCase()
    }
    body {
        ((List<String>) items).each { String item ->
            p item.substring(0, Math.min(item.length(), 50))
        }
    }
}
"""

Error Handling

import groovy.text.markup.TemplateConfiguration;
import groovy.text.markup.MarkupTemplateEngine;

try {
    TemplateConfiguration config = new TemplateConfiguration();
    MarkupTemplateEngine engine = new MarkupTemplateEngine(config);
    Template template = engine.createTemplate(templateSource);
    String result = template.make(binding).toString();
} catch (CompilationFailedException e) {
    // Template compilation error
    System.err.println("Template compilation failed: " + e.getMessage());
} catch (Exception e) {
    // Other errors during template processing
    System.err.println("Template error: " + e.getMessage());
}

Performance Considerations

Template Caching

TemplateConfiguration config = new TemplateConfiguration();
config.setCacheTemplates(true); // Enable template caching

MarkupTemplateEngine engine = new MarkupTemplateEngine(config);

// Templates are automatically cached by source content
Template template1 = engine.createTemplate(templateSource);
Template template2 = engine.createTemplate(templateSource); // Reuses cached template

Memory Efficiency

The MarkupTemplateEngine uses streaming output and can handle large documents efficiently:

// Stream large output directly to file
Template template = engine.createTemplate(largeTemplateSource);
try (FileWriter writer = new FileWriter("large-output.xml")) {
    template.make(binding).writeTo(writer);
}

Integration Examples

Web Application Integration

// Servlet example
@WebServlet("/template")
public class TemplateServlet extends HttpServlet {
    private MarkupTemplateEngine engine;
    
    @Override
    public void init() {
        TemplateConfiguration config = new TemplateConfiguration();
        config.setAutoEscape(true); // Important for web content
        config.setAutoIndent(true);
        this.engine = new MarkupTemplateEngine(config);
    }
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws IOException {
        resp.setContentType("text/html;charset=UTF-8");
        
        Map<String, Object> model = new HashMap<>();
        model.put("user", req.getUserPrincipal().getName());
        model.put("timestamp", new Date());
        
        Template template = engine.createTemplate(getTemplateSource());
        template.make(model).writeTo(resp.getWriter());
    }
}

Spring Framework Integration

@Configuration
public class TemplateConfig {
    
    @Bean
    public MarkupTemplateEngine markupTemplateEngine() {
        TemplateConfiguration config = new TemplateConfiguration();
        config.setAutoEscape(true);
        config.setAutoIndent(true);
        config.setCacheTemplates(true);
        return new MarkupTemplateEngine(config);
    }
}

@Controller
public class PageController {
    
    @Autowired
    private MarkupTemplateEngine templateEngine;
    
    @GetMapping("/page")
    public void renderPage(HttpServletResponse response, Model model) 
            throws IOException {
        response.setContentType("text/html");
        
        Template template = templateEngine.createTemplate(templateSource);
        template.make(model.asMap()).writeTo(response.getWriter());
    }
}

Best Practices

Template Organization

  1. Use layouts for common structure
  2. Create reusable fragments with includes
  3. Extend BaseTemplate for custom utility methods
  4. Use meaningful template file names and organization

Performance Optimization

  1. Enable template caching in production
  2. Use streaming output for large content
  3. Minimize model object creation
  4. Configure appropriate auto-formatting settings

Security

  1. Enable auto-escaping for web content
  2. Validate and sanitize input data
  3. Use type checking where possible
  4. Be careful with yieldUnescaped() method

Code Quality

  1. Use consistent indentation in templates
  2. Group related template methods
  3. Document complex template logic
  4. Test templates with various data scenarios

Install with Tessl CLI

npx tessl i tessl/maven-org-codehaus-groovy--groovy-templates

docs

basic-template-engines.md

index.md

markup-template-engine.md

streaming-template-engine.md

xml-template-engine.md

tile.json