0
# Object Wrapping
1
2
Object wrapping is the process of converting Java objects into TemplateModel objects that can be used in FreeMarker templates. The ObjectWrapper interface and its implementations provide this functionality.
3
4
## Core Object Wrapper Interface
5
6
```java { .api }
7
interface ObjectWrapper {
8
TemplateModel wrap(Object obj) throws TemplateModelException;
9
}
10
11
// Enhanced object wrapper with unwrapping capabilities
12
interface RichObjectWrapper extends ObjectWrapper {
13
TemplateModel wrap(Object obj) throws TemplateModelException;
14
}
15
```
16
17
## Default Object Wrapper
18
19
The `DefaultObjectWrapper` is the recommended object wrapper for most use cases. It extends `BeansWrapper` and adds support for DOM nodes.
20
21
```java { .api }
22
class DefaultObjectWrapper extends BeansWrapper {
23
// Constructors
24
DefaultObjectWrapper(Version incompatibleImprovements);
25
26
// Factory method for default instance
27
static DefaultObjectWrapper getDefaultInstance();
28
29
// Wrapping methods
30
TemplateModel wrap(Object obj) throws TemplateModelException;
31
32
// Configuration inherited from BeansWrapper
33
void setExposeFields(boolean exposeFields);
34
boolean isExposeFields();
35
void setExposureLevel(int exposureLevel);
36
int getExposureLevel();
37
}
38
```
39
40
### Usage Example
41
42
```java
43
// Create with specific version
44
DefaultObjectWrapper wrapper = new DefaultObjectWrapper(Configuration.VERSION_2_3_34);
45
46
// Use default instance (shared, thread-safe)
47
DefaultObjectWrapper wrapper = DefaultObjectWrapper.getDefaultInstance();
48
49
// Configure in Configuration
50
Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);
51
cfg.setObjectWrapper(wrapper);
52
```
53
54
## Default Object Wrapper Builder
55
56
For advanced configuration, use the builder pattern:
57
58
```java { .api }
59
class DefaultObjectWrapperBuilder extends DefaultObjectWrapperConfiguration {
60
// Constructor
61
DefaultObjectWrapperBuilder(Version incompatibleImprovements);
62
63
// Build method
64
DefaultObjectWrapper build();
65
66
// Configuration methods
67
DefaultObjectWrapperBuilder setExposeFields(boolean exposeFields);
68
DefaultObjectWrapperBuilder setExposureLevel(int exposureLevel);
69
DefaultObjectWrapperBuilder setForceLegacyNonListCollections(boolean force);
70
DefaultObjectWrapperBuilder setDefaultDateType(int defaultDateType);
71
DefaultObjectWrapperBuilder setOuterIdentity(ObjectWrapperAndUnwrapper outerIdentity);
72
DefaultObjectWrapperBuilder setStrict(boolean strict);
73
DefaultObjectWrapperBuilder setUseModelCache(boolean useModelCache);
74
DefaultObjectWrapperBuilder setWrapAsHashModel(boolean wrapAsHashModel);
75
}
76
```
77
78
### Builder Usage Example
79
80
```java
81
DefaultObjectWrapper wrapper = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_34)
82
.setExposeFields(true)
83
.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY)
84
.setForceLegacyNonListCollections(false)
85
.setDefaultDateType(TemplateDateModel.DATETIME)
86
.build();
87
88
Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);
89
cfg.setObjectWrapper(wrapper);
90
```
91
92
## Beans Wrapper
93
94
The `BeansWrapper` is the foundation for Java bean wrapping, providing access to properties, methods, and fields.
95
96
```java { .api }
97
class BeansWrapper implements RichObjectWrapper, WriteProtectable {
98
// Constructors
99
BeansWrapper();
100
BeansWrapper(Version incompatibleImprovements);
101
102
// Core wrapping methods
103
TemplateModel wrap(Object obj) throws TemplateModelException;
104
Object unwrap(TemplateModel model) throws TemplateModelException;
105
Object unwrap(TemplateModel model, Class targetClass) throws TemplateModelException;
106
107
// Static and enum model access
108
TemplateHashModel getStaticModels();
109
TemplateHashModel getEnumModels();
110
111
// Field exposure configuration
112
void setExposeFields(boolean exposeFields);
113
boolean isExposeFields();
114
115
// Exposure level control
116
void setExposureLevel(int exposureLevel);
117
int getExposureLevel();
118
119
// Method appearance customization
120
void setMethodAppearanceFineTuner(MethodAppearanceFineTuner tuner);
121
MethodAppearanceFineTuner getMethodAppearanceFineTuner();
122
123
// Security and access control
124
void setSecurityManager(SecurityManager securityManager);
125
SecurityManager getSecurityManager();
126
127
// Null model handling
128
void setNullModel(TemplateModel nullModel);
129
TemplateModel getNullModel();
130
131
// Date type configuration
132
void setDefaultDateType(int defaultDateType);
133
int getDefaultDateType();
134
135
// Outer identity (for unwrapping)
136
void setOuterIdentity(ObjectWrapperAndUnwrapper outerIdentity);
137
ObjectWrapperAndUnwrapper getOuterIdentity();
138
139
// Strict bean access
140
void setStrict(boolean strict);
141
boolean isStrict();
142
143
// Model caching
144
void setUseModelCache(boolean useCache);
145
boolean getUseModelCache();
146
void clearModelCache();
147
148
// Constants for exposure levels
149
static final int EXPOSE_ALL = 0;
150
static final int EXPOSE_PROPERTIES_ONLY = 1;
151
static final int EXPOSE_NOTHING = 2;
152
}
153
```
154
155
### BeansWrapper Usage Examples
156
157
```java
158
// Basic beans wrapper setup
159
BeansWrapper wrapper = new BeansWrapper(Configuration.VERSION_2_3_34);
160
wrapper.setExposeFields(true);
161
wrapper.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
162
163
// Access static methods and fields
164
TemplateHashModel staticModels = wrapper.getStaticModels();
165
TemplateModel stringClass = staticModels.get("java.lang.String");
166
// In template: ${statics["java.lang.String"].valueOf(123)}
167
168
// Access enum constants
169
TemplateHashModel enumModels = wrapper.getEnumModels();
170
TemplateModel dayOfWeek = enumModels.get("java.time.DayOfWeek");
171
// In template: ${enums["java.time.DayOfWeek"].MONDAY}
172
173
// Custom method appearance
174
wrapper.setMethodAppearanceFineTuner(new MethodAppearanceFineTuner() {
175
public void process(MethodAppearanceDecision decision, Method method, Class clazz) {
176
if (method.getName().startsWith("internal")) {
177
decision.setExposeMethodAs(null); // Hide internal methods
178
}
179
}
180
});
181
```
182
183
## Static Models Access
184
185
```java { .api }
186
class StaticModels implements TemplateHashModel {
187
StaticModels(BeansWrapper wrapper);
188
TemplateModel get(String key) throws TemplateModelException;
189
boolean isEmpty() throws TemplateModelException;
190
}
191
192
// Individual static model for a class
193
class StaticModel implements TemplateHashModel, TemplateScalarModel {
194
StaticModel(Class clazz, BeansWrapper wrapper);
195
TemplateModel get(String key) throws TemplateModelException;
196
boolean isEmpty() throws TemplateModelException;
197
String getAsString() throws TemplateModelException;
198
}
199
```
200
201
### Static Models Usage
202
203
```java
204
// Setup static models access
205
BeansWrapper wrapper = new BeansWrapper(Configuration.VERSION_2_3_34);
206
TemplateHashModel staticModels = wrapper.getStaticModels();
207
208
Map<String, Object> dataModel = new HashMap<>();
209
dataModel.put("statics", staticModels);
210
211
// In template, access static methods:
212
// ${statics["java.lang.Math"].max(10, 20)}
213
// ${statics["java.util.Collections"].emptyList()}
214
// ${statics["java.lang.System"].currentTimeMillis()}
215
```
216
217
## Enum Models Access
218
219
```java { .api }
220
class EnumModels implements TemplateHashModel {
221
EnumModels(BeansWrapper wrapper);
222
TemplateModel get(String key) throws TemplateModelException;
223
boolean isEmpty() throws TemplateModelException;
224
}
225
226
// Individual enum model
227
class EnumModel extends StaticModel {
228
EnumModel(Class enumClass, BeansWrapper wrapper);
229
}
230
```
231
232
### Enum Models Usage
233
234
```java
235
// Setup enum models access
236
BeansWrapper wrapper = new BeansWrapper(Configuration.VERSION_2_3_34);
237
TemplateHashModel enumModels = wrapper.getEnumModels();
238
239
Map<String, Object> dataModel = new HashMap<>();
240
dataModel.put("enums", enumModels);
241
242
// In template, access enum constants:
243
// ${enums["java.time.DayOfWeek"].MONDAY}
244
// ${enums["java.util.concurrent.TimeUnit"].SECONDS}
245
```
246
247
## Simple Object Wrapper
248
249
For simpler use cases that don't require bean introspection:
250
251
```java { .api }
252
class SimpleObjectWrapper extends DefaultObjectWrapper {
253
SimpleObjectWrapper();
254
SimpleObjectWrapper(boolean simpleMapWrapper);
255
256
TemplateModel wrap(Object obj) throws TemplateModelException;
257
}
258
```
259
260
### SimpleObjectWrapper Usage
261
262
```java
263
// Simple wrapper that uses only Simple* model classes
264
SimpleObjectWrapper wrapper = new SimpleObjectWrapper();
265
Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);
266
cfg.setObjectWrapper(wrapper);
267
268
// This wrapper will convert:
269
// - Strings to SimpleScalar
270
// - Numbers to SimpleNumber
271
// - Booleans to TemplateBooleanModel.TRUE/FALSE
272
// - Collections to SimpleSequence
273
// - Maps to SimpleHash
274
// - Other objects using toString() to SimpleScalar
275
```
276
277
## Method Appearance Customization
278
279
```java { .api }
280
interface MethodAppearanceFineTuner {
281
void process(MethodAppearanceDecision decision, Method method, Class clazz);
282
}
283
284
class MethodAppearanceDecision {
285
void setExposeMethodAs(String name);
286
void setMethodShadowsProperty(boolean shadows);
287
void setReplaceExistingProperty(boolean replace);
288
String getExposeMethodAs();
289
boolean getMethodShadowsProperty();
290
boolean getReplaceExistingProperty();
291
}
292
```
293
294
### Method Appearance Customization Example
295
296
```java
297
BeansWrapper wrapper = new BeansWrapper(Configuration.VERSION_2_3_34);
298
299
wrapper.setMethodAppearanceFineTuner(new MethodAppearanceFineTuner() {
300
public void process(MethodAppearanceDecision decision, Method method, Class clazz) {
301
String methodName = method.getName();
302
303
// Hide methods starting with underscore
304
if (methodName.startsWith("_")) {
305
decision.setExposeMethodAs(null);
306
}
307
308
// Rename getXXX methods to just XXX for cleaner template access
309
if (methodName.startsWith("get") && methodName.length() > 3) {
310
String propertyName = methodName.substring(3);
311
propertyName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
312
decision.setExposeMethodAs(propertyName);
313
decision.setMethodShadowsProperty(false);
314
}
315
316
// Hide deprecated methods
317
if (method.isAnnotationPresent(Deprecated.class)) {
318
decision.setExposeMethodAs(null);
319
}
320
}
321
});
322
```
323
324
## Bean Model Classes
325
326
### BeanModel
327
328
```java { .api }
329
class BeanModel implements TemplateHashModel, AdapterTemplateModel, WrapperTemplateModel {
330
BeanModel(Object object, BeansWrapper wrapper);
331
TemplateModel get(String key) throws TemplateModelException;
332
boolean isEmpty() throws TemplateModelException;
333
Object getAdaptedObject(Class hint);
334
Object getWrappedObject();
335
BeansWrapper getBeansWrapper();
336
}
337
```
338
339
### StringModel
340
341
```java { .api }
342
class StringModel extends BeanModel implements TemplateScalarModel {
343
StringModel(Object object, BeansWrapper wrapper);
344
String getAsString() throws TemplateModelException;
345
}
346
```
347
348
### NumberModel
349
350
```java { .api }
351
class NumberModel extends BeanModel implements TemplateNumberModel {
352
NumberModel(Number number, BeansWrapper wrapper);
353
Number getAsNumber() throws TemplateModelException;
354
}
355
```
356
357
### DateModel
358
359
```java { .api }
360
class DateModel extends BeanModel implements TemplateDateModel, TemplateHashModel {
361
DateModel(Date date, BeansWrapper wrapper);
362
DateModel(Date date, BeansWrapper wrapper, int type);
363
Date getAsDate() throws TemplateModelException;
364
int getDateType();
365
}
366
```
367
368
## Wrapping Behavior
369
370
### Automatic Wrapping Rules
371
372
The DefaultObjectWrapper automatically wraps objects according to these rules:
373
374
1. **null** → `TemplateModel.NOTHING`
375
2. **TemplateModel** → returned as-is
376
3. **String** → `StringModel` (extends both BeanModel and TemplateScalarModel)
377
4. **Number** → `NumberModel` (extends both BeanModel and TemplateNumberModel)
378
5. **Date** → `DateModel` (extends both BeanModel and TemplateDateModel)
379
6. **Boolean** → `TemplateBooleanModel.TRUE` or `TemplateBooleanModel.FALSE`
380
7. **Map** → `DefaultMapAdapter` (implements TemplateHashModelEx2)
381
8. **List** → `DefaultListAdapter` (implements TemplateSequenceModel)
382
9. **Array** → `DefaultArrayAdapter` (implements TemplateSequenceModel)
383
10. **Collection** → `DefaultCollectionAdapter` (implements TemplateCollectionModel)
384
11. **Iterator** → `DefaultIteratorAdapter` (implements TemplateCollectionModel)
385
12. **Enumeration** → `DefaultEnumerationAdapter` (implements TemplateCollectionModel)
386
13. **Node** (DOM) → `NodeModel` (if DOM support enabled)
387
14. **Other objects** → `BeanModel` (provides access to properties and methods)
388
389
### Custom Wrapping
390
391
You can create custom object wrappers for specialized behavior:
392
393
```java
394
public class CustomObjectWrapper extends DefaultObjectWrapper {
395
public CustomObjectWrapper(Version incompatibleImprovements) {
396
super(incompatibleImprovements);
397
}
398
399
@Override
400
public TemplateModel wrap(Object obj) throws TemplateModelException {
401
// Custom wrapping for specific types
402
if (obj instanceof MyCustomClass) {
403
return new MyCustomTemplateModel((MyCustomClass) obj);
404
}
405
406
// Fall back to default wrapping
407
return super.wrap(obj);
408
}
409
}
410
```
411
412
## Performance Considerations
413
414
### Model Caching
415
416
BeansWrapper caches introspection results and model instances for better performance:
417
418
```java
419
// Enable model caching (default: true)
420
wrapper.setUseModelCache(true);
421
422
// Clear cache when needed
423
wrapper.clearModelCache();
424
```
425
426
### Thread Safety
427
428
- `DefaultObjectWrapper.getDefaultInstance()` returns a shared, thread-safe instance
429
- Custom wrapper instances should be shared across templates for better performance
430
- BeansWrapper instances are thread-safe after configuration is complete
431
432
### Exposure Level Impact
433
434
Different exposure levels affect performance:
435
436
```java
437
// Fastest - only exposes properties
438
wrapper.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
439
440
// Slower - exposes properties, methods, and fields
441
wrapper.setExposureLevel(BeansWrapper.EXPOSE_ALL);
442
443
// Fastest but most restrictive - no bean introspection
444
wrapper.setExposureLevel(BeansWrapper.EXPOSE_NOTHING);
445
```