Enjoy 3.2 release ^_^

This commit is contained in:
James
2017-07-31 22:34:15 +08:00
parent 46a7c60813
commit b82af8e219
107 changed files with 12029 additions and 192 deletions

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 java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.stat.Scope;
/**
* Break
* java 中 break、continue 可出现在 for 中的最后一行,不一定要套在 if 中
*/
public class Break extends Stat {
public static final Break me = new Break();
private Break() {
}
public void exec(Env env, Scope scope, Writer writer) {
scope.getCtrl().setBreak();
}
}

View File

@@ -0,0 +1,57 @@
/**
* 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 java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.stat.Scope;
/**
* Call 调用模板函数,两种用法:
* 1常规调用
* #@funcName(p1, p2, ..., pn)
* 2安全调用函数被定义才调用否则跳过
* #@funcName?(p1, p2, ..., pn)
*
* 注意:在函数名前面引入 '@' 字符是为了区分模板函数和指令
*/
public class Call extends Stat {
private String funcName;
private ExprList exprList;
private boolean callIfDefined;
public Call(String funcName, ExprList exprList, boolean callIfDefined) {
this.funcName = funcName;
this.exprList = exprList;
this.callIfDefined = callIfDefined;
}
public void exec(Env env, Scope scope, Writer writer) {
Define function = env.getFunction(funcName);
if (function != null) {
function.call(env, scope, exprList, writer);
} else if (callIfDefined) {
return ;
} else {
throw new TemplateException("Template function not defined: " + funcName, location);
}
}
}

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 java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.stat.Scope;
/**
* Continue
*/
public class Continue extends Stat {
public static final Continue me = new Continue();
private Continue() {
}
public void exec(Env env, Scope scope, Writer writer) {
scope.getCtrl().setContinue();
}
}

View File

@@ -0,0 +1,141 @@
/**
* 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 java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
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;
/**
* Define 定义模板函数:
* #define funcName(p1, p2, ..., pn)
* body
* #end
*
* 模板函数类型:
* 1全局共享的模板函数
* 通过 engine.addSharedFunction(...) 添加,所有模板中可调用
* 2模板中定义的局部模板函数
* 在模板中定义的模板函数,只在本模板中有效
*
* 高级用法:
* 1局部模板函数可以与全局共享模板函数同名调用时优先调用模板内模板数
* 2模板内部不能定义同名的局部模板函数
*/
public class Define extends Stat {
private static final String[] NULL_PARAMETER_NAMES = new String[0];
private String functionName;
private String[] parameterNames;
private Stat stat;
public Define(String functionName, ExprList exprList, Stat stat, Location location) {
setLocation(location);
this.functionName = functionName;
this.stat = stat;
Expr[] exprArray = exprList.getExprArray();
if (exprArray.length == 0) {
this.parameterNames = NULL_PARAMETER_NAMES;
return ;
}
parameterNames = new String[exprArray.length];
for (int i=0; i<exprArray.length; i++) {
if (exprArray[i] instanceof Id) {
parameterNames[i] = ((Id)exprArray[i]).getId();
} else {
throw new ParseException("The parameter of template function definition must be identifier", location);
}
}
}
public String getFunctionName() {
return functionName;
}
public String[] getParameterNames() {
return parameterNames;
}
/**
* Define 的继承类可以覆盖此方法实现一些 register 类的动作
*/
public void exec(Env env, Scope scope, Writer writer) {
}
/**
* 真正调用模板函数
*/
public void call(Env env, Scope scope, ExprList exprList, Writer writer) {
if (exprList.length() != parameterNames.length) {
throw new TemplateException("Wrong number of argument to call the template function, right number is: " + parameterNames.length, location);
}
scope = new Scope(scope);
if (exprList.length() > 0) {
Object[] parameterValues = exprList.evalExprList(scope);
for (int i=0; i<parameterValues.length; i++) {
scope.setLocal(parameterNames[i], parameterValues[i]); // 参数赋值
}
}
stat.exec(env, scope, writer);
scope.getCtrl().setJumpNone(); // #define 中的 return、continue、break 全部在此消化
}
public String toString() {
StringBuilder ret = new StringBuilder();
ret.append("#define ").append(functionName).append("(");
for (int i=0; i<parameterNames.length; i++) {
if (i > 0) {
ret.append(", ");
}
ret.append(parameterNames[i]);
}
return ret.append(")").toString();
}
// -----------------------------------------------------------------------
/**
* envForDevMode 属性性以及相关方法仅用于 devMode 判断当前 #define 指令所在资源是否被修改
* 仅用于 EngineConfig 中处理 shared function 的逻辑
*/
private Env envForDevMode;
public void setEnvForDevMode(Env envForDevMode) {
this.envForDevMode = envForDevMode;
}
public boolean isSourceModifiedForDevMode() {
if (envForDevMode == null) {
throw new IllegalStateException("Check engine config: setDevMode(...) must be invoked before addSharedFunction(...)");
}
return envForDevMode.isSourceListModified();
}
}

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 java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.stat.Scope;
/**
* Else
*/
public class Else extends Stat {
private Stat stat;
public Else(Stat stat) {
this.stat = stat;
}
public void exec(Env env, Scope scope, Writer writer) {
stat.exec(env, scope, writer);
}
}

