io.honeybadger.com.github.mustachejava.DefaultMustacheFactory Maven / Gradle / Ivy
package com.github.mustachejava;
import com.github.mustachejava.codes.DefaultMustache;
import com.github.mustachejava.reflect.ReflectionObjectHandler;
import com.github.mustachejava.resolver.DefaultResolver;
import java.io.File;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.function.Function;
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 ConcurrentHashMap 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 ConcurrentHashMap templateCache = createLambdaCache();
protected int recursionLimit = 100;
private final MustacheResolver mustacheResolver;
protected ExecutorService es;
public DefaultMustacheFactory() {
this.mustacheResolver = new DefaultResolver();
}
public DefaultMustacheFactory(MustacheResolver mustacheResolver) {
this.mustacheResolver = mustacheResolver;
}
/**
* Use the classpath to resolve mustache templates.
*
* @param resourceRoot the location in the resources where templates are stored
*/
public DefaultMustacheFactory(String resourceRoot) {
this.mustacheResolver = new DefaultResolver(resourceRoot);
}
/**
* Use the file system to resolve mustache templates.
*
* @param fileRoot the root of the file system where templates are stored
*/
public DefaultMustacheFactory(File fileRoot) {
this.mustacheResolver = new DefaultResolver(fileRoot);
}
/**
* Using the directory, namd and extension, resolve a partial to a name.
*
* @param dir
* @param name
* @param extension
* @return
*/
public String resolvePartialPath(String dir, String name, String extension) {
String filePath = name;
// Do not prepend directory if it is already defined
if (!name.startsWith("/")) {
filePath = dir + filePath;
}
// Do not append extension if it is already defined
if (!name.endsWith(extension)) {
filePath = filePath + extension;
}
String path = new File(filePath).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) {
Reader reader = mustacheResolver.getReader(resourceName);
if (reader == null) {
throw new MustacheNotFoundException(resourceName);
}
return reader;
}
@Override
public void encode(String value, Writer writer) {
escape(value, writer);
}
@Override
public ObjectHandler getObjectHandler() {
return oh;
}
/**
* You can override the default object handler post construction.
*
* @param oh The object handler to use.
*/
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 the executor service
*/
public ExecutorService getExecutorService() {
return es;
}
/**
* If you need to specify your own executor service you can.
*
* @param es The executor service to use for Future evaluation
*/
public void setExecutorService(ExecutorService es) {
this.es = es;
}
public Mustache getFragment(FragmentKey templateKey) {
Mustache mustache = templateCache.computeIfAbsent(templateKey, getFragmentCacheFunction());
mustache.init();
return mustache;
}
protected Function getFragmentCacheFunction() {
return (fragmentKey) -> {
StringReader reader = new StringReader(fragmentKey.templateText);
TemplateContext tc = fragmentKey.tc;
return mc.compile(reader, tc.file(), tc.startChars(), tc.endChars(), tc.startOfLine());
};
}
@Override
public Mustache compile(String name) {
Mustache mustache = mustacheCache.computeIfAbsent(name, getMustacheCacheFunction());
mustache.init();
return mustache;
}
@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();
partialCache.remove();
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 The text to be appended to the output
* @param startOfLine Are we at the start of the line?
* @return the filtered string
*/
public String filterText(String appended, boolean startOfLine) {
return appended;
}
/**
* Maximum recursion limit for partials.
*
* @param recursionLimit the number of recursions we will attempt before failing
*/
public void setRecursionLimit(int recursionLimit) {
this.recursionLimit = recursionLimit;
}
public int getRecursionLimit() {
return recursionLimit;
}
private final ThreadLocal