0
# Markup Template Engine
1
2
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.
3
4
## API
5
6
```java { .api }
7
class MarkupTemplateEngine extends TemplateEngine {
8
MarkupTemplateEngine();
9
MarkupTemplateEngine(TemplateConfiguration config);
10
MarkupTemplateEngine(ClassLoader parentLoader, TemplateConfiguration config);
11
MarkupTemplateEngine(ClassLoader parentLoader, TemplateConfiguration config, TemplateResolver resolver);
12
13
Template createTemplate(Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException;
14
}
15
```
16
17
## Configuration
18
19
The MarkupTemplateEngine uses a comprehensive configuration system for controlling output formatting and behavior.
20
21
```java { .api }
22
class TemplateConfiguration {
23
TemplateConfiguration();
24
TemplateConfiguration(TemplateConfiguration that);
25
26
// XML/HTML output configuration
27
void setDeclarationEncoding(String declarationEncoding);
28
String getDeclarationEncoding();
29
void setExpandEmptyElements(boolean expandEmptyElements);
30
boolean isExpandEmptyElements();
31
void setUseDoubleQuotes(boolean useDoubleQuotes);
32
boolean isUseDoubleQuotes();
33
void setNewLineString(String newLineString);
34
String getNewLineString();
35
36
// Content processing configuration
37
void setAutoEscape(boolean autoEscape);
38
boolean isAutoEscape();
39
void setAutoIndent(boolean autoIndent);
40
boolean isAutoIndent();
41
void setAutoIndentString(String autoIndentString);
42
String getAutoIndentString();
43
void setAutoNewLine(boolean autoNewLine);
44
boolean isAutoNewLine();
45
46
// Template system configuration
47
void setBaseTemplateClass(Class<? extends BaseTemplate> baseTemplateClass);
48
Class<? extends BaseTemplate> getBaseTemplateClass();
49
void setLocale(Locale locale);
50
Locale getLocale();
51
void setCacheTemplates(boolean cacheTemplates);
52
boolean isCacheTemplates();
53
}
54
```
55
56
## Template Syntax
57
58
Unlike other template engines, MarkupTemplateEngine uses a Groovy DSL syntax based on method calls and closures:
59
60
```groovy
61
html {
62
head {
63
title "Page Title"
64
meta(charset: 'UTF-8')
65
}
66
body {
67
h1 "Welcome ${userName}"
68
div(class: 'content') {
69
p "This is content"
70
ul {
71
items.each { item ->
72
li item.name
73
}
74
}
75
}
76
}
77
}
78
```
79
80
## Usage Examples
81
82
### Basic HTML Generation
83
84
```java
85
import groovy.text.markup.MarkupTemplateEngine;
86
import groovy.text.markup.TemplateConfiguration;
87
import groovy.text.Template;
88
import java.util.HashMap;
89
import java.util.Map;
90
91
MarkupTemplateEngine engine = new MarkupTemplateEngine();
92
93
String templateText = """
94
html {
95
head {
96
title title
97
meta(charset: 'UTF-8')
98
}
99
body {
100
h1 "Welcome \${userName}"
101
div(class: 'content') {
102
p message
103
if (showList) {
104
ul {
105
items.each { item ->
106
li item
107
}
108
}
109
}
110
}
111
}
112
}
113
""";
114
115
Template template = engine.createTemplate(templateText);
116
117
Map<String, Object> binding = new HashMap<>();
118
binding.put("title", "My Page");
119
binding.put("userName", "Alice");
120
binding.put("message", "Hello world!");
121
binding.put("showList", true);
122
binding.put("items", Arrays.asList("Item 1", "Item 2", "Item 3"));
123
124
String result = template.make(binding).toString();
125
```
126
127
### Configuration Options
128
129
```java
130
TemplateConfiguration config = new TemplateConfiguration();
131
132
// XML declaration and formatting
133
config.setDeclarationEncoding("UTF-8");
134
config.setExpandEmptyElements(false); // <br/> instead of <br></br>
135
config.setUseDoubleQuotes(true); // Use " instead of '
136
config.setNewLineString("\n");
137
138
// Auto-formatting
139
config.setAutoEscape(true); // Escape HTML entities automatically
140
config.setAutoIndent(true); // Automatic indentation
141
config.setAutoIndentString(" "); // Two spaces for indentation
142
config.setAutoNewLine(true); // Automatic newlines
143
144
// Template system
145
config.setLocale(Locale.ENGLISH);
146
config.setCacheTemplates(true); // Cache compiled templates
147
148
MarkupTemplateEngine engine = new MarkupTemplateEngine(config);
149
```
150
151
### Custom Base Template Class
152
153
```java
154
// Custom base template with helper methods
155
public class MyBaseTemplate extends BaseTemplate {
156
public String formatCurrency(double amount) {
157
return String.format("$%.2f", amount);
158
}
159
160
public void renderUserCard(String name, String email) {
161
div(Map.of("class", "user-card"), () -> {
162
h3(name);
163
p(email);
164
});
165
}
166
}
167
168
TemplateConfiguration config = new TemplateConfiguration();
169
config.setBaseTemplateClass(MyBaseTemplate.class);
170
171
MarkupTemplateEngine engine = new MarkupTemplateEngine(config);
172
```
173
174
### Template with Custom Methods
175
176
```java
177
String templateText = """
178
html {
179
head {
180
title 'E-commerce Site'
181
}
182
body {
183
h1 'Products'
184
div(class: 'products') {
185
products.each { product ->
186
renderProductCard(product)
187
}
188
}
189
div(class: 'total') {
190
p "Total: \${formatCurrency(total)}"
191
}
192
}
193
}
194
""";
195
```
196
197
### XML Generation with Namespaces
198
199
```java
200
String xmlTemplateText = """
201
yieldUnescaped '<?xml version="1.0" encoding="UTF-8"?>'
202
soap('http://schemas.xmlsoap.org/soap/envelope/') {
203
'soap:Envelope' {
204
'soap:Header' {
205
if (authToken) {
206
auth(xmlns: 'http://example.com/auth') {
207
token authToken
208
}
209
}
210
}
211
'soap:Body' {
212
request(xmlns: 'http://example.com/service') {
213
operation operation
214
parameters {
215
params.each { key, value ->
216
parameter(name: key, value)
217
}
218
}
219
}
220
}
221
}
222
}
223
""";
224
```
225
226
### Advanced Layout and Includes
227
228
```java
229
// Main template using layout
230
String templateText = """
231
Map layoutModel = [title: 'Page Title']
232
layout(layoutModel, 'main.tpl')
233
234
// Content for the layout
235
html {
236
body {
237
h2 'Page Content'
238
p message
239
includeGroovy('fragments/sidebar.tpl')
240
}
241
}
242
""";
243
244
// Layout template (main.tpl)
245
String layoutText = """
246
html {
247
head {
248
title title
249
link(rel: 'stylesheet', href: '/css/style.css')
250
}
251
body {
252
header {
253
nav {
254
// Navigation content
255
}
256
}
257
main {
258
bodyContents()
259
}
260
footer {
261
p "© 2024 My Company"
262
}
263
}
264
}
265
""";
266
```
267
268
## BaseTemplate API
269
270
The BaseTemplate class provides utility methods for template development:
271
272
```java { .api }
273
abstract class BaseTemplate {
274
// Content generation
275
BaseTemplate yieldUnescaped(Object obj) throws IOException;
276
BaseTemplate yield(Object obj) throws IOException;
277
String stringOf(Closure cl) throws IOException;
278
Object tryEscape(Object contents);
279
Writer getOut();
280
281
// Template composition and layouts
282
Object layout(Map model, String templateName) throws IOException, ClassNotFoundException;
283
Object layout(Map model, String templateName, boolean inheritModel) throws IOException, ClassNotFoundException;
284
Object fragment(Map model, String templateText) throws IOException, ClassNotFoundException;
285
Closure contents(Closure cl);
286
287
// Template includes
288
void includeGroovy(String templatePath) throws IOException, ClassNotFoundException;
289
void includeEscaped(String templatePath) throws IOException;
290
void includeUnescaped(String templatePath) throws IOException;
291
292
// HTML/XML utilities
293
BaseTemplate comment(Object cs) throws IOException;
294
BaseTemplate xmlDeclaration() throws IOException;
295
BaseTemplate pi(Map<?, ?> attrs) throws IOException;
296
void newLine() throws IOException;
297
298
// Model access
299
Map getModel();
300
301
// Abstract method to implement
302
abstract Object run();
303
}
304
```
305
306
## Type Safety and Compile-Time Checking
307
308
The MarkupTemplateEngine provides compile-time type checking when used with appropriate type annotations:
309
310
```groovy
311
// Template with type hints
312
@groovy.transform.TypeChecked
313
template = """
314
html {
315
head {
316
title ((String) title).toUpperCase()
317
}
318
body {
319
((List<String>) items).each { String item ->
320
p item.substring(0, Math.min(item.length(), 50))
321
}
322
}
323
}
324
"""
325
```
326
327
## Error Handling
328
329
```java
330
import groovy.text.markup.TemplateConfiguration;
331
import groovy.text.markup.MarkupTemplateEngine;
332
333
try {
334
TemplateConfiguration config = new TemplateConfiguration();
335
MarkupTemplateEngine engine = new MarkupTemplateEngine(config);
336
Template template = engine.createTemplate(templateSource);
337
String result = template.make(binding).toString();
338
} catch (CompilationFailedException e) {
339
// Template compilation error
340
System.err.println("Template compilation failed: " + e.getMessage());
341
} catch (Exception e) {
342
// Other errors during template processing
343
System.err.println("Template error: " + e.getMessage());
344
}
345
```
346
347
## Performance Considerations
348
349
### Template Caching
350
351
```java
352
TemplateConfiguration config = new TemplateConfiguration();
353
config.setCacheTemplates(true); // Enable template caching
354
355
MarkupTemplateEngine engine = new MarkupTemplateEngine(config);
356
357
// Templates are automatically cached by source content
358
Template template1 = engine.createTemplate(templateSource);
359
Template template2 = engine.createTemplate(templateSource); // Reuses cached template
360
```
361
362
### Memory Efficiency
363
364
The MarkupTemplateEngine uses streaming output and can handle large documents efficiently:
365
366
```java
367
// Stream large output directly to file
368
Template template = engine.createTemplate(largeTemplateSource);
369
try (FileWriter writer = new FileWriter("large-output.xml")) {
370
template.make(binding).writeTo(writer);
371
}
372
```
373
374
## Integration Examples
375
376
### Web Application Integration
377
378
```java
379
// Servlet example
380
@WebServlet("/template")
381
public class TemplateServlet extends HttpServlet {
382
private MarkupTemplateEngine engine;
383
384
@Override
385
public void init() {
386
TemplateConfiguration config = new TemplateConfiguration();
387
config.setAutoEscape(true); // Important for web content
388
config.setAutoIndent(true);
389
this.engine = new MarkupTemplateEngine(config);
390
}
391
392
@Override
393
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
394
throws IOException {
395
resp.setContentType("text/html;charset=UTF-8");
396
397
Map<String, Object> model = new HashMap<>();
398
model.put("user", req.getUserPrincipal().getName());
399
model.put("timestamp", new Date());
400
401
Template template = engine.createTemplate(getTemplateSource());
402
template.make(model).writeTo(resp.getWriter());
403
}
404
}
405
```
406
407
### Spring Framework Integration
408
409
```java
410
@Configuration
411
public class TemplateConfig {
412
413
@Bean
414
public MarkupTemplateEngine markupTemplateEngine() {
415
TemplateConfiguration config = new TemplateConfiguration();
416
config.setAutoEscape(true);
417
config.setAutoIndent(true);
418
config.setCacheTemplates(true);
419
return new MarkupTemplateEngine(config);
420
}
421
}
422
423
@Controller
424
public class PageController {
425
426
@Autowired
427
private MarkupTemplateEngine templateEngine;
428
429
@GetMapping("/page")
430
public void renderPage(HttpServletResponse response, Model model)
431
throws IOException {
432
response.setContentType("text/html");
433
434
Template template = templateEngine.createTemplate(templateSource);
435
template.make(model.asMap()).writeTo(response.getWriter());
436
}
437
}
438
```
439
440
## Best Practices
441
442
### Template Organization
443
444
1. **Use layouts for common structure**
445
2. **Create reusable fragments with includes**
446
3. **Extend BaseTemplate for custom utility methods**
447
4. **Use meaningful template file names and organization**
448
449
### Performance Optimization
450
451
1. **Enable template caching in production**
452
2. **Use streaming output for large content**
453
3. **Minimize model object creation**
454
4. **Configure appropriate auto-formatting settings**
455
456
### Security
457
458
1. **Enable auto-escaping for web content**
459
2. **Validate and sanitize input data**
460
3. **Use type checking where possible**
461
4. **Be careful with `yieldUnescaped()` method**
462
463
### Code Quality
464
465
1. **Use consistent indentation in templates**
466
2. **Group related template methods**
467
3. **Document complex template logic**
468
4. **Test templates with various data scenarios**