Browse Source

Merge remote-tracking branch 'origin/master' into jfinal-java8

James 8 years ago
parent
commit
5676963214

+ 8 - 0
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;
 	}

+ 21 - 14
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<Object, Expr> 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;

+ 6 - 1
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;
 	}
 }
+
+
+
+
+

+ 16 - 37
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";
 	}

+ 16 - 11
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 (target instanceof java.util.Map) {
+				// Map 的 key 可以是 null,不能抛异常
+			} else {
+				throw new TemplateException("The index of list and array can not be null", location);
+			}
 		}
 		
-		if (array instanceof List) {
+		if (target instanceof List) {
 			if (idx instanceof Integer) {
-				return ((List<?>)array).get((Integer)idx);
+				return ((List<?>)target).get((Integer)idx);
 			}
-			throw new TemplateException("The index of list can only be integer", location);
+			throw new TemplateException("The index of list must be integer", location);
 		}
 		
-		if (array instanceof java.util.Map) {
-			return ((java.util.Map)array).get(idx);
+		if (target instanceof java.util.Map) {
+			return ((java.util.Map)target).get(idx);
 		}
 		
-		if (array.getClass().isArray()) {
+		if (target.getClass().isArray()) {
 			if (idx instanceof Integer) {
-				return java.lang.reflect.Array.get(array, (Integer)idx);
+				return java.lang.reflect.Array.get(target, (Integer)idx);
 			}
-			throw new TemplateException("The index of array can only be integer", location);
+			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);

+ 45 - 0
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();
+	}
 }