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,114 @@
/**
* 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.ext.directive;
import java.io.Writer;
import java.text.SimpleDateFormat;
import com.jfinal.template.Directive;
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.stat.ParseException;
import com.jfinal.template.stat.Scope;
/**
* 不带参时,按默认 pattern 输出当前日期
*
* #date() 指令支持无参时获取当前指令,第一个参数 string 当成是 pattern
*
* 日期输出指令,第一个参数是被输出的 java.util.Date 对象或其子类对象
* 无第二个参数时按默认 patter 输出,第二个参数为 expr 表达式,表示 pattern
* 第二个为 date 时,表示当第一个为 null 时的默认值
*/
public class DateDirective extends Directive {
private Expr valueExpr;
private Expr datePatternExpr;
private int paraNum;
public void setExprList(ExprList exprList) {
this.paraNum = exprList.length();
if (paraNum > 2) {
throw new ParseException("Wrong number parameter of #date directive, two parameters allowed at most", location);
}
if (paraNum == 0) {
this.valueExpr = null;
this.datePatternExpr = null;
} else if (paraNum == 1) {
this.valueExpr = exprList.getExprArray()[0];
this.datePatternExpr = null;
} else if (paraNum == 2) {
this.valueExpr = exprList.getExprArray()[0];
this.datePatternExpr = exprList.getExprArray()[1];
}
}
public void exec(Env env, Scope scope, Writer writer) {
if (paraNum == 0) {
outputToday(env, writer);
} else if (paraNum == 1) {
outputWithoutDatePattern(env, scope, writer);
} else if (paraNum == 2) {
outputWithDatePattern(env, scope, writer);
}
}
private void outputToday(Env env, Writer writer) {
Object value = format(new java.util.Date(), env.getEngineConfig().getDatePattern());
write(writer, value.toString());
}
private void outputWithoutDatePattern(Env env, Scope scope, Writer writer) {
Object value = valueExpr.eval(scope);
if (value != null) {
value = format(value, env.getEngineConfig().getDatePattern());
write(writer, value.toString());
}
}
private void outputWithDatePattern(Env env, Scope scope, Writer writer) {
Object value = valueExpr.eval(scope);
if (value == null) {
return ;
}
Object dp = this.datePatternExpr.eval(scope);
if ( !(dp instanceof String) ) {
throw new TemplateException("The sencond parameter dataPattern of #date directive must be String", location);
}
value = format(value, (String)dp);
write(writer, value.toString());
}
private String format(Object value, String datePattern) {
try {
return new SimpleDateFormat(datePattern).format(value);
} catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e);
}
}
}

View File

@@ -0,0 +1,72 @@
/**
* 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.ext.directive;
import java.io.Writer;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.stat.Scope;
/**
* Escape 对字符串进行转义
* 用法:
* #escape(value)
*/
public class EscapeDirective extends Directive {
public void exec(Env env, Scope scope, Writer writer) {
Object value = exprList.eval(scope);
if (value != null) {
write(writer, escape(value.toString()));
}
}
// TODO 挪到 StrKit 中
private String escape(String str) {
if (str == null || str.length() == 0) {
return str;
}
int len = str.length();
StringBuilder ret = new StringBuilder(len * 2);
for (int i = 0; i < len; i++) {
char cur = str.charAt(i);
switch (cur) {
case '<':
ret.append("&lt;");
break;
case '>':
ret.append("&gt;");
break;
case '\"':
ret.append("&quot;");
break;
case '\'':
ret.append("&apos;"); // IE 不支持 &apos; 考虑 &#39;
break;
case '&':
ret.append("&amp;");
break;
default:
ret.append(cur);
break;
}
}
return ret.toString();
}
}

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.ext.directive;
import java.io.Writer;
import java.text.SimpleDateFormat;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
/**
* 输出当前时间,默认考虑是输出时间,给 pattern 输出可能是 Date、DateTime、Timestamp
* 带 String 参数,表示 pattern
*/
public class NowDirective extends Directive {
public void setExrpList(ExprList exprList) {
if (exprList.length() > 1) {
throw new ParseException("#now directive support one parameter only", location);
}
super.setExprList(exprList);
}
public void exec(Env env, Scope scope, Writer writer) {
String dataPattern;
if (exprList.length() == 0) {
dataPattern = env.getEngineConfig().getDatePattern();
} else {
Object dp = exprList.eval(scope);
if (dp instanceof String) {
dataPattern = (String)dp;
} else {
throw new TemplateException("The parameter of #new directive must be String", location);
}
}
try {
String value = new SimpleDateFormat(dataPattern).format(new java.util.Date());
write(writer, value);
} catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e);
}
}
}

