- Notifications
You must be signed in to change notification settings - Fork130
A Java implementation of the Mustache templating language.
License
samskivert/jmustache
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
This is a Java implementation of theMustache template language.
Zero dependencies. You can include this single tiny library in your project and start makinguse of templates.
Usability on a variety of target platforms. This implementation makes very limited demands onthe JVM in which it runs and as a result is usable on Android, or on other limited JVMs. It iseven possible to avoid the use of reflection and provide all of your data as a series of nestedmaps.
Proguard andJarJarfriendly. Though the library will reflectively access your data (if you desire it), the librarymakes no other internal use of reflection or by name instantiation of classes. So you can embedit 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 provideadditional benefits:
- It is available via Maven Central, see below for details.
- It is reasonably performant. Templates are parsed separately from execution. A template willspecialize its variables on (class of context, name) pairs so that if a variable is firstresolved to be (for example) a field of the context object, that will be attempted directly onsubsequent template invocations, and the slower full resolution will only be tried if accessingthe variable as a field fails.
JMustache is available via Maven Central and can thus be easily added to your Maven, Ivy, etc.projects by adding a dependency oncom.samskivert:jmustache:1.15. Or download the pre-builtjar file.
In addition to the usage section below, the following documentation may be useful:
Using JMustache is very simple. Supply your template as aString or aReader and get back aTemplate that you can execute on any context:
Stringtext ="One, two, {{three}}. Three sir!";Templatetmpl =Mustache.compiler().compile(text);Map<String,String>data =newHashMap<String,String>();data.put("three","five");System.out.println(tmpl.execute(data));// result: "One, two, five. Three sir!"
UseReader andWriter if you're doing something more serious:
voidexecuteTemplate (Readertemplate,Writerout,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 followingmechanisms:
- 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:
classPerson {publicfinalStringname;publicPerson (Stringname,intage) {this.name =name;_age =age; }publicintgetAge () {return_age; }protectedint_age;}Stringtmpl ="{{#persons}}{{name}}: {{age}}\n{{/persons}}";Mustache.compiler().compile(tmpl).execute(newObject() {Objectpersons =Arrays.asList(newPerson("Elvis",75),newPerson("Madonna",52));});// result:// Elvis: 75// Madonna: 52
As you can see from the example, the fields (and methods) need not be public. Thepersons fieldin the anonymous class created to act as a context is accessible. Note that the use of non-publicfields 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 asthe context for each iteration. Empty collections result in zero instances of the section beingincluded in the template. - An unresolvable or null value is treated as false. This behavior can be changed by using
strictSections(). SeeDefault Values for more details. - Any other object results in a single execution of the section with that object as a context.
See the code inMustacheTest.javafor concrete examples. See also theMustache documentation for details on the templatesyntax.
If you wish to make use of partials (e.g.{{>subtmpl}}) you must provide aMustache.TemplateLoader to the compiler when creating it. For example:
finalFiletemplateDir = ...;Mustache.Compilerc =Mustache.compiler().withLoader(newMustache.TemplateLoader() {publicReadergetTemplate (Stringname) {returnnewFileReader(newFile(templateDir,name)); }});Stringtmpl ="...{{>subtmpl}}...";c.compile(tmpl).execute();
The above snippet will loadnew File(templateDir, "subtmpl") when compiling the template.
JMustache implements lambdas by passing you aTemplate.Fragment instance which you can use toexecute the fragment of the template that was passed to the lambda. You can decorate the results ofthe fragment execution, as shown in the standard Mustache documentation on lambdas:
Stringtmpl ="{{#bold}}{{name}} is awesome.{{/bold}}";Mustache.compiler().compile(tmpl).execute(newObject() {Stringname ="Willy";Mustache.Lambdabold =newMustache.Lambda() {publicvoidexecute (Template.Fragmentfrag,Writerout)throwsIOException {out.write("<b>");frag.execute(out);out.write("</b>"); } };});// result:<b>Willyisawesome.</b>
You can also obtain the results of the fragment execution to do things like internationalization orcaching:
Objectctx =newObject() {Mustache.Lambdai18n =newMustache.Lambda() {publicvoidexecute (Template.Fragmentfrag,Writerout)throwsIOException {Stringkey =frag.execute();Stringtext =// look up key in i18n systemout.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 originalMustache template text contained in the section. See the documentation forTemplate.Fragment fordetails on the limitations.
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 avalue for use in all such circumstances, usedefaultValue():
Stringtmpl ="{{exists}} {{nullValued}} {{doesNotExist}}?";Mustache.compiler().defaultValue("what").compile(tmpl).execute(newObject() {Stringexists ="Say";StringnullValued =null;// String doesNotExist});// result:Saywhatwhat?
If you only wish to provide a default value for variables that resolve to null, and wish topreserve exceptions in cases where variables cannot be resolved, usenullValue():
Stringtmpl ="{{exists}} {{nullValued}} {{doesNotExist}}?";Mustache.compiler().nullValue("what").compile(tmpl).execute(newObject() {Stringexists ="Say";StringnullValued =null;// String doesNotExist});// throws MustacheException when executing the template because doesNotExist cannot be resolved
When using aMap as a context,nullValue() will only be used when the map contains a mapping tonull. If the map lacks a mapping for a given variable, then it is considered unresolvable andthrows an exception.
Map<String,String>map =newHashMap<String,String>();map.put("exists","Say");map.put("nullValued",null);// no mapping exists for "doesNotExist"Stringtmpl ="{{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 bothdefaultValue andnullValue in your compiler configuration. Each oneoverrides the other, so whichever one you call last is the behavior you will get. But even if youaccidentally do the right thing, you have confusing code, so don't call both, use one or the other.
Sections are not affected by thenullValue() ordefaultValue() settings. Their behavior isgoverned by a separate configuration:strictSections().
By default, a section that is not resolvable or which resolves tonull will be omitted (andconversely, an inverse section that is not resolvable or resolves tonull will be included). Ifyou usestrictSections(true), sections that refer to an unresolvable value will always throw anexception. Sections that refer to a resolvable butnull value never throw an exception,regardless of thestrictSections() setting.
JMustache extends the basic Mustache template language with some additional functionality. Theseadditional features are enumerated below:
You can change the default HTML escaping behavior when obtaining a compiler:
Mustache.compiler().escapeHTML(false).compile("{{foo}}").execute(newObject() {Stringfoo ="<bar>";});// result: <bar>// not: <bar>
By default, JMustache usesString.valueOf to convert objects to strings when rendering atemplate. You can customize this formatting by implementing theMustache.Formatter interface:
Mustache.compiler().withFormatter(newMustache.Formatter() {publicStringformat (Objectvalue) {if (valueinstanceofDate)return_fmt.format((Date)value);elsereturnString.valueOf(value); }protectedDateFormat_fmt =newSimpleDateFormat("yyyy/MM/dd");}).compile("{{msg}}: {{today}}").execute(newObject() {Stringmsg ="Date";Datetoday =newDate();})// result: Date: 2013/01/08
You can change the escaping behavior when obtaining a compiler, to support file formats other thanHTML and plain text.
If you only need to replace fixed strings in the text, you can useEscapers.simple:
String[][]escapes = {{"[","[[" }, {"]","]]" }};Mustache.compiler().withEscaper(Escapers.simple(escapes)).compile("{{foo}}").execute(newObject() {Stringfoo ="[bar]"; });// result: [[bar]]
Or you can implement theMustache.Escaper interface directly for more control over the escapingprocess.
You can use the special variablethis to refer to the context object itself instead of one of itsmembers. This is particularly useful when iterating over lists.
Mustache.compiler().compile("{{this}}").execute("hello");// returns: helloMustache.compiler().compile("{{#names}}{{this}}{{/names}}").execute(newObject() {List<String>names () {returnArrays.asList("Tom","Dick","Harry"); }});// result: TomDickHarry
Note that you can also use the special variable. to mean the same thing.
Mustache.compiler().compile("{{.}}").execute("hello");// returns: helloMustache.compiler().compile("{{#names}}{{.}}{{/names}}").execute(newObject() {List<String>names () {returnArrays.asList("Tom","Dick","Harry"); }});// result: TomDickHarry
. is apparently supported by other Mustache implementations, though it does not appear in theofficial documentation.
You can use the special variables-first and-last to perform special processing for listelements.-first resolves totrue when inside a section that is processing the first of a listof elements. It resolves tofalse at all other times.-last resolves totrue when inside asection that is processing the last of a list of elements. It resolves tofalse at all othertimes.
One will often make use of these special variables in an inverted section, as follows:
Stringtmpl ="{{#things}}{{^-first}}, {{/-first}}{{this}}{{/things}}";Mustache.compiler().compile(tmpl).execute(newObject() {List<String>things =Arrays.asList("one","two","three");});// result: one, two, three
Note that the values of-first and-last refer only to the inner-most enclosing section. If youare processing a section within a section, there is no way to find out whether you are in the firstor last iteration of an outer section.
The-index special variable contains 1 for the first iteration through a section, 2 for thesecond, 3 for the third and so forth. It contains 0 at all other times. Note that it also contains0 for a section that is populated by a singleton value rather than a list.
Stringtmpl ="My favorite things:\n{{#things}}{{-index}}. {{this}}\n{{/things}}";Mustache.compiler().compile(tmpl).execute(newObject() {List<String>things =Arrays.asList("Peanut butter","Pen spinning","Handstands");});// result:// My favorite things:// 1. Peanut butter// 2. Pen spinning// 3. Handstands
In addition to resolving simple variables using the context, you can use compound variables toextract data from sub-objects of the current context. For example:
Mustache.compiler().compile("Hello {{field.who}}!").execute(newObject() {publicObjectfield =newObject() {publicStringwho () {return"world"; } }});// result: Hello world!
By taking advantage of reflection and bean-property-style lookups, you can do kooky things:
Mustache.compiler().compile("Hello {{class.name}}!").execute(newObject());// result: Hello java.lang.Object!
Note that compound variables are essentially short-hand for using singleton sections. The aboveexamples could also be represented as:
Hello {{#field}}{{who}}{{/field}}!Hello {{#class}}{{name}}{{/class}}!Note also that one semantic difference exists between nested singleton sections and compoundvariables: after resolving the object for the first component of the compound variable, parentcontexts will not be searched when resolving subcomponents.
If the opening or closing section tag are the only thing on a line, any surrounding whitespace andthe line terminator following the tag are trimmed. This allows for civilized templates, like:
Favorite foods:<ul> {{#people}}<li>{{first_name}} {{last_name}} likes {{favorite_food}}.</li> {{/people}}</ul>
which produces output like:
Favorite foods:<ul><li>Elvis Presley likes peanut butter.</li><li>Mahatma Gandhi likes aloo dum.</li></ul>
rather than:
Favorite foods:<ul><li>Elvis Presley likes peanut butter.</li><li>Mahatma Gandhi likes aloo dum.</li></ul>
which would be produced without the newline trimming.
If a variable is not found in a nested context, it is resolved in the next outer context. Thisallows usage like the following:
Stringtemplate ="{{outer}}:\n{{#inner}}{{outer}}.{{this}}\n{{/inner}}";Mustache.compiler().compile(template).execute(newObject() {Stringouter ="foo";List<String>inner =Arrays.asList("bar","baz","bif");});// results:// foo:// foo.bar// foo.baz// foo.bif
Note that if a variableis defined in an inner context, it shadows the same name in the outercontext. There is presently no way to access the variable from the outer context.
For some applications, it may be useful for lambdas to be executed for an inverse section ratherthan having the section omitted altogether. This allows for proper conditional substitution whenstatically translating templates into other languages or contexts:
Stringtemplate ="{{#condition}}result if true{{/condition}}\n" +"{{^condition}}result if false{{/condition}}";Mustache.compiler().compile(template).execute(newObject() {Mustache.InvertibleLambdacondition =newMustache.InvertibleLambda() {publicvoidexecute (Template.Fragmentfrag,Writerout)throwsIOException {// this method is executed when the lambda is referenced in a normal sectionout.write("if (condition) {console.log(\"");out.write(toJavaScriptLiteral(frag.execute()));out.write("\")}"); }publicvoidexecuteInverse (Template.Fragmentfrag,Writerout)throwsIOException {// this method is executed when the lambda is referenced in an inverse sectionout.write("if (!condition) {console.log(\"");out.write(toJavaScriptLiteral(frag.execute()));out.write("\")}"); }privateStringtoJavaScriptLiteral (Stringexecute) {// note: this is NOT a complete implementation of JavaScript string literal escapingreturnexecute.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\""); } };});// results:// if (condition) {console.log("result if true")}// if (!condition) {console.log("result if false")}
Of course, you are not limited strictly to conditional substitution -- you can use anInvertibleLambda whenever you need a single function with two modes of operation.
The more intrusive of these extensions, specifically the searching of parent contexts and the useof compound variables, can be disabled when creating a compiler, like so:
Map<String,String>ctx =newHashMap<String,String>();ctx.put("foo.bar","baz");Mustache.compiler().standardsMode(true).compile("{{foo.bar}}").execute(ctx);// result: baz
JMustache is internally thread safe with the following caveats:
Compilation: compiling templates calls out to a variety of helper classes:
Mustache.Formatter,Mustache.Escaper,Mustache.TemplateLoader,Mustache.Collector. Thedefault implementations of these classes are thread-safe, but if you supply custom instances,then you have to ensure that your custom instances are thread-safe.Execution: executing templates can call out to some helper classes:
Mustache.Lambda,Mustache.VariableFetcher. The default implementations of these classes are thread-safe, butif you supply custom instances, then you have to ensure that your custom instances arethread-safe.Context data: if you mutate the context data passed to template execution while the template isbeing executed, then you subject yourself to race conditions. It is in theory possible to use athread-safe map (
ConcurrentHashMaporCollections.synchronizedMap) for your context data,which would allow you to mutate the data while templates were being rendered based on thatdata, but you're playing with fire by doing that. I don't recommend it. If your data issupplied as POJOs where fields or methods are called via reflection to populate your templates,volatile fields and synchronized methods could similarly be used to support simultaneousreading and mutating, but again you could easily make a mistake that introduces race conditionsor cause weirdness when executing your templates. The safest approach when rendering the sametemplate via simultaneous threads is to pass immutable/unchanging data as the context for eachexecution.VariableFetchercache: template execution uses one internal cache to store resolvedVariableFetcherinstances (because resolving a variable fetcher is expensive). This cache isthread-safe by virtue of using aConcurrentHashMap. It's possible for a bit of extra work tobe done if two threads resolve the same variable at the same time, but they won't conflict withone another, they'll simply both resolve the variable instead of one resolving the variable andthe other using the cached resolution.
So the executive summary is: as long as all helper classes you supply are thread-safe (or you usethe defaults), it is safe to share aMustache.Compiler instance across threads to compiletemplates. If you pass immutable data to your templates when executing, it is safe to have multiplethreads simultaneously execute a singleTemplate instance.
In the name of simplicity, some features of Mustache were omitted or simplified:
{{= =}}only supports one or two character delimiters. This is just because I'm lazy and itsimplifies the parser.
About
A Java implementation of the Mustache templating language.
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.