1. What is Trimou?

Trimou is yet another Mustache implementation written in Java. It’s goal is to provide a simple to use and easy to extend templating engine for any Java SE or Java EE application. So far there are three ready-to-use extensions which provide integration with CDI, Servlets and PrettyTime (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

  • Extended processing of lambdas

  • Template inheritance

  • Very basic i18n support

  • 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 unsigned integer),

  • iterIndex and iterHasNext keywords can be used inside iteration block,

  • 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}}
  {{iterIndex}} <5>
  {{name}} <6>
  {{iterHasNext}}, {{/iterHasNext}} <7>
{{/items}}

{{#quxEnumClass.values}} <8>
  {{this}}
{{/quxEnumClass.values}}
  1. Try to get a value of key "foo" from supplied data map

  2. If "foo" is an instance of Map, get the value of key "bar"; otherwise 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 index of the iteration block

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

  7. render a comma if the iteration has more elements (iterHasNext is true)

  8. 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 (&, ", <, >). Trimou also escapes all ISO-8859-1 characters by default.

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

However this might be a little bit more useful:

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.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

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

MustacheEngine engine = MustacheEngineBuilder.newBuilder().build(); <1>
Mustache mustache = engine.compileMustache("myTemplateName", "{{! empty template}}"); <2>
String output = mustache.render(null); <3>

<1> <2> <3>
String output = MustacheEngineBuilder
                  .newBuilder()
                  .build()
                  .compileMustache("myTemplateName", "{{! empty template}}")
                  .render(null);
  1. Build the engine

  2. Compile the template

  3. Render the text

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 load the template contents for the given template name. So that it’s not necessary to supply the template contents every time the template is compiled. Moreover the compiled template is automatically put in the template cache (no compilation happens the next time the template is requested).

Note
Template cache is required for partials!
MustacheEngine engine = MustacheEngineBuilder
                           .newBuilder()
                           .addTemplateLocator(new FilesystemTemplateLocator(1, "txt", "/home/trimou/resources")) <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.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

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.

3.4. Basic i18n support

Trimou has a very basic i18n support. Basically it provides three optional resolvers: org.trimou.engine.resolver.i18n.NumberFormatResolver, org.trimou.engine.resolver.i18n.DateTimeFormatResolver, org.trimou.engine.resolver.i18n.ResourceBundleResolver and one optional lambda: org.trimou.lambda.i18n.ResourceBundleLambda. All these components rely on org.trimou.engine.locale.LocaleSupport implementation to get the current Locale. See javadoc for more info.

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

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.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

You can set the custom org.trimou.engine.text.TextSupport instance with org.trimou.engine.MustacheEngineBuilder.setTextSupport() method. So far there’s only one method to implement - escapeHtml(String) (see also Escaping HTML). Implement your own logic to extend functionality or improve performance.

4.4. LocaleSupport

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

4.5. 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.

There are two ways to register a custom listener:

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

  • MustacheEngineBuilder.addMustacheListener() method.

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

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 lookup a normal-scoped CDI bean with the given name (key).

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(new ServletContextTemplateLocator(10, "/WEB-INF/templates"))
  .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.

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