All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.mustachejava.DefaultMustacheFactory Maven / Gradle / Ivy

There is a newer version: 0.9.14
Show newest version
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.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

import java.io.*;
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();

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

  @Override
  public MustacheVisitor createMustacheVisitor() {
    return new DefaultMustacheVisitor(this);
  }

  @Override
  public Reader getReader(String resourceName) {
    ClassLoader ccl = Thread.currentThread().getContextClassLoader();
    InputStream is = ccl.getResourceAsStream(
            (resourceRoot == null ? "" : resourceRoot) + resourceName);
    if (is == null) {
      File file = fileRoot == null ? new File(resourceName) : new File(fileRoot, resourceName);
      if (file.exists() && file.isFile()) {
        try {
          is = new FileInputStream(file);
        } catch (FileNotFoundException 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 {
      return templateCache.get(templateKey);
    } catch (ExecutionException e) {
      throw handle(e);
    }
  }

  private MustacheException handle(ExecutionException 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 (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;
  }

  protected class MustacheCacheLoader extends CacheLoader {
    @Override
    public Mustache load(String key) throws Exception {
      return mc.compile(key);
    }
  }

  protected class FragmentCacheLoader extends CacheLoader {
    @Override
    public Mustache load(FragmentKey fragmentKey) throws Exception {
      StringReader reader = new StringReader(fragmentKey.templateText);
      TemplateContext tc = fragmentKey.tc;
      return compile(reader, tc.file(), tc.startChars(), tc.endChars());
    }
  }

  protected LoadingCache createMustacheCache() {
    return CacheBuilder.newBuilder().build(new MustacheCacheLoader());
  }

  protected LoadingCache createLambdaCache() {
    return CacheBuilder.newBuilder().build(new FragmentCacheLoader());
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy