or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

basic-template-engines.mdindex.mdmarkup-template-engine.mdstreaming-template-engine.mdxml-template-engine.md

markup-template-engine.mddocs/

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**