Package httl.spi.translators.templates

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

/*
* 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.Engine;
import httl.Node;
import httl.Resource;
import httl.Template;
import httl.Visitor;
import httl.ast.BlockDirective;
import httl.ast.MacroDirective;
import httl.spi.Converter;
import httl.spi.Interceptor;
import httl.spi.Listener;
import httl.util.ClassUtils;
import httl.util.StringUtils;
import httl.util.UnsafeStringWriter;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.text.ParseException;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
* AbstractTemplate. (SPI, Prototype, ThreadSafe)
*
* @see httl.Engine#getTemplate(String)
*
* @author Liang Fei (liangfei0201 AT gmail DOT com)
*/
public abstract class AbstractTemplate implements Template {

  private final Resource resource;

  private final Node root;

  private final Template parent;

  private final String name;

  private final String encoding;

  private final Locale locale;
 
  private final long lastModified;

  private final long length;

  public AbstractTemplate(Resource resource, Node root, Template parent) {
    this.resource = resource;
    this.root = root;
    this.parent = parent;
    this.name = buildName(resource, root);
    this.encoding = resource.getEncoding();
    this.locale = resource.getLocale();
    // 注意:lastModified被用作缓存的更新条件,resource.getLastModified()很慢,必须缓存
    this.lastModified = resource.getLastModified();
    this.length = resource.getLength();
  }

  private static String buildName(Resource resource, Node root) {
    StringBuilder builder = new StringBuilder();
    builder.append(resource.getName());
    while (root instanceof MacroDirective) {
      builder.append("#");
      builder.append(((MacroDirective) root).getName());
      root = root.getParent();
    }
    return builder.toString();
  }

  private Converter<Object, Object> mapConverter;

  private Converter<Object, Object> outConverter;
 
  private Interceptor interceptor;

  private Object convertOut(Object out) throws IOException, ParseException {
    if (outConverter != null && out != null
        && ! (out instanceof OutputStream)
        && ! (out instanceof Writer)) {
      return outConverter.convert(out, getVariables());
    }
    return out;
  }
 
  @SuppressWarnings("unchecked")
  private Map<String, Object> convertMap(Object context) throws ParseException {
    if (mapConverter != null && context != null && ! (context instanceof Map)) {
      try {
        context = mapConverter.convert(context, getVariables());
      } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
      }
    }
    if (context == null || context instanceof Map) {
      return (Map<String, Object>) context;
    } else {
      throw new IllegalArgumentException("No such Converter to convert the " + context.getClass().getName() + " to Map.");
    }
  }

  public void render(Object parameters, Object out) throws IOException, ParseException {
    Map<String, Object> map = convertMap(parameters);
    out = convertOut(out);
    Context context = Context.pushContext(map);
    try {
      context.setTemplate(this);
      if (out instanceof OutputStream) {
        context.setOut((OutputStream) out);
      } else if (out instanceof Writer) {
        context.setOut((Writer) out);
      } else {
        throw new IllegalArgumentException("No such Converter to convert the " + out.getClass().getName() + " to OutputStream or Writer.");
      }
      if (interceptor != null) {
        interceptor.render(context, new Listener() {
          public void render(Context context) throws IOException, ParseException {
            _render(context);
          }
        });
      } else {
        _render(context);
      }
    } catch (ParseException e) {
      throw toLocatedParseException(e, this);
    } finally {
      Context.popContext();
    }
  }

  private void _render(Context context) throws IOException, ParseException {
    try {
      doRender(context);
    } catch (RuntimeException e) {
      throw (RuntimeException) e;
    } catch (IOException e) {
      throw (IOException) e;
    } catch (ParseException e) {
      throw (ParseException) e;
    } catch (Exception e) {
      throw new RuntimeException(ClassUtils.toString(e), e);
    }
  }

  protected abstract void doRender(Context context) throws Exception;

  protected Resource getResource() {
    return resource;
  }

  protected Node getRoot() {
    return root;
  }

  protected Converter<Object, Object> getMapConverter() {
    return mapConverter;
  }

  protected Converter<Object, Object> getOutConverter() {
    return outConverter;
  }

  protected Interceptor getInterceptor() {
    return interceptor;
  }

  public void setInterceptor(Interceptor interceptor) {
    this.interceptor = interceptor;
  }

  public void setMapConverter(Converter<Object, Object> mapConverter) {
    this.mapConverter = mapConverter;
  }

  public void setOutConverter(Converter<Object, Object> outConverter) {
    this.outConverter = outConverter;
  }

  public String getName() {
    return name;
  }

  public String getEncoding() {
    return encoding;
  }

  public Locale getLocale() {
    return locale;
  }

  public long getLastModified() {
    return lastModified;
  }

  public long getLength() {
    return length;
  }

  public String getSource() throws IOException {
    return resource.getSource();
  }

  public Reader openReader() throws IOException {
    return resource.openReader();
  }

  public InputStream openStream() throws IOException {
    return resource.openStream();
  }

  public Engine getEngine() {
    return resource.getEngine();
  }

  public int getOffset() {
    return root.getOffset();
  }

  public Template getParent() {
    return parent;
  }

  @SuppressWarnings({ "unchecked", "rawtypes" })
  public List<Node> getChildren() {
    return (List) ((BlockDirective) root).getChildren();
  }

  public boolean isMacro() {
    return root instanceof MacroDirective;
  }

  public void accept(Visitor visitor) throws IOException, ParseException {
    if (visitor.visit(this)) {
      for (Node node : getChildren()) {
        node.accept(visitor);
      }
    }
  }

  public Object evaluate() throws ParseException {
    return evaluate(null);
  }

  public void render(Object out) throws IOException, ParseException {
    render(null, out);
  }

  public void render() throws IOException, ParseException {
    render(null, Context.getContext().getOut());
  }

  public Object evaluate(Object context) throws ParseException {
    UnsafeStringWriter writer = new UnsafeStringWriter();
    try {
      render(context, writer);
    } catch (IOException e) {
      throw new RuntimeException(e.getMessage(), e);
    }
    return writer.toString();
  }
 
  public static ParseException toLocatedParseException(ParseException e, Resource resource) {
    if (e.getMessage() != null
        && e.getMessage().contains("Occur to offset:")) {
      return e;
    }
    int offset = e.getErrorOffset();
    if (offset <= 0) {
      return e;
    }
    String location = null;
    try {
      Reader reader = resource.openReader();
      try {
        location = StringUtils.getLocationMessage(resource.getName(), reader, offset);
      } finally {
        reader.close();
      }
    } catch (Throwable t) {
    }
    return new ParseException(e.getMessage()  + "\nOccur to offset: " + offset +
                 (StringUtils.isEmpty(location) ? "" : ", " + location)
                 + ", stack: " + ClassUtils.toString(e), offset);
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    String name = getName();
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
  }
 
  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    AbstractTemplate other = (AbstractTemplate) obj;
    String name = getName();
    String otherName = other.getName();
    if (name == null) {
      if (otherName != null) return false;
    } else if (!name.equals(otherName)) return false;
    return true;
  }

  @Override
  public String toString() {
    return getName();
  }

}
TOP

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

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.