enjoy 3.3 release ^_^

This commit is contained in:
James
2017-11-21 22:43:34 +08:00
parent 28eb105ffa
commit 61aa1d2082
70 changed files with 3378 additions and 299 deletions

View File

@@ -16,8 +16,8 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope;
/**

View File

@@ -16,10 +16,10 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope;
/**

View File

@@ -16,8 +16,8 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope;
/**

View File

@@ -16,7 +16,6 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.stat.Location;
@@ -25,6 +24,7 @@ import com.jfinal.template.stat.Scope;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.expr.ast.Id;
import com.jfinal.template.io.Writer;
/**
* Define 定义模板函数:
@@ -50,10 +50,10 @@ public class Define extends Stat {
private String[] parameterNames;
private Stat stat;
public Define(String functionName, ExprList exprList, Stat stat, Location location) {
public Define(String functionName, ExprList exprList, StatList statList, Location location) {
setLocation(location);
this.functionName = functionName;
this.stat = stat;
this.stat = statList.getActualStat();
Expr[] exprArray = exprList.getExprArray();
if (exprArray.length == 0) {

View File

@@ -16,8 +16,8 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope;
/**
@@ -27,8 +27,8 @@ public class Else extends Stat {
private Stat stat;
public Else(Stat stat) {
this.stat = stat;
public Else(StatList statList) {
this.stat = statList.getActualStat();
}
public void exec(Env env, Scope scope, Writer writer) {

View File

@@ -16,10 +16,11 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.expr.ast.Logic;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
@@ -29,16 +30,16 @@ import com.jfinal.template.stat.Scope;
*/
public class ElseIf extends Stat {
private ExprList cond;
private Expr cond;
private Stat stat;
private Stat elseIfOrElse;
public ElseIf(ExprList cond, Stat stat, Location location) {
public ElseIf(ExprList cond, StatList statList, Location location) {
if (cond.length() == 0) {
throw new ParseException("The condition expression of #elseif statement can not be blank", location);
throw new ParseException("The condition expression of #else if statement can not be blank", location);
}
this.cond = cond;
this.stat = stat;
this.cond = cond.getActualExpr();
this.stat = statList.getActualStat();
}
/**

View File

@@ -16,12 +16,12 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import java.util.Iterator;
import com.jfinal.template.Env;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ForCtrl;
import com.jfinal.template.expr.ast.Logic;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Ctrl;
import com.jfinal.template.stat.Scope;
@@ -38,12 +38,12 @@ import com.jfinal.template.stat.Scope;
public class For extends Stat {
private ForCtrl forCtrl;
private StatList statList;
private Stat stat;
private Stat _else;
public For(ForCtrl forCtrl, StatList statList, Stat _else) {
this.forCtrl = forCtrl;
this.statList = statList;
this.stat = statList.getActualStat();
this._else = _else;
}
@@ -71,7 +71,7 @@ public class For extends Stat {
String itemName = forCtrl.getId();
while(it.hasNext()) {
scope.setLocal(itemName, it.next());
statList.exec(env, scope, writer);
stat.exec(env, scope, writer);
forIteratorStatus.nextState();
if (ctrl.isJump()) {
@@ -108,7 +108,7 @@ public class For extends Stat {
ctrl.setLocalAssignment();
for (init.eval(scope); cond == null || Logic.isTrue(cond.eval(scope)); update.eval(scope)) {
ctrl.setWisdomAssignment();
statList.exec(env, scope, writer);
stat.exec(env, scope, writer);
ctrl.setLocalAssignment();
forLoopStatus.nextState();

View File

@@ -25,7 +25,7 @@ public class ForEntry implements Entry<Object, Object> {
private Entry<Object, Object> entry;
public ForEntry(Entry<Object, Object> entry) {
public void init(Entry<Object, Object> entry) {
this.entry = entry;
}

View File

@@ -60,11 +60,6 @@ public class ForIteratorStatus {
@SuppressWarnings("unchecked")
private void init(Object target) {
if (target == null) {
size = 0;
iterator = NullIterator.me;
return ;
}
if (target instanceof Collection) {
size = ((Collection<?>)target).size();
iterator = ((Collection<?>)target).iterator();
@@ -75,6 +70,11 @@ public class ForIteratorStatus {
iterator = new MapIterator(((Map<Object, Object>)target).entrySet().iterator());
return ;
}
if (target == null) { // 必须放在 target.getClass() 之前,避免空指针异常
size = 0;
iterator = NullIterator.me;
return ;
}
if (target.getClass().isArray()) {
size = Array.getLength(target);
iterator = new ArrayIterator(target, size);
@@ -148,6 +148,7 @@ public class ForIteratorStatus {
class MapIterator implements Iterator<Entry<Object, Object>> {
private Iterator<Entry<Object, Object>> iterator;
private ForEntry forEntry = new ForEntry();
public MapIterator(Iterator<Entry<Object, Object>> iterator) {
this.iterator = iterator;
@@ -158,7 +159,8 @@ class MapIterator implements Iterator<Entry<Object, Object>> {
}
public Entry<Object, Object> next() {
return new ForEntry((Entry<Object, Object>)iterator.next());
forEntry.init(iterator.next());
return forEntry;
}
public void remove() {

View File

@@ -16,10 +16,11 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.expr.ast.Logic;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
@@ -29,16 +30,16 @@ import com.jfinal.template.stat.Scope;
*/
public class If extends Stat {
private ExprList cond;
private Expr cond;
private Stat stat;
private Stat elseIfOrElse;
public If(ExprList cond, Stat stat, Location location) {
public If(ExprList cond, StatList statList, Location location) {
if (cond.length() == 0) {
throw new ParseException("The condition expression of #if statement can not be blank", location);
}
this.cond = cond;
this.stat = stat;
this.cond = cond.getActualExpr();
this.stat = statList.getActualStat();
}
/**

View File

@@ -16,13 +16,13 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.EngineConfig;
import com.jfinal.template.Env;
import com.jfinal.template.expr.ast.Assign;
import com.jfinal.template.expr.ast.Const;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.source.ISource;
import com.jfinal.template.stat.Ctrl;
import com.jfinal.template.stat.Location;
@@ -94,7 +94,7 @@ public class Include extends Stat {
if (config.isDevMode()) {
env.addSource(fileSource);
}
this.stat = parser.parse();
this.stat = parser.parse().getActualStat();
} catch (Exception e) {
// 文件路径不正确抛出异常时添加 location 信息
throw new ParseException(e.getMessage(), location, e);

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.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 com.jfinal.template.stat.ast;
import com.jfinal.template.Env;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope;
/**
* NullStat
*/
public class NullStat extends Stat {
public static final NullStat me = new NullStat();
private NullStat() {}
public void exec(Env env, Scope scope, Writer writer) {
}
}

View File

@@ -16,10 +16,11 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
@@ -34,21 +35,41 @@ import com.jfinal.template.stat.Scope;
*/
public class Output extends Stat {
private ExprList exprList;
private Expr expr;
public Output(ExprList exprList, Location location) {
if (exprList.length() == 0) {
throw new ParseException("The expression of output directive like #(expression) can not be blank", location);
}
this.exprList = exprList;
this.expr = exprList.getActualExpr();
}
public void exec(Env env, Scope scope, Writer writer) {
try {
Object value = exprList.eval(scope);
if (value != null) {
String str = value.toString();
Object value = expr.eval(scope);
if (value instanceof String) {
String str = (String)value;
writer.write(str, 0, str.length());
} else if (value instanceof Number) {
Class<?> c = value.getClass();
if (c == Integer.class) {
writer.write((Integer)value);
} else if (c == Long.class) {
writer.write((Long)value);
} else if (c == Double.class) {
writer.write((Double)value);
} else if (c == Float.class) {
writer.write((Float)value);
} else if (c == Short.class) {
writer.write((Short)value);
} else {
writer.write(value.toString());
}
} else if (value instanceof Boolean) {
writer.write((Boolean)value);
} else if (value != null) {
writer.write(value.toString());
}
} catch(TemplateException e) {
throw e;

View File

@@ -16,8 +16,8 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope;
/**

View File

@@ -16,11 +16,11 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.expr.ast.Assign;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
@@ -36,7 +36,7 @@ import com.jfinal.template.stat.Scope;
*/
public class Set extends Stat {
private ExprList exprList;
private Expr expr;
public Set(ExprList exprList, Location location) {
if (exprList.length() == 0) {
@@ -48,12 +48,12 @@ public class Set extends Stat {
throw new ParseException("#set directive only supports assignment expressions", location);
}
}
this.exprList = exprList;
this.expr = exprList.getActualExpr();
}
public void exec(Env env, Scope scope, Writer writer) {
scope.getCtrl().setWisdomAssignment();
exprList.eval(scope);
expr.eval(scope);
}
}

View File

@@ -16,11 +16,11 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.expr.ast.Assign;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Ctrl;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
@@ -33,7 +33,7 @@ import com.jfinal.template.stat.Scope;
*/
public class SetGlobal extends Stat {
private ExprList exprList;
private Expr expr;
public SetGlobal(ExprList exprList, Location location) {
if (exprList.length() == 0) {
@@ -45,14 +45,14 @@ public class SetGlobal extends Stat {
throw new ParseException("#setGlobal directive only supports assignment expressions", location);
}
}
this.exprList = exprList;
this.expr = exprList.getActualExpr();
}
public void exec(Env env, Scope scope, Writer writer) {
Ctrl ctrl = scope.getCtrl();
try {
ctrl.setGlobalAssignment();
exprList.eval(scope);
expr.eval(scope);
} finally {
ctrl.setWisdomAssignment();
}

View File

@@ -16,11 +16,11 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.expr.ast.Assign;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Ctrl;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
@@ -34,7 +34,7 @@ import com.jfinal.template.stat.Scope;
*/
public class SetLocal extends Stat {
final ExprList exprList;
final Expr expr;
public SetLocal(ExprList exprList, Location location) {
if (exprList.length() == 0) {
@@ -46,14 +46,14 @@ public class SetLocal extends Stat {
throw new ParseException("#setLocal directive only supports assignment expressions", location);
}
}
this.exprList = exprList;
this.expr = exprList.getActualExpr();
}
public void exec(Env env, Scope scope, Writer writer) {
Ctrl ctrl = scope.getCtrl();
try {
ctrl.setLocalAssignment();
exprList.eval(scope);
expr.eval(scope);
} finally {
ctrl.setWisdomAssignment();
}

View File

@@ -17,10 +17,10 @@
package com.jfinal.template.stat.ast;
import java.io.IOException;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.Scope;
@@ -59,19 +59,6 @@ public abstract class Stat {
throw new TemplateException(e.getMessage(), location, e);
}
}
protected void write(Writer writer, char[] chars) {
try {
writer.write(chars, 0, chars.length);
} catch (IOException e) {
throw new TemplateException(e.getMessage(), location, e);
}
}
}

View File

@@ -16,10 +16,10 @@
package com.jfinal.template.stat.ast;
import java.io.Writer;
import java.util.List;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Ctrl;
import com.jfinal.template.stat.Scope;
@@ -28,24 +28,44 @@ import com.jfinal.template.stat.Scope;
*/
public class StatList extends Stat {
public static final Stat[] NULL_STATS = new Stat[0];
public static final Stat NULL_STAT = NullStat.me;
public static final Stat[] NULL_STAT_ARRAY = new Stat[0];
private Stat[] statArray;
public StatList(List<Stat> statList) {
if (statList.size() > 0) {
this.statArray = statList.toArray(new Stat[statList.size()]);
} else {
this.statArray = NULL_STATS;
this.statArray = NULL_STAT_ARRAY;
}
}
/**
* 持有 StatList 的指令可以通过此方法提升 AST 执行性能
* 1当 statArray.length > 1 时返回 StatList 自身
* 2当 statArray.length == 1 时返回 statArray[0]
* 3其它情况返回 NullStat
*
* 意义在于,当满足前面两个条件时,避免掉了 StatList.exec(...) 方法中的判断与循环
*/
public Stat getActualStat() {
if (statArray.length > 1) {
return this;
} else if (statArray.length == 1) {
return statArray[0];
} else {
return NULL_STAT;
}
}
public void exec(Env env, Scope scope, Writer writer) {
Ctrl ctrl = scope.getCtrl();
for (Stat stat : statArray) {
for (int i=0; i<statArray.length; i++) {
if (ctrl.isJump()) {
break ;
}
stat.exec(env, scope, writer);
statArray[i].exec(env, scope, writer);
}
}

View File

@@ -17,41 +17,98 @@
package com.jfinal.template.stat.ast;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.Charset;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.io.IWritable;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope;
/**
* Text 输出纯文本块以及使用 "#[[" 与 "]]#" 指定的非解析块
*/
public class Text extends Stat {
public class Text extends Stat implements IWritable {
private char[] text;
// content、bytes、chars 三者必有一者不为 null
// 在 OutputStream、Writer 混合模式下 bytes、chars 同时不为null
private StringBuilder content;
private Charset charset;
private byte[] bytes;
private char[] chars;
public Text(StringBuilder content) {
this.text = new char[content.length()];
content.getChars(0, content.length(), this.text, 0);
// content 初始值在 Lexer 中已确保不为 null
public Text(StringBuilder content, String encoding) {
this.content = content;
this.charset = Charset.forName(encoding);
this.bytes = null;
this.chars = null;
}
public void exec(Env env, Scope scope, Writer writer) {
try {
writer.write(text, 0, text.length);
writer.write(this);
} catch (IOException e) {
throw new TemplateException(e.getMessage(), location, e);
}
}
public boolean isEmpty() {
return text.length == 0;
public byte[] getBytes() {
if (bytes != null) {
return bytes;
}
if (content != null) {
bytes = content.toString().getBytes(charset);
content = null;
return bytes;
}
bytes = new String(chars).getBytes(charset);
return bytes;
}
public String getContent() {
return text != null ? new String(text) : null;
public char[] getChars() {
if (chars != null) {
return chars;
}
if (content != null) {
char[] charsTemp = new char[content.length()];
content.getChars(0, content.length(), charsTemp, 0);
chars = charsTemp;
content = null;
return chars;
}
String strTemp = new String(bytes, charset);
char[] charsTemp = new char[strTemp.length()];
strTemp.getChars(0, strTemp.length(), charsTemp, 0);
chars = charsTemp;
return chars;
}
public boolean isEmpty() {
if (content != null) {
return content.length() == 0;
} else if (bytes != null) {
return bytes.length == 0;
} else {
return chars.length == 0;
}
}
// public String getContent() {
// return text != null ? new String(text) : null;
// }
public String toString() {
return text != null ? new String(text) : "";
if (bytes != null) {
return new String(bytes, charset);
} else if (chars != null) {
return new String(chars);
} else {
return content.toString();
}
}
}