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 java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.regex.Pattern;

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 com.github.mustachejava.reflect.ReflectionObjectHandler;

/**
 * Simplest possible code factory
 */
public class DefaultMustacheFactory implements MustacheFactory {

  private final MustacheParser mc = new MustacheParser(this);
  private final Map templateCache = new ConcurrentHashMap();
  private final LoadingCache mustacheCache = CacheBuilder.newBuilder().build(
          new CacheLoader() {
            @Override
            public Mustache load(String key) throws Exception {
              return mc.compile(key);
            }
          });
  private ObjectHandler oh = new ReflectionObjectHandler();

  private String resourceRoot;
  private File fileRoot;

  private ListeningExecutorService les;

  public DefaultMustacheFactory() {
  }

  public DefaultMustacheFactory(String resourceRoot) {
    if (!resourceRoot.endsWith("/")) resourceRoot += "/";
    this.resourceRoot = resourceRoot;
  }

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

  @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 {
          return new BufferedReader(new FileReader(file));
        } catch (FileNotFoundException e) {
          throw new MustacheException("Found file, could not open: " + file, e);
        }
      }
      throw new MustacheException("Template " + resourceName + " not found");
    } else {
      return new BufferedReader(new InputStreamReader(is, Charset.forName("utf-8")));
    }
  }

  private static Pattern escapedPattern = Pattern.compile("^&\\w+;");

  // Override this in a super class if you don't want encoding or would like
  // to change the way encoding works. Also, if you use unexecute, make sure
  // also do the inverse in decode.
  @Override
  public void encode(String value, Writer writer) {
    try {
      int position = 0;
      int length = value.length();
      for (int i = 0; i < length; i++) {
        char c = value.charAt(i);
        switch (c) {
          case '&':
            if (!escapedPattern.matcher(value.substring(i, length)).find()) {
              position = append(value, writer, position, i, "&");
            } else {
              if (position != 0) {
                position = append(value, writer, position, i, "&");
              }
            }
            break;
          case '\\':
            position = append(value, writer, position, i, "\\\\");
            break;
          case '"':
            position = append(value, writer, position, i, """);
            break;
          case '<':
            position = append(value, writer, position, i, "<");
            break;
          case '>':
            position = append(value, writer, position, i, ">");
            break;
          case '\n':
            position = append(value, writer, position, i, "
");
            break;
        }
      }
      writer.append(value, position, length);
    } catch (IOException e) {
      throw new MustacheException("Failed to encode value: " + value);
    }
  }

  private int append(String value, Writer writer, int position, int i, String replace) throws IOException {
    writer.append(value, position, i);
    writer.append(replace);
    return i + 1;
  }

  @Override
  public ObjectHandler getObjectHandler() {
    return oh;
  }

  public void setObjectHandler(ObjectHandler oh) {
    this.oh = oh;
  }

  public ExecutorService getExecutorService() {
    return les;
  }

  public void setExecutorService(ExecutorService es) {
    if (es instanceof ListeningExecutorService) {
      les = (ListeningExecutorService) es;
    } else {
      les = MoreExecutors.listeningDecorator(es);
    }
  }

  public Mustache getTemplate(String templateText) {
    return templateCache.get(templateText);
  }

  public void putTemplate(String templateText, Mustache mustache) {
    templateCache.put(templateText, mustache);
  }

  @Override
  public Mustache compile(String name) {
    try {
      Mustache mustache = mustacheCache.get(name);
      mustache.init();
      return mustache;
    } catch (ExecutionException e) {
      Throwable cause = e.getCause();
      if (cause instanceof MustacheException) {
        throw (MustacheException) cause;
      }
      throw new MustacheException(cause);
    }
  }

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy