Package com.floreysoft.jmte.template

Source Code of com.floreysoft.jmte.template.InterpretedTemplate

package com.floreysoft.jmte.template;

import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import com.floreysoft.jmte.DefaultModelAdaptor;
import com.floreysoft.jmte.Engine;
import com.floreysoft.jmte.ModelAdaptor;
import com.floreysoft.jmte.ProcessListener;
import com.floreysoft.jmte.ProcessListener.Action;
import com.floreysoft.jmte.ScopedMap;
import com.floreysoft.jmte.TemplateContext;
import com.floreysoft.jmte.token.ElseToken;
import com.floreysoft.jmte.token.EndToken;
import com.floreysoft.jmte.token.ExpressionToken;
import com.floreysoft.jmte.token.ForEachToken;
import com.floreysoft.jmte.token.IfToken;
import com.floreysoft.jmte.token.InvalidToken;
import com.floreysoft.jmte.token.PlainTextToken;
import com.floreysoft.jmte.token.StringToken;
import com.floreysoft.jmte.token.Token;
import com.floreysoft.jmte.token.TokenStream;

public class InterpretedTemplate extends AbstractTemplate {

  protected final TokenStream tokenStream;
  protected transient StringBuilder output;
  protected transient TemplateContext context;

  public InterpretedTemplate(String template, String sourceName, Engine engine) {
    this.template = template;
    this.engine = engine;
    this.sourceName = sourceName;
    tokenStream = new TokenStream(sourceName, template, engine
        .getExprStartToken(), engine.getExprEndToken());
    tokenStream.prefill();
  }

  @Override
  @SuppressWarnings("unchecked")
  public synchronized Set<String> getUsedVariables() {
    if (this.usedVariables != null) {
      return this.usedVariables;
    }

    this.usedVariables = new TreeSet<String>();
    final Engine engine = new Engine();
    final ScopedMap scopedMap = new ScopedMap(Collections.EMPTY_MAP);

    ProcessListener processListener = new ProcessListener() {

      @Override
      public void log(TemplateContext context, Token token, Action action) {
        if (token instanceof ExpressionToken) {
          String variable = ((ExpressionToken) token).getExpression();
          if (!isLocal(variable)) {
            usedVariables.add(variable);
          }
        }
      }

      // a variable is local if any enclosing foreach has it as a step
      // variable
      private boolean isLocal(String variable) {
        for (Token token : context.scopes) {
          if (token instanceof ForEachToken) {
            String foreachVarName = ((ForEachToken) token)
                .getVarName();
            if (foreachVarName.equals(variable)) {
              return true;
            }
          }
        }
        return false;

      }

    };
    final Locale locale = Locale.getDefault();
    context = new TemplateContext(template, locale, sourceName, scopedMap,
        new DefaultModelAdaptor(), engine, engine.getErrorHandler(), processListener);

    transformPure(context);
    return usedVariables;
  }

  @Override
  public synchronized String transform(Map<String, Object> model, Locale locale,
      ModelAdaptor modelAdaptor, ProcessListener processListener) {
    try {
      context = new TemplateContext(template, locale, sourceName, new ScopedMap(
          model), modelAdaptor, engine, engine.getErrorHandler(), processListener);
      String transformed = transformPure(context);
      return transformed;
    } finally {
      context = null;
      output = null;
    }
  }

  protected String transformPure(TemplateContext context) {
    tokenStream.reset();
    output = new StringBuilder(
        (int) (context.template.length() * context.engine
            .getExpansionSizeFactor()));
    tokenStream.nextToken();
    while (tokenStream.currentToken() != null) {
      content(false);
    }
    return output.toString();
  }

