enjoy 3.3 release ^_^
This commit is contained in:
@@ -94,9 +94,11 @@ public class Compare extends Expr {
|
||||
case Arith.FLOAT:
|
||||
// 此法仅适用于两个对象类型相同的情况,升级为 BigDecimal 后精度会再高几个数量级
|
||||
// return Float.floatToIntBits(l.floatValue()) == Float.floatToIntBits(r.floatValue());
|
||||
return l.floatValue() == r.floatValue();
|
||||
case Arith.DOUBLE:
|
||||
// 此法仅适用于两个对象类型相同的情况,升级为 BigDecimal 后精度会再高几个数量级
|
||||
// return Double.doubleToLongBits(l.doubleValue()) == Double.doubleToLongBits(r.doubleValue());
|
||||
return l.doubleValue() == r.doubleValue();
|
||||
case Arith.BIGDECIMAL:
|
||||
BigDecimal[] bd = toBigDecimals(l, r);
|
||||
return (bd[0]).compareTo(bd[1]) == 0;
|
||||
@@ -120,8 +122,10 @@ public class Compare extends Expr {
|
||||
return l.longValue() > r.longValue();
|
||||
case Arith.FLOAT:
|
||||
// return Float.floatToIntBits(l.floatValue()) > Float.floatToIntBits(r.floatValue());
|
||||
return l.floatValue() > r.floatValue();
|
||||
case Arith.DOUBLE:
|
||||
// return Double.doubleToLongBits(l.doubleValue()) > Double.doubleToLongBits(r.doubleValue());
|
||||
return l.doubleValue() > r.doubleValue();
|
||||
case Arith.BIGDECIMAL:
|
||||
BigDecimal[] bd = toBigDecimals(l, r);
|
||||
return (bd[0]).compareTo(bd[1]) > 0;
|
||||
@@ -150,8 +154,10 @@ public class Compare extends Expr {
|
||||
return l.longValue() >= r.longValue();
|
||||
case Arith.FLOAT:
|
||||
// return Float.floatToIntBits(l.floatValue()) >= Float.floatToIntBits(r.floatValue());
|
||||
return l.floatValue() >= r.floatValue();
|
||||
case Arith.DOUBLE:
|
||||
// return Double.doubleToLongBits(l.doubleValue()) >= Double.doubleToLongBits(r.doubleValue());
|
||||
return l.doubleValue() >= r.doubleValue();
|
||||
case Arith.BIGDECIMAL:
|
||||
BigDecimal[] bd = toBigDecimals(l, r);
|
||||
return (bd[0]).compareTo(bd[1]) >= 0;
|
||||
@@ -180,8 +186,10 @@ public class Compare extends Expr {
|
||||
return l.longValue() < r.longValue();
|
||||
case Arith.FLOAT:
|
||||
// return Float.floatToIntBits(l.floatValue()) < Float.floatToIntBits(r.floatValue());
|
||||
return l.floatValue() < r.floatValue();
|
||||
case Arith.DOUBLE:
|
||||
// return Double.doubleToLongBits(l.doubleValue()) < Double.doubleToLongBits(r.doubleValue());
|
||||
return l.doubleValue() < r.doubleValue();
|
||||
case Arith.BIGDECIMAL:
|
||||
BigDecimal[] bd = toBigDecimals(l, r);
|
||||
return (bd[0]).compareTo(bd[1]) < 0;
|
||||
@@ -210,8 +218,10 @@ public class Compare extends Expr {
|
||||
return l.longValue() <= r.longValue();
|
||||
case Arith.FLOAT:
|
||||
// return Float.floatToIntBits(l.floatValue()) <= Float.floatToIntBits(r.floatValue());
|
||||
return l.floatValue() <= r.floatValue();
|
||||
case Arith.DOUBLE:
|
||||
// return Double.doubleToLongBits(l.doubleValue()) <= Double.doubleToLongBits(r.doubleValue());
|
||||
return l.doubleValue() <= r.doubleValue();
|
||||
case Arith.BIGDECIMAL:
|
||||
BigDecimal[] bd = toBigDecimals(l, r);
|
||||
return (bd[0]).compareTo(bd[1]) <= 0;
|
||||
|
@@ -72,10 +72,6 @@ public class Const extends Expr {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
public boolean isStr() {
|
||||
return type == Sym.STR;
|
||||
}
|
||||
@@ -139,6 +135,10 @@ public class Const extends Expr {
|
||||
public Double getDouble() {
|
||||
return (Double)value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value != null ? value.toString() : "null";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.jfinal.template.expr.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.jfinal.template.TemplateException;
|
||||
import com.jfinal.template.stat.Scope;
|
||||
@@ -25,24 +26,40 @@ import com.jfinal.template.stat.Scope;
|
||||
*/
|
||||
public class ExprList extends Expr {
|
||||
|
||||
public static final Expr NULL_EXPR = NullExpr.me;
|
||||
public static final Expr[] NULL_EXPR_ARRAY = new Expr[0];
|
||||
public static final ExprList NULL_EXPR_LIST = new ExprList(new ArrayList<Expr>(0));
|
||||
|
||||
public static final Object[] NULL_OBJECT_ARRAY = new Object[0];
|
||||
public static final ExprList NULL_EXPR_LIST = new ExprList();
|
||||
|
||||
private Expr[] exprArray;
|
||||
|
||||
private ExprList() {
|
||||
this.exprArray = NULL_EXPR_ARRAY;
|
||||
}
|
||||
|
||||
public ExprList(List<Expr> exprList) {
|
||||
if (exprList != null && exprList.size() > 0) {
|
||||
if (exprList.size() > 0) {
|
||||
exprArray = exprList.toArray(new Expr[exprList.size()]);
|
||||
} else {
|
||||
exprArray = NULL_EXPR_ARRAY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 持有 ExprList 的指令可以通过此方法提升 AST 执行性能
|
||||
* 1:当 exprArray.length == 1 时返回 exprArray[0]
|
||||
* 2:当 exprArray.length == 0 时返回 NullExpr
|
||||
* 3:其它情况返回 ExprList 自身
|
||||
*
|
||||
* 意义在于,当满足前面两个条件时,避免掉了 ExprList.eval(...) 方法中的判断与循环
|
||||
*/
|
||||
public Expr getActualExpr() {
|
||||
if (exprArray.length == 1) {
|
||||
return exprArray[0];
|
||||
} else if (exprArray.length == 0) {
|
||||
return NULL_EXPR;
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public Expr[] getExprArray() {
|
||||
return exprArray;
|
||||
}
|
||||
@@ -54,6 +71,14 @@ public class ExprList extends Expr {
|
||||
return exprArray[index];
|
||||
}
|
||||
|
||||
public Expr getFirstExpr() {
|
||||
return exprArray.length > 0 ? exprArray[0] : null;
|
||||
}
|
||||
|
||||
public Expr getLastExpr() {
|
||||
return exprArray.length > 0 ? exprArray[exprArray.length - 1] : null;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return exprArray.length;
|
||||
}
|
||||
@@ -62,11 +87,20 @@ public class ExprList extends Expr {
|
||||
* 对所有表达式求值,只返回最后一个表达式的值
|
||||
*/
|
||||
public Object eval(Scope scope) {
|
||||
Object ret = null;
|
||||
for (Expr expr : exprArray) {
|
||||
ret = expr.eval(scope);
|
||||
// 优化:绝大多数情况下 length 等于 1
|
||||
if (exprArray.length == 1) {
|
||||
return exprArray[0].eval(scope);
|
||||
}
|
||||
return ret;
|
||||
|
||||
if (exprArray.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int end = exprArray.length - 1;
|
||||
for (int i=0; i<end; i++) {
|
||||
exprArray[i].eval(scope);
|
||||
}
|
||||
return exprArray[end].eval(scope);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,3 +121,4 @@ public class ExprList extends Expr {
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -17,6 +17,7 @@
|
||||
package com.jfinal.template.expr.ast;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import com.jfinal.kit.HashKit;
|
||||
import com.jfinal.kit.StrKit;
|
||||
// import com.jfinal.plugin.activerecord.Model;
|
||||
// import com.jfinal.plugin.activerecord.Record;
|
||||
@@ -40,6 +41,7 @@ public class Field extends Expr {
|
||||
private Expr expr;
|
||||
private String fieldName;
|
||||
private String getterName;
|
||||
private long getterNameHash;
|
||||
|
||||
public Field(Expr expr, String fieldName, Location location) {
|
||||
if (expr == null) {
|
||||
@@ -48,6 +50,8 @@ public class Field extends Expr {
|
||||
this.expr = expr;
|
||||
this.fieldName = fieldName;
|
||||
this.getterName = "get" + StrKit.firstCharToUpperCase(fieldName);
|
||||
// fnv1a64 hash 到比 String.hashCode() 更大的 long 值范围
|
||||
this.getterNameHash = HashKit.fnv1a64(getterName);
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@@ -65,7 +69,8 @@ public class Field extends Expr {
|
||||
}
|
||||
|
||||
Class<?> targetClass = target.getClass();
|
||||
String key = FieldKit.getFieldKey(targetClass, getterName);
|
||||
Long key = buildFieldKey(targetClass);
|
||||
|
||||
MethodInfo getter;
|
||||
try {
|
||||
getter = MethodKit.getGetterMethod(key, targetClass, getterName);
|
||||
@@ -112,6 +117,10 @@ public class Field extends Expr {
|
||||
}
|
||||
throw new TemplateException("Field not found: \"" + fieldName + "\" and getter method not found: \"" + getterName + "()\"", location);
|
||||
}
|
||||
|
||||
private Long buildFieldKey(Class<?> targetClass) {
|
||||
return targetClass.getName().hashCode() ^ getterNameHash;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -17,21 +17,21 @@
|
||||
package com.jfinal.template.expr.ast;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* FieldKit
|
||||
*/
|
||||
public class FieldKit {
|
||||
|
||||
private static final ConcurrentHashMap<String, Object> fieldCache = new ConcurrentHashMap<String, Object>();
|
||||
private static final HashMap<Long, Object> fieldCache = new HashMap<Long, Object>();
|
||||
|
||||
public static Field getField(String key, Class<?> targetClass, String fieldName) {
|
||||
public static Field getField(Long key, Class<?> targetClass, String fieldName) {
|
||||
Object field = fieldCache.get(key);
|
||||
if (field == null) {
|
||||
field = doGetField(targetClass, fieldName);
|
||||
if (field != null) {
|
||||
fieldCache.putIfAbsent(key, field);
|
||||
fieldCache.put(key, field);
|
||||
} else {
|
||||
// 对于不存在的 Field,只进行一次获取操作,主要为了支持 null safe,未来需要考虑内存泄漏风险
|
||||
fieldCache.put(key, Boolean.FALSE);
|
||||
@@ -49,14 +49,6 @@ public class FieldKit {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Field 用于缓存的 key
|
||||
*/
|
||||
public static String getFieldKey(Class<?> targetClass, String getterName) {
|
||||
return new StringBuilder(64).append(targetClass.getName())
|
||||
.append('.').append(getterName).toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -58,10 +58,10 @@ public class ForCtrl extends Expr {
|
||||
/**
|
||||
* exprList? ';' expr? ';' exprList?
|
||||
*/
|
||||
public ForCtrl(Expr init, Expr cond, Expr update, Location location) {
|
||||
this.init = init;
|
||||
public ForCtrl(ExprList init, Expr cond, ExprList update, Location location) {
|
||||
this.init = init.getActualExpr();
|
||||
this.cond = cond;
|
||||
this.update = update;
|
||||
this.update = update.getActualExpr();
|
||||
this.id = null;
|
||||
this.expr = null;
|
||||
this.location = location;
|
||||
|
@@ -37,6 +37,16 @@ public class Logic extends Expr {
|
||||
private Expr left; // ! 运算没有 left 参数
|
||||
private Expr right;
|
||||
|
||||
// 默认为新工作模式
|
||||
private static boolean newWorkMode = true;
|
||||
/**
|
||||
* 设置为旧工作模式,为了兼容 jfinal 3.3 之前的版本
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setToOldWorkMode() {
|
||||
newWorkMode = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造 || && 结点
|
||||
*/
|
||||
@@ -95,44 +105,55 @@ public class Logic extends Expr {
|
||||
* 规则:
|
||||
* 1:null 返回 false
|
||||
* 2:boolean 类型,原值返回
|
||||
* 3:Map、Connection(List被包括在内) 返回 size() > 0
|
||||
* 4:数组,返回 length > 0
|
||||
* 5:String、StringBuilder、StringBuffer 等继承自 CharSequence 类的对象,返回 length > 0
|
||||
* 6:Number 类型,返回 value != 0
|
||||
* 7:Iterator 返回 hasNext() 值
|
||||
* 8:其它返回 true
|
||||
* 3:String、StringBuilder 等一切继承自 CharSequence 类的对象,返回 length > 0
|
||||
* 4:其它返回 true
|
||||
*
|
||||
* 通过 Logic.setToOldWorkMode() 设置,可支持老版本中的以下四个规则:
|
||||
* 1:Number 类型,返回 value != 0
|
||||
* 2:Map、Collection(List被包括在内) 返回 size() > 0
|
||||
* 3:数组,返回 length > 0
|
||||
* 4:Iterator 返回 hasNext() 值
|
||||
*/
|
||||
public static boolean isTrue(Object v) {
|
||||
if (v == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (v instanceof Boolean) {
|
||||
return (Boolean)v;
|
||||
}
|
||||
if (v instanceof Collection) {
|
||||
return ((Collection<?>)v).size() > 0;
|
||||
}
|
||||
if (v instanceof Map) {
|
||||
return ((Map<?, ?>)v).size() > 0;
|
||||
}
|
||||
if (v.getClass().isArray()) {
|
||||
return Array.getLength(v) > 0;
|
||||
}
|
||||
if (v instanceof CharSequence) {
|
||||
return ((CharSequence)v).length() > 0;
|
||||
}
|
||||
if (v instanceof Number) {
|
||||
if (v instanceof Double) {
|
||||
return ((Number)v).doubleValue() != 0;
|
||||
|
||||
// 如果不是新工作模式,则对下面类型进行判断
|
||||
if ( !newWorkMode ) {
|
||||
if (v instanceof Number) {
|
||||
if (v instanceof Double) {
|
||||
return ((Number)v).doubleValue() != 0;
|
||||
}
|
||||
if (v instanceof Float) {
|
||||
return ((Number)v).floatValue() != 0;
|
||||
}
|
||||
return ((Number)v).intValue() != 0;
|
||||
}
|
||||
if (v instanceof Float) {
|
||||
return ((Number)v).floatValue() != 0;
|
||||
|
||||
// 下面四种类型的判断已提供了 shared method 扩展,用法如下:
|
||||
// #if(notEmpty(object)) 以及 #if(isEmpty(object))
|
||||
if (v instanceof Collection) {
|
||||
return ((Collection<?>)v).size() > 0;
|
||||
}
|
||||
if (v instanceof Map) {
|
||||
return ((Map<?, ?>)v).size() > 0;
|
||||
}
|
||||
if (v.getClass().isArray()) {
|
||||
return Array.getLength(v) > 0;
|
||||
}
|
||||
if (v instanceof Iterator) {
|
||||
return ((Iterator<?>)v).hasNext();
|
||||
}
|
||||
return ((Number)v).intValue() != 0;
|
||||
}
|
||||
if (v instanceof Iterator) {
|
||||
return ((Iterator<?>)v).hasNext();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -26,14 +26,14 @@ import java.lang.reflect.Modifier;
|
||||
*/
|
||||
public class MethodInfo {
|
||||
|
||||
protected final String key;
|
||||
protected final Long key;
|
||||
protected final Class<?> clazz;
|
||||
protected final Method method;
|
||||
|
||||
protected final boolean isVarArgs;
|
||||
protected final Class<?>[] paraTypes;
|
||||
|
||||
public MethodInfo(String key, Class<?> clazz, Method method) {
|
||||
public MethodInfo(Long key, Class<?> clazz, Method method) {
|
||||
this.key = key;
|
||||
this.clazz = clazz;
|
||||
this.method = method;
|
||||
@@ -64,7 +64,7 @@ public class MethodInfo {
|
||||
return method.invoke(target, finalArgValues);
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
public Long getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
|
@@ -26,7 +26,7 @@ public class MethodInfoExt extends MethodInfo {
|
||||
|
||||
protected Object objectOfExtensionClass;
|
||||
|
||||
public MethodInfoExt(Object objectOfExtensionClass, String key, Class<?> clazz, Method method) {
|
||||
public MethodInfoExt(Object objectOfExtensionClass, Long key, Class<?> clazz, Method method) {
|
||||
super(key, clazz, method);
|
||||
this.objectOfExtensionClass = objectOfExtensionClass;
|
||||
|
||||
|
132
src/main/java/com/jfinal/template/expr/ast/MethodKeyBuilder.java
Normal file
132
src/main/java/com/jfinal/template/expr/ast/MethodKeyBuilder.java
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* 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.expr.ast;
|
||||
|
||||
import com.jfinal.kit.HashKit;
|
||||
|
||||
/**
|
||||
* MethodKeyBuilder
|
||||
*/
|
||||
public abstract class MethodKeyBuilder {
|
||||
|
||||
/**
|
||||
* 生成指定 class、指定方法名、指定方法形参类型的 key 值,用于缓存
|
||||
*/
|
||||
public abstract Long getMethodKey(Class<?> targetClass, String methodName, Class<?>[] argTypes);
|
||||
|
||||
// 默认使用 FastMethodKeyBuilder
|
||||
static MethodKeyBuilder instance = new FastMethodKeyBuilder();
|
||||
|
||||
public static MethodKeyBuilder getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换到 StrictMethodKeyBuilder
|
||||
*
|
||||
* <pre>
|
||||
* 特别注意:
|
||||
* 如果希望将 configEngine(Engine me) 中的 Engine 切换到 StrictMethodKeyBuilder,
|
||||
* 需要在 YourJFinalConfig extends JFinalConfig 中利用如下代码块才能生效:
|
||||
* static {
|
||||
* MethodKeyBuilder.useStrictMethodKeyBuilder();
|
||||
* }
|
||||
*
|
||||
* 原因是在 com.jfinal.core.Config 中 new Engine() 时 useStrictMethodKeyBuilder()
|
||||
* 方法并未生效,所以 extension method 生成 method key 时仍然使用的是 FastMethodKeyBuilder
|
||||
* 以至于在运行时,使用 StrictMethodKeyBuilder 生成的 key 找不到 extension method
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public static void useStrictMethodKeyBuilder() {
|
||||
MethodKeyBuilder.instance = new StrictMethodKeyBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换到用户自定义 MethodKeyBuilder
|
||||
*/
|
||||
public static void setMethodKeyBuilder(MethodKeyBuilder methodKeyBuilder) {
|
||||
if (methodKeyBuilder == null) {
|
||||
throw new IllegalArgumentException("methodKeyBuilder can not be null");
|
||||
}
|
||||
MethodKeyBuilder.instance = methodKeyBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* FastMethodKeyBuilder
|
||||
*
|
||||
* targetClass、methodName、argTypes 的 hash 直接使用 String.hashCode()
|
||||
* String.hashCode() 会被缓存,性能更好
|
||||
*/
|
||||
public static class FastMethodKeyBuilder extends MethodKeyBuilder {
|
||||
public Long getMethodKey(Class<?> targetClass, String methodName, Class<?>[] argTypes) {
|
||||
long hash = HashKit.FNV_OFFSET_BASIS_64;
|
||||
hash ^= targetClass.getName().hashCode();
|
||||
hash *= HashKit.FNV_PRIME_64;
|
||||
|
||||
hash ^= methodName.hashCode();
|
||||
hash *= HashKit.FNV_PRIME_64;
|
||||
|
||||
if (argTypes != null) {
|
||||
for (int i=0; i<argTypes.length; i++) {
|
||||
Class<?> type = argTypes[i];
|
||||
if (type != null) {
|
||||
hash ^= type.getName().hashCode();
|
||||
hash *= HashKit.FNV_PRIME_64;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* StrictMethodKeyBuilder
|
||||
*
|
||||
* targetClass、methodName、argTypes 三部分全部使用 fnv1a64 算法计算 hash
|
||||
*/
|
||||
public static class StrictMethodKeyBuilder extends MethodKeyBuilder {
|
||||
public Long getMethodKey(Class<?> targetClass, String methodName, Class<?>[] argTypes) {
|
||||
long hash = HashKit.FNV_OFFSET_BASIS_64;
|
||||
|
||||
hash = fnv1a64(hash, targetClass.getName());
|
||||
hash = fnv1a64(hash, methodName);
|
||||
if (argTypes != null) {
|
||||
for (int i=0; i<argTypes.length; i++) {
|
||||
Class<?> type = argTypes[i];
|
||||
if (type != null) {
|
||||
hash = fnv1a64(hash, type.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
private long fnv1a64(long offsetBasis, String key) {
|
||||
long hash = offsetBasis;
|
||||
for(int i=0, size=key.length(); i<size; i++) {
|
||||
hash ^= key.charAt(i);
|
||||
hash *= HashKit.FNV_PRIME_64;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -22,8 +22,6 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import com.jfinal.kit.HashKit;
|
||||
import com.jfinal.kit.ReflectKit;
|
||||
import com.jfinal.template.ext.extensionmethod.ByteExt;
|
||||
import com.jfinal.template.ext.extensionmethod.DoubleExt;
|
||||
@@ -42,7 +40,7 @@ public class MethodKit {
|
||||
private static final Set<String> forbiddenMethods = new HashSet<String>();
|
||||
private static final Set<Class<?>> forbiddenClasses = new HashSet<Class<?>>();
|
||||
private static final Map<Class<?>, Class<?>> primitiveMap = new HashMap<Class<?>, Class<?>>();
|
||||
private static final ConcurrentHashMap<String, Object> methodCache = new ConcurrentHashMap<String, Object>();
|
||||
private static final HashMap<Long, Object> methodCache = new HashMap<Long, Object>();
|
||||
|
||||
// 初始化在模板中调用 method 时所在的被禁止使用类
|
||||
static {
|
||||
@@ -105,12 +103,12 @@ public class MethodKit {
|
||||
|
||||
public static MethodInfo getMethod(Class<?> targetClass, String methodName, Object[] argValues) {
|
||||
Class<?>[] argTypes = getArgTypes(argValues);
|
||||
String key = getMethodKey(targetClass, methodName, argTypes);
|
||||
Long key = getMethodKey(targetClass, methodName, argTypes);
|
||||
Object method = methodCache.get(key);
|
||||
if (method == null) {
|
||||
method = doGetMethod(key, targetClass, methodName, argTypes);
|
||||
if (method != null) {
|
||||
methodCache.putIfAbsent(key, method);
|
||||
methodCache.put(key, method);
|
||||
} else {
|
||||
// 对于不存在的 Method,只进行一次获取操作,主要为了支持 null safe,未来需要考虑内存泄漏风险
|
||||
methodCache.put(key, Boolean.FALSE);
|
||||
@@ -123,12 +121,12 @@ public class MethodKit {
|
||||
* 获取 getter 方法
|
||||
* 使用与 Field 相同的 key,避免生成两次 key值
|
||||
*/
|
||||
public static MethodInfo getGetterMethod(String key, Class<?> targetClass, String methodName) {
|
||||
public static MethodInfo getGetterMethod(Long key, Class<?> targetClass, String methodName) {
|
||||
Object getterMethod = methodCache.get(key);
|
||||
if (getterMethod == null) {
|
||||
getterMethod = doGetMethod(key, targetClass, methodName, NULL_ARG_TYPES);
|
||||
if (getterMethod != null) {
|
||||
methodCache.putIfAbsent(key, getterMethod);
|
||||
methodCache.put(key, getterMethod);
|
||||
} else {
|
||||
methodCache.put(key, Boolean.FALSE);
|
||||
}
|
||||
@@ -147,7 +145,7 @@ public class MethodKit {
|
||||
return argTypes;
|
||||
}
|
||||
|
||||
private static MethodInfo doGetMethod(String key, Class<?> targetClass, String methodName, Class<?>[] argTypes) {
|
||||
private static MethodInfo doGetMethod(Long key, Class<?> targetClass, String methodName, Class<?>[] argTypes) {
|
||||
if (forbiddenClasses.contains(targetClass)) {
|
||||
throw new RuntimeException("Forbidden class: " + targetClass.getName());
|
||||
}
|
||||
@@ -229,23 +227,8 @@ public class MethodKit {
|
||||
/**
|
||||
* 获取方法用于缓存的 key
|
||||
*/
|
||||
private static String getMethodKey(Class<?> targetClass, String methodName, Class<?>[] argTypes) {
|
||||
StringBuilder key = new StringBuilder(96);
|
||||
key.append(targetClass.getName());
|
||||
key.append('.').append(methodName);
|
||||
if (argTypes != null && argTypes.length > 0) {
|
||||
createArgTypesDigest(argTypes, key);
|
||||
}
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
static void createArgTypesDigest(Class<?>[] argTypes, StringBuilder key) {
|
||||
StringBuilder argTypesDigest = new StringBuilder(64);
|
||||
for (int i=0; i<argTypes.length; i++) {
|
||||
Class<?> type = argTypes[i];
|
||||
argTypesDigest.append(type != null ? type.getName() : "null");
|
||||
}
|
||||
key.append(HashKit.md5(argTypesDigest.toString()));
|
||||
private static Long getMethodKey(Class<?> targetClass, String methodName, Class<?>[] argTypes) {
|
||||
return MethodKeyBuilder.instance.getMethodKey(targetClass, methodName, argTypes);
|
||||
}
|
||||
|
||||
// 以下代码实现 extension method 功能 --------------------
|
||||
@@ -290,7 +273,7 @@ public class MethodKit {
|
||||
throw new RuntimeException("Extension method \"" + methodName + "\" is already exists in class \"" + targetClass.getName() + "\"");
|
||||
}
|
||||
} catch (NoSuchMethodException e) { // Method 找不到才能添加该扩展方法
|
||||
String key = MethodKit.getMethodKey(targetClass, methodName, toBoxedType(targetParaTypes));
|
||||
Long key = MethodKit.getMethodKey(targetClass, methodName, toBoxedType(targetParaTypes));
|
||||
if (methodCache.containsKey(key)) {
|
||||
throw new RuntimeException(buildMethodSignatureForException("The extension method is already exists: " + extensionClass.getName() + ".", methodName, targetParaTypes));
|
||||
}
|
||||
@@ -319,7 +302,7 @@ public class MethodKit {
|
||||
Class<?>[] targetParaTypes = new Class<?>[extensionMethodParaTypes.length - 1];
|
||||
System.arraycopy(extensionMethodParaTypes, 1, targetParaTypes, 0, targetParaTypes.length);
|
||||
|
||||
String key = MethodKit.getMethodKey(targetClass, methodName, toBoxedType(targetParaTypes));
|
||||
Long key = MethodKit.getMethodKey(targetClass, methodName, toBoxedType(targetParaTypes));
|
||||
methodCache.remove(key);
|
||||
}
|
||||
}
|
||||
|
36
src/main/java/com/jfinal/template/expr/ast/NullExpr.java
Normal file
36
src/main/java/com/jfinal/template/expr/ast/NullExpr.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.expr.ast;
|
||||
|
||||
import com.jfinal.template.stat.Scope;
|
||||
|
||||
/**
|
||||
* NullExpr
|
||||
*/
|
||||
public class NullExpr extends Expr {
|
||||
|
||||
public static final NullExpr me = new NullExpr();
|
||||
|
||||
private NullExpr() {}
|
||||
|
||||
public Object eval(Scope scope) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -49,15 +49,18 @@ public class NullSafe extends Expr {
|
||||
Ctrl ctrl = scope.getCtrl();
|
||||
boolean oldNullSafeValue = ctrl.isNullSafe();
|
||||
|
||||
Object ret;
|
||||
try {
|
||||
ctrl.setNullSafe(true);
|
||||
ret = left.eval(scope);
|
||||
Object ret = left.eval(scope);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
} finally {
|
||||
ctrl.setNullSafe(oldNullSafeValue);
|
||||
}
|
||||
|
||||
return ret == null && right != null ? right.eval(scope) : ret;
|
||||
// right 表达式处于 null safe 区域之外
|
||||
return right != null ? right.eval(scope) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,3 +69,4 @@ public class NullSafe extends Expr {
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -21,10 +21,11 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.HashMap;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import com.jfinal.kit.HashKit;
|
||||
import com.jfinal.kit.ReflectKit;
|
||||
|
||||
/**
|
||||
@@ -32,27 +33,27 @@ import com.jfinal.kit.ReflectKit;
|
||||
*/
|
||||
public class SharedMethodKit {
|
||||
|
||||
private static final Set<String> excludedMethodKey = new HashSet<String>();
|
||||
private static final Set<Long> excludedMethodKey = new HashSet<Long>();
|
||||
|
||||
static {
|
||||
Method[] methods = Object.class.getMethods();
|
||||
for (Method method : methods) {
|
||||
String key = getSharedMethodKey(method.getName(), method.getParameterTypes());
|
||||
Long key = getSharedMethodKey(method.getName(), method.getParameterTypes());
|
||||
excludedMethodKey.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
private final List<SharedMethodInfo> sharedMethodList = new ArrayList<SharedMethodInfo>();
|
||||
private final ConcurrentHashMap<String, SharedMethodInfo> methodCache = new ConcurrentHashMap<String, SharedMethodInfo>();
|
||||
private final HashMap<Long, SharedMethodInfo> methodCache = new HashMap<Long, SharedMethodInfo>();
|
||||
|
||||
public SharedMethodInfo getSharedMethodInfo(String methodName, Object[] argValues) {
|
||||
Class<?>[] argTypes = MethodKit.getArgTypes(argValues);
|
||||
String key = getSharedMethodKey(methodName, argTypes);
|
||||
Long key = getSharedMethodKey(methodName, argTypes);
|
||||
SharedMethodInfo method = methodCache.get(key);
|
||||
if (method == null) {
|
||||
method = doGetSharedMethodInfo(methodName, argTypes);
|
||||
if (method != null) {
|
||||
methodCache.putIfAbsent(key, method);
|
||||
methodCache.put(key, method);
|
||||
}
|
||||
// shared method 不支持 null safe,不缓存: methodCache.put(key, Boolean.FALSE)
|
||||
}
|
||||
@@ -110,7 +111,7 @@ public class SharedMethodKit {
|
||||
SharedMethodInfo current = it.next();
|
||||
String methodName = method.getName();
|
||||
if (current.getName().equals(methodName)) {
|
||||
String key = getSharedMethodKey(methodName, method.getParameterTypes());
|
||||
Long key = getSharedMethodKey(methodName, method.getParameterTypes());
|
||||
if (current.getKey().equals(key)) {
|
||||
it.remove();
|
||||
}
|
||||
@@ -125,7 +126,7 @@ public class SharedMethodKit {
|
||||
|
||||
Method[] methods = sharedClass.getMethods();
|
||||
for (Method method : methods) {
|
||||
String key = getSharedMethodKey(method.getName(), method.getParameterTypes());
|
||||
Long key = getSharedMethodKey(method.getName(), method.getParameterTypes());
|
||||
if (excludedMethodKey.contains(key)) {
|
||||
continue ;
|
||||
}
|
||||
@@ -144,19 +145,27 @@ public class SharedMethodKit {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getSharedMethodKey(String methodName, Class<?>[] argTypes) {
|
||||
StringBuilder key = new StringBuilder(64);
|
||||
key.append(methodName);
|
||||
if (argTypes != null && argTypes.length > 0) {
|
||||
MethodKit.createArgTypesDigest(argTypes, key);
|
||||
private static Long getSharedMethodKey(String methodName, Class<?>[] argTypes) {
|
||||
long hash = HashKit.FNV_OFFSET_BASIS_64;
|
||||
hash ^= methodName.hashCode();
|
||||
hash *= HashKit.FNV_PRIME_64;
|
||||
|
||||
if (argTypes != null) {
|
||||
for (int i=0; i<argTypes.length; i++) {
|
||||
Class<?> type = argTypes[i];
|
||||
if (type != null) {
|
||||
hash ^= type.getName().hashCode();
|
||||
hash *= HashKit.FNV_PRIME_64;
|
||||
}
|
||||
}
|
||||
}
|
||||
return key.toString();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static class SharedMethodInfo extends MethodInfo {
|
||||
final Object target;
|
||||
|
||||
private SharedMethodInfo(String key, Class<?> clazz, Method method, Object target) {
|
||||
private SharedMethodInfo(Long key, Class<?> clazz, Method method, Object target) {
|
||||
super(key, clazz, method);
|
||||
this.target = target;
|
||||
}
|
||||
|
Reference in New Issue
Block a user