Package cn.bran.japid.template

Source Code of cn.bran.japid.template.JapidTemplateBaseWithoutPlay

/**
* Copyright 2010 Bing Ran<bing_ran@hotmail.com>
*
* 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 cn.bran.japid.template;

import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;

import cn.bran.japid.MyTuple2;
import cn.bran.japid.tags.Each;
import cn.bran.japid.tags.Each.BreakLoop;
import cn.bran.japid.tags.Each.ContinueLoop;
import cn.bran.japid.util.StringUtils;

import cn.bran.japid.classmeta.MimeTypeEnum;
import cn.bran.japid.compiler.NamedArg;
import cn.bran.japid.compiler.NamedArgRuntime;
import cn.bran.japid.util.HTMLUtils;
import cn.bran.japid.util.JapidFlags;
import cn.bran.japid.util.WebUtils;

/**
* a java based template suing StringBuilder as the content buffer, no play
* dependency.
*
* @author bran
*
*/
public abstract class JapidTemplateBaseWithoutPlay implements Serializable {
  public String sourceTemplate = "";
  private StringBuilder out;
  private Map<String, String> headers;// = new TreeMap<String, String>();


  // directive for tracing templates navigation
  private Boolean traceFile = null;

  private String contentType = "";

  private Boolean stopwatch = null;
  long startTime = System.nanoTime(); // nano-second when starting rendering
  protected long renderingTime = -1; // in microsecond

  // the template that calls this as a tag
  protected JapidTemplateBaseWithoutPlay caller;

  // <marker, time consumption>
  public List<MyTuple2<String, Long>> timeLogs;
 
  private void init() {
    if (headers == null) {
       headers = new TreeMap<String, String>();
//       headers = new HashMap<String, String>();
       headers.put("Content-Type", "text/html; charset=utf-8");
    }
    if (timeLogs == null) {
      timeLogs = new LinkedList<MyTuple2<String, Long>>();
    }
    if (out == null)
      out = new StringBuilder(4000);
  }

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

  protected StringBuilder getOut() {
    return out;
  }

  // public JapidTemplateBase() {
  //
  // };

  protected void putHeader(String k, String v) {
    headers.put(k, v);
  }

  protected Map<String, String> getHeaders() {
    return this.headers;
  }

  public JapidTemplateBaseWithoutPlay(StringBuilder out2) {
    this.out = out2;
    init();
  }

  public JapidTemplateBaseWithoutPlay(JapidTemplateBaseWithoutPlay caller) {
    if (caller != null) {
      out = caller.getOut();
    }
    this.caller = caller;
    this.timeLogs = caller.timeLogs;
    this.headers = caller.getHeaders();
    this.stopwatch = caller.stopwatch;
    init();
  }

  // don't use it since it will lead to new instance of stringencoder
  // Charset UTF8 = Charset.forName("UTF-8");

  final protected void p(String s) {
    if (s != null && !s.isEmpty())
      out.append(s);

//    writeString(s);
  }

  final protected void pln(String s) {
    if (s != null && !s.isEmpty())
      out.append(s);

//    writeString(s);
    out.append('\n');
  }

  /**
   * @param s
   * @throws IOException
   * @throws UnsupportedEncodingException
   */
  final private void writeString(String s) {
    // ByteBuffer bb = StringUtils.encodeUTF8(s);
    // out.write(bb.array(), 0, bb.position());
    // ok my code is slower in large trunk of data
    if (s != null && !s.isEmpty())
      out.append(s);
  }

  // final protected void pln(byte[] ba) {
  // try {
  // out.write(ba);
  // out.write('\n');
  // } catch (IOException e) {
  // throw new RuntimeException(e);
  // }
  // }

  final protected void p(Object s) {
    if (s != null) {
      writeString(s.toString());
      // out.append(s);
    }
  }

  final protected void pln(Object s) {
    if (s != null)
      writeString(s.toString());
    pln();
  }

  final protected void pln() {
    out.append('\n');
  }

  /**
   * The template pattern to implement the template/layout relationship.
   * Clients call a template's render(), which store params in fields and
   * calls in super class's layout, which does the whole page layout and calls
   * back child's doLayout to get the child content.
   */
  protected void layout() {
    doLayout();
  }

  protected abstract void doLayout();

