Apache FreeMarker is a template engine: a Java library to generate text output based on templates and changing data.
—
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.
interface TemplateModel {
// Singleton constant representing null/empty values
TemplateModel NOTHING = GeneralPurposeNothing.INSTANCE;
}All template model interfaces extend this base interface. The NOTHING constant represents null or empty values in templates.
interface TemplateScalarModel extends TemplateModel {
String getAsString() throws TemplateModelException;
}
// Simple implementation
class SimpleScalar implements TemplateScalarModel {
SimpleScalar(String value);
String getAsString() throws TemplateModelException;
}Usage example:
TemplateModel stringValue = new SimpleScalar("Hello World");
// In template: ${stringValue} outputs "Hello World"interface TemplateNumberModel extends TemplateModel {
Number getAsNumber() throws TemplateModelException;
}
// Simple implementation
class SimpleNumber implements TemplateNumberModel {
SimpleNumber(Number value);
Number getAsNumber() throws TemplateModelException;
}Usage example:
TemplateModel numberValue = new SimpleNumber(42);
// In template: ${numberValue} outputs "42"
// In template: ${numberValue + 8} outputs "50"interface TemplateBooleanModel extends TemplateModel {
boolean getAsBoolean() throws TemplateModelException;
// Predefined constants
TemplateBooleanModel TRUE = TrueBooleanModel.INSTANCE;
TemplateBooleanModel FALSE = FalseBooleanModel.INSTANCE;
}
// True implementation
class TrueBooleanModel implements TemplateBooleanModel {
static final TrueBooleanModel INSTANCE = new TrueBooleanModel();
boolean getAsBoolean() throws TemplateModelException;
}
// False implementation
class FalseBooleanModel implements TemplateBooleanModel {
static final FalseBooleanModel INSTANCE = new FalseBooleanModel();
boolean getAsBoolean() throws TemplateModelException;
}Usage example:
TemplateModel isActive = TemplateBooleanModel.TRUE;
// In template: <#if isActive>Active</#if>interface TemplateDateModel extends TemplateModel {
Date getAsDate() throws TemplateModelException;
int getDateType();
// Date type constants
int DATE = 1; // Date only (no time)
int TIME = 2; // Time only (no date)
int DATETIME = 3; // Both date and time
int UNKNOWN = 0; // Unknown date type
}
// Simple implementation
class SimpleDate implements TemplateDateModel {
SimpleDate(Date date, int type);
Date getAsDate() throws TemplateModelException;
int getDateType();
}Usage example:
TemplateModel currentDate = new SimpleDate(new Date(), TemplateDateModel.DATETIME);
// In template: ${currentDate?string("yyyy-MM-dd HH:mm:ss")}interface TemplateSequenceModel extends TemplateModel {
TemplateModel get(int index) throws TemplateModelException;
int size() throws TemplateModelException;
}
// Simple implementation
class SimpleSequence extends WrappingTemplateModel implements TemplateSequenceModel {
SimpleSequence();
SimpleSequence(ObjectWrapper wrapper);
SimpleSequence(Collection collection, ObjectWrapper wrapper);
void add(Object obj);
TemplateModel get(int index) throws TemplateModelException;
int size() throws TemplateModelException;
List toList() throws TemplateModelException;
}Usage example:
SimpleSequence items = new SimpleSequence();
items.add("Apple");
items.add("Banana");
items.add("Cherry");
// In template: <#list items as item>${item}</#list>interface TemplateHashModel extends TemplateModel {
TemplateModel get(String key) throws TemplateModelException;
boolean isEmpty() throws TemplateModelException;
}
// Extended hash model with iteration capabilities
interface TemplateHashModelEx extends TemplateHashModel {
TemplateCollectionModel keys() throws TemplateModelException;
TemplateCollectionModel values() throws TemplateModelException;
int size() throws TemplateModelException;
}
// Enhanced hash model with key-value iteration
interface TemplateHashModelEx2 extends TemplateHashModelEx {
KeyValuePairIterator keyValuePairIterator() throws TemplateModelException;
}
// Simple implementation
class SimpleHash extends WrappingTemplateModel implements TemplateHashModelEx2 {
SimpleHash();
SimpleHash(ObjectWrapper wrapper);
SimpleHash(Map map, ObjectWrapper wrapper);
void put(String key, Object value);
void put(String key, boolean value);
void put(String key, Number value);
void remove(String key);
void clear();
TemplateModel get(String key) throws TemplateModelException;
boolean isEmpty() throws TemplateModelException;
int size() throws TemplateModelException;
TemplateCollectionModel keys() throws TemplateModelException;
TemplateCollectionModel values() throws TemplateModelException;
KeyValuePairIterator keyValuePairIterator() throws TemplateModelException;
}Usage example:
SimpleHash person = new SimpleHash();
person.put("name", "John Doe");
person.put("age", 30);
person.put("active", true);
// In template: ${person.name} is ${person.age} years oldinterface TemplateCollectionModel extends TemplateModel {
TemplateModelIterator iterator() throws TemplateModelException;
}
// Extended collection with size information
interface TemplateCollectionModelEx extends TemplateCollectionModel {
int size() throws TemplateModelException;
}
// Simple implementation
class SimpleCollection extends WrappingTemplateModel implements TemplateCollectionModelEx {
SimpleCollection(Collection collection);
SimpleCollection(Collection collection, ObjectWrapper wrapper);
SimpleCollection(Iterator iterator);
SimpleCollection(Iterator iterator, ObjectWrapper wrapper);
TemplateModelIterator iterator() throws TemplateModelException;
int size() throws TemplateModelException;
}interface TemplateMethodModel extends TemplateModel {
Object exec(List arguments) throws TemplateModelException;
}
// Enhanced method model with TemplateModel arguments
interface TemplateMethodModelEx extends TemplateMethodModel {
Object exec(List arguments) throws TemplateModelException;
}Usage example:
TemplateMethodModelEx upperCase = new TemplateMethodModelEx() {
public Object exec(List arguments) throws TemplateModelException {
if (arguments.size() != 1) {
throw new TemplateModelException("Wrong number of arguments");
}
String s = ((TemplateScalarModel) arguments.get(0)).getAsString();
return new SimpleScalar(s.toUpperCase());
}
};
// In template: ${upperCase("hello")} outputs "HELLO"interface TemplateDirectiveModel extends TemplateModel {
void execute(Environment env, Map params, TemplateModel[] loopVars,
TemplateDirectiveBody body) throws TemplateException, IOException;
}
// Body interface for directive content
interface TemplateDirectiveBody {
void render(Writer out) throws TemplateException, IOException;
}Usage example:
TemplateDirectiveModel repeat = new TemplateDirectiveModel() {
public void execute(Environment env, Map params, TemplateModel[] loopVars,
TemplateDirectiveBody body) throws TemplateException, IOException {
int count = ((TemplateNumberModel) params.get("count")).getAsNumber().intValue();
for (int i = 0; i < count; i++) {
loopVars[0] = new SimpleNumber(i);
body.render(env.getOut());
}
}
};
// In template: <@repeat count=3 ; i>Item ${i}</@repeat>interface TemplateTransformModel extends TemplateModel {
Writer getWriter(Writer out, Map args) throws TemplateModelException, IOException;
}Usage example:
TemplateTransformModel upperCaseTransform = new TemplateTransformModel() {
public Writer getWriter(Writer out, Map args) throws TemplateModelException, IOException {
return new FilterWriter(out) {
public void write(String str) throws IOException {
out.write(str.toUpperCase());
}
};
}
};
// In template: <@upperCaseTransform>hello world</@upperCaseTransform>interface TemplateNodeModel extends TemplateModel {
TemplateModel get(String key) throws TemplateModelException;
TemplateSequenceModel getChildNodes() throws TemplateModelException;
String getNodeName() throws TemplateModelException;
String getNodeType() throws TemplateModelException;
TemplateNodeModel getParentNode() throws TemplateModelException;
}
// Extended node model with namespace support
interface TemplateNodeModelEx extends TemplateNodeModel {
String getNodeNamespace() throws TemplateModelException;
String getNamespaceURI(String prefix) throws TemplateModelException;
}interface TemplateModelIterator {
TemplateModel next() throws TemplateModelException;
boolean hasNext() throws TemplateModelException;
}
interface KeyValuePairIterator {
KeyValuePair next() throws TemplateModelException;
boolean hasNext() throws TemplateModelException;
}
interface KeyValuePair {
TemplateModel getKey() throws TemplateModelException;
TemplateModel getValue() throws TemplateModelException;
}class DefaultListAdapter extends WrappingTemplateModel implements TemplateSequenceModel, AdapterTemplateModel {
DefaultListAdapter(List list, ObjectWrapper wrapper);
TemplateModel get(int index) throws TemplateModelException;
int size() throws TemplateModelException;
Object getAdaptedObject(Class hint);
}
class DefaultMapAdapter extends WrappingTemplateModel implements TemplateHashModelEx2, AdapterTemplateModel {
DefaultMapAdapter(Map map, ObjectWrapper wrapper);
TemplateModel get(String key) throws TemplateModelException;
boolean isEmpty() throws TemplateModelException;
int size() throws TemplateModelException;
TemplateCollectionModel keys() throws TemplateModelException;
TemplateCollectionModel values() throws TemplateModelException;
Object getAdaptedObject(Class hint);
}
class DefaultArrayAdapter extends WrappingTemplateModel implements TemplateSequenceModel, AdapterTemplateModel {
DefaultArrayAdapter(Object array, ObjectWrapper wrapper);
TemplateModel get(int index) throws TemplateModelException;
int size() throws TemplateModelException;
Object getAdaptedObject(Class hint);
}class DefaultIteratorAdapter extends WrappingTemplateModel implements TemplateCollectionModel, AdapterTemplateModel {
DefaultIteratorAdapter(Iterator iterator, ObjectWrapper wrapper);
TemplateModelIterator iterator() throws TemplateModelException;
Object getAdaptedObject(Class hint);
}
class DefaultEnumerationAdapter extends WrappingTemplateModel implements TemplateCollectionModel, AdapterTemplateModel {
DefaultEnumerationAdapter(Enumeration enumeration, ObjectWrapper wrapper);
TemplateModelIterator iterator() throws TemplateModelException;
Object getAdaptedObject(Class hint);
}abstract class WrappingTemplateModel implements TemplateModel, AdapterTemplateModel {
WrappingTemplateModel(ObjectWrapper objectWrapper);
ObjectWrapper getObjectWrapper();
TemplateModel wrap(Object obj) throws TemplateModelException;
}
// Represents null/nothing values
class GeneralPurposeNothing implements TemplateModel {
static final GeneralPurposeNothing INSTANCE = new GeneralPurposeNothing();
}
// For wrapping objects that implement multiple model interfaces
interface AdapterTemplateModel extends TemplateModel {
Object getAdaptedObject(Class hint);
}For basic use cases, use the Simple* classes:
// Create simple models
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("title", "My Page"); // Auto-wrapped to SimpleScalar
dataModel.put("count", 42); // Auto-wrapped to SimpleNumber
dataModel.put("active", true); // Auto-wrapped to TrueBooleanModel
// Or create explicitly
dataModel.put("message", new SimpleScalar("Hello"));
dataModel.put("items", new SimpleSequence(Arrays.asList("A", "B", "C")));For complex scenarios, implement model interfaces directly:
public class ProductModel implements TemplateHashModel {
private Product product;
public ProductModel(Product product) {
this.product = product;
}
public TemplateModel get(String key) throws TemplateModelException {
if ("name".equals(key)) return new SimpleScalar(product.getName());
if ("price".equals(key)) return new SimpleNumber(product.getPrice());
if ("inStock".equals(key)) return product.isInStock() ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
return null;
}
public boolean isEmpty() {
return false;
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-freemarker--freemarker