View File

@@ -0,0 +1,37 @@
/**
* 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.ext.directive;
import java.io.Writer;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.stat.Scope;
/**
* 输出随机数
*/
public class RandomDirective extends Directive {
private java.util.Random random = new java.util.Random();
public void exec(Env env, Scope scope, Writer writer) {
write(writer, String.valueOf(random.nextInt()));
}
}

View File

@@ -0,0 +1,182 @@
/**
* 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.ext.directive;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import com.jfinal.template.Directive;
import com.jfinal.template.EngineConfig;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.Assign;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.source.ISource;
import com.jfinal.template.stat.Ctrl;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Parser;
import com.jfinal.template.stat.Scope;
import com.jfinal.template.stat.ast.Define;
import com.jfinal.template.stat.ast.Include;
import com.jfinal.template.stat.ast.Stat;
/**
* #render 指令用于动态渲染子模板,作为 include 指令的补充
*
* <pre>
* 两种用法:
* 1只传入一个参数参数可以是 String 常量,也可以是任意表达式
* #render("_hot.html")
* #render(subFile)
*
* 2传入任意多个参数除第一个参数以外的所有参数必须是赋值表达式用于实现参数传递功能
* #render("_hot.html", title = "热门新闻", list = newsList)
*
* 上例中传递了 title、list 两个参数,可以代替父模板中的 #set 指令传参方式
* 并且此方式传入的参数只在子模板作用域有效,不会污染父模板作用域
*
* 这种传参方式有利于将子模板模块化,例如上例的调用改成如下的参数:
* #render("_hot.html", title = "热门项目", list = projectList)
* 通过这种传参方式在子模板 _hot.html 之中,完全不需要修改对于 title 与 list
* 这两个变量的处理代码,就实现了对 “热门项目” 数据的渲染
*
* </pre>
*/
public class RenderDirective extends Directive {
private String parentFileName;
private Map<String, StatInfo> statInfoCache = new HashMap<String,StatInfo>();
public void setExprList(ExprList exprList) {
int len = exprList.length();
if (len == 0) {
throw new ParseException("The parameter of #render directive can not be blank", 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 #render directive must be an assignment expression", location);
}
}
}
/**
* 从 location 中获取父模板的 fileName用于生成 subFileName
* 如果是孙子模板,那么 parentFileName 为最顶层的模板,而非直接上层的模板
*/
this.parentFileName = location.getTemplateFile();
this.exprList = exprList;
}
/**
* 对 exprList 进行求值,并将第一个表达式的值作为模板名称返回,
* 开启 local assignment 保障 #render 指令参数表达式列表
* 中的赋值表达式在当前 scope 中进行,有利于模块化
*/
private Object evalAssignExpressionAndGetFileName(Scope scope) {
Ctrl ctrl = scope.getCtrl();
try {
ctrl.setLocalAssignment();
return exprList.evalExprList(scope)[0];
} finally {
ctrl.setWisdomAssignment();
}
}
public void exec(Env env, Scope scope, Writer writer) {
// 在 exprList.eval(scope) 之前创建,使赋值表达式在本作用域内进行
scope = new Scope(scope);
Object value = evalAssignExpressionAndGetFileName(scope);
if (!(value instanceof String)) {
throw new TemplateException("The parameter value of #render directive must be String", location);
}
String subFileName = Include.getSubFileName((String)value, parentFileName);
StatInfo statInfo = statInfoCache.get(subFileName);
if (statInfo == null) {
statInfo = parseStatInfo(env, subFileName);
statInfoCache.put(subFileName, statInfo);
} else if (env.getEngineConfig().isDevMode()) {
// statInfo.env.isSourceListModified() 逻辑可以支持 #render 子模板中的 #include 过来的子模板在 devMode 下在修改后可被重加载
if (statInfo.source.isModified() || statInfo.env.isSourceListModified()) {
statInfo = parseStatInfo(env, subFileName);
statInfoCache.put(subFileName, statInfo);
}
}
statInfo.stat.exec(statInfo.env, scope, writer);
scope.getCtrl().setJumpNone();
}
private StatInfo parseStatInfo(Env env, String subFileName) {
EngineConfig config = env.getEngineConfig();
// FileSource fileSource = new FileSource(config.getBaseTemplatePath(), subFileName, config.getEncoding());
ISource fileSource = config.getSourceFactory().getSource(config.getBaseTemplatePath(), subFileName, config.getEncoding());
try {
EnvSub envSub = new EnvSub(env);
Stat stat = new Parser(envSub, fileSource.getContent(), subFileName).parse();
return new StatInfo(envSub, stat, fileSource);
} catch (Exception e) {
throw new ParseException(e.getMessage(), location, e);
}
}
private static class StatInfo {
EnvSub env;
Stat stat;
ISource source;
StatInfo(EnvSub env, Stat stat, ISource source) {
this.env = env;
this.stat = stat;
this.source = source;
}
}
/**
* EnvSub 用于将子模板与父模板中的模板函数隔离开来,
* 否则在子模板被修改并被重新解析时会再次添加子模板中的
* 模板函数,从而抛出异常
*
* EnvSub 也可以使子模板中定义的模板函数不与上层产生冲突,
* 有利于动态型模板渲染的模块化
*
* 注意: #render 子模板中定义的模板函数无法被上层调用
*/
private static class EnvSub extends Env {
Env parentEnv;
public EnvSub(Env parentEnv) {
super(parentEnv.getEngineConfig());
this.parentEnv = parentEnv;
}
/**
* 接管父类 getFunction(),先从子模板中找模板函数,找不到再去父模板中找
*/
public Define getFunction(String functionName) {
Define func = functionMap.get(functionName);
return func != null ? func : parentEnv.getFunction(functionName);
}
}
}