  static protected byte[] getBytes(String src) {
    if (src == null || src.length() == 0)
      return new byte[] {};

    try {
      return src.getBytes("UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public String toString() {
    return this.out.toString();
  }

  /**
   * reflect this object for a method of the name
   *
   * @param methodName
   * @return
   */
  protected String get(String methodName, String defaultVal) {
    try {
      Method method = this.getClass().getMethod(methodName, (Class[]) null);
      String invoke = (String) method.invoke(this, (Object[]) null);
      return invoke;
    } catch (Exception e) {
      return defaultVal;
    }
  }

  /**
   * reflect this object for a method of the name
   *
   * @param methodName
   * @return
   */
  protected String get(String methodName) {
    try {
      Method method = this.getClass().getMethod(methodName, (Class[]) null);
      String invoke = (String) method.invoke(this, (Object[]) null);
      return invoke;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  protected boolean asBoolean(Object o) {
    return WebUtils.asBoolean(o);
  }

  /**
   * escape the string representation of the object to make it HTML safe.
   *
   * @param o
   * @return
   */
  public static String escape(Object o) {
    if (o == null)
      return null;
    return HTMLUtils.htmlEscape(o.toString());
  }

  /**
   * @param currentClass
   */
  public static Method getRenderMethod(Class<? extends JapidTemplateBaseWithoutPlay> currentClass) {
    java.lang.reflect.Method[] methods = currentClass.getDeclaredMethods();

    Method r = null;
    for (java.lang.reflect.Method m : methods) {
      if (m.getName().equals("render")) {
        Class<?>[] parameterTypes = m.getParameterTypes();
        int paramLength = parameterTypes.length;
        if (paramLength == 1) {
          Class<?> t = parameterTypes[0];
          if (t != NamedArgRuntime[].class) {
            if (r == null)
              r = m;
          }
        } else {
          boolean hasNamedArg = false;
          for (Class<?> c : parameterTypes) {
            if (c == NamedArgRuntime.class || c == NamedArgRuntime[].class) {
              hasNamedArg = true;
              break;
            }
          }
          if (!hasNamedArg) {
            // a candidate. choose the one with longer param list
            if (r == null)
              r = m;
            else if (paramLength > r.getParameterTypes().length)
              r = m;
          }
        }
      }
    }
    if (r != null)
      return r;
    else
      throw new RuntimeException("no render method found for the template: " + currentClass.getCanonicalName());
  }

  /*
   * based on https://github.com/branaway/Japid/issues/12 This static mapping
   * will be later user in method renderModel to construct an proper Object[]
   * array which is needed to invoke the method render(Object... args) over
   * reflection.
   */

  public java.lang.reflect.Method renderMethodInstance;
  public boolean hasDoBody = false;

  protected void setHasDoBody() {
    hasDoBody = true;
  }

  protected void setRenderMethod(Method renderMethod) {
    // System.out.println("-> setrender name: " + renderMethod);
    renderMethodInstance = renderMethod;
  }

  public String[] argNamesInstance = null;

  protected void setArgNames(String[] argNames) {
    // System.out.println("-> set args names: " + argNames);
    this.argNamesInstance = argNames;
  }

  public String[] argTypesInstance = null;

  protected void setArgTypes(String[] argTypes) {
    // System.out.println("-> set args names: " + argNames);
    this.argTypesInstance = argTypes;
  }

  public Object[] argDefaultsInstance = null;
  private MimeTypeEnum mimeType;
  private Boolean traceFileExit = null;
  public static boolean globalTraceFile = false;
  public static Boolean globalTraceFileHtml = null;
  public static Boolean globalTraceFileJson = null;

  protected void setArgDefaults(Object[] argDefaults) {
    // System.out.println("-> set args names: " + argNames);
    this.argDefaultsInstance = argDefaults;
  }

  // public cn.bran.japid.template.RenderResult
  // renderModel(cn.bran.japid.template.JapidModelMap model) {
  // // a static utils method of JapidModelMap to build up an Object[] array.
  // Nulls are used where the args are omitted.
  // Object[] args = model.buildArgs(argNamesInstance);
  // try {
  // return (cn.bran.japid.template.RenderResult )
  // renderMethodInstance.invoke(this, args);
  // } catch (IllegalArgumentException e) {
  // throw new RuntimeException(e);
  // } catch (IllegalAccessException e) {
  // throw new RuntimeException(e);
  // } catch (InvocationTargetException e) {
  // Throwable t = e.getTargetException();
  // throw new RuntimeException(t);
  // }
  // }
  //
  protected static NamedArgRuntime named(String name, Object val) {
    return new NamedArgRuntime(name, val);
  }

  public cn.bran.japid.template.RenderResult render(NamedArgRuntime... named) {
    Object[] args = null;
    if (hasDoBody) // called without the callback block
      args = buildArgs(named, null);
    else
      args = buildArgs(named);
    return runRenderer(args);
  }

  /**
   * @param args
   * @return
   */
  protected cn.bran.japid.template.RenderResult runRenderer(Object[] args) {
    try {
      return (cn.bran.japid.template.RenderResult) renderMethodInstance.invoke(this, args);
    } catch (IllegalArgumentException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
      Throwable t = e.getTargetException();
      throw new RuntimeException(t);
    }
  }

  /**
   * build
   *
   * @param argNames
   * @param namedArgs
   * @return
   */
  public Object[] buildArgs(NamedArgRuntime[] namedArgs) {
    Map<String, Object> map = new HashMap<String, Object>();
    for (NamedArgRuntime na : namedArgs) {
      map.put(na.name, na.val);
    }

    Object[] ret = new Object[argNamesInstance.length];

    for (int i = 0; i < argNamesInstance.length; i++) {
      String name = argNamesInstance[i];
      if (map.containsKey(name)) {
        ret[i] = map.remove(name);
      } else {
        // any default set?
        Object defa = this.argDefaultsInstance[i];
        if (defa != null)
          ret[i] = defa;
        else {
          // set default value for primitives and Strings, or null for
          // complex object
          String type = argTypesInstance[i];
          Object defaultVal = getDefaultValForType(type);
          ret[i] = defaultVal;
        }
      }
    }
    if (map.size() > 0) {
      Set<String> keys = map.keySet();
      String sep = ", ";
      String ks = "[" + StringUtils.join(keys, sep) + "]";
      String vs = "[" + StringUtils.join(argNamesInstance, sep) + "]";
      throw new RuntimeException("One or more argument names are not valid: " + ks
          + ". Valid argument names are: " + vs);
    }
    return ret;
  }

  protected Object[] buildArgs(NamedArgRuntime[] named, Object body) {
    Object[] obsNoBody = buildArgs(named);
    int len = obsNoBody.length;
    Object[] ret = new Object[len + 1];
    System.arraycopy(obsNoBody, 0, ret, 0, len);
    ret[len] = body;
    return ret;
  }

  private static Object getDefaultValForType(String type) {
    if (type.equals("String"))
      return "";
    else if (/* type.equals("Boolean") || */type.equals("boolean"))
      return false;
    else if (type.equals("char") /* || type.equals("Character") */)
      return (char) 0;
    else if (type.equals("byte") /* || type.equals("Byte") */)
      return (byte) 0;
    else if (type.equals("short") /* || type.equals("Short") */)
      return (short) 0;
    else if (type.equals("int") /* || type.equals("Integer") */)
      return 0;
    else if (type.equals("float") /* || type.equals("Float") */)
      return 0f;
    else if (type.equals("long") /* || type.equals("Long") */)
      return 0L;
    else if (type.equals("double") /* || type.equals("Double") */)
      return 0d;

    return null;
  }

  protected void handleException(RuntimeException e) {
    throw e;
  }

  protected void setSourceTemplate(String st) {
    this.sourceTemplate = st;
  }

  /**
   * templates call this method to insert the current template name in a
   * mine-type sensitive comment. It does not respect the `tracefile
   * directive. It's useful to mark template files that generate xml/xhtml
   * that requires doctype tag in the first line of the output. It's a
   * convenient substitute of the `tracefile directive in such cases.
   *
   * @author Bing Ran (bing.ran@hotmail.com)
   */
  protected void traceFile() {
    this.traceFileExit = true;
    p(makeBeginBorder(this.sourceTemplate));
  }

  /**
   * @author Bing Ran (bing.ran@hotmail.com)
   * @return
   */
  protected String makeBeginBorder(String viewSource) {
    if (StringUtils.isEmpty(contentType))
      return null;

    String formatter = getContentCommentFormatter(contentType);
    if (formatter == null)
      return "";

    return String.format(formatter, "enter: \"" + viewSource + "\"");

  }

  /**
   * @author Bing Ran (bing.ran@hotmail.com)
   * @return
   */
  protected String makeEndBorder(String viewSource) {
    if (StringUtils.isEmpty(contentType))
      return null;

    String formatter = getContentCommentFormatter(contentType);
    if (formatter == null)
      return "";

    String content = "exit: \"" + viewSource + "\"";
    if (shouldRecordTime()) {
      // add time consumption to the endline for debugging purpose
      content += ". Duration/μs: " + renderingTime;
    }

    return String.format(formatter, content);

  }

  /**
   * determine if the current template should mark the entrance and the exit
   * in the output
   *
   * @author Bing Ran (bing.ran@hotmail.com)
   * @return
   */
  private boolean shouldTraceFile() {
    if (traceFile != null)
      return traceFile;
    else if (this.mimeType == MimeTypeEnum.xml || this.mimeType == MimeTypeEnum.html)
      if (globalTraceFileHtml != null)
        return globalTraceFileHtml;
      else
        return globalTraceFile;
    else if (this.mimeType == MimeTypeEnum.js || this.mimeType == MimeTypeEnum.json)
      if (globalTraceFileJson != null)
        return globalTraceFileJson;
      else
        return globalTraceFile;

    return false;

  }

  protected void beginDoLayout(String viewSource) {
    if (shouldTraceFile())
      p(makeBeginBorder(viewSource));

//    if (shouldRecordTime()) {
//      startTime = System.nanoTime();
//    }
  }

  /**
   * @author Bing Ran (bing.ran@gmail.com)
   * @return
   */
  private boolean shouldRecordTime() {
//    if (caller != null && caller.shouldRecordTime())
//      return true;
//    else
      return isStopwatch();
  }

  protected void endDoLayout(String viewSource) {
    if (shouldRecordTime()) {
      calcDuration();
      logTime(sourceTemplate, renderingTime);
    }

    if (shouldTraceFile())
      p(makeEndBorder(viewSource));
    else if (traceFileExit != null && traceFileExit)
      p(makeEndBorder(viewSource));

  }

  private void calcDuration() {
    long duration = System.nanoTime() - startTime;
    renderingTime = duration / 1000;

//    JapidFlags._log("Time consumed to render \"" + sourceTemplate + "\": " + renderingTime + " μs");
  }

  public static String getContentCommentFormatter(String contentTypeString) {
    if (contentTypeString.contains("xml") || contentTypeString.contains("html"))
      return "<!-- %s -->";

    if (contentTypeString.contains("json") || contentTypeString.contains("javascript")
        || contentTypeString.contains("css"))
      return "/* %s */";
    return null;
  }

  /**
   * @return the contentType
   */
  public String getContentType() {
    return contentType;
  }

  /**
   * @param contentType
   *            the contentType to set
   */
  public void setContentType(String contentType) {
    this.contentType = contentType;
    if (contentType.contains("xml"))
      this.mimeType = MimeTypeEnum.xml;
    else if (contentType.contains("html"))
      this.mimeType = MimeTypeEnum.html;
    else if (contentType.contains("javascript"))
      this.mimeType = MimeTypeEnum.js;
    else if (contentType.contains("json"))
      this.mimeType = MimeTypeEnum.json;
    else if (contentType.contains("css"))
      this.mimeType = MimeTypeEnum.css;
  }

  /**
   * @return the traceFile
   */
  public Boolean getTraceFile() {
    return traceFile;
  }

  /**
   * @param traceFile
   *            the traceFile to set
   */
  public void setTraceFile(Boolean traceFile) {
    this.traceFile = traceFile;
  }

  /**
   * @deprecated the Each tag is deprecated in favor of using native loop
   */
  protected void breakLoop() {
    throw new BreakLoop();
  }

  /**
   * @deprecated the Each tag is deprecated in favor of using native loop
   */
  protected void continueLoop() {
    throw new ContinueLoop();
  }

  /**
   * @author Bing Ran (bing.ran@gmail.com)
   * @param strings
   * @return
   */
  protected static int getCollectionSize(Object col) {

    if (col instanceof Collection) {
      return ((Collection) col).size();
    }

    if (col.getClass().isArray()) {
      return Array.getLength(col);
    }

    if (col instanceof Iterable || col instanceof Iterator) {
      return -1;
    }

    return -1;
  }

  public boolean isStopwatch() {
    return stopwatch != null ? stopwatch : false;
  }

  public void setStopwatchOn() {
    this.stopwatch = true;
  }
// XXX reconsider this later. consider layout with param case carefully.
//  protected void startRendering() {
//    try {
//      layout();
//    } catch (RuntimeException __e) {
//      handleException(__e);
//    }
//  }

  protected cn.bran.japid.template.RenderResult getRenderResult() {
    return new cn.bran.japid.template.RenderResult(getHeaders(), getOut(), renderingTime);
  }

  /**
   * For debugging purpose. Can be called by `
   * @author Bing Ran (bing.ran@gmail.com)
   * @param marker
   */
  protected void logDuration(String marker) {
    if (shouldRecordTime()) {
      long endtime = System.nanoTime();
      long duration = endtime - startTime;
      long t = duration / 1000;

      logTime(marker, t);
      // JapidFlags._log("Time consumed up to \"" + marker + "\" in \"" +
      // sourceTemplate + "\": " + t + " μs");
    }
  }

  /**
   * @author Bing Ran (bing.ran@gmail.com)
   * @param marker
   * @param t
   */
  private void logTime(String marker, long t) {
//    if (caller != null) {
//      caller.logTime(marker, t);
//    } else {
      timeLogs.add(new MyTuple2(marker, t));
//    }
  }
}
TOP

Related Classes of cn.bran.japid.template.JapidTemplateBaseWithoutPlay

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.