0
# Markup Templates
1
2
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.
3
4
## Capabilities
5
6
### Markup Template Engine Class
7
8
Advanced template engine with type checking, configuration, and optimization features.
9
10
```java { .api }
11
/**
12
* Advanced template engine leveraging StreamingMarkupBuilder for optimized XML/XHTML generation.
13
* Includes compile-time type checking, template caching, and extensive configuration options.
14
*/
15
public class MarkupTemplateEngine extends TemplateEngine {
16
/**
17
* Creates a new MarkupTemplateEngine with default configuration
18
*/
19
public MarkupTemplateEngine();
20
21
/**
22
* Creates a new MarkupTemplateEngine with custom configuration
23
* @param config TemplateConfiguration for engine settings
24
*/
25
public MarkupTemplateEngine(TemplateConfiguration config);
26
27
/**
28
* Creates a new MarkupTemplateEngine with custom class loader and configuration
29
* @param parentLoader ClassLoader for template compilation
30
* @param config TemplateConfiguration for engine settings
31
*/
32
public MarkupTemplateEngine(ClassLoader parentLoader, TemplateConfiguration config);
33
34
/**
35
* Creates a new MarkupTemplateEngine with full customization
36
* @param parentLoader ClassLoader for template compilation
37
* @param config TemplateConfiguration for engine settings
38
* @param resolver TemplateResolver for resolving template resources
39
*/
40
public MarkupTemplateEngine(ClassLoader parentLoader, TemplateConfiguration config, TemplateResolver resolver);
41
}
42
```
43
44
### Template Configuration Class
45
46
Comprehensive configuration options for markup template generation.
47
48
```java { .api }
49
/**
50
* Configuration options for MarkupTemplateEngine
51
*/
52
public class TemplateConfiguration {
53
/**
54
* Creates a new TemplateConfiguration with default settings
55
*/
56
public TemplateConfiguration();
57
58
/**
59
* Creates a copy of an existing TemplateConfiguration
60
* @param that configuration to copy
61
*/
62
public TemplateConfiguration(TemplateConfiguration that);
63
64
// XML Declaration Configuration
65
public String getDeclarationEncoding();
66
public void setDeclarationEncoding(String declarationEncoding);
67
68
// Element Formatting
69
public boolean isExpandEmptyElements();
70
public void setExpandEmptyElements(boolean expandEmptyElements);
71
72
// Attribute Quoting
73
public boolean isUseDoubleQuotes();
74
public void setUseDoubleQuotes(boolean useDoubleQuotes);
75
76
// Line Handling
77
public String getNewLineString();
78
public void setNewLineString(String newLineString);
79
80
// Content Processing
81
public boolean isAutoEscape();
82
public void setAutoEscape(boolean autoEscape);
83
84
// Indentation Control
85
public boolean isAutoIndent();
86
public void setAutoIndent(boolean autoIndent);
87
public String getAutoIndentString();
88
public void setAutoIndentString(String autoIndentString);
89
90
// Newline Management
91
public boolean isAutoNewLine();
92
public void setAutoNewLine(boolean autoNewLine);
93
94
// Template Base Class
95
public Class<? extends BaseTemplate> getBaseTemplateClass();
96
public void setBaseTemplateClass(Class<? extends BaseTemplate> baseTemplateClass);
97
98
// Internationalization
99
public Locale getLocale();
100
public void setLocale(Locale locale);
101
102
// Performance
103
public boolean isCacheTemplates();
104
public void setCacheTemplates(boolean cacheTemplates);
105
}
106
```
107
108
### Base Template Class
109
110
Abstract base class providing utility methods for markup generation.
111
112
```java { .api }
113
/**
114
* Base class for all markup templates providing utility methods for markup generation.
115
* Thread-safe when using distinct instances per thread/document.
116
*/
117
public abstract class BaseTemplate implements Writable {
118
/**
119
* Constructor for base template
120
* @param templateEngine the markup template engine
121
* @param model data model for template
122
* @param modelTypes type information for model properties
123
* @param configuration template configuration
124
*/
125
public BaseTemplate(MarkupTemplateEngine templateEngine, Map model, Map<String,String> modelTypes, TemplateConfiguration configuration);
126
127
/**
128
* Get the template data model
129
* @return Map containing template data
130
*/
131
public Map getModel();
132
133
/**
134
* Execute the template logic
135
* @return result of template execution
136
*/
137
public abstract Object run();
138
139
/**
140
* Render object with XML escaping for safe output
141
* @param obj object to render
142
* @return this template instance for chaining
143
* @throws IOException if output fails
144
*/
145
public BaseTemplate yield(Object obj) throws IOException;
146
147
/**
148
* Render object without escaping (raw output)
149
* @param obj object to render unescaped
150
* @return this template instance for chaining
151
* @throws IOException if output fails
152
*/
153
public BaseTemplate yieldUnescaped(Object obj) throws IOException;
154
155
/**
156
* Create string representation of closure output
157
* @param cl closure to execute and capture output
158
* @return string representation of closure output
159
* @throws IOException if output fails
160
*/
161
public String stringOf(Closure cl) throws IOException;
162
163
/**
164
* Render XML comment
165
* @param cs comment content
166
* @return this template instance for chaining
167
* @throws IOException if output fails
168
*/
169
public BaseTemplate comment(Object cs) throws IOException;
170
171
/**
172
* Render XML declaration
173
* @return this template instance for chaining
174
* @throws IOException if output fails
175
*/
176
public BaseTemplate xmlDeclaration() throws IOException;
177
178
/**
179
* Render processing instruction
180
* @param attrs attributes for processing instruction
181
* @return this template instance for chaining
182
* @throws IOException if output fails
183
*/
184
public BaseTemplate pi(Map<?, ?> attrs) throws IOException;
185
186
/**
187
* Handle dynamic method calls for tag generation
188
* @param tagName name of the tag
189
* @param args tag arguments
190
* @return tag output
191
* @throws IOException if output fails
192
*/
193
public Object methodMissing(String tagName, Object args) throws IOException;
194
195
/**
196
* Include another Groovy template
197
* @param templatePath path to template to include
198
* @throws IOException if template loading or rendering fails
199
* @throws ClassNotFoundException if template class cannot be found
200
*/
201
public void includeGroovy(String templatePath) throws IOException, ClassNotFoundException;
202
203
/**
204
* Include template with escaped output
205
* @param templatePath path to template to include
206
* @throws IOException if template loading or rendering fails
207
*/
208
public void includeEscaped(String templatePath) throws IOException;
209
210
/**
211
* Include template with unescaped output
212
* @param templatePath path to template to include
213
* @throws IOException if template loading or rendering fails
214
*/
215
public void includeUnescaped(String templatePath) throws IOException;
216
217
/**
218
* Try to escape content if auto-escape is enabled
219
* @param contents content to potentially escape
220
* @return escaped or original content
221
*/
222
public Object tryEscape(Object contents);
223
224
/**
225
* Get the output writer
226
* @return the writer for template output
227
*/
228
public Writer getOut();
229
230
/**
231
* Write a newline to output
232
* @throws IOException if write fails
233
*/
234
public void newLine() throws IOException;
235
236
/**
237
* Create template fragment from inline template text
238
* @param model data model for fragment
239
* @param templateText template source text
240
* @return fragment output
241
* @throws IOException if rendering fails
242
* @throws ClassNotFoundException if template class cannot be found
243
*/
244
public Object fragment(Map model, String templateText) throws IOException, ClassNotFoundException;
245
246
/**
247
* Apply layout template
248
* @param model data model for layout
249
* @param templateName name of layout template
250
* @return layout output
251
* @throws IOException if rendering fails
252
* @throws ClassNotFoundException if template class cannot be found
253
*/
254
public Object layout(Map model, String templateName) throws IOException, ClassNotFoundException;
255
256
/**
257
* Apply layout template with optional model inheritance
258
* @param model data model for layout
259
* @param templateName name of layout template
260
* @param inheritModel whether to inherit current model
261
* @return layout output
262
* @throws IOException if rendering fails
263
* @throws ClassNotFoundException if template class cannot be found
264
*/
265
public Object layout(Map model, String templateName, boolean inheritModel) throws IOException, ClassNotFoundException;
266
267
/**
268
* Define content section for layout
269
* @param cl closure defining content
270
* @return content closure
271
*/
272
public Closure contents(Closure cl);
273
}
274
```
275
276
**Usage Examples:**
277
278
```java
279
import groovy.text.markup.MarkupTemplateEngine;
280
import groovy.text.markup.TemplateConfiguration;
281
import groovy.text.Template;
282
import java.util.Map;
283
import java.util.HashMap;
284
import java.util.Locale;
285
286
// Basic usage with default configuration
287
MarkupTemplateEngine engine = new MarkupTemplateEngine();
288
289
// Advanced configuration
290
TemplateConfiguration config = new TemplateConfiguration();
291
config.setAutoIndent(true);
292
config.setAutoIndentString(" "); // 4 spaces
293
config.setAutoNewLine(true);
294
config.setAutoEscape(true);
295
config.setUseDoubleQuotes(true);
296
config.setExpandEmptyElements(false);
297
config.setDeclarationEncoding("UTF-8");
298
config.setLocale(Locale.ENGLISH);
299
300
MarkupTemplateEngine configuredEngine = new MarkupTemplateEngine(config);
301
302
// Markup template using Groovy markup DSL
303
String template = """
304
html {
305
head {
306
title(pageTitle)
307
meta(charset: 'UTF-8')
308
}
309
body {
310
h1(pageTitle)
311
div(class: 'content') {
312
p("Welcome, \${user.name}!")
313
ul {
314
items.each { item ->
315
li(item.name)
316
}
317
}
318
}
319
}
320
}
321
""";
322
323
Template compiledTemplate = engine.createTemplate(template);
324
325
Map<String, Object> model = new HashMap<>();
326
model.put("pageTitle", "My Page");
327
328
Map<String, String> user = new HashMap<>();
329
user.put("name", "John Doe");
330
model.put("user", user);
331
332
List<Map<String, String>> items = Arrays.asList(
333
Collections.singletonMap("name", "Item 1"),
334
Collections.singletonMap("name", "Item 2"),
335
Collections.singletonMap("name", "Item 3")
336
);
337
model.put("items", items);
338
339
String result = compiledTemplate.make(model).toString();
340
```
341
342
### Markup DSL Syntax
343
344
MarkupTemplateEngine uses a Groovy-based DSL for generating markup:
345
346
#### Basic Element Creation
347
```groovy
348
// Template syntax
349
html {
350
head {
351
title('My Page')
352
}
353
body {
354
h1('Welcome')
355
p('This is a paragraph')
356
}
357
}
358
```
359
360
#### Elements with Attributes
361
```groovy
362
// Elements with attributes
363
div(class: 'container', id: 'main') {
364
img(src: '/images/logo.png', alt: 'Logo')
365
a(href: 'https://example.com', target: '_blank', 'Click here')
366
}
367
368
// Empty elements
369
input(type: 'text', name: 'username', placeholder: 'Enter username')
370
br()
371
hr()
372
```
373
374
#### Variable Substitution
375
```groovy
376
// Using variables from model
377
h1(pageTitle)
378
p("Welcome, ${user.name}!")
379
380
// Conditional content
381
if (user.isAdmin) {
382
div(class: 'admin-panel') {
383
p('Admin controls')
384
}
385
}
386
```
387
388
#### Loops and Iteration
389
```groovy
390
// Iterating over collections
391
ul {
392
items.each { item ->
393
li {
394
a(href: item.url, item.title)
395
}
396
}
397
}
398
399
// With index
400
ol {
401
products.eachWithIndex { product, index ->
402
li("${index + 1}. ${product.name} - \$${product.price}")
403
}
404
}
405
```
406
407
### Configuration Options
408
409
#### Auto-Indentation
410
```java
411
TemplateConfiguration config = new TemplateConfiguration();
412
config.setAutoIndent(true);
413
config.setAutoIndentString(" "); // 2 spaces (default)
414
// config.setAutoIndentString("\t"); // Use tabs
415
// config.setAutoIndentString(" "); // 4 spaces
416
```
417
418
#### Auto-Escaping
419
```java
420
config.setAutoEscape(true); // Automatically escape content for XML safety
421
```
422
423
#### Element Formatting
424
```java
425
// Control empty element format
426
config.setExpandEmptyElements(false); // <br/> (default)
427
config.setExpandEmptyElements(true); // <br></br>
428
429
// Control attribute quotes
430
config.setUseDoubleQuotes(false); // class='container' (default)
431
config.setUseDoubleQuotes(true); // class="container"
432
```
433
434
#### Line Handling
435
```java
436
config.setAutoNewLine(true); // Automatically add newlines
437
config.setNewLineString("\n"); // Unix line endings (default)
438
config.setNewLineString("\r\n"); // Windows line endings
439
```
440
441
#### XML Declaration
442
```java
443
config.setDeclarationEncoding("UTF-8"); // Adds <?xml version="1.0" encoding="UTF-8"?>
444
```
445
446
### Template Caching
447
448
MarkupTemplateEngine supports template caching for improved performance:
449
450
```java
451
TemplateConfiguration config = new TemplateConfiguration();
452
config.setCacheTemplates(true); // Enable caching (default)
453
454
MarkupTemplateEngine engine = new MarkupTemplateEngine(config);
455
456
// Templates are automatically cached and reused
457
Template template1 = engine.createTemplate(templateSource);
458
Template template2 = engine.createTemplate(templateSource); // Returns cached version
459
```
460
461
### Custom Base Template
462
463
Extend BaseTemplate to add custom utility methods:
464
465
```java
466
public class MyCustomTemplate extends BaseTemplate {
467
public MyCustomTemplate(MarkupTemplateEngine engine, Map model,
468
Map<String,String> modelTypes, TemplateConfiguration config) {
469
super(engine, model, modelTypes, config);
470
}
471
472
// Custom helper method
473
public MyCustomTemplate formatCurrency(double amount) throws IOException {
474
yield(String.format("$%.2f", amount));
475
return this;
476
}
477
478
// Custom helper for dates
479
public MyCustomTemplate formatDate(Date date) throws IOException {
480
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
481
yield(formatter.format(date));
482
return this;
483
}
484
}
485
486
// Configure engine to use custom base template
487
TemplateConfiguration config = new TemplateConfiguration();
488
config.setBaseTemplateClass(MyCustomTemplate.class);
489
MarkupTemplateEngine engine = new MarkupTemplateEngine(config);
490
```
491
492
### Template Resolver
493
494
Interface for custom template path resolution.
495
496
```java { .api }
497
/**
498
* Interface for resolving template paths to URLs
499
*/
500
public interface TemplateResolver {
501
/**
502
* Configure the resolver with class loader and template configuration
503
* @param templateClassLoader class loader for template compilation
504
* @param configuration template configuration settings
505
*/
506
void configure(ClassLoader templateClassLoader, TemplateConfiguration configuration);
507
508
/**
509
* Resolve template path to URL
510
* @param templatePath path to resolve
511
* @return URL for the template resource
512
* @throws IOException if template cannot be found or accessed
513
*/
514
URL resolveTemplate(String templatePath) throws IOException;
515
}
516
```
517
518
**Built-in Resolvers:**
519
520
```java { .api }
521
/**
522
* Default template resolver that searches classpath
523
*/
524
public static class DefaultTemplateResolver implements TemplateResolver {
525
public DefaultTemplateResolver();
526
public void configure(ClassLoader templateClassLoader, TemplateConfiguration configuration);
527
public URL resolveTemplate(String templatePath) throws IOException;
528
}
529
530
/**
531
* Caching template resolver for improved performance
532
*/
533
public static class CachingTemplateResolver extends DefaultTemplateResolver {
534
public CachingTemplateResolver(Map<String, URL> cache);
535
public CachingTemplateResolver();
536
public void configure(ClassLoader templateClassLoader, TemplateConfiguration configuration);
537
public URL resolveTemplate(String templatePath) throws IOException;
538
}
539
```
540
541
**Usage Example:**
542
543
```java
544
public class MyTemplateResolver implements TemplateResolver {
545
@Override
546
public void configure(ClassLoader templateClassLoader, TemplateConfiguration configuration) {
547
// Initialize resolver with classloader and configuration
548
}
549
550
@Override
551
public URL resolveTemplate(String templatePath) throws IOException {
552
// Custom logic for resolving template paths
553
return getClass().getResource("/templates/" + templatePath);
554
}
555
}
556
557
MyTemplateResolver resolver = new MyTemplateResolver();
558
MarkupTemplateEngine engine = new MarkupTemplateEngine(
559
getClass().getClassLoader(),
560
new TemplateConfiguration(),
561
resolver
562
);
563
```
564
565
### Type Checking
566
567
MarkupTemplateEngine includes compile-time type checking:
568
569
```java
570
// Templates are type-checked at compile time
571
String template = """
572
html {
573
body {
574
// This would cause a compile-time error if 'user' is not defined in model
575
p("Hello \${user.name}")
576
}
577
}
578
""";
579
580
// Type information can be provided for better checking
581
Map<String, String> modelTypes = new HashMap<>();
582
modelTypes.put("user", "User"); // Specify that 'user' is of type 'User'
583
```
584
585
### Advanced Features
586
587
#### Template Resource
588
589
Utility class for template path resolution with locale support.
590
591
```java { .api }
592
/**
593
* Represents a template resource with optional locale support
594
*/
595
public static class TemplateResource {
596
/**
597
* Parse a template path into a TemplateResource
598
* @param fullPath the full template path
599
* @return parsed TemplateResource
600
*/
601
public static TemplateResource parse(String fullPath);
602
603
/**
604
* Create variant with specific locale
605
* @param locale locale to use
606
* @return TemplateResource with locale
607
*/
608
public TemplateResource withLocale(String locale);
609
610
/**
611
* Check if this resource has a locale
612
* @return true if locale is specified
613
*/
614
public boolean hasLocale();
615
616
/**
617
* String representation of the resource path
618
* @return string representation
619
*/
620
public String toString();
621
}
622
```
623
624
#### Include Type Support
625
```java { .api }
626
/**
627
* Enumeration for different template inclusion types
628
*/
629
public enum IncludeType {
630
template("includeGroovy"),
631
escaped("includeEscaped"),
632
unescaped("includeUnescaped");
633
634
/**
635
* Get the method name for this include type
636
* @return method name
637
*/
638
public String getMethodName();
639
}
640
```
641
642
### Delegating Indent Writer
643
644
Writer that provides automatic indentation support for template output.
645
646
```java { .api }
647
/**
648
* Writer that delegates to another writer and provides indentation support
649
*/
650
public class DelegatingIndentWriter extends Writer {
651
public static final String SPACES = " ";
652
public static final String TAB = "\t";
653
654
/**
655
* Create indent writer with default spaces indentation
656
* @param delegate writer to delegate to
657
*/
658
public DelegatingIndentWriter(Writer delegate);
659
660
/**
661
* Create indent writer with custom indentation
662
* @param delegate writer to delegate to
663
* @param indentString string to use for indentation
664
*/
665
public DelegatingIndentWriter(Writer delegate, String indentString);
666
667
/**
668
* Increase indentation level
669
* @return new indentation level
670
*/
671
public int next();
672
673
/**
674
* Decrease indentation level
675
* @return new indentation level
676
*/
677
public int previous();
678
679
/**
680
* Write current indentation to output
681
* @throws IOException if write fails
682
*/
683
public void writeIndent() throws IOException;
684
685
// Writer methods (inherited)
686
public void write(int c) throws IOException;
687
public void write(char[] cbuf) throws IOException;
688
public void write(char[] cbuf, int off, int len) throws IOException;
689
public void write(String str) throws IOException;
690
public void write(String str, int off, int len) throws IOException;
691
public Writer append(CharSequence csq) throws IOException;
692
public Writer append(CharSequence csq, int start, int end) throws IOException;
693
public Writer append(char c) throws IOException;
694
public void flush() throws IOException;
695
public void close() throws IOException;
696
}
697
```
698
699
### Error Handling
700
701
MarkupTemplateEngine provides comprehensive error handling:
702
703
```java
704
try {
705
MarkupTemplateEngine engine = new MarkupTemplateEngine();
706
Template template = engine.createTemplate(templateSource);
707
String result = template.make(model).toString();
708
} catch (CompilationFailedException e) {
709
// Template compilation failed due to syntax errors
710
System.err.println("Compilation error: " + e.getMessage());
711
} catch (ClassNotFoundException e) {
712
// Missing dependencies or custom base template class
713
System.err.println("Class not found: " + e.getMessage());
714
} catch (IOException e) {
715
// I/O error reading template source
716
System.err.println("I/O error: " + e.getMessage());
717
} catch (Exception e) {
718
// Runtime errors during template execution
719
System.err.println("Runtime error: " + e.getMessage());
720
}
721
```
722
723
### Best Practices
724
725
1. **Configuration Reuse**: Create TemplateConfiguration once and reuse across engine instances
726
2. **Template Caching**: Enable caching for frequently used templates
727
3. **Thread Safety**: Use separate template instances per thread
728
4. **Custom Base Templates**: Extend BaseTemplate for application-specific helpers
729
5. **Type Safety**: Leverage compile-time type checking for robust templates
730
6. **Performance**: Use MarkupTemplateEngine for high-performance markup generation
731
7. **Memory Management**: Let unused templates be garbage collected