View File

@@ -0,0 +1,63 @@
/**
* 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 java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.expr.ast.Logic;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
/**
* ElseIf
*/
public class ElseIf extends Stat {
private ExprList cond;
private Stat stat;
private Stat elseIfOrElse;
public ElseIf(ExprList cond, Stat stat, Location location) {
if (cond.length() == 0) {
throw new ParseException("The condition expression of #elseif statement can not be blank", location);
}
this.cond = cond;
this.stat = stat;
}
/**
* take over setStat(...) method of super class
*/
public void setStat(Stat elseIfOrElse) {
this.elseIfOrElse = elseIfOrElse;
}
public void exec(Env env, Scope scope, Writer writer) {
if (Logic.isTrue(cond.eval(scope))) {
stat.exec(env, scope, writer);
} else if (elseIfOrElse != null) {
elseIfOrElse.exec(env, scope, writer);
}
}
}

View File

@@ -0,0 +1,140 @@
/**
* 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 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.stat.Ctrl;
import com.jfinal.template.stat.Scope;
/**
* For 循环控制,支持 List、Map、数组、Collection、Iterator、Iterable
* Enumeration、null 以及任意单个对象的迭代,简单说是支持所有对象迭代
*
* 主要用法:
* 1#for(item : list) #(item) #end
* 2#for(item : list) #(item) #else content #end
* 3#for(i=0; i<9; i++) #(item) #end
* 4#for(i=0; i<9; i++) #(item) #else content #end
*/
public class For extends Stat {
private ForCtrl forCtrl;
private StatList statList;
private Stat _else;
public For(ForCtrl forCtrl, StatList statList, Stat _else) {
this.forCtrl = forCtrl;
this.statList = statList;
this._else = _else;
}
public void exec(Env env, Scope scope, Writer writer) {
scope = new Scope(scope);
if (forCtrl.isIterator()) {
forIterator(env, scope, writer);
} else {
forLoop(env, scope, writer);
}
}
/**
* #for( id : expr)
*/
private void forIterator(Env env, Scope scope, Writer writer) {
Ctrl ctrl = scope.getCtrl();
Object outer = scope.get("for");
ctrl.setLocalAssignment();
ForIteratorStatus forIteratorStatus = new ForIteratorStatus(outer, forCtrl.getExpr().eval(scope), location);
ctrl.setWisdomAssignment();
scope.setLocal("for", forIteratorStatus);
Iterator<?> it = forIteratorStatus.getIterator();
String itemName = forCtrl.getId();
while(it.hasNext()) {
scope.setLocal(itemName, it.next());
statList.exec(env, scope, writer);
forIteratorStatus.nextState();
if (ctrl.isJump()) {
if (ctrl.isBreak()) {
ctrl.setJumpNone();
break ;
} else if (ctrl.isContinue()) {
ctrl.setJumpNone();
continue ;
} else {
return ;
}
}
}
if (_else != null && forIteratorStatus.getIndex() == 0) {
_else.exec(env, scope, writer);
}
}
/**
* #for(exprList; cond; update)
*/
private void forLoop(Env env, Scope scope, Writer writer) {
Ctrl ctrl = scope.getCtrl();
Object outer = scope.get("for");
ForLoopStatus forLoopStatus = new ForLoopStatus(outer);
scope.setLocal("for", forLoopStatus);
Expr init = forCtrl.getInit();
Expr cond = forCtrl.getCond();
Expr update = forCtrl.getUpdate();
ctrl.setLocalAssignment();
for (init.eval(scope); cond == null || Logic.isTrue(cond.eval(scope)); update.eval(scope)) {
ctrl.setWisdomAssignment();
statList.exec(env, scope, writer);
ctrl.setLocalAssignment();
forLoopStatus.nextState();
if (ctrl.isJump()) {
if (ctrl.isBreak()) {
ctrl.setJumpNone();
break ;
} else if (ctrl.isContinue()) {
ctrl.setJumpNone();
continue ;
} else {
ctrl.setWisdomAssignment();
return ;
}
}
}
ctrl.setWisdomAssignment();
if (_else != null && forLoopStatus.getIndex() == 0) {
_else.exec(env, scope, writer);
}
}
}

