Jmustache
A Java implementation of the Mustache templating language.
Install / Use
/learn @samskivert/JmustacheREADME
This is a Java implementation of the Mustache template language.
Motivations
-
Zero dependencies. You can include this single tiny library in your project and start making use of templates.
-
Usability on a variety of target platforms. This implementation makes very limited demands on the JVM in which it runs and as a result is usable on Android, or on other limited JVMs. It is even possible to avoid the use of reflection and provide all of your data as a series of nested maps.
-
Proguard and JarJar friendly. Though the library will reflectively access your data (if you desire it), the library makes no other internal use of reflection or by name instantiation of classes. So you can embed it using Proguard or JarJar without any annoying surprises.
-
Minimal API footprint. There are really only two methods you need to know about:
compileandexecute. You can even chain them together in cases where performance is of no consequence.
Its existence justified by the above motivations, this implementation then strives to provide additional benefits:
- It is available via Maven Central, see below for details.
- It is reasonably performant. Templates are parsed separately from execution. A template will specialize its variables on (class of context, name) pairs so that if a variable is first resolved to be (for example) a field of the context object, that will be attempted directly on subsequent template invocations, and the slower full resolution will only be tried if accessing the variable as a field fails.
Get It
JMustache is available via Maven Central and can thus be easily added to your Maven, Ivy, etc.
projects by adding a dependency on com.samskivert:jmustache:1.15. Or download the pre-built
jar file.
Documentation
In addition to the usage section below, the following documentation may be useful:
Usage
Using JMustache is very simple. Supply your template as a String or a Reader and get back a
Template that you can execute on any context:
String text = "One, two, {{three}}. Three sir!";
Template tmpl = Mustache.compiler().compile(text);
Map<String, String> data = new HashMap<String, String>();
data.put("three", "five");
System.out.println(tmpl.execute(data));
// result: "One, two, five. Three sir!"
Use Reader and Writer if you're doing something more serious:
void executeTemplate (Reader template, Writer out, Map<String, String> data) {
Mustache.compiler().compile(template).execute(data, out);
}
The execution context can be any Java object. Variables will be resolved via the following mechanisms:
- If the context is a
MustacheCustomContext,MustacheCustomContext.getwill be used. - If the context is a
Map,Map.getwill be used. - If a non-void method with the same name as the variable exists, it will be called.
- If a non-void method named (for variable
foo)getFooexists, it will be called. - If a field with the same name as the variable exists, its contents will be used.
Example:
class Person {
public final String name;
public Person (String name, int age) {
this.name = name;
_age = age;
}
public int getAge () {
return _age;
}
protected int _age;
}
String tmpl = "{{#persons}}{{name}}: {{age}}\n{{/persons}}";
Mustache.compiler().compile(tmpl).execute(new Object() {
Object persons = Arrays.asList(new Person("Elvis", 75), new Person("Madonna", 52));
});
// result:
// Elvis: 75
// Madonna: 52
As you can see from the example, the fields (and methods) need not be public. The persons field
in the anonymous class created to act as a context is accessible. Note that the use of non-public
fields will not work in a sandboxed security environment.
Sections behave as you would expect:
Booleanvalues enable or disable the section.- Array,
Iterator, orIterablevalues repeatedly execute the section with each element used as the context for each iteration. Empty collections result in zero instances of the section being included in the template. - An unresolvable or null value is treated as false. This behavior can be changed by using
strictSections(). See Default Values for more details. - Any other object results in a single execution of the section with that object as a context.
See the code in MustacheTest.java for concrete examples. See also the Mustache documentation for details on the template syntax.
Partials
If you wish to make use of partials (e.g. {{>subtmpl}}) you must provide a
Mustache.TemplateLoader to the compiler when creating it. For example:
final File templateDir = ...;
Mustache.Compiler c = Mustache.compiler().withLoader(new Mustache.TemplateLoader() {
public Reader getTemplate (String name) {
return new FileReader(new File(templateDir, name));
}
});
String tmpl = "...{{>subtmpl}}...";
c.compile(tmpl).execute();
The above snippet will load new File(templateDir, "subtmpl") when compiling the template.
Lambdas
JMustache implements lambdas by passing you a Template.Fragment instance which you can use to
execute the fragment of the template that was passed to the lambda. You can decorate the results of
the fragment execution, as shown in the standard Mustache documentation on lambdas:
String tmpl = "{{#bold}}{{name}} is awesome.{{/bold}}";
Mustache.compiler().compile(tmpl).execute(new Object() {
String name = "Willy";
Mustache.Lambda bold = new Mustache.Lambda() {
public void execute (Template.Fragment frag, Writer out) throws IOException {
out.write("<b>");
frag.execute(out);
out.write("</b>");
}
};
});
// result:
<b>Willy is awesome.</b>
You can also obtain the results of the fragment execution to do things like internationalization or caching:
Object ctx = new Object() {
Mustache.Lambda i18n = new Mustache.Lambda() {
public void execute (Template.Fragment frag, Writer out) throws IOException {
String key = frag.execute();
String text = // look up key in i18n system
out.write(text);
}
};
};
// template might look something like:
<h2>{{#i18n}}title{{/i18n}}</h2>
{{#i18n}}welcome_msg{{/i18n}}
There is also limited support for decompiling (unexecuting) the template and obtaining the original Mustache template text contained in the section. See the documentation for [Template.Fragment] for details on the limitations.
Default Values
By default, an exception will be thrown any time a variable cannot be resolved, or resolves to null
(except for sections, see below). You can change this behavior in two ways. If you want to provide a
value for use in all such circumstances, use defaultValue():
String tmpl = "{{exists}} {{nullValued}} {{doesNotExist}}?";
Mustache.compiler().defaultValue("what").compile(tmpl).execute(new Object() {
String exists = "Say";
String nullValued = null;
// String doesNotExist
});
// result:
Say what what?
If you only wish to provide a default value for variables that resolve to null, and wish to
preserve exceptions in cases where variables cannot be resolved, use nullValue():
String tmpl = "{{exists}} {{nullValued}} {{doesNotExist}}?";
Mustache.compiler().nullValue("what").compile(tmpl).execute(new Object() {
String exists = "Say";
String nullValued = null;
// String doesNotExist
});
// throws MustacheException when executing the template because doesNotExist cannot be resolved
When using a Map as a context, nullValue() will only be used when the map contains a mapping to
null. If the map lacks a mapping for a given variable, then it is considered unresolvable and
throws an exception.
Map<String,String> map = new HashMap<String,String>();
map.put("exists", "Say");
map.put("nullValued", null);
// no mapping exists for "doesNotExist"
String tmpl = "{{exists}} {{nullValued}} {{doesNotExist}}?";
Mustache.compiler().nullValue("what").compile(tmpl).execute(map);
// throws MustacheException when executing the template because doesNotExist cannot be resolved
Do not use both defaultValue and nullValue in your compiler configuration. Each one
overrides the other, so whichever one you call last is the behavior you will get. But even if you
accidentally do the right thing, you have confusing code, so don't call both, use one or the other.
Sections
Sections are not affected by the nullValue() or defaultValue() settings. Their behavior is
governed by a separate configuration: strictSections().
By default, a section that is not resolvable or which resolves to null will be omitted (and
conversely, an inverse section that is not resolvable or resolves to null will be included). If
you use strictSections(true), sections that refer to an unresolvable value will always throw an
exception. Sections that refer to a resolvable but null value never throw an exception,
regardless of the strictSections() setting.
Extensions
JMustache extends the basic Mustache template language with some additional functionality. These additional features are enumerated below:
