1. What is Trimou?

Trimou is yet another Java templating engine. It’s a Mustache implementation but Helpers API (inspired by Handlebars.js) is also supported. The goal is to provide a simple to use and easy to extend templating engine for any Java SE or Java EE application. There are some ready-to-use extensions which provide integration with CDI, Servlets, PrettyTime, HtmlCompressor, google-gson, JSON Processing Object Model API (JSR 353), Spring MVC, Dropwizard and EL 3.0 (see Extensions section for more info).

Note
Trimou is available under the Apache License 2.0.

2. Features

The basic features of every Mustache implementation are defined in the Mustache spec. Trimou passes all the tests (version 1.1.2), except "Section - Alternate Delimiters" from lambdas optional module.

The most notable enhancements are:

  • Template caching

  • Template inheritance

  • Nested templates

  • Helpers API (inspired by Handlebars.js)

  • Basic i18n support

  • Extended processing of lambdas

  • Extension points (template locators, resolvers, text support, …​)

2.1. Variables, interpolation tags

Note
The interpolation tag’s content is called a key hereafter. The key consists of one or more dot-separated parts - a key part ("foo"["foo"], "foo.bar.name"["foo, "bar", "name"]). The current object the key part is resolved against is called a context object hereafter.

In general Trimou interpolation works very similar to the spec description - walk the context object stack from top to bottom, and resolve the value for the given part of the key (first part againts the context stack, other parts against the result from the former resolution).

However there are some differences:

  • it’s possible to use this keyword to reference the object atop the context object stack; the spec only allows .,

  • if the context object is an instance of a java.util.Map, value of the entry with the given key is resolved,

  • for any context object Trimou tries to:

    • find and invoke an accessible public method with no params, non-void return type and name keypart, getKeypart or isKeypart defined on the context object’s class and its superclasses (except for java.lang.Object),

    • find an accessible public field with name keypart and get its value,

  • java.util.List and array elements can be accessed via index (the key must be an unsigned integer),

  • an iteration metada object is available inside an iteration block, the default alias is iter:

    • the alias can be configured, see Configuration,

    • this metadata has some useful properties: iter.index (the first element is at index 1), iter.position (the first element has position 0), iter.hasNext, iter.isFirst and iter.isLast,

    • alternatively iterIndex, iterHasNext, iterIsFirst and iterIsLast keywords can be used,

  • Trimou allows you to define a resolver that does not handle the context object stack at all (e.g. looks up a CDI bean).

Examples:
{{foo}} (1)

{{foo.bar}} (2)

{{list.0}} (3)

{{array.5}} (4)

{{#items}}
  {{iter.index}} (5)
  {{#iter.isFirst}} The is the first one! {{/iter.isFirst}} (6)
  {{#iter.isLast}} This is the last one! {{/iter.isLast}} (7)
  {{name}} (8)
  {{#iter.hasNext}}, {{/iter.hasNext}} (9)
{{/items}}

{{#quxEnumClass.values}} (10)
  {{this}}
{{/quxEnumClass.values}}
  1. Try to get a value of key "foo" from the context object stack, e.g. if the supplied data context object is an instance of Map get the value of key "foo"

  2. Try to get a value of key "bar" from the context object resolved in <1>, e.g. try to invoke bar(), getBar() or isBar() on the "foo" instance or get the value of the field with name "bar"

  3. Get the first element

  4. Get the sixth element

  5. The current iteration index (the first element is at index 1)

  6. Render the text for the first iteration

  7. Render the text for the last iteration

  8. "name" is resolved against the context object stack (iteration element, supplied data map)

  9. Render a comma if the iteration has more elements (iterHasNext is true)

  10. It’s also possible to invoke static methods; quxEnumClass is an enum class here and we iterate over the array returned from static method values()

Note
The set of resolvers may be extended - so in fact the above mentioned applies to the default set of resolvers only.

2.1.1. Escaping HTML

The interpolated value is escaped unless & is used. The spec only tests the basic escaping (&, ", <, >).

Examples:
{{foo}} (1)

{{& foo}} (2)

{{{foo}}} (3)
  1. Escape foo

  2. Do not escape foo

  3. Do not escape foo; works only for default delimiters!

Tip
You can implement your own escaping logic, e.g. to improve escaping performance - see Configure the engine and TextSupport sections.

2.2. Sections

The section content is rendered one or more times if there is an object found for the given key. If the found object is:

  • non-empty Iterable or array, the content is rendered for each element,

  • a Boolean of value true, the content is rendered once,

  • an instance of Lambda, the content is processed according to the lambda’s specification,

  • any other non-null object represents a nested context.

The section content is not rendered if there is no object found, or the found object is:

  • a Boolean of value false,

  • an Iterable with no elements,

  • an empty array.

Examples:
{{#boolean}}
   This line will be rendered only if "boolean" key resolves to java.lang.Boolean#TRUE, or true
{{/boolean}}

{{#iterable_or_array}}
  This line will be rendered for each element, and the element is pushed on the context object stack
{{/iterable_or_array}}

2.3. Inverted sections

The content is rendered if there is no object found in the context, or is a Boolean of value false, or is an Iterable with no elements, or is an empty array.

Examples:
{{^iterable}}
  This line will be rendered if the resolved iterable has no elements
{{/iterable}}

2.4. Partials

Partials only work if at least one template locator is in action. Otherwise the template cache is not used and there is no way to locate the required partial (template). See Configure the engine and Template locator sections for more info.

Examples:
{{#items}}
  {{>item_detail}} - process the template with name "item_detail" for each iteration element
{{/items}}

2.5. Delimiters

Examples:
{{=%% %%=}} - from now on use custom delimiters

%%foo.name%% - interpolate "foo.name"

%%={{ }}=%% - switch back to default values
Tip
It’s also possible to change the delimiters globally, see Configuration.

2.6. Lambdas

You can implement org.trimou.lambda.Lambda interface in order to define a lambda/callable object. Predefined abstract org.trimou.lambda.SpecCompliantLambda follows the behaviour defined by the spec:

Lambda makeMeBold = new SpecCompliantLambda() {
  @Override
  public String invoke(String text) {
    return "<b>" ` text ` "</b>";
  }
}

and template

{{#makeMeBold}}
  Any text...{{name}}
{{/makeMeBold}}

results in:

  <b>Any text...{{name}}</b>
-> the variable is not interpolated

Alternatively, you can use predefined abstract classes like org.trimou.lambda.InputProcessingLambda:

Lambda makeMeUppercase = new InputProcessingLambda() {
  @Override
  public String invoke(String text) {
    return text.toUpperCase();
  }
  @Override
  public boolean isReturnValueInterpolated() {
    return false;
  }
}

and template

{{#makeMeUppercase}}
  Any text...{{name}}
{{/makeMeUppercase}}

results in:

  ANY TEXT...EDGAR
-> the variable is interpolated before the lambda invoke() method is invoked

See org.trimou.lambda.Lambda API javadoc for more info.

2.6.1. Simple lambdas

org.trimou.lambda.SimpleLambdas utility class and its builder allow to create simple lambdas using JDK8 funcional interfaces:

MustacheEngine engine = MustacheEngineBuilder
        .newBuilder()
        .addGlobalData("toLowerCase",
            SimpleLambdas.invoke((t) -> t.toLowerCase())
        ).build();

2.7. Extending templates

This feature is not supported in the spec. Trimou basically follows the way mustache.java implements the template inheritance. In the extended template, the sections to extend are defined - use $ to identify such sections. In extending templates, the extending sections are defined - again, use $ to identify such sections. Sections to extend may define the default content.

Following template with name "super":

This a template to extend
{{$header}} -> section to extend
  The default header
{{/header}}
In between...
{{$content}} -> section to extend
  The default content
{{/content}}
&copy; 2013

can be extended in this way:

Hello world!
{{<super}}
  {{$header}} -> extending section
    My own header
  {{/header}}
  Only extending sections are considered...
{{/super}}
Lalala...

and the result is:

Hello world!
This a template to extend (1)
    My own header (2)
In between...
  The default content (3)
&copy; 2013 (4)
Lalala...
  1. "super" start

  2. section "header" is extended

  3. section "content" has the default content

  4. "super" end

2.8. Built-in helpers

Since version 1.5.0 helpers API inspired by Handlebars.js is supported. There are five helpers registered automatically: if, unless, each, with (see http://handlebarsjs.com for examples :-) and is (an inline version of if).

Tip
if and unless helpers also support multiple params evaluation. See the javadoc for more info.

Other helpers may be registered via MustacheEngineBuilder.registerHelper() or MustacheEngineBuilder.registerHelpers() methods. org.trimou.handlebars.HelpersBuilder is useful when registering most built-in helpers with sensible default names.

See also Helper section to know how to create your own custom helpers.

Note
Handlebars support is enabled by default. See HANDLEBARS_SUPPORT_ENABLED in Configuration properties.

Trimou provides some useful helpers which are not registered automatically:

Class Description Default name

org.trimou.handlebars.ChooseHelper

Works similarly as the JSP c:choose tag - it renders the content of the first when section whose first parameter is not falsy. If no when section is rendered, otherwise section is rendered, if present.

choose

org.trimou.handlebars.SwitchHelper

Works similarly as the Java switch statement.

switch

org.trimou.handlebars.SetHelper

Works similarly as WithHelper except the current hash is pushed on the context stack.

set

org.trimou.handlebars.EvalHelper

Allows to build the key dynamically and evaluate it afterwards.

eval

org.trimou.handlebars.IncludeHelper

Works similarly as the partial tag except the name of the template to include may be obtained dynamically.

include

org.trimou.handlebars.LogHelper

Logs debug messages.

log

org.trimou.handlebars.JoinHelper

Takes all the objects specified as the parameters and joins the Object#toString() values together with the specified delimiter (optional). Elements of Iterable and array are treated as separate objects. An optional lambda may be applied to each value.

join

org.trimou.handlebars.EmbedHelper

Embeds the template source (by default as a JavaScript snippet).

embed

org.trimou.handlebars.NullCheckHelper

Renders a block if the param is/isn’t null.

isNull/isNotNull

org.trimou.handlebars.EqualsHelper

Renders a block if the first param does/doesn’t equal to the second param.

isEq/isNotEq

org.trimou.handlebars.NumericExpressionHelper

A simple numeric expression helper. {{#numExpr myVal 10 op="gt"}}

numExpr

org.trimou.handlebars.NumberIsEvenHelper

Renders a block/text if the param is an even number.

isEven

org.trimou.handlebars.NumberIsOddHelper

Renders a block/text if the param is an odd number.

isOdd

org.trimou.handlebars.i18n.ResourceBundleHelper

Displays localized messages.

N/A

org.trimou.handlebars.i18n.DateTimeFormatHelper

Displays localized times.

N/A

org.trimou.handlebars.i18n.TimeFormatHelper

An alternative to DateTimeFormatHelper which makes use of java.time package in JDK 8 (JSR-310). It supports new temporal types and should also be less resource-intensive.

N/A

org.trimou.handlebars.AsyncHelper

A helper whose content is rendered asynchronously.

async

org.trimou.handlebars.InvokeHelper

Invokes public methods with parameters via reflection. {{invoke "f" "b" on="foo" m="replace"}}

invoke

org.trimou.handlebars.AlternativesHelper

Renders the first non-null/nonempty (default condition) parameter. It’s useful to specify default values: {{alt username "Joe"}}

alt

org.trimou.handlebars.CacheHelper

Allows to cache template fragments in memory. It’s useful for resource-intensive parts of the template that rarely change.

cache

org.trimou.handlebars.RepeatHelper

Allows to repeat a section multiple times or until the while expression evaluates to a "falsy" value.

repeat

2.8.1. Example of ResourceBundleHelper

Suppose we have the following resource bundle file:

messages.properties
my.message.key=My name is %s!
hello.key.messageformat=Hello {0}!

We can use a ResourceBundleHelper to render messages:

Register the helper
MustacheEngine engine = MustacheEngineBuilder
                           .newBuilder()
                           .registerHelper("msg", new ResourceBundleHelper("messages")) (1)
                           .build();
...
Template
{{msg "my.message.key" "Martin"}}
{{msg "hello.key.messageformat" "world" format="message"}}
Expected output
My name is Martin!
Hello world!

2.9. Nested templates

Any template may define any number of nested templates - use a section with + to identify a nested template. A nested template is only available within a defining template through the partial tag (or a special helper, e.g. IncludeHelper). If there is a regular template with the same name available the nested template has precedence. It’s not possible to define a nested template within a nested template definition.

Nested Template Example
{{! This is the nested template definition }}
{{+item_detail}}
  Name: {{name}}
  Price: {{price}}
{{/item_detail}}

{{! Inject partial }}
{{#each activeItems}}
  {{>item_detail}}
{{/each}}

{{! Inject partial again }}
{{#each allItems}}
  {{>item_detail}}
{{/each}}
Note
The support for nested templates is enabled by default. See also NESTED_TEMPLATE_SUPPORT_ENABLED in Configuration properties.

3. How to use

3.1. Get started

First, get the trimou-core.jar and its dependencies (guava,slf4j-api and commons-lang3 at the moment).

<dependency>
  <groupId>org.trimou</groupId>
  <artifactId>trimou-core</artifactId>
  <version>${version.trimou}</version>
</dependency>

And now for something completely different…​

3.1.1. The simplest possible scenario

String data = "Hello world!";
String template = "{{this}}";
MustacheEngine engine = MustacheEngineBuilder.newBuilder().build(); (1)
Mustache mustache = engine.compileMustache("myTemplateName", template); (2)
String output = mustache.render(data); (3)

(1) (2) (3)
String output = MustacheEngineBuilder
                  .newBuilder()
                  .build()
                  .compileMustache("myTemplateName", template)
                  .render(data);

// Both snippets will render "Hello world!"
  1. Build the engine

  2. Compile the template

  3. Render the template

Note
Instances of MustacheEngineBuilder are not reusable. The builder is considered immutable once the build() method is called - subsequent invocations of any modifying method or build() methods result in IllegalStateException.

3.1.2. Provide your own Appendable

MustacheEngine engine = MustacheEngineBuilder.newBuilder().build();
Mustache mustache = engine.compileMustache("fooTemplate", "{{foo}}");

// It's possible to pass a java.lang.Appendable impl, e.g. any java.io.Writer
StringWriter writer = new StringWriter();

mustache.render(writer, ImmutableMap.<String, Object> of("foo", "bar"));
// writer.toString() -> "bar"

3.1.3. Configure the engine

You may want to:

Simply use appropriate MustacheEngineBuilder methods, e.g.:

MustacheEngine engine = MustacheEngineBuilder
                            .newBuilder()
                            .addGlobalData("fooLambda", mySuperUsefulLambdaInstance)
                            .build();

3.2. Make use of template cache and template locators

Template locators automatically locate the template contents for the given template id (name, path, …​). So that it’s not necessary to supply the template contents every time the template is compiled. Moreover if the template cache is enabled the compiled template is automatically put in the cache and no compilation happens the next time the template is requested.

Note
Template locators are required for partials!
MustacheEngine engine = MustacheEngineBuilder
                           .newBuilder()
                           .addTemplateLocator(new FilesystemTemplateLocator(1, "/home/trimou/resources", "txt")) (1)
                           .build();
Mustache mustache = engine.getMustache("foo"); (2)
String output = mustache.render(null);
  1. Add a filesystem-based template locator with priority 1, root path "/home/trim/resources", template files have suffix "txt"

  2. Get the template with name "foo" from the template cache, compile it if not compiled before

There may be more than one template locators registered with the engine. Locators with higher priority are called first.

Tip
Use MustacheEngine#invalidateTemplateCache() to invalidate all template cache entries and force recompilation.

3.2.1. Note about file encoding

Trimou does not perform any file encoding detection and conversion. Instead, any template locator must provide a java.io.Reader instance which is able to convert between Unicode and a other character encodings. Built-in locators don’t detect file encoding but use system file encoding by default. But it’s possible (and recommended) to define the default file encoding with configuration property EngineConfigurationKey.DEFAULT_FILE_ENCODING (see also configuration properties).

Note
Applications are encouraged to always define a default file encoding per every MustacheEngine instance.

3.3. Configuration properties

Trimou engine properties can be configured through system properties, trimou.properties file or the property can be set manually with MustacheEngineBuilder.setProperty(String, Object) method. Manually set properties have higher priority than system properties which have higher priority than properties from trimou.properties file.

Note
Trimou logs all configuration properties and values during engine initialization
Table 1. Engine configuration keys - see also org.trimou.engine.config.EngineConfigurationKey enum
Enum value / property key Default value Description

START_DELIMITER

org.trimou.engine.config.startDelimiter

{{

The default start delimiter.

END_DELIMITER

org.trimou.engine.config.endDelimiter

}}

The default end delimiter

PRECOMPILE_ALL_TEMPLATES

org.trimou.engine.config.precompileAllTemplates

false

If enabled, all available templates from all available template locators will be compiled during engine initialization.

REMOVE_STANDALONE_LINES

org.trimou.engine.config.removeStandaloneLines

true

Remove "standalone lines" from each template during compilation to fullfill the spec requirements (and get more readable output :-)

REMOVE_UNNECESSARY_SEGMENTS

org.trimou.engine.config.removeUnnecessarySegments

true

Remove unnecessary segments (e.g. comments and delimiters tags) from each template during compilation. Having this enabled results in spec not-compliant output, but may improve performance a little bit.

NO_VALUE_INDICATES_PROBLEM

org.trimou.engine.config.noValueIndicatesProblem

false

DEPRECATED - see MissingValueHandler SPI.

By default a variable miss returns an empty string. If set to true a org.trimou.exception.MustacheException with code org.trimou.exception.MustacheProblem.RENDER_NO_VALUE is thrown.

DEBUG_MODE

org.trimou.engine.config.debugMode

false

Debug mode disables the template cache and provides some more logging during template rendering.

CACHE_SECTION_LITERAL_BLOCK

org.trimou.engine.config.cacheSectionLiteralBlock

false

The section-based literal blocks can be cached. This may be useful to optimize some lambdas processing scenarios, though it’s memory intensive.

TEMPLATE_RECURSIVE_INVOCATION_LIMIT

org.trimou.engine.config.templateRecursiveInvocationLimit

10

The limit of recursive template invocation (partials, template inheritance); 0 - recursive invocation is forbidden.

SKIP_VALUE_ESCAPING

org.trimou.engine.config.skipValueEscaping

false

If true interpolated values are never escaped, i.e. org.trimou.engine.text.TextSupport.escapeHtml() is never called.

DEFAULT_FILE_ENCODING

org.trimou.engine.config.defaultFileEncoding

System property "file.encoding"

The encoding every template locator should use if reading template from a file. System file encoding by default.

TEMPLATE_CACHE_ENABLED

org.trimou.engine.config.templateCacheEnabled

true

The template cache is enabled by default. If set to false every MustacheEngine.getMustache() invocation results in template lookup.

TEMPLATE_CACHE_EXPIRATION_TIMEOUT org.trimou.engine.config.templateCacheExpirationTimeout

0

The template cache expiration timeout in seconds. Zero and negative values mean no timeout. The template cachec never expires by default.

HANDLEBARS_SUPPORT_ENABLED org.trimou.engine.config.handlebarsSupportEnabled

true

Handlebars support is enabled by default. Right now only handlebars-like helpers are supported.

REUSE_LINE_SEPARATOR_SEGMENTS org.trimou.engine.config.reuseLineSeparatorSegments

true

If set to true the line separators will be reused within template to conserve memory.

ITERATION_METADATA_ALIAS org.trimou.engine.config.iterationMetadataAlias

iter

The alias for iteration metadata object available inside an iteration block.

RESOLVER_HINTS_ENABLED org.trimou.engine.config.resolverHintsEnabled

true

If set to true the evaluation of simple variables, e.g. . or foo, is optimized.

NESTED_TEMPLATE_SUPPORT_ENABLED org.trimou.engine.config.nestedTemplateSupportEnabled

true

If set to true the nested templates are supported. Otherwise any start tag of a nested template definition is considered to be a regular variable tag.

3.4. Basic i18n support

Trimou has a basic i18n support. There are some optional components provided to handle i18n requirements. All these components rely on org.trimou.engine.locale.LocaleSupport implementation to get the current Locale, see also LocaleSupport.

Table 2. i18n components
Type Class Description

Resolver

org.trimou.engine.resolver.i18n.NumberFormatResolver

Basic number formatting.

Resolver

org.trimou.engine.resolver.i18n.DateTimeFormatResolver

Basic date and time formatting.

Resolver

org.trimou.engine.resolver.i18n.ResourceBundleResolver

Resolves localized messages. Unlike ResourceBundleLambda this resolver is not limited to String-based values. However keep in mind that resource bundle keys may not contain dots.

Helper

org.trimou.handlebars.i18n.DateTimeFormatHelper

This is an alternative to DateTimeFormatResolver. The main advantage lies in the ability to specify custom pattern per tag: {{formatTime now pattern="DD-MM-yyyy HH:mm"}}.

Helper

org.trimou.handlebars.i18n.ResourceBundleHelper

The most flexible way of rendering localized messages. Supports message parameters and multiple resource bundles.

Lambda

org.trimou.lambda.i18n.ResourceBundleLambda

Renders localized messages. Unlike ResourceBundleResolver this lambda supports resource bundle keys that contain dots.

3.4.1. DateTimeFormatResolver example

MustacheEngine engine = MustacheEngineBuilder
                           .newBuilder()
                           .setProperty(DateTimeFormatResolver.CUSTOM_PATTERN_KEY, "DD-MM-YYYY HH:mm") (1)
                           .addResolver(new DateTimeFormatResolver()) (2)
                           .build();
Mustache mustache = engine.getMustache("foo");
String output = mustache.render(ImmutableMap.<String, Object> of("now", new Date()));
  1. DateTimeFormatResolver also supports custom formatting pattern

  2. Manually add resolver

foo.html
Now: {{now.formatCustom}}

results in something similar:

Now: 03-05-2013 22:05

3.5. Debugging

If you encounter a problem during template processing/rendering, try to:

  • configure Simple Logging Facade for Java (SLF4J) - increase the log level for org.trimou loggers

  • enable debug mode - this disables the template cache and provides some more logging during template rendering (otherwise disabled due to performance)

  • implement your own MissingValueHandler - to handle variable miss during interpolation of a variable tag

  • use LogHelper - this might useful for production environments

4. How to extend

Basically, all the extension points are focused on MustacheEngine configuration. Some components may be automatically added using the org.trimou.engine.config.ConfigurationExtension and JDK service-provider loading facility. Others may be added manually via MustacheEngineBuilder methods. See existing extensions to get acquainted with the basic principles.

Note
Automatic org.trimou.engine.config.ConfigurationExtension processing may be disabled per engine - see also MustacheEngineBuilder#omitServiceLoaderConfigurationExtensions().

4.1. Resolver

Resolvers define the set of resolvable objects for your templates. The built-in set of resolvers should satisfy most of the basic requirements.

4.1.1. Custom resolvers

Warning
Implementing/adding a custom resolver may have serious impact on the engine functionality and performance.

All resolvers have a priority and resolvers with higher priority are called first. Keep in mind that all resolvers must be thread-safe. There are two ways to extend the basic set of resolvers:

  • automatically via org.trimou.engine.config.ConfigurationExtension,

  • you can also use MustacheEngineBuilder.addResolver() method.

Tip
trimou-extension-cdi extension provides CDIBeanResolver to lookup normal-scoped CDI beans with name. trimou-extension-servlet extension provides HttpServletRequestResolver to get the current Servlet request wrapper.

4.1.2. EnhancedResolver

An enhanced resolver should be able to create a Hint for a sucessfully resolved context object and name. A hint could be used to skip the resolver chain for a part of the key of a specific tag and improve the interpolation performance.

Note
Hints are enabled by default. See RESOLVER_HINTS_ENABLED in Configuration properties.

4.2. TemplateLocator

Template locators automatically locate the template contents for the given template identifier. The form of the template identifier is not defined, however in most cases the id will represent a template name, e.g. foo and foo.html, or virtual path like order/orderDetail. The default virtual path separator is / and can be configured via org.trimou.engine.locator.PathTemplateLocator.VIRTUAL_PATH_SEPARATOR_KEY. Template locators may only be added with MustacheEngineBuilder.addTemplateLocator() method.

There are three basic built-in implementations. org.trimou.engine.locator.FilesystemTemplateLocator finds templates on the filesystem, within the given root directory (watch out, this wouldn’t be likely portable across various operating systems). org.trimou.engine.locator.ClassPathTemplateLocator makes use of ClassLoader, either thread context class loader (TCCL) or custom CL set via constructor. org.trimou.engine.locator.MapTemplateLocator is backed by a Map. See javadoc for more configuration info.

Tip
Locators with higher priority are called first.
Tip
trimou-extension-servlet extension provides org.trimou.servlet.locator.ServletContextTemplateLocator to be used in web apps deployed to a servlet container.

4.3. TextSupport

org.trimou.engine.text.TextSupport is used to escape variable text if necessary (see also Escaping HTML). You can set the custom instance with org.trimou.engine.MustacheEngineBuilder.setTextSupport() method. Implement your own logic to extend functionality or improve performance!

4.4. LocaleSupport

org.trimou.engine.locale.LocaleSupport allows the engine and its components (e.g. resolvers) to get the current locale via getCurrentLocale(). You can set the custom org.trimou.engine.locale.LocaleSupport instance with org.trimou.engine.MustacheEngineBuilder.setLocaleSupport() method.

4.5. MustacheListener

Any registered org.trimou.engine.listener.MustacheListener receives notifications about template processing. In particular compilationFinished() method is invoked when a template is compiled, renderingStarted() and renderingFinished() methods are invoked for each template rendering. parsingStarted() is invoked right before a template is processed by the parser. Listeners are invoked in the order of their registration, except for renderingFinished() method which is invoked in reverse order.

There are two ways to register a custom listener:

  • MustacheEngineBuilder.addMustacheListener() method,

  • automatically via org.trimou.engine.config.ConfigurationExtension (extension listeners are always registered after manually added listeners).

Note
Code inside a listener may throw an unchecked exception - this aborts further processing of template and no more listeners are invoked afterwards.

4.6. Helper

org.trimou.handlebars.Helper API is inspired by Handlebars but it’s not 100% compatible. Mainly, it does not define the "inverse" section, so for example the built-in if helper doesn’t support else block. On the other hand any helper is able to validate the tag definition (see Helper.validate()) and fail fast if there’s invalid number of arguments etc.

A helper may be registered via MustacheEngineBuilder.registerHelper() or MustacheEngineBuilder.registerHelpers() methods. Note that each helper must be registered with a unique name. If there are more helpers registered with the same name an IllegalArgumentException is thrown during engine build. There is a special method MustacheEngineBuilder.registerHelpers(Map<String, Helper>, boolean) which allows to overwrite the existing helper instance (e.g. to define a custom if helper). Some built-in helpers are registered automatically.

Note
The number of registered helpers should not affect the engine performance (unlike the number of registered resolvers).

The main advantage of helpers is the ability to consume multiple parameters and optional hash map. A parameter or a hash map value may be a literal (see also LiteralSupport), a value placeholder (evaluated at runtime, during each execution of a helper) or a list of literals and value placeholders.

{{#if foo}}Foo is a value placeholder, is evaluated each time the helper is executed{{/if}}

{{#if true}}true is a literal{{/if}}

{{#each [foo, 1]}}
    [foo, 1] is a list containing value placeholder and literal
{{/each}}

Check out org.trimou.handlebars.Options and the source of built-in helpers to see what helpers can do.

4.6.1. Simple helpers

org.trimou.handlebars.SimpleHelpers utility class and its builder allow to create simple helpers using JDK8 funcional interfaces. It’s even possible to validate the helper definition and provide configuration keys.

MustacheEngine engine = MustacheEngineBuilder
        .newBuilder()
        .registerHelper("toLowerCase", SimpleHelpers.execute(
            (o, c) -> {
                o.append(o.getParameters().get(0).toString().toLowerCase());
            })
        ).build();

4.7. MissingValueHandler

org.trimou.engine.interpolation.MissingValueHandler handles variable miss (no value found) during interpolation of a variable tag. By default org.trimou.engine.interpolation.NoOpMissingValueHandler is used so that a miss does not result in any special operation. However you can set your own handler through the MustacheEngineBuilder.setMissingValueHandler() method. There is also org.trimou.engine.interpolation.ThrowingExceptionMissingValueHandler which throws an exception in case of a miss (actually it replaces deprecated configuration property EngineConfigurationKey#NO_VALUE_INDICATES_PROBLEM).

4.8. KeySplitter

org.trimou.engine.interpolation.KeySplitter is responsible for splitting a variable key. org.trimou.engine.interpolation.DotKeySplitter which follows the dot notation is used by default. org.trimou.engine.interpolation.BracketDotKeySplitter enables to use bracket notation and literals in variable keys. E.g. {{messages["my.message.key"]}}. You can set your own splitter through the MustacheEngineBuilder.setKeySplitter() method.

4.9. ComputingCacheFactory

org.trimou.engine.cache.ComputingCache is a simple abstraction for thread-safe computing (lazy loading) cache. It’s used in some internal components (e.g. ReflectionResolver) and may also be used in custom components too. org.trimou.engine.cache.ComputingCacheFactory component is responsible for creating new instances of ComputingCache. The default computing cache implementation is backed by java.util.concurrent.ConcurrentHashMap.

4.10. IdentifierGenerator

org.trimou.engine.id.IdentifierGenerator is used to generate identifiers for various components and use-cases (e.g. Mustache, MustacheRenderingEvent and one-off lambda names). There are some restrictions on the uniqueness of the generated id - see also the javadoc.

4.11. LiteralSupport

org.trimou.engine.interpolation.LiteralSupport allows to customize the way the helpers extract literals from params and hash values. The default implementation currently supports String ("foo" or 'foo'), Integer, Long (10L or 10l), Boolean (true or false) literals.

4.12. ValueConverter

org.trimou.engine.convert.ValueConverter is used to convert an object to a string representation. Converters are mostly used in variable tags, e.g. {{foo}}. A converter has also a priority - converters with higher priority are called first. If no converter is able to convert an object, Object#toString() is used.

5. Extensions

5.1. CDI

Maven dependency
<dependency>
  <groupId>org.trimou</groupId>
  <artifactId>trimou-extension-cdi</artifactId>
  <version>${version.trimou}</version>
</dependency>

5.1.1. CDIBeanResolver

Tries to resolve a CDI bean with the given name (i.e. annotated with @Named or with a @Named stereotype).

5.1.2. Rendering context

The rendering scope is active during each rendering of a template, i.e. during Mustache.render() invocation - there is exactly one bean instance per rendering which is destroyed after the rendering is finished. This could be useful in SE environments where usually only @ApplicationScoped and @Dependent built-in scopes are available. You can annotate your bean with org.trimou.cdi.context.RenderingScoped to declare the rendering scope.

5.2. Servlets

At the moment only Servlet 3.x API is supported.

Maven dependency
<dependency>
  <groupId>org.trimou</groupId>
  <artifactId>trimou-extension-servlet</artifactId>
  <version>${version.trimou}</version>
</dependency>

5.2.1. ServletContextTemplateLocator

Locates the template anywhere in the web app. The root path must begin with a / and is interpreted as relative to the current context root, or relative to the /META-INF/resources directory of a JAR file inside the web application’s /WEB-INF/lib directory.

MustacheEngineBuilder
  .newBuilder()
  .addTemplateLocator(ServletContextTemplateLocator.builder().setRootPath("/WEB-INF/templates").build())
  .build();

5.2.2. HttpServletRequestResolver

Resolves a key of value request to HttpServletRequestWrapper. Why the wrapper? Well, we just don’t think it’s the right thing to call the request object directly.

5.2.3. RequestLocaleSupport

Obtains the current locate from the current servlet request.

5.3. PrettyTime

Maven dependency
<dependency>
  <groupId>org.trimou</groupId>
  <artifactId>trimou-extension-prettytime</artifactId>
  <version>${version.trimou}</version>
</dependency>

5.3.1. PrettyTimeResolver

This resolver allows you to use PrettyTime date-formatting in your templates.

Note
The PrettyTimeResolver is automatically loaded if you place the extension jar on the classpath.
Simple example
MustacheEngine engine = MustacheEngineBuilder
                             .newBuilder()
                             .build();
Mustache mustache = engine.compileMustache("prettyTime","{{now.prettyTime}}");
String output = mustache.render(ImmutableMap.<String, Object> of("now", new Date()));
// Renders something similar:
// moments from now

5.3.2. PrettyTimeHelper

Developers are encouraged to use this helper instead of PrettyTimeResolver to avoid the negative performance impact during interpolation. However, PrettyTimeResolver is registered automatically through PrettyTimeConfigurationExtension. So don’t forget to disable the resolver by means of org.trimou.prettytime.resolver.PrettyTimeResolver.ENABLED_KEY, e.g. use org.trimou.prettytime.resolver.PrettyTimeResolver.enabled=false in your properties file.

5.4. Minify

Minify extension allows you to minify your HTML and XML templates (or any other type of content if you provide your own org.trimou.minify.Minifier implementation). Trimou integrates small and efficient HtmlCompressor library. There are two ways to minify the templates. It’s possible to register a special listener to minify templates before parsing/compilation or use a special lambda to minify some parts of the template contents.

Tip
org.trimou.minify.Minify helper methods are useful to create the default listeners and lambdas (i.e. if you don’t require some extra configuration).
Note
From the performance point of view: both listener and lambda decrease the size of the rendered template. However listeners may also improve the rendering performance (template is minified only once - before the compilation). Whereas lambdas will likely make rendering performance worse (part of the template is minified every time the lambda is invoked).
Maven dependency
<dependency>
  <groupId>org.trimou</groupId>
  <artifactId>trimou-extension-minify</artifactId>
  <version>${version.trimou}</version>
</dependency>

5.4.1. MinifyListener

Simple example
MustacheEngine engine = MustacheEngineBuilder
                             .newBuilder()
                             .addMustacheListener(Minify.htmlListener())
                             .build();
Mustache mustache = engine.compileMustache("minify_html","<html><body>     <!-- My comment -->{{foo}}  </body></html>");
String output = mustache.render(ImmutableMap.<String, Object> of("foo", "FOO"));
// Renders:
// <html><body> FOO </body></html>
Tip
It’s also possible to customize the underlying com.googlecode.htmlcompressor.compressor.HtmlCompressor instance - see also our MinifyListenerTest.

5.4.2. MinifyLambda

Simple example
MustacheEngine engine = MustacheEngineBuilder
                             .newBuilder()
                             .build();
Mustache mustache = engine.compileMustache("minify_html_lambda","<html><body><!-- Remains -->{{#mini}}<!-- Will be removed -->   FOO {{/mini}}</body></html>");
String output = mustache.render(ImmutableMap.<String, Object> of("mini", Minify.htmlLambda()));
// Renders:
// <html><body><!-- Remains --> FOO </body></html>

5.4.3. Minifier interface

You can also implement your own minifier and leverage existing infrastructure:

MustacheEngine engine = MustacheEngineBuilder
                             .newBuilder()
                             .addMustacheListener(Minify.customListener(new AbstractMinifier() {
                                @Override
                                public Reader minify(String mustacheName, Reader mustacheContents) {
                                    return mustacheName.endsWith("html") ? mySuperMinification(Reader mustacheContents) : mustacheContents;
                                }
                             }))).build();

5.5. Gson

Gson extension brings some basic support for JSON format by means of google-gson APIs.

Maven dependency
<dependency>
  <groupId>org.trimou</groupId>
  <artifactId>trimou-extension-gson</artifactId>
  <version>${version.trimou}</version>
</dependency>

5.5.1. GsonValueConverter

Converts JsonPrimitive to JsonPrimitive.getAsString() and JsonNull to an empty string. This converter is enabled by default and could be disabled by setting org.trimou.gson.converter.GsonValueConverter.enabled configuration property to false.

5.5.2. JsonElementResolver

org.trimou.gson.resolver.JsonElementResolver makes it easier to work with com.google.gson.JsonElement instances. It is automatically loaded if you place the extension jar on the classpath.

  • allows to access JsonObject properties via dot notation

    • e.g. if foo is an instance of JsonObject then foo.bar is translated to foo.get("bar")

  • JsonArray elements can be accessed via index

    • e.g. if foo is an instance of JsonArray then foo.1 is translated to foo.get(1)

JsonNull and JsonPrimitive might be automatically unwrapped if org.trimou.gson.resolver.JsonElementResolver.unwrapJsonPrimitive configuration property is set to true. JsonNull is resolved as a Placeholder#NULL and JsonPrimitive is unwrapped according to its type. E.g. foo.bar in JsonObject example is translated to foo.get("bar").getAsNumber() if bar is an instance of a java.lang.Number. Since 2.1 this automatic unwrapping is disabled by default - see JsonElementResolver javadoc.

Note
Unwrapping only works if JsonElementResolver is involved! So for example if you iterate over ["Jim", true, 5] (and GsonValueConverter is not enabled), a special keyword unwrapThis must be used so that the primitives are unwrapped: {{#jsonArray}}{{unwrapThis}}{{/jsonArray}}.
Example code
Example data
{
    "firstName": "Jan",
    "lastName": "Novy",
    "age": 30,
    "address": {
        "street": "Nova",
        "city": "Prague",
        "state": "CZ",
        "postalCode": "11000"
    },
    "phoneNumbers": [
        {
            "type": "home",
            "number": "`42002012345"
        },
        {
            "type": "mobile",
            "number": "`420728000111"
        }
    ]
}
Example template
Last name: {{lastName}}
Street: {{address.street}}
Phone numbers: {{#phoneNumbers}}{{number}}{{#iterHasNext}}, {{/iterHasNext}}{{/phoneNumbers}}
Type of the first phone number: {{phoneNumbers.0.type}}
Type of the second phone number: {{phoneNumbers.1.type}}
Java code
// Load the test data
JsonElement jsonElement = new JsonParser().parse(...);
// JsonElementResolver is loaded automatically
MustacheEngine engine = MustacheEngineBuilder
                             .newBuilder()
                             .build();
Mustache mustache = engine.getMustache("json_test.mustache");
String output = mustache.render(jsonElement);
Expected output
Last name: Novy
Street: Nova
Phone numbers: `42002012345, `420728000111
Type of the first phone number: home
Type of the second phone number: mobile

5.6. JSON Processing (JSR 353)

This extension simplifies the usage of Object Model API along with Trimou templates.

Maven dependency
<dependency>
  <groupId>org.trimou</groupId>
  <artifactId>trimou-extension-json-p</artifactId>
  <version>${version.trimou}</version>
</dependency>

5.6.1. JsonProcessingValueConverter

Converts JsonString to JsonString.getString() and JsonValue.NULL to an empty string. This converter is enabled by default and could be disabled by setting org.trimou.jsonp.converter.JsonProcessingValueConverter.enabled configuration property to false.

5.6.2. JsonValueResolver

Warning
Since 2.1 this resolver is disabled by default.

org.trimou.jsonp.resolver.JsonValueResolver makes it easier to work with javax.json.JsonValue instances. It is automatically loaded if you place the extension jar on the classpath. Since JsonObject implements Map and JsonArray implements List this resolver is only useful if automatic unwrapping is required. Automatic unwrapping means resolving JsonString#getString() for a JsonString, JsonNumber#bigDecimalValue() for a JsonNumber, Boolean#TRUE for a JsonValue#TRUE, Boolean#FALSE for JsonValue#FALSE and Placeholder#NULL for a JsonValue#NULL. However, unwrapping only works if JsonValueResolver is involved! So for example if you iterate over ["Jim", true, 5], a special keyword unwrapThis must be used so that the primitives are unwrapped:

{{#jsonArray}}{{unwrapThis}}{{/jsonArray}}
Note
This resolver should always have higher priority than MapResolver to be able to process instances of JsonObject.
Example code
Example data
{
    "firstName": "Jan",
    "lastName": "Novy",
    "age": 30,
    "address": {
        "street": "Nova",
        "city": "Prague",
        "state": "CZ",
        "postalCode": "11000"
    },
    "phoneNumbers": [
        {
            "type": "home",
            "number": "`42002012345"
        },
        {
            "type": "mobile",
            "number": "`420728000111"
        }
    ]
}
Example template
Last name: {{lastName}}
Street: {{address.street}}
Phone numbers: {{#phoneNumbers}}{{number}}{{#iterHasNext}}, {{/iterHasNext}}{{/phoneNumbers}}
Type of the first phone number: {{phoneNumbers.0.type}}
Type of the second phone number: {{phoneNumbers.1.type}}
Java code
// Load the test data
JsonStructure jsonStructure = Json.createReader(...).read();
// JsonValueResolver is loaded automatically
MustacheEngine engine = MustacheEngineBuilder
                             .newBuilder()
                             .build();
Mustache mustache = engine.getMustache("json_test.mustache");
String output = mustache.render(jsonElement);
Expected output
Last name: Novy
Street: Nova
Phone numbers: `42002012345, `420728000111
Type of the first phone number: home
Type of the second phone number: mobile

5.7. Spring MVC integration

This extension provides a basic Spring MVC integration.

Maven dependency
<dependency>
  <groupId>org.trimou</groupId>
  <artifactId>trimou-extension-spring-mvc</artifactId>
  <version>${version.trimou}</version>
</dependency>

5.8. Dropwizard

This extension provides a basic Dropwizard integration.

Maven dependency
<dependency>
  <groupId>org.trimou</groupId>
  <artifactId>trimou-extension-dropwizard</artifactId>
  <version>${version.trimou}</version>
</dependency>

5.8.1. TrimouViewRenderer

org.trimou.dropwizard.views.TrimouViewRenderer is a io.dropwizard.views.ViewRenderer implementation backed by Trimou. There’s a simple builder for convenience: org.trimou.dropwizard.views.TrimouViewRenderer.Builder.

5.9. EL 3.0

This extension provides a basic EL 3.0 integration.

Maven dependency
<dependency>
  <groupId>org.trimou</groupId>
  <artifactId>trimou-extension-el</artifactId>
  <version>${version.trimou}</version>
</dependency>

5.9.1. ELIfHelper

org.trimou.el.ELIfHelper extends the built-in IfHelper in the sense that a String param is evaluated as EL expression:

{{#if "item.price gt 200"}}
  {{item.name}}
{{/if}}
Note
The ELIfHelper is automatically registered if you place the extension on the class path.

5.9.2. ELHelper

org.trimou.el.ELHelper evaluates the Object#toString() of the first parameter. If the helper represents a section and the value is not null the value is pushed on the context stack and the section is rendered. If the helper represents a variable and the value is null, the current MissingValueHandler is used. If the helper represents a variable and the final value is not null the the value’s Object#toString() is rendered.

{{el 'item.active ? "active" : "inactive"'}}
Note
The ELHelper is automatically registered if you place the extension on the class path.