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

org.nutz.jst.impl.JstImpl Maven / Gradle / Ivy

There is a newer version: 1.0.3
Show newest version
package org.nutz.jst.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;

import org.nutz.jst.Jst;
import org.nutz.jst.JstException;
import org.nutz.jst.block.JstBlock;
import org.nutz.jst.block.JstEvalBlock;
import org.nutz.jst.block.JstStaticBlock;
import org.nutz.jst.block.JstStatmentBlock;
import org.nutz.lang.Streams;
import org.nutz.lang.util.Context;

/**
 * 基本用法: String re = Jst.create(tpl).render(ctx);
 * @author wendal([email protected])
 *
 */
public class JstImpl {

    protected List jstBlocks = new ArrayList();
    protected ArrayList strs = new ArrayList();
    protected ArrayList lines = new ArrayList();
    protected String jsStr;
    protected ScriptEngine engine;
    protected CompiledScript compiledScript;
    protected String name = "";
    protected String source;
    protected boolean debug;
    protected String singleLineJs = "#";
    protected String[] multiLineJs = new String[] {""};

    protected JstImpl() {
        this.engine = Jst.getGlobalEngine();
    }

    public JstImpl(String str) {
        this();
        this.source = str;
    }

    public JstImpl(Reader r) {
        this(Streams.readAndClose(r));
    }

