Package httl.spi.translators.templates

Source Code of httl.spi.translators.templates.InterpretedVisitor

/*
* Copyright 2011-2013 HTTL Team.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*      http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package httl.spi.translators.templates;

import httl.Context;
import httl.Node;
import httl.Template;
import httl.ast.AstVisitor;
import httl.ast.BinaryOperator;
import httl.ast.BreakDirective;
import httl.ast.Constant;
import httl.ast.ElseDirective;
import httl.ast.ForDirective;
import httl.ast.IfDirective;
import httl.ast.MacroDirective;
import httl.ast.SetDirective;
import httl.ast.Text;
import httl.ast.UnaryOperator;
import httl.ast.ValueDirective;
import httl.ast.Variable;
import httl.spi.Filter;
import httl.spi.Formatter;
import httl.spi.Switcher;
import httl.util.ClassUtils;
import httl.util.CollectionUtils;
import httl.util.LinkedStack;
import httl.util.Status;
import httl.util.StringUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.ParseException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class InterpretedVisitor extends AstVisitor {

  private Filter currentTextFilter;

  private Filter textFilter;

  private Filter valueFilter;

  private Formatter<Object> formatter;

  private Switcher<Filter> textFilterSwitcher;

  private Switcher<Filter> valueFilterSwitcher;

  private Switcher<Formatter<Object>> formatterSwitcher;

  private String filterVariable;

  private String formatterVariable;

  private String[] forVariable;

  private String ifVariable;

  private String breakVariable;

  private String outputEncoding;

  private Map<Class<?>, Object> importMethods;

  private Map<String, Template> importMacros;

  private Template template;

  private Object out;

  private String preText;

  private final LinkedStack<Object> parameterStack = new LinkedStack<Object>();

  public void setTextFilterSwitcher(Switcher<Filter> textFilterSwitcher) {
    this.textFilterSwitcher = textFilterSwitcher;
  }

  public void setValueFilterSwitcher(Switcher<Filter> valueFilterSwitcher) {
    this.valueFilterSwitcher = valueFilterSwitcher;
  }

  public void setFormatterSwitcher(Switcher<Formatter<Object>> formatterSwitcher) {
    this.formatterSwitcher = formatterSwitcher;
  }

  public void setFilterVariable(String filterVariable) {
    this.filterVariable = filterVariable;
  }

  public void setFormatterVariable(String formatterVariable) {
    this.formatterVariable = formatterVariable;
  }

  public void setImportMacros(Map<String, Template> importMacros) {
    this.importMacros = importMacros;
  }

  public void setImportMethods(Map<Class<?>, Object> importMethods) {
    this.importMethods = importMethods;
  }

  public void setTemplate(Template template) {
    this.template = template;
  }

  public void setOut(Object out) {
    this.out = out;
  }

  public void setOutputEncoding(String outputEncoding) {
    this.outputEncoding = outputEncoding;
  }

  public void setFormatter(Formatter<Object> formatter) {
    this.formatter = formatter;
  }

  public void setTextFilter(Filter textFilter) {
    this.textFilter = textFilter;
    this.currentTextFilter = textFilter;
  }

  public void setValueFilter(Filter valueFilter) {
    this.valueFilter = valueFilter;
  }

  public void setIfVariable(String ifVariable) {
    this.ifVariable = ifVariable;
  }

  public void setForVariable(String[] forVariable) {
    this.forVariable = forVariable;
  }

  private Object popExpressionResult(int offset) throws IOException, ParseException {
    Object result = parameterStack.pop();
    if (! parameterStack.isEmpty()) {
      throw new ParseException("The directive expression error.", offset);
    }
    return result;
  }

  @Override
  public void visit(Text node) throws IOException, ParseException {
    try {
      String text = node.getContent();
      if (textFilterSwitcher != null || valueFilterSwitcher != null || formatterSwitcher != null) {
        Set<String> locations = new HashSet<String>();
        List<String> textLocations = textFilterSwitcher == null ? null : textFilterSwitcher.locations();
        if (textLocations != null) {
          locations.addAll(textLocations);
        }
        List<String> valueLocations = valueFilterSwitcher == null ? null : valueFilterSwitcher.locations();
        if (valueLocations != null) {
          locations.addAll(valueLocations);
        }
        List<String> formatterLocations = formatterSwitcher == null ? null : formatterSwitcher.locations();
        if (formatterLocations != null) {
          locations.addAll(formatterLocations);
        }
        if (locations != null && locations.size() > 0) {
          Map<Integer, Set<String>> switchesd = new TreeMap<Integer, Set<String>>();
          for (String location : locations) {
            int i = -1;
            while ((i = text.indexOf(location, i + 1)) >= 0) {
              Integer key = Integer.valueOf(i);
              Set<String> values = switchesd.get(key);
              if (values == null) {
                values = new HashSet<String>();
                switchesd.put(key, values);
              }
              values.add(location);
            }
          }
          if (switchesd.size() > 0) {
            int begin = 0;
            for (Map.Entry<Integer, Set<String>> entry : switchesd.entrySet()) {
              int end = entry.getKey();
              String part = text.substring(begin, end);
              if (StringUtils.isNotEmpty(part)) {
                part = currentTextFilter == null ? part : currentTextFilter.filter(preText, part);
                preText = part;
                if (out instanceof Writer) {
                  ((Writer) out).write(part);
                } else if (out instanceof OutputStream) {
                  ((OutputStream) out).write(part.getBytes(outputEncoding));
                }
              }
              begin = end;
              for (String location : entry.getValue()) {
                if (textLocations != null && textLocations.contains(location)) {
                  currentTextFilter = textFilterSwitcher.switchover(location, textFilter);
                }
                if (valueLocations != null && valueLocations.contains(location)) {
                  Context.getContext().put(filterVariable, valueFilterSwitcher.switchover(location, valueFilter));
                }
                if (formatterLocations != null && formatterLocations.contains(location)) {
                  Context.getContext().put(formatterVariable, formatterSwitcher.switchover(location, formatter));
                }
              }
            }
            if (begin > 0) {
              text = text.substring(begin);
            }
          }
        }
      }
      if (StringUtils.isNotEmpty(text)) {
        text = currentTextFilter == null ? text : currentTextFilter.filter(preText, text);
        preText = text;
        if (out instanceof Writer) {
          ((Writer) out).write(text);
        } else if (out instanceof OutputStream) {
          ((OutputStream) out).write(text.getBytes(outputEncoding));
        }
      }
    } catch (IOException e) {
      throw new ParseException(e.getMessage(), node.getOffset());
    }
  }

  @SuppressWarnings("unchecked")
  @Override
  public void visit(ValueDirective node) throws IOException, ParseException {
    Object result = popExpressionResult(node.getOffset());
    if (result instanceof Template) {
      ((Template) result).render(out);
    } else {
      Formatter<Object> format = (Formatter<Object>) Context.getContext().get(formatterVariable, formatter);
      String text = format == null ? StringUtils.toString(result) : format.toString(null, result);
      Filter filter = (Filter) Context.getContext().get(filterVariable, valueFilter);
      if (! node.isNoFilter() && filter != null) {
        text =  filter.filter(node.getExpression().toString(), text);
      }
      try {
        if (text != null) {
          if (out instanceof Writer) {
            ((Writer) out).write(text);
          } else if (out instanceof OutputStream) {
            ((OutputStream) out).write(text.getBytes(outputEncoding));
          }
        }
      } catch (IOException e) {
        throw new ParseException(e.getMessage(), node.getOffset());
      }
    }
  }

  @Override
  public void visit(SetDirective node) throws IOException, ParseException {
    if (node.getExpression() != null) {
      Object result = popExpressionResult(node.getOffset());
      if (node.isExport() && Context.getContext().getParent() != null) {
        Context.getContext().getParent().put(node.getName(), result);
      } else {
        Context.getContext().put(node.getName(), result);
      }
    }
  }

  @Override
  public void visit(BreakDirective node) throws IOException, ParseException {
    boolean result = true;
    if (node.getExpression() != null) {
      result = ClassUtils.isTrue(popExpressionResult(node.getOffset()));
    }
    if (result) {
      Context.getContext().put(breakVariable, true);
    }
  }

  @Override
  public boolean visit(IfDirective node) throws IOException, ParseException {
    boolean result = ClassUtils.isTrue(popExpressionResult(node.getOffset()));
    Context.getContext().put(ifVariable, result);
    return result;
  }

  @Override
  public boolean visit(ElseDirective node) throws IOException, ParseException {
    boolean result = true;
    if (node.getExpression() != null) {
      result = ClassUtils.isTrue(popExpressionResult(node.getOffset()));
    }
    result = result && ! ClassUtils.isTrue(Context.getContext().get(ifVariable));
    if (result) {
      Context.getContext().put(ifVariable, true);
    }
    return result;
  }

  @Override
  public boolean visit(ForDirective node) throws IOException, ParseException {
    Object data = popExpressionResult(node.getOffset());
    boolean result = ClassUtils.isTrue(data);
    Context.getContext().put(ifVariable, result);
    Iterator<?> iterator = CollectionUtils.toIterator(data);
    Status status = new Status((Status) Context.getContext().get(forVariable[0]), data);
    for (String var : forVariable) {
      Context.getContext().put(var, status);
    }
    loop: while (iterator.hasNext()) {
      Object item = iterator.next();
      Context.getContext().put(node.getName(), item);
      for (Node child : node.getChildren()) {
        child.accept(this);
        if (ClassUtils.isTrue(Context.getContext().get(breakVariable))) {
          Context.getContext().remove(breakVariable);
          break loop;
        }
      }
      status.increment();
    }
    for (String var : forVariable) {
      Context.getContext().put(var, status.getParent());
    }
    return false;
  }

  @Override
  public boolean visit(MacroDirective node) throws IOException, ParseException {
    return false;
  }

  @Override
  public boolean visit(Template node) throws IOException, ParseException {
    for (Node child : node.getChildren()) {
      child.accept(this);
      if (ClassUtils.isTrue(Context.getContext().get(breakVariable))) {
        Context.getContext().remove(breakVariable);
        break;
      }
    }
    return false;
  }

  @Override
  public void visit(Constant node) throws IOException, ParseException {
    parameterStack.push(node.getValue());
  }

  @Override
  public void visit(Variable node) throws IOException, ParseException {
    parameterStack.push(Context.getContext().get(node.getName()));
  }

  @Override
  public void visit(UnaryOperator node) throws IOException, ParseException {
    Object parameter = parameterStack.pop();
    Object result = null;
    String name = node.getName();
    String filteredName = ClassUtils.filterJavaKeyword(name);
    Object[] args;
    if (parameter == null
        && node.getParameter() instanceof Constant
        && ((Constant) node.getParameter()).isBoxed()) {
      args = new Object[0];
    } else if (parameter instanceof Object[]) {
      args = (Object[]) parameter;
    } else {
      args = new Object[] { parameter };
    }
    Class<?>[] types = new Class<?>[args.length];
    for (int i = 0; i < args.length; i ++) {
      types[i] = args[i] == null ? null : args[i].getClass();
    }
    boolean found = false;
    if (importMethods != null && importMethods.size() > 0) {
      for (Map.Entry<Class<?>, Object> entry : importMethods.entrySet()) {
        Class<?> function = entry.getKey();
        try {
          Method method = ClassUtils.searchMethod(function, filteredName, types, true);
          if (! Object.class.equals(method.getDeclaringClass())) {
            Class<?> type = method.getReturnType();
            if (type == void.class) {
              throw new ParseException("Can not call void method " + method.getName() + " in class " + function.getName(), node.getOffset());
            }
            if (Modifier.isStatic(method.getModifiers())) {
              result = method.invoke(null, args);
            } else {
              result = method.invoke(entry.getValue(), args);
            }
            found = true;
            break;
          }
        } catch (NoSuchMethodException e) {
        } catch (Exception e) {
          throw new ParseException("Failed to invoke method " + ClassUtils.getMethodFullName(filteredName, types) + " in class " + function.getCanonicalName() + ", cause: " + ClassUtils.dumpException(e), node.getOffset());
        }
      }
    }
    if (! found) {
      Template macro;
      Object value = Context.getContext().get(name);
      if (value instanceof Template) {
        macro = (Template) value;
      } else {
        macro = template.getMacros().get(name);
        if (macro == null && importMacros != null) {
          macro = importMacros.get(name);
        }
      }
      if (macro != null) {
        result = macro.evaluate(args);
      } else {
        throw new ParseException("No such macro \"" + filteredName + "\" or import method " + ClassUtils.getMethodFullName(filteredName, types) + ".", node.getOffset());
      }
    }
    parameterStack.push(result);
  }

  @Override
  public void visit(BinaryOperator node) throws IOException, ParseException {
    Object rightParameter = parameterStack.pop();
    Object leftParameter = parameterStack.pop();
    Object result = null;
    if (leftParameter != null) {
      String name = node.getName();
      Class<?> leftClass = leftParameter.getClass();
      if ("to".equals(name) && rightParameter instanceof String) {
        result = leftParameter;
      } else if ("class".equals(name)) {
        if (node.getLeftParameter() instanceof Constant
            && ! ((Constant) node.getLeftParameter()).isBoxed()) {
          result = ClassUtils.getUnboxedClass(leftClass);
        } else {
          result = leftClass;
        }
      } else {
        name = ClassUtils.filterJavaKeyword(name);
        Object[] args;
        if (rightParameter == null) {
          args = new Object[0];
        } else if (rightParameter.getClass().equals(Object[].class)) {
          args = (Object[]) rightParameter;
        } else {
          args = new Object[] { rightParameter };
        }
        result = null;
        boolean found = false;
        if (importMethods != null && importMethods.size() > 0) {
          Object[] staticArgs = new Object[args.length + 1];
          staticArgs[0] = leftParameter;
          System.arraycopy(args, 0, staticArgs, 1, args.length);
          Class<?>[] types = new Class<?>[staticArgs.length];
          for (int i = 0; i < staticArgs.length; i ++) {
            types[i] = staticArgs[i] == null ? null : staticArgs[i].getClass();
          }
          for (Map.Entry<Class<?>, Object> entry : importMethods.entrySet()) {
            Class<?> function = entry.getKey();
            try {
              Method method = ClassUtils.searchMethod(function, name, types, true);
              if (Object.class.equals(method.getDeclaringClass())) {
                break;
              }
              Class<?> type = method.getReturnType();
              if (type == void.class) {
                throw new ParseException("Can not call void method " + method.getName() + " in class " + function.getName(), node.getOffset());
              }
              if (Modifier.isStatic(method.getModifiers())) {
                result = method.invoke(null, staticArgs);
              } else {
                result = method.invoke(entry.getValue(), staticArgs);
              }
              found = true;
              break;
            } catch (NoSuchMethodException e) {
            } catch (Exception e) {
              throw new ParseException("Failed to invoke method " + ClassUtils.getMethodFullName(name, types) + " in class " + function.getCanonicalName() + ", cause: " + e.getMessage(), node.getOffset());
            }
          }
        }
        if (! found) {
          try {
            Class<?>[] types = new Class<?>[args.length];
            for (int i = 0; i < args.length; i ++) {
              types[i] = args[i] == null ? null : args[i].getClass();
            }
            try {
              Method method = ClassUtils.searchMethod(leftClass, name, types, true);
              if (! method.isAccessible()) {
                method.setAccessible(true);
              }
              result = method.invoke(leftParameter, args);
              found = true;
            } catch (NoSuchMethodException e) {
              if (args.length == 0) {
                try {
                  result = ClassUtils.searchProperty(leftParameter, name);
                  found = true;
                } catch (NoSuchFieldException e2) {
                }
              }
              if (! found) {
                if (leftParameter instanceof Template) {
                  Template macro = ((Template) leftParameter).getMacros().get(name);
                  if (macro != null) {
                    result = macro.evaluate(args);
                  } else {
                    throw new ParseException("No such macro or method " + name + " in " + leftParameter.getClass().getCanonicalName(), node.getOffset());
                  }
                } else if (leftParameter instanceof Class) {
                  Class<?> function = (Class<?>) leftParameter;
                  Method method = ClassUtils.searchMethod(function, name, types, true);
                  Class<?> type = method.getReturnType();
                  if (type == void.class) {
                    throw new ParseException("Can not call void method " + method.getName() + " in class " + function.getName(), node.getOffset());
                  }
                  if (! Modifier.isStatic(method.getModifiers())) {
                    throw new ParseException("Can not call non-static method " + method.getName() + " in class " + function.getName(), node.getOffset());
                  }
                  result = method.invoke(null, args);
                } else {
                  throw new ParseException("No such method " + name + " in " + leftParameter.getClass().getCanonicalName(), node.getOffset());
                }
              }
            }
          } catch (ParseException e) {
            throw e;
          } catch (Exception e) {
            throw new ParseException(ClassUtils.toString(e), node.getOffset());
          }
        }
      }
    }
    parameterStack.push(result);
  }

}
TOP

Related Classes of httl.spi.translators.templates.InterpretedVisitor

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.