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

io.honeybadger.com.github.mustachejava.DeferringMustacheFactory Maven / Gradle / Ivy

There is a newer version: 2.1.2
Show newest version
package com.github.mustachejava;

import com.github.mustachejava.codes.PartialCode;
import com.github.mustachejava.util.GuardException;
import com.github.mustachejava.util.InternalArrayList;
import com.github.mustachejava.util.Wrapper;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;

/**
 * This allows you to automatically defer evaluation of partials. By default
 * it generates HTML but you can override that behavior.
 */
public class DeferringMustacheFactory extends DefaultMustacheFactory {

  public static final Object DEFERRED = new Object();

  public DeferringMustacheFactory() {
  }

  public DeferringMustacheFactory(String resourceRoot) {
    super(resourceRoot);
  }

  public DeferringMustacheFactory(File fileRoot) {
    super(fileRoot);
  }

  private static class Deferral {
    final long id;
    final Future future;

    Deferral(long id, Future future) {
      this.id = id;
      this.future = future;
    }
  }

  public static class DeferredCallable implements Callable {

    private List deferrals = new ArrayList<>();

    public void add(Deferral deferral) {
      deferrals.add(deferral);
    }

    @Override
    public String call() throws Exception {
      StringBuilder sb = new StringBuilder();
      for (Deferral deferral : deferrals) {
        Object o = deferral.future.get();
        if (o != null) {
          writeDeferral(sb, deferral, o);
        }
      }
      return sb.toString();
    }
  }

  @Override
  public MustacheVisitor createMustacheVisitor() {
    final AtomicLong id = new AtomicLong(0);
    return new DefaultMustacheVisitor(this) {
      @Override
      public void partial(TemplateContext tc, final String variable, final String indent) {
        TemplateContext partialTC = new TemplateContext("{{", "}}", tc.file(), tc.line(), tc.startOfLine());
        final Long divid = id.incrementAndGet();
        list.add(new PartialCode(partialTC, df, variable) {
          Wrapper deferredWrapper;

          @Override
          public Writer execute(Writer writer, final List scopes) {

            final Object object = get(scopes);
            final DeferredCallable deferredCallable = getDeferred(scopes);
            if (object == DEFERRED && deferredCallable != null) {
              try {
                writeTarget(writer, divid);
                writer.append(appended);
              } catch (IOException e) {
                throw new MustacheException("Failed to write", e, tc);
              }
              // Make a copy of the scopes so we don't change them
              List scopesCopy = new InternalArrayList<>(scopes);
              deferredCallable.add(
                      new Deferral(divid, getExecutorService().submit(() -> {
                        try {
                          StringWriter sw = new StringWriter();
                          partial.execute(sw, scopesCopy).close();
                          return sw.toString();
                        } catch (IOException e) {
                          throw new MustacheException("Failed to writer", e, tc);
                        }
                      })));
              return writer;
            } else {
              return appendText(partial.execute(writer, scopes));
            }
          }

          private DeferredCallable getDeferred(List scopes) {
            try {
              if (deferredWrapper == null) {
                deferredWrapper = getObjectHandler().find("deferred", scopes);
              }
              return (DeferredCallable) deferredWrapper.call(scopes);
            } catch (GuardException e) {
              deferredWrapper = null;
              return getDeferred(scopes);
            }
          }
        });
      }
    };
  }

  protected void writeTarget(Writer writer, Long divid) throws IOException {
    writer.append("
"); } protected static void writeDeferral(StringBuilder sb, Deferral deferral, Object o) { sb.append(""); } }