View File

@@ -0,0 +1,46 @@
/**
* 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 java.util.Map.Entry;
/**
* ForEntry 包装 HashMap、LinkedHashMap 等 Map 类型的 Entry 对象
*/
public class ForEntry implements Entry<Object, Object> {
private Entry<Object, Object> entry;
public ForEntry(Entry<Object, Object> entry) {
this.entry = entry;
}
public Object getKey() {
return entry.getKey();
}
public Object getValue() {
return entry.getValue();
}
public Object setValue(Object value) {
return entry.setValue(value);
}
}

View File

@@ -0,0 +1,244 @@
/**
* 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 java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import com.jfinal.template.TemplateException;
import com.jfinal.template.stat.Location;
/**
* ForIteratorStatus
* 封装 #for( id : expr) 迭代语句状态,便于模板中获取
*
* 使用以下表达式可以模板中获取迭代状态:
* for.size 被迭代集合元素数量,不支持 Iterator 与 Iterable
* for.index 从 0 下始的下标
* for.count 从 1 开始的计数器
* for.first 是否第一个元素
* for.last 是否最后一个元素
* for.odd 是否第奇数个元素
* for.even 是否第偶数个元素
* for.outer 获取外层 for 对象,便于获取外层 for 循环状态
* 例如: for.outer.index
*/
public class ForIteratorStatus {
private Object outer;
private int index;
private int size;
private Iterator<?> iterator;
private Location location;
public ForIteratorStatus(Object outer, Object target, Location location) {
this.outer = outer;
this.index = 0;
this.location = location;
init(target);
}
@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();
return ;
}
if (target instanceof Map<?, ?>) {
size = ((Map<?, ?>)target).size();
iterator = new MapIterator(((Map<Object, Object>)target).entrySet().iterator());
return ;
}
if (target.getClass().isArray()) {
size = Array.getLength(target);
iterator = new ArrayIterator(target, size);
return ;
}
if (target instanceof Iterator) {
size = -1;
iterator = (Iterator<?>)target;
return ;
}
if (target instanceof Iterable) {
size = -1;
iterator = ((Iterable<?>)target).iterator();
return ;
}
if (target instanceof Enumeration) {
ArrayList<?> list = Collections.list((Enumeration<?>)target);
size = list.size();
iterator = list.iterator();
return ;
}
size = 1;
iterator = new SingleObjectIterator(target);
}
Iterator<?> getIterator() {
return iterator;
}
void nextState() {
index++;
}
public Object getOuter() {
return outer;
}
public int getIndex() {
return index;
}
public int getCount() {
return index + 1;
}
public int getSize() {
if (size >= 0) {
return size;
}
throw new TemplateException("No such method getSize() of the iterator", location);
}
public boolean getFirst() {
return index == 0;
}
public boolean getLast() {
return !iterator.hasNext();
}
public boolean getOdd() {
return index % 2 == 0;
}
public boolean getEven() {
return index % 2 != 0;
}
}
class MapIterator implements Iterator<Entry<Object, Object>> {
private Iterator<Entry<Object, Object>> iterator;
public MapIterator(Iterator<Entry<Object, Object>> iterator) {
this.iterator = iterator;
}
public boolean hasNext() {
return iterator.hasNext();
}
public Entry<Object, Object> next() {
return new ForEntry((Entry<Object, Object>)iterator.next());
}
public void remove() {
throw new UnsupportedOperationException();
}
}
class ArrayIterator implements Iterator<Object> {
private Object array;
private int size;
private int index;
ArrayIterator(Object array, int size) {
this.array = array;
this.size = size;
this.index = 0;
}
public boolean hasNext() {
return index < size;
}
public Object next() {
return Array.get(array, index++);
}
public void remove() {
throw new UnsupportedOperationException();
}
}
class SingleObjectIterator implements Iterator<Object> {
private Object target;
private boolean hasNext = true;
public SingleObjectIterator(Object target) {
this.target = target;
}
public boolean hasNext() {
return hasNext;
}
public Object next() {
if (hasNext) {
hasNext = false;
return target;
}
throw new NoSuchElementException();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
class NullIterator implements Iterator<Object> {
static final Iterator<?> me = new NullIterator();
private NullIterator() {
}
public boolean hasNext() {
return false;
}
public Object next() {
throw new NoSuchElementException();
}
public void remove() {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,74 @@
/**
* 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;
/**
* ForLoopStatus
* 封装 #for( init; cond; update) 循环的状态,便于模板中获取
*
* 如下表达式可从模板中获取循环状态:
* for.index 从 0 下始的下标
* for.count 从 1 开始的计数器
* for.first 是否第一个元素
* for.odd 是否第奇数个元素
* for.even 是否第偶数个元素
* for.outer 获取外层 for 对象,便于获取外层 for 循环状态
* 例如: for.outer.index
*
* 注意比迭代型循环语句少支持两个状态取值表达式for.size、for.last
*/
public class ForLoopStatus {
private Object outer;
private int index;
public ForLoopStatus(Object outer) {
this.outer = outer;
this.index = 0;
}
void nextState() {
index++;
}
public Object getOuter() {
return outer;
}
public int getIndex() {
return index;
}
public int getCount() {
return index + 1;
}
public boolean getFirst() {
return index == 0;
}
public boolean getOdd() {
return index % 2 == 0;
}
public boolean getEven() {
return index % 2 != 0;
}
}

View File

@@ -0,0 +1,61 @@
/**
* 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 java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.expr.ast.Logic;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
/**
* If
*/
public class If extends Stat {
private ExprList cond;
private Stat stat;
private Stat elseIfOrElse;
public If(ExprList cond, Stat stat, 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;
}
/**
* take over setStat(...) method of super class
*/
public void setStat(Stat elseIfOrElse) {
this.elseIfOrElse = elseIfOrElse;
}
public void exec(Env env, Scope scope, Writer writer) {
if (Logic.isTrue(cond.eval(scope))) {
stat.exec(env, scope, writer);
} else if (elseIfOrElse != null) {
elseIfOrElse.exec(env, scope, writer);
}
}
}

View File

@@ -0,0 +1,161 @@
/**
* 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 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.source.ISource;
import com.jfinal.template.stat.Ctrl;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Parser;
import com.jfinal.template.stat.Scope;
/**
* Include
*
* 1父模板被缓存时被 include 的模板会被间接缓存,无需关心缓存问题
* 2同一个模板文件被多个父模板 include所处的背景环境不同例如各父模板中定义的模板函数不同
* 各父模板所处的相对路径不同,所以多个父模板不能共用一次 parse 出来的结果而是在每个被include
* 的地方重新 parse
*
* <pre>
* 两种用法:
* 1只传入一个参数参数必须是 String 常量,如果希望第一个参数是变量可以使用 #render 指令去实现
* #include("_hot.html")
*
* 2传入任意多个参数除第一个参数以外的所有参数必须是赋值表达式用于实现参数传递功能
* #include("_hot.html", title = "热门新闻", list = newsList)
*
* 上例中传递了 title、list 两个参数,可以代替父模板中的 #set 指令传参方式
* 并且此方式传入的参数只在子模板作用域有效,不会污染父模板作用域
*
* 这种传参方式有利于将子模板模块化,例如上例的调用改成如下的参数:
* #include("_hot.html", title = "热门项目", list = projectList)
* 通过这种传参方式在子模板 _hot.html 之中,完全不需要修改对于 title 与 list
* 这两个变量的处理代码,就实现了对 “热门项目” 数据的渲染
* </pre>
*/
public class Include extends Stat {
private Assign[] assignArray;
private Stat stat;
public Include(Env env, ExprList exprList, String parentFileName, Location location) {
int len = exprList.length();
if (len == 0) {
throw new ParseException("The parameter of #include directive can not be blank", location);
}
// 第一个参数必须为 String 类型
Expr expr = exprList.getExpr(0);
if (expr instanceof Const && ((Const)expr).isStr()) {
} else {
throw new ParseException("The first parameter of #include directive must be String", location);
}
// 其它参数必须为赋值表达式
if (len > 1) {
for (int i = 1; i < len; i++) {
if (!(exprList.getExpr(i) instanceof Assign)) {
throw new ParseException("The " + i + "th parameter of #include directive must be an assignment expression", location);
}
}
}
parseSubTemplate(env, ((Const)expr).getStr(), parentFileName, location);
getAssignExpression(exprList);
}
private void parseSubTemplate(Env env, String fileName, String parentFileName, Location location) {
String subFileName = getSubFileName(fileName, parentFileName);
EngineConfig config = env.getEngineConfig();
// FileSource fileSource = new FileSource(config.getBaseTemplatePath(), subFileName, config.getEncoding());
ISource fileSource = config.getSourceFactory().getSource(config.getBaseTemplatePath(), subFileName, config.getEncoding());
try {
Parser parser = new Parser(env, fileSource.getContent(), subFileName);
if (config.isDevMode()) {
env.addSource(fileSource);
}
this.stat = parser.parse();
} catch (Exception e) {
// 文件路径不正确抛出异常时添加 location 信息
throw new ParseException(e.getMessage(), location, e);
}
}
/**
* 获取在父模板之下子模板的最终文件名,子模板目录相对于父模板文件目录来确定
* 以 "/" 打头则以 baseTemplatePath 为根,否则以父文件所在路径为根
*/
public static String getSubFileName(String fileName, String parentFileName) {
if (parentFileName == null) {
return fileName;
}
if (fileName.startsWith("/")) {
return fileName;
}
int index = parentFileName.lastIndexOf('/');
if (index == -1) {
return fileName;
}
return parentFileName.substring(0, index + 1) + fileName;
}
private void getAssignExpression(ExprList exprList) {
int len = exprList.length();
if (len > 1) {
assignArray = new Assign[len - 1];
for (int i = 0; i < assignArray.length; i++) {
assignArray[i] = (Assign)exprList.getExpr(i + 1);
}
} else {
assignArray = null;
}
}
public void exec(Env env, Scope scope, Writer writer) {
scope = new Scope(scope);
if (assignArray != null) {
evalAssignExpression(scope);
}
stat.exec(env, scope, writer);
scope.getCtrl().setJumpNone();
}
private void evalAssignExpression(Scope scope) {
Ctrl ctrl = scope.getCtrl();
try {
ctrl.setLocalAssignment();
for (Assign assign : assignArray) {
assign.eval(scope);
}
} finally {
ctrl.setWisdomAssignment();
}
}
}

View File

@@ -0,0 +1,63 @@
/**
* 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 java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
/**
* Output 输出指令
*
* 用法:
* 1#(value)
* 2#(x = 1, y = 2, x + y)
* 3#(seoTitle ?? 'JFinal 极速开发社区')
*/
public class Output extends Stat {
private ExprList exprList;
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;
}
public void exec(Env env, Scope scope, Writer writer) {
try {
Object value = exprList.eval(scope);
if (value != null) {
String str = value.toString();
writer.write(str, 0, str.length());
}
} catch(TemplateException e) {
throw e;
} catch(Exception e) {
throw new TemplateException(e.getMessage(), location, e);
}
}
}

View File

@@ -0,0 +1,44 @@
/**
* 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 java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.stat.Scope;
/**
* Return
* 通常用于 #define 指令内部,不支持返回值
*/
public class Return extends Stat {
public static final Return me = new Return();
private Return() {
}
public void exec(Env env, Scope scope, Writer writer) {
scope.getCtrl().setReturn();
}
}

View File

@@ -0,0 +1,59 @@
/**
* 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 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.stat.Location;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
/**
* Set 赋值,从内向外作用域查找变量,找到则替换变量值,否则在顶层作用域赋值
*
* 用法:
* 1#set(k = v)
* 2#set(k1 = v1, k2 = v2, ..., kn = vn)
* 3#set(x = 1+2)
* 4#set(x = 1+2, y = 3>4, ..., z = c ? a : b)
*/
public class Set extends Stat {
private ExprList exprList;
public Set(ExprList exprList, Location location) {
if (exprList.length() == 0) {
throw new ParseException("The parameter of #set directive can not be blank", location);
}
for (Expr expr : exprList.getExprArray()) {
if ( !(expr instanceof Assign) ) {
throw new ParseException("#set directive only supports assignment expressions", location);
}
}
this.exprList = exprList;
}
public void exec(Env env, Scope scope, Writer writer) {
scope.getCtrl().setWisdomAssignment();
exprList.eval(scope);
}
}

View File

@@ -0,0 +1,65 @@
/**
* 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 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.stat.Ctrl;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
/**
* SetLocal 设置全局变量,全局作用域是指本次请求的整个 template
*
* 适用于极少数的在内层作用域中希望直接操作顶层作用域的场景
*/
public class SetGlobal extends Stat {
private ExprList exprList;
public SetGlobal(ExprList exprList, Location location) {
if (exprList.length() == 0) {
throw new ParseException("The parameter of #setGlobal directive can not be blank", location);
}
for (Expr expr : exprList.getExprArray()) {
if ( !(expr instanceof Assign) ) {
throw new ParseException("#setGlobal directive only supports assignment expressions", location);
}
}
this.exprList = exprList;
}
public void exec(Env env, Scope scope, Writer writer) {
Ctrl ctrl = scope.getCtrl();
try {
ctrl.setGlobalAssignment();
exprList.eval(scope);
} finally {
ctrl.setWisdomAssignment();
}
}
}

View File

@@ -0,0 +1,66 @@
/**
* 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 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.stat.Ctrl;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
/**
* SetLocal 设置局部变量
*
* 通常用于 #define #include 指令内部需要与外层作用域区分,以便于定义重用型模块的场景
* 也常用于 #for 循环内部的临时变量
*/
public class SetLocal extends Stat {
final ExprList exprList;
public SetLocal(ExprList exprList, Location location) {
if (exprList.length() == 0) {
throw new ParseException("The parameter of #setLocal directive can not be blank", location);
}
for (Expr expr : exprList.getExprArray()) {
if ( !(expr instanceof Assign) ) {
throw new ParseException("#setLocal directive only supports assignment expressions", location);
}
}
this.exprList = exprList;
}
public void exec(Env env, Scope scope, Writer writer) {
Ctrl ctrl = scope.getCtrl();
try {
ctrl.setLocalAssignment();
exprList.eval(scope);
} finally {
ctrl.setWisdomAssignment();
}
}
}

View File

@@ -0,0 +1,77 @@
/**
* 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 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.stat.Location;
import com.jfinal.template.stat.Scope;
/**
* Stat
*/
public abstract class Stat {
protected Location location;
public Stat setLocation(Location location) {
this.location = location;
return this;
}
public Location getLocation() {
return location;
}
public void setExprList(ExprList exprList) {
}
public void setStat(Stat stat) {
}
public abstract void exec(Env env, Scope scope, Writer writer);
public boolean hasEnd() {
return false;
}
protected void write(Writer writer, String str) {
try {
writer.write(str, 0, str.length());
} catch (IOException e) {
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

@@ -0,0 +1,64 @@
/**
* 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 java.io.Writer;
import java.util.List;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.stat.Ctrl;
import com.jfinal.template.stat.Scope;
/**
* StatList
*/
public class StatList extends Stat {
public static final Stat[] NULL_STATS = 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;
}
}
public void exec(Env env, Scope scope, Writer writer) {
Ctrl ctrl = scope.getCtrl();
for (Stat stat : statArray) {
if (ctrl.isJump()) {
break ;
}
stat.exec(env, scope, writer);
}
}
public int length() {
return statArray.length;
}
public Stat getStat(int index) {
if (index < 0 || index >= statArray.length) {
throw new TemplateException("Index out of bounds: index = " + index + ", length = " + statArray.length, location);
}
return statArray[index];
}
}

View File

@@ -0,0 +1,59 @@
/**
* 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 java.io.IOException;
import java.io.Writer;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.stat.Scope;
/**
* Text 输出纯文本块以及使用 "#[[" 与 "]]#" 指定的非解析块
*/
public class Text extends Stat {
private char[] text;
public Text(StringBuilder content) {
this.text = new char[content.length()];
content.getChars(0, content.length(), this.text, 0);
}
public void exec(Env env, Scope scope, Writer writer) {
try {
writer.write(text, 0, text.length);
} catch (IOException e) {
throw new TemplateException(e.getMessage(), location, e);
}
}
public boolean isEmpty() {
return text.length == 0;
}
public String getContent() {
return text != null ? new String(text) : null;
}
public String toString() {
return text != null ? new String(text) : "";
}
}