View File

@@ -0,0 +1,97 @@
/**
* 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.ext.directive;
import java.io.Writer;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.FastStringWriter;
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.expr.ast.Id;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
/**
* #string 指令方便定义大量的多行文本变量,这个是 java 语言中极为需要的功能
*
* 定义:
* #string(name)
* 在此是大量的字符串
* #end
*
* 使用:
* #(name)
*/
public class StringDirective extends Directive {
private String name;
private boolean isLocalAssignment = false;
public void setExprList(ExprList exprList) {
Expr[] exprArray = exprList.getExprArray();
if (exprArray.length == 0) {
throw new ParseException("#string directive parameter cant not be null", location);
}
if (exprArray.length > 2) {
throw new ParseException("wrong number of #string directive parameter, two parameters allowed at most", location);
}
if (!(exprArray[0] instanceof Id)) {
throw new ParseException("#string first parameter must be identifier", location);
}
this.name = ((Id)exprArray[0]).getId();
if (exprArray.length == 2) {
if (exprArray[1] instanceof Const) {
if (((Const)exprArray[1]).isBoolean()) {
this.isLocalAssignment = ((Const)exprArray[1]).getBoolean();
} else {
throw new ParseException("#string sencond parameter must be boolean", location);
}
}
}
}
public void exec(Env env, Scope scope, Writer writer) {
FastStringWriter fsw = new FastStringWriter();
stat.exec(env, scope, fsw);
if (this.isLocalAssignment) {
scope.setLocal(name, fsw.toString());
} else {
scope.set(name, fsw.toString());
}
}
/**
* hasEnd() 方法返回 true 时,表示该指令拥有指令体以及 #end 结束块
* 模板引擎在解析时会将 "指令体" 赋值到 stat 属性中,在 exec(...) 方法中
* 可通过 stat.exec(...) 执行 "指令体" 内部的所有指令
*/
public boolean hasEnd() {
return true;
}
}