0
# Template Model System
1
2
The template model system provides interfaces for representing different data types in FreeMarker templates. These interfaces enable seamless integration between Java objects and template expressions.
3
4
## Core Model Interfaces
5
6
### Base Interface
7
8
```java { .api }
9
interface TemplateModel {
10
// Singleton constant representing null/empty values
11
TemplateModel NOTHING = GeneralPurposeNothing.INSTANCE;
12
}
13
```
14
15
All template model interfaces extend this base interface. The `NOTHING` constant represents null or empty values in templates.
16
17
### Scalar Models (Strings)
18
19
```java { .api }
20
interface TemplateScalarModel extends TemplateModel {
21
String getAsString() throws TemplateModelException;
22
}
23
24
// Simple implementation
25
class SimpleScalar implements TemplateScalarModel {
26
SimpleScalar(String value);
27
String getAsString() throws TemplateModelException;
28
}
29
```
30
31
Usage example:
32
```java
33
TemplateModel stringValue = new SimpleScalar("Hello World");
34
// In template: ${stringValue} outputs "Hello World"
35
```
36
37
### Number Models
38
39
```java { .api }
40
interface TemplateNumberModel extends TemplateModel {
41
Number getAsNumber() throws TemplateModelException;
42
}
43
44
// Simple implementation
45
class SimpleNumber implements TemplateNumberModel {
46
SimpleNumber(Number value);
47
Number getAsNumber() throws TemplateModelException;
48
}
49
```
50
51
Usage example:
52
```java
53
TemplateModel numberValue = new SimpleNumber(42);
54
// In template: ${numberValue} outputs "42"
55
// In template: ${numberValue + 8} outputs "50"
56
```
57
58
### Boolean Models
59
60
```java { .api }
61
interface TemplateBooleanModel extends TemplateModel {
62
boolean getAsBoolean() throws TemplateModelException;
63
64
// Predefined constants
65
TemplateBooleanModel TRUE = TrueBooleanModel.INSTANCE;
66
TemplateBooleanModel FALSE = FalseBooleanModel.INSTANCE;
67
}
68
69
// True implementation
70
class TrueBooleanModel implements TemplateBooleanModel {
71
static final TrueBooleanModel INSTANCE = new TrueBooleanModel();
72
boolean getAsBoolean() throws TemplateModelException;
73
}
74
75
// False implementation
76
class FalseBooleanModel implements TemplateBooleanModel {
77
static final FalseBooleanModel INSTANCE = new FalseBooleanModel();
78
boolean getAsBoolean() throws TemplateModelException;
79
}
80
```
81
82
Usage example:
83
```java
84
TemplateModel isActive = TemplateBooleanModel.TRUE;
85
// In template: <#if isActive>Active</#if>
86
```
87
88
### Date Models
89
90
```java { .api }
91
interface TemplateDateModel extends TemplateModel {
92
Date getAsDate() throws TemplateModelException;
93
int getDateType();
94
95
// Date type constants
96
int DATE = 1; // Date only (no time)
97
int TIME = 2; // Time only (no date)
98
int DATETIME = 3; // Both date and time
99
int UNKNOWN = 0; // Unknown date type
100
}
101
102
// Simple implementation
103
class SimpleDate implements TemplateDateModel {
104
SimpleDate(Date date, int type);
105
Date getAsDate() throws TemplateModelException;
106
int getDateType();
107
}
108
```
109
110
Usage example:
111
```java
112
TemplateModel currentDate = new SimpleDate(new Date(), TemplateDateModel.DATETIME);
113
// In template: ${currentDate?string("yyyy-MM-dd HH:mm:ss")}
114
```
115
116
## Collection Models
117
118
### Sequence Models (Lists/Arrays)
119
120
```java { .api }
121
interface TemplateSequenceModel extends TemplateModel {
122
TemplateModel get(int index) throws TemplateModelException;
123
int size() throws TemplateModelException;
124
}
125
126
// Simple implementation
127
class SimpleSequence extends WrappingTemplateModel implements TemplateSequenceModel {
128
SimpleSequence();
129
SimpleSequence(ObjectWrapper wrapper);
130
SimpleSequence(Collection collection, ObjectWrapper wrapper);
131
132
void add(Object obj);
133
TemplateModel get(int index) throws TemplateModelException;
134
int size() throws TemplateModelException;
135
List toList() throws TemplateModelException;
136
}
137
```
138
139
Usage example:
140
```java
141
SimpleSequence items = new SimpleSequence();
142
items.add("Apple");
143
items.add("Banana");
144
items.add("Cherry");
145
// In template: <#list items as item>${item}</#list>
146
```
147
148
### Hash Models (Maps/Objects)
149
150
```java { .api }
151
interface TemplateHashModel extends TemplateModel {
152
TemplateModel get(String key) throws TemplateModelException;
153
boolean isEmpty() throws TemplateModelException;
154
}
155
156
// Extended hash model with iteration capabilities
157
interface TemplateHashModelEx extends TemplateHashModel {
158
TemplateCollectionModel keys() throws TemplateModelException;
159
TemplateCollectionModel values() throws TemplateModelException;
160
int size() throws TemplateModelException;
161
}
162
163
// Enhanced hash model with key-value iteration
164
interface TemplateHashModelEx2 extends TemplateHashModelEx {
165
KeyValuePairIterator keyValuePairIterator() throws TemplateModelException;
166
}
167
168
// Simple implementation
169
class SimpleHash extends WrappingTemplateModel implements TemplateHashModelEx2 {
170
SimpleHash();
171
SimpleHash(ObjectWrapper wrapper);
172
SimpleHash(Map map, ObjectWrapper wrapper);
173
174
void put(String key, Object value);
175
void put(String key, boolean value);
176
void put(String key, Number value);
177
void remove(String key);
178
void clear();
179
180
TemplateModel get(String key) throws TemplateModelException;
181
boolean isEmpty() throws TemplateModelException;
182
int size() throws TemplateModelException;
183
TemplateCollectionModel keys() throws TemplateModelException;
184
TemplateCollectionModel values() throws TemplateModelException;
185
KeyValuePairIterator keyValuePairIterator() throws TemplateModelException;
186
}
187
```
188
189
Usage example:
190
```java
191
SimpleHash person = new SimpleHash();
192
person.put("name", "John Doe");
193
person.put("age", 30);
194
person.put("active", true);
195
// In template: ${person.name} is ${person.age} years old
196
```
197
198
### Collection Models (Iteration)
199
200
```java { .api }
201
interface TemplateCollectionModel extends TemplateModel {
202
TemplateModelIterator iterator() throws TemplateModelException;
203
}
204
205
// Extended collection with size information
206
interface TemplateCollectionModelEx extends TemplateCollectionModel {
207
int size() throws TemplateModelException;
208
}
209
210
// Simple implementation
211
class SimpleCollection extends WrappingTemplateModel implements TemplateCollectionModelEx {
212
SimpleCollection(Collection collection);
213
SimpleCollection(Collection collection, ObjectWrapper wrapper);
214
SimpleCollection(Iterator iterator);
215
SimpleCollection(Iterator iterator, ObjectWrapper wrapper);
216
217
TemplateModelIterator iterator() throws TemplateModelException;
218
int size() throws TemplateModelException;
219
}
220
```
221
222
## Functional Models
223
224
### Method Models
225
226
```java { .api }
227
interface TemplateMethodModel extends TemplateModel {
228
Object exec(List arguments) throws TemplateModelException;
229
}
230
231
// Enhanced method model with TemplateModel arguments
232
interface TemplateMethodModelEx extends TemplateMethodModel {
233
Object exec(List arguments) throws TemplateModelException;
234
}
235
```
236
237
Usage example:
238
```java
239
TemplateMethodModelEx upperCase = new TemplateMethodModelEx() {
240
public Object exec(List arguments) throws TemplateModelException {
241
if (arguments.size() != 1) {
242
throw new TemplateModelException("Wrong number of arguments");
243
}
244
String s = ((TemplateScalarModel) arguments.get(0)).getAsString();
245
return new SimpleScalar(s.toUpperCase());
246
}
247
};
248
// In template: ${upperCase("hello")} outputs "HELLO"
249
```
250
251
### Directive Models
252
253
```java { .api }
254
interface TemplateDirectiveModel extends TemplateModel {
255
void execute(Environment env, Map params, TemplateModel[] loopVars,
256
TemplateDirectiveBody body) throws TemplateException, IOException;
257
}
258
259
// Body interface for directive content
260
interface TemplateDirectiveBody {
261
void render(Writer out) throws TemplateException, IOException;
262
}
263
```
264
265
Usage example:
266
```java
267
TemplateDirectiveModel repeat = new TemplateDirectiveModel() {
268
public void execute(Environment env, Map params, TemplateModel[] loopVars,
269
TemplateDirectiveBody body) throws TemplateException, IOException {
270
int count = ((TemplateNumberModel) params.get("count")).getAsNumber().intValue();
271
for (int i = 0; i < count; i++) {
272
loopVars[0] = new SimpleNumber(i);
273
body.render(env.getOut());
274
}
275
}
276
};
277
// In template: <@repeat count=3 ; i>Item ${i}</@repeat>
278
```
279
280
### Transform Models
281
282
```java { .api }
283
interface TemplateTransformModel extends TemplateModel {
284
Writer getWriter(Writer out, Map args) throws TemplateModelException, IOException;
285
}
286
```
287
288
Usage example:
289
```java
290
TemplateTransformModel upperCaseTransform = new TemplateTransformModel() {
291
public Writer getWriter(Writer out, Map args) throws TemplateModelException, IOException {
292
return new FilterWriter(out) {
293
public void write(String str) throws IOException {
294
out.write(str.toUpperCase());
295
}
296
};
297
}
298
};
299
// In template: <@upperCaseTransform>hello world</@upperCaseTransform>
300
```
301
302
## Node Models (XML/Tree Structures)
303
304
```java { .api }
305
interface TemplateNodeModel extends TemplateModel {
306
TemplateModel get(String key) throws TemplateModelException;
307
TemplateSequenceModel getChildNodes() throws TemplateModelException;
308
String getNodeName() throws TemplateModelException;
309
String getNodeType() throws TemplateModelException;
310
TemplateNodeModel getParentNode() throws TemplateModelException;
311
}
312
313
// Extended node model with namespace support
314
interface TemplateNodeModelEx extends TemplateNodeModel {
315
String getNodeNamespace() throws TemplateModelException;
316
String getNamespaceURI(String prefix) throws TemplateModelException;
317
}
318
```
319
320
## Iteration Support
321
322
```java { .api }
323
interface TemplateModelIterator {
324
TemplateModel next() throws TemplateModelException;
325
boolean hasNext() throws TemplateModelException;
326
}
327
328
interface KeyValuePairIterator {
329
KeyValuePair next() throws TemplateModelException;
330
boolean hasNext() throws TemplateModelException;
331
}
332
333
interface KeyValuePair {
334
TemplateModel getKey() throws TemplateModelException;
335
TemplateModel getValue() throws TemplateModelException;
336
}
337
```
338
339
## Adapter Classes
340
341
### Collection Adapters
342
343
```java { .api }
344
class DefaultListAdapter extends WrappingTemplateModel implements TemplateSequenceModel, AdapterTemplateModel {
345
DefaultListAdapter(List list, ObjectWrapper wrapper);
346
TemplateModel get(int index) throws TemplateModelException;
347
int size() throws TemplateModelException;
348
Object getAdaptedObject(Class hint);
349
}
350
351
class DefaultMapAdapter extends WrappingTemplateModel implements TemplateHashModelEx2, AdapterTemplateModel {
352
DefaultMapAdapter(Map map, ObjectWrapper wrapper);
353
TemplateModel get(String key) throws TemplateModelException;
354
boolean isEmpty() throws TemplateModelException;
355
int size() throws TemplateModelException;
356
TemplateCollectionModel keys() throws TemplateModelException;
357
TemplateCollectionModel values() throws TemplateModelException;
358
Object getAdaptedObject(Class hint);
359
}
360
361
class DefaultArrayAdapter extends WrappingTemplateModel implements TemplateSequenceModel, AdapterTemplateModel {
362
DefaultArrayAdapter(Object array, ObjectWrapper wrapper);
363
TemplateModel get(int index) throws TemplateModelException;
364
int size() throws TemplateModelException;
365
Object getAdaptedObject(Class hint);
366
}
367
```
368
369
### Iterator Adapters
370
371
```java { .api }
372
class DefaultIteratorAdapter extends WrappingTemplateModel implements TemplateCollectionModel, AdapterTemplateModel {
373
DefaultIteratorAdapter(Iterator iterator, ObjectWrapper wrapper);
374
TemplateModelIterator iterator() throws TemplateModelException;
375
Object getAdaptedObject(Class hint);
376
}
377
378
class DefaultEnumerationAdapter extends WrappingTemplateModel implements TemplateCollectionModel, AdapterTemplateModel {
379
DefaultEnumerationAdapter(Enumeration enumeration, ObjectWrapper wrapper);
380
TemplateModelIterator iterator() throws TemplateModelException;
381
Object getAdaptedObject(Class hint);
382
}
383
```
384
385
## Utility Classes
386
387
```java { .api }
388
abstract class WrappingTemplateModel implements TemplateModel, AdapterTemplateModel {
389
WrappingTemplateModel(ObjectWrapper objectWrapper);
390
ObjectWrapper getObjectWrapper();
391
TemplateModel wrap(Object obj) throws TemplateModelException;
392
}
393
394
// Represents null/nothing values
395
class GeneralPurposeNothing implements TemplateModel {
396
static final GeneralPurposeNothing INSTANCE = new GeneralPurposeNothing();
397
}
398
399
// For wrapping objects that implement multiple model interfaces
400
interface AdapterTemplateModel extends TemplateModel {
401
Object getAdaptedObject(Class hint);
402
}
403
```
404
405
## Model Creation Best Practices
406
407
### Using Simple Model Classes
408
409
For basic use cases, use the Simple* classes:
410
411
```java
412
// Create simple models
413
Map<String, Object> dataModel = new HashMap<>();
414
dataModel.put("title", "My Page"); // Auto-wrapped to SimpleScalar
415
dataModel.put("count", 42); // Auto-wrapped to SimpleNumber
416
dataModel.put("active", true); // Auto-wrapped to TrueBooleanModel
417
418
// Or create explicitly
419
dataModel.put("message", new SimpleScalar("Hello"));
420
dataModel.put("items", new SimpleSequence(Arrays.asList("A", "B", "C")));
421
```
422
423
### Custom Model Implementation
424
425
For complex scenarios, implement model interfaces directly:
426
427
```java
428
public class ProductModel implements TemplateHashModel {
429
private Product product;
430
431
public ProductModel(Product product) {
432
this.product = product;
433
}
434
435
public TemplateModel get(String key) throws TemplateModelException {
436
if ("name".equals(key)) return new SimpleScalar(product.getName());
437
if ("price".equals(key)) return new SimpleNumber(product.getPrice());
438
if ("inStock".equals(key)) return product.isInStock() ?
439
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
440
return null;
441
}
442
443
public boolean isEmpty() {
444
return false;
445
}
446
}
447
```