diff --git a/.gitignore b/.gitignore
index 0e2b059..f8bca96 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,3 +51,4 @@ a_little_config_pro.txt
dev_plan.txt
+
diff --git a/LICENSE b/LICENSE
index ff3e6fc..37fb606 100644
--- a/LICENSE
+++ b/LICENSE
@@ -192,3 +192,5 @@ third-party archives.
+
+
diff --git a/README.md b/README.md
index 7ac8883..e89b072 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
### Enjoy
-Enjoy 是基于 Java 语言的极轻量极魔板引擎。极轻量级仅 171K 并且不依赖任何第三方。极简设计仅 if、for、set、define、include、render 六个核心指令,让学习成本低到极致。独创 DKFF(Dynamic Key Feature Forward) 词法分析算法与 DLRD (Double Layer Recursive Descent)语法分析算法,避免使用 javacc、antlr、jflex 生成器,令代码量少到极致。
+Enjoy 是基于 Java 语言的极轻量极魔板引擎。极轻量级仅 171K 并且不依赖任何第三方。极简设计仅 if、for、switch、set、define、include、render 七个核心指令,让学习成本低到极致。独创 DKFF(Dynamic Key Feature Forward) 词法分析算法与 DLRD (Double Layer Recursive Descent)语法分析算法,避免使用 javacc、antlr、jflex 生成器,令代码量少到极致。
#### Enjoy 主要特点
-- 消灭传统模板引擎中大量繁杂概念,仅六个核心指令,学习成本极低
-- 独创 DKFF 词法分析算法与 DLRD 语法分析算法,避免使用javacc、antlr
+- 消灭传统模板引擎中大量繁杂概念,仅七个核心指令,学习成本极低
+- 独创 DKFF 词法分析算法与 DLRD 语法分析算法,避免使用 javacc、antlr
- 功能强大,极为简单覆盖掉 freemarker、velocity 的核心功能
- 扩展性强,支持多种扩展方式,且是唯一支持指令级扩展的模板引擎
- 与 java 打通式设计,在模板中与 java 交互极为方便
diff --git a/pom.xml b/pom.xml
index f46d4c3..3bc8925 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,8 +73,8 @@
maven-compiler-plugin
3.6.1
- 1.7
- 1.7
+ 1.8
+ 1.8
@@ -121,7 +121,7 @@
- true
+ false
diff --git a/src/main/java/com/jfinal/kit/Kv.java b/src/main/java/com/jfinal/kit/Kv.java
index b00a896..a0d3edb 100644
--- a/src/main/java/com/jfinal/kit/Kv.java
+++ b/src/main/java/com/jfinal/kit/Kv.java
@@ -113,6 +113,20 @@ public class Kv extends HashMap {
return this;
}
+ public Kv setIfNotBlank(Object key, String value) {
+ if (StrKit.notBlank(value)) {
+ set(key, value);
+ }
+ return this;
+ }
+
+ public Kv setIfNotNull(Object key, Object value) {
+ if (value != null) {
+ set(key, value);
+ }
+ return this;
+ }
+
public Kv set(Map map) {
super.putAll(map);
return this;
diff --git a/src/main/java/com/jfinal/kit/ReflectKit.java b/src/main/java/com/jfinal/kit/ReflectKit.java
index 0f7347e..3d1fd2c 100644
--- a/src/main/java/com/jfinal/kit/ReflectKit.java
+++ b/src/main/java/com/jfinal/kit/ReflectKit.java
@@ -24,7 +24,7 @@ public class ReflectKit {
public static Object newInstance(Class> clazz) {
try {
return clazz.newInstance();
- } catch (Exception e) {
+ } catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
diff --git a/src/main/java/com/jfinal/template/Engine.java b/src/main/java/com/jfinal/template/Engine.java
index d908b35..44cfb49 100644
--- a/src/main/java/com/jfinal/template/Engine.java
+++ b/src/main/java/com/jfinal/template/Engine.java
@@ -520,8 +520,8 @@ public class Engine {
* Engine.addFieldGetter(1, new IsMethodFieldGetter());
*
* 注:IsMethodFieldGetter 系统已经提供,只是默认没有启用。该实现类通过调用
- * target.isXxx() 方法获取 target.xxx 表达式的值,其中 xxx 字段必须是
- * Boolean/boolean 类型
+ * target.isXxx() 方法获取 target.xxx 表达式的值,其中 isXxx() 返回值
+ * 必须是 Boolean/boolean 类型才会被调用
*/
public static void addFieldGetter(int index, FieldGetter fieldGetter) {
FieldKit.addFieldGetter(index, fieldGetter);
diff --git a/src/main/java/com/jfinal/template/EngineConfig.java b/src/main/java/com/jfinal/template/EngineConfig.java
index 33fafe8..f5e096a 100644
--- a/src/main/java/com/jfinal/template/EngineConfig.java
+++ b/src/main/java/com/jfinal/template/EngineConfig.java
@@ -73,9 +73,9 @@ public class EngineConfig {
addDirective("string", StringDirective.class);
addDirective("random", RandomDirective.class);
addDirective("number", NumberDirective.class);
+ addDirective("call", CallDirective.class);
// Add official shared method of Template Engine
- // addSharedMethod(new Json());
addSharedMethod(new SharedMethodLib());
}
@@ -83,6 +83,7 @@ public class EngineConfig {
* Add shared function with file
*/
public void addSharedFunction(String fileName) {
+ fileName = fileName.replace("\\", "/");
// FileSource fileSource = new FileSource(baseTemplatePath, fileName, encoding);
ISource source = sourceFactory.getSource(baseTemplatePath, fileName, encoding);
doAddSharedFunction(source, fileName);
@@ -261,8 +262,9 @@ public class EngineConfig {
throw new IllegalArgumentException("baseTemplatePath can not be blank");
}
baseTemplatePath = baseTemplatePath.trim();
+ baseTemplatePath = baseTemplatePath.replace("\\", "/");
if (baseTemplatePath.length() > 1) {
- if (baseTemplatePath.endsWith("/") || baseTemplatePath.endsWith("\\")) {
+ if (baseTemplatePath.endsWith("/")) {
baseTemplatePath = baseTemplatePath.substring(0, baseTemplatePath.length() - 1);
}
}
diff --git a/src/main/java/com/jfinal/template/expr/ExprParser.java b/src/main/java/com/jfinal/template/expr/ExprParser.java
index f724a49..626107e 100644
--- a/src/main/java/com/jfinal/template/expr/ExprParser.java
+++ b/src/main/java/com/jfinal/template/expr/ExprParser.java
@@ -21,28 +21,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import com.jfinal.template.EngineConfig;
import com.jfinal.template.expr.Sym;
-import com.jfinal.template.expr.ast.Arith;
-import com.jfinal.template.expr.ast.Array;
-import com.jfinal.template.expr.ast.Assign;
-import com.jfinal.template.expr.ast.Compare;
-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.Field;
-import com.jfinal.template.expr.ast.ForCtrl;
-import com.jfinal.template.expr.ast.Id;
-import com.jfinal.template.expr.ast.IncDec;
-import com.jfinal.template.expr.ast.Index;
-import com.jfinal.template.expr.ast.Logic;
-import com.jfinal.template.expr.ast.Map;
-import com.jfinal.template.expr.ast.Method;
-import com.jfinal.template.expr.ast.NullSafe;
-import com.jfinal.template.expr.ast.RangeArray;
-import com.jfinal.template.expr.ast.SharedMethod;
-import com.jfinal.template.expr.ast.StaticField;
-import com.jfinal.template.expr.ast.StaticMethod;
-import com.jfinal.template.expr.ast.Ternary;
-import com.jfinal.template.expr.ast.Unary;
+import com.jfinal.template.expr.ast.*;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParaToken;
import com.jfinal.template.stat.ParseException;
diff --git a/src/main/java/com/jfinal/template/expr/ast/Field.java b/src/main/java/com/jfinal/template/expr/ast/Field.java
index 929863b..6da9705 100644
--- a/src/main/java/com/jfinal/template/expr/ast/Field.java
+++ b/src/main/java/com/jfinal/template/expr/ast/Field.java
@@ -73,9 +73,7 @@ public class Field extends Expr {
if (fieldGetter.notNull()) {
return fieldGetter.get(target, fieldName);
}
- } catch (TemplateException e) {
- throw e;
- } catch (ParseException e) {
+ } catch (TemplateException | ParseException e) {
throw e;
} catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e);
diff --git a/src/main/java/com/jfinal/template/expr/ast/FieldGetters.java b/src/main/java/com/jfinal/template/expr/ast/FieldGetters.java
index 3114e6d..dc3d806 100644
--- a/src/main/java/com/jfinal/template/expr/ast/FieldGetters.java
+++ b/src/main/java/com/jfinal/template/expr/ast/FieldGetters.java
@@ -69,9 +69,9 @@ public class FieldGetters {
java.lang.reflect.Method[] methodArray = targetClass.getMethods();
for (java.lang.reflect.Method method : methodArray) {
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0) {
- if (MethodKit.isForbiddenMethod(getterName)) {
- throw new RuntimeException("Forbidden method: " + getterName);
- }
+ // if (MethodKit.isForbiddenMethod(getterName)) {
+ // throw new RuntimeException("Forbidden method: " + getterName);
+ // }
return new GetterMethodFieldGetter(method);
}
diff --git a/src/main/java/com/jfinal/template/expr/ast/Method.java b/src/main/java/com/jfinal/template/expr/ast/Method.java
index 1503d57..237e330 100644
--- a/src/main/java/com/jfinal/template/expr/ast/Method.java
+++ b/src/main/java/com/jfinal/template/expr/ast/Method.java
@@ -88,9 +88,7 @@ public class Method extends Expr {
}
throw new TemplateException(buildMethodNotFoundSignature("public method not found: " + target.getClass().getName() + ".", methodName, argValues), location);
- } catch (TemplateException e) {
- throw e;
- } catch (ParseException e) {
+ } catch (TemplateException | ParseException e) {
throw e;
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
diff --git a/src/main/java/com/jfinal/template/expr/ast/SharedMethod.java b/src/main/java/com/jfinal/template/expr/ast/SharedMethod.java
index fb3f434..0930824 100644
--- a/src/main/java/com/jfinal/template/expr/ast/SharedMethod.java
+++ b/src/main/java/com/jfinal/template/expr/ast/SharedMethod.java
@@ -63,9 +63,7 @@ public class SharedMethod extends Expr {
throw new TemplateException(Method.buildMethodNotFoundSignature("Shared method not found: ", methodName, argValues), location);
}
- } catch (TemplateException e) {
- throw e;
- } catch (ParseException e) {
+ } catch (TemplateException | ParseException e) {
throw e;
} catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e);
diff --git a/src/main/java/com/jfinal/template/expr/ast/StaticMethod.java b/src/main/java/com/jfinal/template/expr/ast/StaticMethod.java
index 6e950af..5b14bd7 100644
--- a/src/main/java/com/jfinal/template/expr/ast/StaticMethod.java
+++ b/src/main/java/com/jfinal/template/expr/ast/StaticMethod.java
@@ -72,9 +72,7 @@ public class StaticMethod extends Expr {
throw new TemplateException(Method.buildMethodNotFoundSignature("public static method not found: " + clazz.getName() + "::", methodName, argValues), location);
}
- } catch (TemplateException e) {
- throw e;
- } catch (ParseException e) {
+ } catch (TemplateException | ParseException e) {
throw e;
} catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e);
diff --git a/src/main/java/com/jfinal/template/ext/directive/CallDirective.java b/src/main/java/com/jfinal/template/ext/directive/CallDirective.java
new file mode 100644
index 0000000..561bc19
--- /dev/null
+++ b/src/main/java/com/jfinal/template/ext/directive/CallDirective.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2011-2019, 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.util.ArrayList;
+import com.jfinal.template.Directive;
+import com.jfinal.template.Env;
+import com.jfinal.template.TemplateException;
+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.stat.ParseException;
+import com.jfinal.template.stat.Scope;
+import com.jfinal.template.stat.ast.Define;
+
+/**
+ * CallDirective 动态调用模板函数
+ *
+ * 模板函数的名称与参数都可以动态指定,提升模板函数调用的灵活性
+ *
+ * 例如:
+ * #call(funcName, p1, p2, ..., pn)
+ * 其中 funcName,为函数名,p1、p2、pn 为被调用函数所使用的参数
+ *
+ *
+ * 如果希望模板函数不存在时忽略其调用,添加常量值 true 在第一个参数位置即可
+ * 例如:
+ * #call(true, funcName, p1, p2, ..., pn)
+ *
+ *
+ * TODO 后续优化看一下 ast.Call.java
+ */
+public class CallDirective extends Directive {
+
+ protected Expr funcNameExpr;
+ protected ExprList paraExpr;
+
+ protected boolean nullSafe = false; // 是否支持函数名不存在时跳过
+
+ public void setExprList(ExprList exprList) {
+ int len = exprList.length();
+ if (len == 0) {
+ throw new ParseException("模板函数名不能缺失", location);
+ }
+
+ int index = 0;
+ Expr expr = exprList.getExpr(index);
+ if (expr instanceof Const && ((Const)expr).isBoolean()) {
+ if (len == 1) {
+ throw new ParseException("模板函数名不能缺失", location);
+ }
+
+ nullSafe = ((Const)expr).getBoolean();
+ index++;
+ }
+
+ funcNameExpr = exprList.getExpr(index++);
+
+ ArrayList list = new ArrayList();
+ for (int i=index; i
+ * 示例:
+ * #switch (month)
+ * #case (1, 3, 5, 7, 8, 10, 12)
+ * #(month) 月有 31 天
+ * #case (2)
+ * #(month) 月平年有28天,闰年有29天
+ * #default
+ * 月份错误: #(month ?? "null")
+ * #end
+ *
+ * 如上代码所示,如果 #case 指令参数有多个值,那么可以用逗号分隔,
+ * 上述逗号表达式的值 1, 3, 5, 7, 8, 10, 12 之中只要有一个与
+ * switch 指令参数 month 相等的话,该 case 分支就会被执行,
+ * 该特性从根本上消灭了 #break 指令的必要性
+ *
+ *
+ * 除了常量值以外 #case 参数还可以是任意表达式
+ * 例如:
+ * #case (a, b, c, x + y, obj.method(z))
+ *
+ * 上述代码中 #case 参数中的所有表达式先会被求值,然后逐一与 #switch
+ * 参数进行对比,同样也是只要有一个对比相等,则该 case 分支就会被执行
+ *
+ *
+ */
+public class Switch extends Stat implements CaseSetter {
+
+ private Expr expr;
+ private Case nextCase;
+ private Default _default;
+
+ public Switch(ExprList exprList, Location location) {
+ if (exprList.length() == 0) {
+ throw new ParseException("The parameter of #switch directive can not be blank", location);
+ }
+ this.expr = exprList.getActualExpr();
+ }
+
+ public void setNextCase(Case nextCase) {
+ this.nextCase = nextCase;
+ }
+
+ public void setDefault(Default _default, Location location) {
+ if (this._default != null) {
+ throw new ParseException("The #default case of #switch is already defined", location);
+ }
+ this._default = _default;
+ }
+
+ public void exec(Env env, Scope scope, Writer writer) {
+ Object switchValue = expr.eval(scope);
+
+ if (nextCase != null && nextCase.execIfMatch(switchValue, env, scope, writer)) {
+ return ;
+ }
+
+ if (_default != null) {
+ _default.exec(env, scope, writer);
+ }
+ }
+
+ public boolean hasEnd() {
+ return true;
+ }
+}
+
+
+
+
+
+
+