    protected void parse(BufferedReader br) {
        try {
            String line = null;
            int lineNumber = -1;
            while ((line = br.readLine()) != null) {
                lineNumber++;
                lines.add(line);
                String _line = line.trim();
                if (_line.startsWith(singleLineJs)) {
                    JstStatmentBlock block = new JstStatmentBlock();
                    block.lineNumber = lineNumber;
                    block.str = line.substring(line.indexOf(singleLineJs)+1);
                    jstBlocks.add(block);
                    continue;
                }
                if (_line.startsWith(multiLineJs[0])) {
                    JstStatmentBlock block = new JstStatmentBlock();
                    block.lineNumber = lineNumber;
                    jstBlocks.add(block);
                    if (_line.endsWith(multiLineJs[1])) {
                        block.str = line.substring(line.indexOf(multiLineJs[0])+multiLineJs[0].length(), line.lastIndexOf(multiLineJs[1]));
                        continue;
                    }
                    else {
                        block.str = line.substring(line.indexOf(multiLineJs[0])+multiLineJs[0].length());
                    }
                    while ((line = br.readLine()) != null) {
                        lineNumber++;
                        lines.add(line);
                        _line = line.trim();
                        block = new JstStatmentBlock();
                        block.lineNumber = lineNumber;
                        jstBlocks.add(block);
                        block.str = line;
                        if (_line.endsWith(multiLineJs[1])) {
                            block.str = line.substring(0, line.lastIndexOf(multiLineJs[1]));
                            break;
                        }
                    }
                    continue;
                }
                if (line.contains("${")) {
                    int start = 0;
                    while (true) {
                        int new_start = line.indexOf("${", start);
                        if (new_start == -1) {
                            if (line.length() > start) {
                                addStaticBlock(line.substring(start), lineNumber);
                            }
                            addStaticBlock("\r\n", lineNumber);
                            break;
                        }
                        // 如果是 $${ 跳过解析
                        if (new_start > 0 && line.charAt(new_start - 1) == '$') {
                            addStaticBlock(line.substring(start, new_start) + "{", lineNumber);
                            start = new_start + 2;
                            continue;
                        }
                        if (new_start > start) {
                            addStaticBlock(line.substring(start, new_start), lineNumber);
                        }
                        int end = line.indexOf("}", new_start);
                        if (end < 1) {
                            throw new RuntimeException("miss } at line " + lineNumber);
                        }
                        jstBlocks.add(new JstEvalBlock(line.substring(new_start + 2, end), lineNumber));
                        start = end + 1;
                    }
                } else {
                    addStaticBlock(line + "\r\n", lineNumber);
                }
            }
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    protected void addStaticBlock(String str, int lineNumber) {
        if (jstBlocks.isEmpty()) {
            jstBlocks.add(new JstStaticBlock(str, lineNumber));
            return;
        }
        JstBlock jstBlock = jstBlocks.get(jstBlocks.size() - 1);
        if (jstBlock instanceof JstStaticBlock && ((JstStaticBlock) jstBlock).lineNumber == lineNumber) {
            ((JstStaticBlock) jstBlock).str += str;
            return;
        }
        jstBlocks.add(new JstStaticBlock(str, lineNumber));
    }

    /**
     * 生成js文本并编译,如果已经生成过,则直接返回
     */
    public String compile() {
        return compile(false);
    }

    /**
     * 生成js文本并编译
     * @param force 是否强制重新生成
     */
    public String compile(boolean force) {
        if (force || jsStr == null) {
            parse(new BufferedReader(new StringReader(source)));
            StringBuilder sb = new StringBuilder();
            sb.append("var $f = function(){");
            JstBlock[] blocks = this.jstBlocks.toArray(new JstBlock[this.jstBlocks.size()]);
            JstBlock prev = null;
            for (int i = 0; i < blocks.length; i++) {
                JstBlock jstBlock = blocks[i];
                if (prev != null && prev.lineNumber != jstBlock.lineNumber) {
                    sb.append("\r\n");
                }
                if (jstBlock instanceof JstStaticBlock) {
                    sb.append("$out.write($strs[").append(strs.size()).append("]);");
                    strs.add(jstBlock.str);
                } else {
                    jstBlock.asJs(sb);
                }
                prev = jstBlock;
            }
            sb.append("};$f();");
            jsStr = sb.toString();
        }
        if (force || compiledScript == null) {
            try {
                compiledScript = ((Compilable) engine).compile(jsStr);
            }
            catch (ScriptException e) {
                throw makeException(e);
            }
        }
        return jsStr;
    }

    /**
     * 渲染并写入Writer
     */
    public Object render(Context ctx, Writer w) {
        try {
            ScriptContext newContext = new SimpleScriptContext();
            Bindings bindings = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
            bindings.putAll(ctx.getInnerMap());
            bindings.put("$out", w);
            bindings.put("$strs", strs);
            bindings.putAll(Jst.getGlobal());
            newContext.setAttribute(ScriptEngine.FILENAME, name, ScriptContext.ENGINE_SCOPE);
            if (debug || compiledScript == null)
                return engine.eval(jsStr, newContext);
            return compiledScript.eval(newContext);
        }
        catch (ScriptException e) {
            throw makeException(e);
        }
    }

    /**
     * 使用ctx作为上下文渲染,并返回字符串
     */
    public String render(Context ctx) {
        StringWriter w = new StringWriter();
        render(ctx, w);
        return w.toString();
    }

    /**
     * 根据ScriptException生成带模板源码的报错信息
     */
    protected JstException makeException(ScriptException e) {
        int lineNumber = e.getLineNumber();
        StringBuilder sb = new StringBuilder(e.getMessage());
        for (int i = lineNumber - 2; i <= lineNumber + 2; i++) {
            if (i < 1)
                continue;
            if (i > lines.size())
                break;
            sb.append(String.format("\r\n%3d %s %s", i, i == lineNumber ? '>' : ':', lines.get(i - 1)));
        }
        String msg = sb.toString();
        Throwable cause = e.getCause();
        if (cause == null)
            return new JstException(msg);
        return new JstException(msg, cause);
    }

    /**
     * 获取内部Block序列
     */
    public List getBlocks() {
        return jstBlocks;
    }

    /**
     * 设置文件名,在debug模式下能打印到报错信息中
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 设置debug模式,默认是false
     */
    public void setDebug(boolean debug) {
        this.debug = debug;
    }
    
    /**
     * 为当前模板独立设置一个引擎
     * @param engine
     */
    public void setEngine(ScriptEngine engine) {
        this.engine = engine;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy