Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A Java implementation of the Mustache templating language.

License

NotificationsYou must be signed in to change notification settings

samskivert/jmustache

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

This is a Java implementation of theMustache template language.

Build Status

Motivations

  • 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:compile andexecute. 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.

Get It

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.

Documentation

In addition to the usage section below, the following documentation may be useful:

Usage

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 aMustacheCustomContext,MustacheCustomContext.get will be used.
  • If the context is aMap,Map.get will 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 variablefoo)getFoo exists, 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:

  • Boolean values enable or disable the section.
  • Array,Iterator, orIterable values 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 usingstrictSections(). 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.

Partials

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.

Lambdas

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.

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

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.

Extensions

JMustache extends the basic Mustache template language with some additional functionality. Theseadditional features are enumerated below:

Not escaping HTML by default

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: &lt;bar&gt;

User-defined object formatting

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

User-defined escaping rules

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.

Special variables

this

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.

-first and -last

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.

-index

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

Compound variables

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.

Newline trimming

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.

Nested Contexts

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.

Invertible Lambdas

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.

Standards Mode

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

Thread Safety

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 (ConcurrentHashMap orCollections.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.

  • VariableFetcher cache: template execution uses one internal cache to store resolvedVariableFetcher instances (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.

Limitations

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

Stars

Watchers

Forks

Packages

No packages published

Contributors27

Languages


[8]ページ先頭

©2009-2025 Movatter.jp