org.nutz.jst.impl.JstImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jst Show documentation
Show all versions of jst Show documentation
JST is template system base on nashorn
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