or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

caching-loading.mdcore-processing.mdexception-handling.mdextensions.mdindex.mdobject-wrapping.mdoutput-formats.mdtemplate-models.md

object-wrapping.mddocs/

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

```