From e0abc218b1c3ebc0a65a70c3ae585f5ede7aa801 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 10 Dec 2017 19:40:40 +0800 Subject: [PATCH] =?UTF-8?q?Map=20=E5=AE=9A=E4=B9=89=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E8=A1=A8=E8=BE=BE=E5=BC=8F=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=94=AF=E6=8C=81=20key=20=E5=8F=AF=E4=BB=A5=E4=B8=BA=20int?= =?UTF-8?q?=E3=80=81long=E3=80=81float=E3=80=81double=E3=80=81true?= =?UTF-8?q?=E3=80=81false=E3=80=81null=20=E5=B8=B8=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/jfinal/template/Engine.java | 8 +++ .../com/jfinal/template/expr/ExprParser.java | 35 +++++++----- .../java/com/jfinal/template/expr/NumTok.java | 7 ++- .../com/jfinal/template/expr/ast/Const.java | 53 ++++++------------- .../com/jfinal/template/expr/ast/Index.java | 37 +++++++------ .../com/jfinal/template/expr/ast/Unary.java | 45 ++++++++++++++++ 6 files changed, 117 insertions(+), 68 deletions(-) diff --git a/src/main/java/com/jfinal/template/Engine.java b/src/main/java/com/jfinal/template/Engine.java index 1493fcb..9a3f14a 100644 --- a/src/main/java/com/jfinal/template/Engine.java +++ b/src/main/java/com/jfinal/template/Engine.java @@ -22,6 +22,7 @@ import java.util.Map; import com.jfinal.kit.HashKit; import com.jfinal.kit.StrKit; import com.jfinal.template.expr.ast.MethodKit; +import com.jfinal.template.source.ClassPathSourceFactory; import com.jfinal.template.source.ISource; import com.jfinal.template.source.ISourceFactory; import com.jfinal.template.source.StringSource; @@ -426,6 +427,13 @@ public class Engine { return this; } + /** + * 设置为 ClassPathSourceFactory 的快捷方法 + */ + public Engine setToClassPathSourceFactory() { + return setSourceFactory(new ClassPathSourceFactory()); + } + public ISourceFactory getSourceFactory() { return sourceFactory; } diff --git a/src/main/java/com/jfinal/template/expr/ExprParser.java b/src/main/java/com/jfinal/template/expr/ExprParser.java index 08f0165..2ddd1f0 100644 --- a/src/main/java/com/jfinal/template/expr/ExprParser.java +++ b/src/main/java/com/jfinal/template/expr/ExprParser.java @@ -292,7 +292,7 @@ public class ExprParser { case ADD: case SUB: move(); - return new Unary(tok.sym, unary(), location); + return new Unary(tok.sym, unary(), location).toConstIfPossible(); case INC: case DEC: move(); @@ -468,21 +468,26 @@ public class ExprParser { } /** - * mapEntry : (ID | STR) ':' expr + * mapEntry : (ID | STR | INT | LONG | FLOAT | DOUBLE | TRUE | FALSE | NULL) ':' expr + * 设计目标为 map 定义与初始化,所以 ID 仅当成 STR 不进行求值 */ void buildMapEntry(LinkedHashMap map) { - Tok tok = peek(); - if (tok.sym == Sym.ID || tok.sym == Sym.STR) { - move(); - match(Sym.COLON); - Expr value = expr(); - if (value == null) { - throw new ParseException("Expression error: the value on the right side of map entry can not be blank", location); - } - map.put(tok.value(), value); - return ; + Expr keyExpr = expr(); + Object key; + if (keyExpr instanceof Id) { + key = ((Id)keyExpr).getId(); + } else if (keyExpr instanceof Const) { + key = ((Const)keyExpr).getValue(); + } else { + throw new ParseException("Expression error: the value of map key must be identifier, String, Boolean, null or Number", location); } - throw new ParseException("Expression error: the value of map key must be identifier or String", location); + + match(Sym.COLON); + Expr value = expr(); + if (value == null) { + throw new ParseException("Expression error: the value on the right side of map entry can not be blank", location); + } + map.put(key, value); } /** @@ -528,12 +533,14 @@ public class ExprParser { move(); return new Id(tok.value()); case STR: + move(); + return new Const(tok.sym, tok.value()); case INT: case LONG: case FLOAT: case DOUBLE: move(); - return new Const(tok.sym, tok.value()); + return new Const(tok.sym, ((NumTok)tok).getNumberValue()); case TRUE: move(); return Const.TRUE; diff --git a/src/main/java/com/jfinal/template/expr/NumTok.java b/src/main/java/com/jfinal/template/expr/NumTok.java index cccfe8e..c6eeae9 100644 --- a/src/main/java/com/jfinal/template/expr/NumTok.java +++ b/src/main/java/com/jfinal/template/expr/NumTok.java @@ -43,7 +43,7 @@ import com.jfinal.template.stat.ParseException; */ public class NumTok extends Tok { - private Object value; + private Number value; NumTok(Sym sym, String s, int radix, boolean isScientificNotation, Location location) { super(sym, location.getRow()); @@ -101,3 +101,8 @@ public class NumTok extends Tok { return sym.value() + " : " + value; } } + + + + + diff --git a/src/main/java/com/jfinal/template/expr/ast/Const.java b/src/main/java/com/jfinal/template/expr/ast/Const.java index 31bacdf..41f2df6 100644 --- a/src/main/java/com/jfinal/template/expr/ast/Const.java +++ b/src/main/java/com/jfinal/template/expr/ast/Const.java @@ -20,54 +20,25 @@ import com.jfinal.template.expr.Sym; import com.jfinal.template.stat.Scope; /** - * STR INT LONG FLOAT DOUBLE true false null + * STR INT LONG FLOAT DOUBLE TRUE FALSE NULL */ public class Const extends Expr { - public static final Const TRUE = new Const(Boolean.TRUE, Sym.TRUE); - public static final Const FALSE = new Const(Boolean.FALSE, Sym.FALSE); - public static final Const NULL = new Const(null, Sym.NULL); + public static final Const TRUE = new Const(Sym.TRUE, Boolean.TRUE); + public static final Const FALSE = new Const(Sym.FALSE, Boolean.FALSE); + public static final Const NULL = new Const(Sym.NULL, null); - private Sym type; - private Object value; + private final Sym type; + private final Object value; /** - * 构造 TRUE FALSE NULL 常量,无需对 value 进行转换 + * INT LONG FLOAT DOUBLE 常量已在 NumTok 中转换成了确切的类型,无需再次转换 */ - private Const(Object value, Sym type) { + public Const(Sym type, Object value) { this.type = type; this.value = value; } - public Const(Sym type, String value) { - this.type = type; - this.value = typeConvert(type, value); - } - - private Object typeConvert(Sym type, String value) { - switch (type) { - case STR: - return value; - case INT: - return Integer.parseInt(value); - case LONG: - return Long.parseLong(value); - case FLOAT: - return Float.parseFloat(value); - case DOUBLE: - return Double.parseDouble(value); - /* - case TRUE: - case FALSE: - return Boolean.parseBoolean(value); - case NULL: - return null; - */ - default: - throw new RuntimeException("never happend"); - } - } - public Object eval(Scope scope) { return value; } @@ -108,6 +79,10 @@ public class Const extends Expr { return type == Sym.DOUBLE; } + public boolean isNumber() { + return value instanceof Number; + } + public Object getValue() { return value; } @@ -136,6 +111,10 @@ public class Const extends Expr { return (Double)value; } + public Number getNumber() { + return (Number)value; + } + public String toString() { return value != null ? value.toString() : "null"; } diff --git a/src/main/java/com/jfinal/template/expr/ast/Index.java b/src/main/java/com/jfinal/template/expr/ast/Index.java index c08e5ee..16749b2 100644 --- a/src/main/java/com/jfinal/template/expr/ast/Index.java +++ b/src/main/java/com/jfinal/template/expr/ast/Index.java @@ -43,8 +43,8 @@ public class Index extends Expr { @SuppressWarnings("rawtypes") public Object eval(Scope scope) { - Object array = expr.eval(scope); - if (array == null) { + Object target = expr.eval(scope); + if (target == null) { if (scope.getCtrl().isNullSafe()) { return null; } @@ -56,25 +56,30 @@ public class Index extends Expr { if (scope.getCtrl().isNullSafe()) { return null; } - throw new TemplateException("The index of list/array and the key of map can not be null", location); - } - - if (array instanceof List) { - if (idx instanceof Integer) { - return ((List)array).get((Integer)idx); + + if (target instanceof java.util.Map) { + // Map 的 key 可以是 null,不能抛异常 + } else { + throw new TemplateException("The index of list and array can not be null", location); } - throw new TemplateException("The index of list can only be integer", location); } - if (array instanceof java.util.Map) { - return ((java.util.Map)array).get(idx); - } - - if (array.getClass().isArray()) { + if (target instanceof List) { if (idx instanceof Integer) { - return java.lang.reflect.Array.get(array, (Integer)idx); + return ((List)target).get((Integer)idx); } - throw new TemplateException("The index of array can only be integer", location); + throw new TemplateException("The index of list must be integer", location); + } + + if (target instanceof java.util.Map) { + return ((java.util.Map)target).get(idx); + } + + if (target.getClass().isArray()) { + if (idx instanceof Integer) { + return java.lang.reflect.Array.get(target, (Integer)idx); + } + throw new TemplateException("The index of array must be integer", location); } throw new TemplateException("Only the list array and map is supported by index access", location); diff --git a/src/main/java/com/jfinal/template/expr/ast/Unary.java b/src/main/java/com/jfinal/template/expr/ast/Unary.java index 2599fbe..1311d70 100644 --- a/src/main/java/com/jfinal/template/expr/ast/Unary.java +++ b/src/main/java/com/jfinal/template/expr/ast/Unary.java @@ -83,6 +83,51 @@ public class Unary extends Expr { throw new TemplateException("Unsupported operator: " + op.value(), location); } } + + /** + * 如果可能的话,将 Unary 表达式转化成 Const 表达式,类似于 ExprParser.buildMapEntry() 需要这种转化来简化实现 + * 除了可简化程序外,还起到一定的性能优化作用 + * + * Number : +123 -456 +3.14 -0.12 + * Boolean : !true !false + * + * 特别注意: + * Boolean 的支持并不需要,!true、!false 已在 ExprParser 中被 Logic 表达式接管,在此仅为逻辑上的完备性而添加 + */ + public Expr toConstIfPossible() { + if (expr instanceof Const && (op == Sym.SUB || op == Sym.ADD || op == Sym.NOT)) { + } else { + return this; + } + + Expr ret = this; + Const c = (Const)expr; + if (op == Sym.SUB) { + if (c.isInt()) { + ret = new Const(Sym.INT, -c.getInt()); + } else if (c.isLong()) { + ret = new Const(Sym.LONG, -c.getLong()); + } else if (c.isFloat()) { + ret = new Const(Sym.FLOAT, -c.getFloat()); + } else if (c.isDouble()) { + ret = new Const(Sym.DOUBLE, -c.getDouble()); + } + } else if (op == Sym.ADD) { + if (c.isNumber()) { + ret = c; + } + } else if (op == Sym.NOT) { + if (c.isBoolean()) { + ret = c.isTrue() ? Const.FALSE : Const.TRUE; + } + } + + return ret; + } + + public String toString() { + return op.toString() + expr.toString(); + } }