  @SuppressWarnings({ "unchecked", "rawtypes" })
  private void foreach(boolean inheritedSkip) {
    ForEachToken feToken = (ForEachToken) tokenStream.currentToken();
    Iterable iterable = (Iterable) feToken.evaluate(context);
    // begin a fresh iteration with a reset index
    feToken.setIterator(iterable.iterator());
    feToken.resetIndex();
    tokenStream.consume();

    context.model.enterScope();
    context.push(feToken);
    try {

      // in case we do not want to evaluate the body, we just do a quick
      // scan until the matching end
      if (inheritedSkip || !feToken.iterator().hasNext()) {
        Token contentToken;
        while ((contentToken = tokenStream.currentToken()) != null
            && !(contentToken instanceof EndToken)) {
          content(true);
        }
        if (contentToken == null) {
          engine.getErrorHandler().error("missing-end", feToken);
        } else {
          tokenStream.consume();
          context.notifyProcessListener(contentToken, Action.END);
        }
      } else {

        while (feToken.iterator().hasNext()) {

          context.model.put(feToken.getVarName(), feToken.advance());
          addSpecialVariables(feToken, context.model);

          // for each iteration we need to rewind to the beginning
          // of the for loop
          tokenStream.rewind(feToken);
          Token contentToken;
          while ((contentToken = tokenStream.currentToken()) != null
              && !(contentToken instanceof EndToken)) {
            content(false);
          }
          if (contentToken == null) {
            engine.getErrorHandler().error("missing-end", feToken);
          } else {
            tokenStream.consume();
            context.notifyProcessListener(contentToken, Action.END);
          }
          if (!feToken.isLast()) {
            output.append(feToken.getSeparator());
          }
        }
      }

    } finally {
      context.model.exitScope();
      context.pop();
    }
  }

  private void condition(boolean inheritedSkip) {
    IfToken ifToken = (IfToken) tokenStream.currentToken();
    tokenStream.consume();

    context.push(ifToken);
    try {
      boolean localSkip;
      if (inheritedSkip) {
        localSkip = true;
      } else {
        localSkip = !(Boolean) ifToken.evaluate(context);
      }

      Token contentToken;
      while ((contentToken = tokenStream.currentToken()) != null
          && !(contentToken instanceof EndToken)
          && !(contentToken instanceof ElseToken)) {
        content(localSkip);
      }

      if (contentToken instanceof ElseToken) {
        tokenStream.consume();
        // toggle for else branch
        if (!inheritedSkip) {
          localSkip = !localSkip;
        }
        context.notifyProcessListener(contentToken,
            inheritedSkip ? Action.SKIP : Action.EVAL);

        while ((contentToken = tokenStream.currentToken()) != null
            && !(contentToken instanceof EndToken)) {
          content(localSkip);
        }

      }

      if (contentToken == null) {
        engine.getErrorHandler().error("missing-end", ifToken);
      } else {
        tokenStream.consume();
        context.notifyProcessListener(contentToken, Action.END);
      }
    } finally {
      context.pop();
    }
  }

  private void content(boolean skip) {
    Token token = tokenStream.currentToken();
    context.notifyProcessListener(token, skip ? Action.SKIP : Action.EVAL);
    if (token instanceof PlainTextToken) {
      tokenStream.consume();
      if (!skip) {
        output.append(token.getText());
      }
    } else if (token instanceof StringToken) {
      tokenStream.consume();
      if (!skip) {
        String expanded = (String) token.evaluate(context);
        output.append(expanded);
      }
    } else if (token instanceof ForEachToken) {
      foreach(skip);
    } else if (token instanceof IfToken) {
      condition(skip);
    } else if (token instanceof ElseToken) {
      tokenStream.consume();
      engine.getErrorHandler().error("else-out-of-scope", token);
    } else if (token instanceof EndToken) {
      tokenStream.consume();
      engine.getErrorHandler().error("unmatched-end", token);
    } else if (token instanceof InvalidToken) {
      tokenStream.consume();
      engine.getErrorHandler().error("invalid-expression", token);
    } else {
      tokenStream.consume();
      // what ever else there may be, we just evaluate it
      String evaluated = (String) token.evaluate(context);
      output.append(evaluated);
    }

  }

  @Override
  public String toString() {
    return template;
  }
}
TOP

Related Classes of com.floreysoft.jmte.template.InterpretedTemplate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.