com.github.mustachejava.DefaultMustacheFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of compiler Show documentation
Show all versions of compiler Show documentation
Implementation of mustache.js for Java
package com.github.mustachejava;
import com.github.mustachejava.reflect.ReflectionObjectHandler;
import com.google.common.base.Charsets;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.io.Files;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.UncheckedExecutionException;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import static com.github.mustachejava.util.HtmlEscaper.escape;
/**
* Simplest possible code factory
*/
public class DefaultMustacheFactory implements MustacheFactory {
/**
* Create the default cache for mustache compilations. This is basically
* required by the specification to handle recursive templates.
*/
protected final LoadingCache mustacheCache = createMustacheCache();
/**
* This is the default object handler.
*/
protected ObjectHandler oh = new ReflectionObjectHandler();
/**
* This parser should work with any MustacheFactory
*/
protected final MustacheParser mc = new MustacheParser(this);
/**
* New templates that are generated at runtime are cached here. The template key
* includes the text of the template and the context so we get proper error
* messages and debugging information.
*/
protected final LoadingCache templateCache = createLambdaCache();
protected int recursionLimit = 100;
private final String resourceRoot;
private final File fileRoot;
protected ListeningExecutorService les;
public DefaultMustacheFactory() {
this.resourceRoot = null;
this.fileRoot = null;
}
/**
* Use the classpath to resolve mustache templates.
*
* @param resourceRoot
*/
public DefaultMustacheFactory(String resourceRoot) {
if (!resourceRoot.endsWith("/")) resourceRoot += "/";
this.resourceRoot = resourceRoot;
this.fileRoot = null;
}
/**
* Use the file system to resolve mustache templates.
*
* @param fileRoot
*/
public DefaultMustacheFactory(File fileRoot) {
if (!fileRoot.exists()) {
throw new MustacheException(fileRoot + " does not exist");
}
if (!fileRoot.isDirectory()) {
throw new MustacheException(fileRoot + " is not a directory");
}
this.fileRoot = fileRoot;
this.resourceRoot = null;
}
public String resolvePartialPath(String dir, String name, String extension) {
String path;
if (name.startsWith("/")) {
path = Files.simplifyPath(new File(name + extension).getPath());
} else {
path = Files.simplifyPath(new File(dir + name + extension).getPath());
}
return ensureForwardSlash(path);
}
private static String ensureForwardSlash(String path) {
return path.replace('\\', '/');
}
@Override
public MustacheVisitor createMustacheVisitor() {
return new DefaultMustacheVisitor(this);
}
@Override
public Reader getReader(String resourceName) {
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
String name = (resourceRoot == null ? "" : resourceRoot) + resourceName;
InputStream is = ccl.getResourceAsStream(name);
if (is == null) {
is = DefaultMustacheFactory.class.getClassLoader().getResourceAsStream(name);
}
if (is == null) {
File file = fileRoot == null ? new File(resourceName) : new File(fileRoot, resourceName);
if (file.exists() && file.isFile()) {
try {
// Check to make sure that the file is under the file root or current directory.
// Without this check you might accidentally open a security whole when exposing
// mustache templates to end users.
File checkRoot = fileRoot == null ? new File("").getCanonicalFile() : fileRoot.getCanonicalFile();
File parent = file.getCanonicalFile();
while ((parent = parent.getParentFile()) != null) {
if (parent.equals(checkRoot)) break;
}
if (parent == null) {
throw new MustacheException("File not under root: " + checkRoot.getAbsolutePath());
}
is = new FileInputStream(file);
} catch (IOException e) {
throw new MustacheException("Found file, could not open: " + file, e);
}
}
}
if (is == null) {
throw new MustacheException("Template '" + resourceName + "' not found");
} else {
return new BufferedReader(new InputStreamReader(is, Charsets.UTF_8));
}
}
@Override
public void encode(String value, Writer writer) {
escape(value, writer, true);
}
@Override
public ObjectHandler getObjectHandler() {
return oh;
}
/**
* You can override the default object handler post construction.
*
* @param oh
*/
public void setObjectHandler(ObjectHandler oh) {
this.oh = oh;
}
/**
* There is an ExecutorService that is used when executing parallel
* operations when a Callable is returned from a mustache value or iterable.
*
* @return
*/
public ExecutorService getExecutorService() {
return les;
}
/**
* If you need to specify your own executor service you can.
*
* @param es
*/
public void setExecutorService(ExecutorService es) {
if (es instanceof ListeningExecutorService) {
les = (ListeningExecutorService) es;
} else {
les = MoreExecutors.listeningDecorator(es);
}
}
public Mustache getFragment(FragmentKey templateKey) {
try {
Mustache mustache = templateCache.get(templateKey);
mustache.init();
return mustache;
} catch (ExecutionException e) {
throw handle(e);
}
}
private MustacheException handle(Exception e) {
Throwable cause = e.getCause();
if (cause instanceof MustacheException) {
return (MustacheException) cause;
}
return new MustacheException(cause);
}
@Override
public Mustache compile(String name) {
try {
Mustache mustache = mustacheCache.get(name);
mustache.init();
return mustache;
} catch (UncheckedExecutionException e) {
throw handle(e);
} catch (ExecutionException e) {
throw handle(e);
}
}
@Override
public Mustache compile(Reader reader, String name) {
return compile(reader, name, "{{", "}}");
}
// Template functions need this to comply with the specification
public Mustache compile(Reader reader, String file, String sm, String em) {
Mustache compile = mc.compile(reader, file, sm, em);
compile.init();
return compile;
}
@Override
public String translate(String from) {
return from;
}
/**
* Override this method to apply any filtering to text that will appear
* verbatim in the output template.
*
*
* @param appended
* @param b
* @return
*/
public String filterText(String appended, boolean b) {
return appended;
}
/**
* Maximum recursion limit for partials.
*/
public void setRecursionLimit(int recursionLimit) {
this.recursionLimit = recursionLimit;
}
public int getRecursionLimit() {
return recursionLimit;
}
private final ThreadLocal
© 2015 - 2025 Weber Informatics LLC | Privacy Policy