/**
* jetbrick-template
* http://subchen.github.io/jetbrick-template/
*
* Copyright 2010-2014 Guoqiang Chen. All rights reserved.
* Email: subchen@gmail.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 jetbrick.template.parser;
import java.lang.reflect.*;
import java.util.*;
import jetbrick.template.JetContext;
import jetbrick.template.parser.support.MethodFinderUtils;
import jetbrick.template.parser.support.TypedKlass;
import jetbrick.template.runtime.*;
import jetbrick.template.utils.ClassLoaderUtils;
import jetbrick.template.utils.ExceptionUtils;
import jetbrick.template.utils.finder.PackagesFinder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VariableResolver {
private static final Logger log = LoggerFactory.getLogger(VariableResolver.class);
private Set<String> importedPackageList = new LinkedHashSet<String>(); // 全局 import 的 Package
private Map<String, Class<?>> importedClassMap = Collections.synchronizedMap(new HashMap<String, Class<?>>(64)); // 全局 import 独立的 Class
private Map<String, TypedKlass> variableMap = new HashMap<String, TypedKlass>(); // 全局定义的变量
private Map<String, List<Method>> methodMap1 = new HashMap<String, List<Method>>(64); // 全局导入的 method 类
private Map<String, List<Method>> methodMap2 = new HashMap<String, List<Method>>(32); // 全局导入的 method 类 (带 JetPageContext)
private Map<String, List<Method>> functionMap1 = new HashMap<String, List<Method>>(32); // 全局导入的 function 类
private Map<String, List<Method>> functionMap2 = new HashMap<String, List<Method>>(); // 全局导入的 function 类 (带 JetPageContext)
private Map<String, List<Method>> tagMap = new HashMap<String, List<Method>>(); // 全局导入的 tag 类 (带 JetTagContext)
private static final Map<String, Member> bean_field_cache = Collections.synchronizedMap(new HashMap<String, Member>(64));
private static final Map<String, Method> bean_method_cache = Collections.synchronizedMap(new HashMap<String, Method>(128));
private static final Map<String, Constructor<?>> bean_constructor_cache = Collections.synchronizedMap(new HashMap<String, Constructor<?>>());
private static final Map<String, Method> static_tool_method_cache = Collections.synchronizedMap(new HashMap<String, Method>(128));
private static final Map<String, Method> static_function_cache = Collections.synchronizedMap(new HashMap<String, Method>(64));
private static final Map<String, Method> static_tag_method_cache = Collections.synchronizedMap(new HashMap<String, Method>());
private static final Map<String, Field> static_field_cache = Collections.synchronizedMap(new HashMap<String, Field>(16));
private static final Map<String, Method> static_method_cache = Collections.synchronizedMap(new HashMap<String, Method>(16));
public VariableResolver() {
addImportPackage("java.lang");
addImportPackage("java.util");
//
addMethodClass(JetMethods.class.getName());
addFunctionClass(JetFunctions.class.getName());
addTagClass(JetTags.class.getName());
}
public void addImportPackage(String pkg) {
if (pkg.endsWith(".**")) {
log.info("import package: {}", pkg);
pkg = pkg.substring(0, pkg.length() - 3);
Set<String> pkgs = PackagesFinder.getPackages(pkg);
pkgs.add(pkg); // add self
for (String subpkg : pkgs) {
if (importedPackageList.add(subpkg)) {
log.info("found sub package: {}.*", subpkg);
}
}
return;
}
if (pkg.endsWith(".*")) {
pkg = pkg.substring(0, pkg.length() - 2);
}
if (importedPackageList.add(pkg)) {
log.info("import package: {}.*", pkg);
}
}
public void addImportClass(String klassName) {
try {
Class<?> klass = ClassLoaderUtils.loadClass(klassName);
if (importedClassMap.put(klass.getSimpleName(), klass) == null) {
log.info("import class: " + klass.getName());
}
} catch (ClassNotFoundException e) {
throw ExceptionUtils.uncheck(e);
}
}
public void addGlobalVariable(String klassName, String name) {
TypedKlass klass = resolveTypedKlass(klassName);
if (klass == null || klass == TypedKlass.NULL) {
throw new RuntimeException("ClassNotFoundException: " + klassName);
}
if (variableMap.put(name, klass) == null) {
log.info("add variable: {} {}", klass.getSource(), name);
}
}
public void addMethodClass(String klassName) {
Class<?> klass = resolveClass(klassName);
if (klass == null) {
throw new IllegalStateException("Cannot resolve class: " + klassName);
}
addMethodClass(klass);
}
public void addMethodClass(Class<?> klass) {
for (Method method : klass.getMethods()) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 0) {
continue;
}
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) {
String name = method.getName();
List<Method> list;
if (parameterTypes.length > 1 && JetPageContext.class.equals(parameterTypes[1])) {
list = methodMap2.get(name);
if (list == null) {
list = new ArrayList<Method>(4);
methodMap2.put(name, list);
}
} else {
list = methodMap1.get(name);
if (list == null) {
list = new ArrayList<Method>(4);
methodMap1.put(name, list);
}
}
list.add(method);
}
}
log.info("add method class: {}", klass.getName());
}
public void addFunctionClass(String klassName) {
Class<?> klass = resolveClass(klassName);
if (klass == null) {
throw new IllegalStateException("Cannot resolve class: " + klassName);
}
addFunctionClass(klass);
}
public void addFunctionClass(Class<?> klass) {
for (Method method : klass.getMethods()) {
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) {
String name = method.getName();
List<Method> list;
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length > 0 && JetPageContext.class.equals(parameterTypes[0])) {
list = functionMap2.get(name);
if (list == null) {
list = new ArrayList<Method>(4);
functionMap2.put(name, list);
}
} else {
list = functionMap1.get(name);
if (list == null) {
list = new ArrayList<Method>(4);
functionMap1.put(name, list);
}
}
list.add(method);
}
}
log.info("add function class: {}", klass.getName());
}
public void addTagClass(String klassName) {
Class<?> klass = resolveClass(klassName);
if (klass == null) {
throw new IllegalStateException("Cannot resolve class: " + klassName);
}
addTagClass(klass);
}
public void addTagClass(Class<?> klass) {
for (Method method : klass.getMethods()) {
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) {
if (!Void.TYPE.equals(method.getReturnType())) {
continue;
}
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length > 0 && JetTagContext.class.equals(parameterTypes[0])) {
String name = method.getName();
List<Method> list = tagMap.get(name);
if (list == null) {
list = new ArrayList<Method>(4);
tagMap.put(name, list);
}
list.add(method);
}
}
}
log.info("add tag class: {}", klass.getName());
}
// 根据名称,查找到对应的class (支持完整的泛型声明)
public TypedKlass resolveTypedKlass(String klassName) {
if (klassName == null) return null;
try {
return new TypedKlassParser(klassName.toCharArray(), this).asTypedKlass();
} catch (Throwable e) {
return null;
}
}
// 根据名称,查找到对应的class
public Class<?> resolveClass(String klassName) {
return doGetClass(klassName);
}
private Class<?> doGetClass(String klassName) {
try {
return ClassLoaderUtils.loadClass(klassName);
} catch (ClassNotFoundException e) {
}
// 先查单个类的导入情况
Class<?> klass = importedClassMap.get(klassName);
if (klass != null) return klass;
// 只对类似 String 或者 Map.Entry 这样的才尝试进行包名补齐
int lpos = klassName.indexOf('.');
if (lpos < 0 || lpos == klassName.lastIndexOf('.')) {
if (lpos > 0) {
// 内部类
klassName = klassName.replace('.', '$');
}
for (String pkg : importedPackageList) {
try {
klass = ClassLoaderUtils.loadClass(pkg + "." + klassName);
importedClassMap.put(klassName, klass); // cache for speed
return klass;
} catch (ClassNotFoundException e) {
}
}
}
return null;
}
public TypedKlass resolveVariable(String identifier) {
TypedKlass klass = variableMap.get(identifier);
if (klass == null) {
klass = TypedKlass.Object;
}
return klass;
}
// 找到对应的属性Get方法
public Member resolveProperty(Class<?> beanClass, String name) {
String key = getPrivateCacheKeyName(beanClass.getName(), name, null);
// lookup cache
Member member = bean_field_cache.get(key);
if (member == null) {
synchronized (bean_field_cache) {
member = doResolveProperty(beanClass, name);
if (member != null) {
bean_field_cache.put(key, member);
}
}
}
return member;
}
private Member doResolveProperty(Class<?> beanClass, String name) {
// public method
Method[] methods = beanClass.getMethods();
// getXXX() or isXXX()
String suffix = name.substring(0, 1).toUpperCase() + name.substring(1);
String method_get = "get" + suffix;
String method_is = "is" + suffix;
for (Method method : methods) {
String methodName = method.getName();
if (method_get.equals(methodName) || method_is.equals(methodName)) {
if (method.getParameterTypes().length == 0) {
return method;
}
}
}
// public field
try {
return beanClass.getField(name);
} catch (NoSuchFieldException e) {
}
try {
// get(Object) for Map
if (Map.class.isAssignableFrom(beanClass)) {
return beanClass.getMethod("get", Object.class);
}
// get(String) for JetContext, JFinal Model, ...
try {
return beanClass.getMethod("get", String.class);
} catch (NoSuchMethodException e) {
}
} catch (Exception e) {
throw ExceptionUtils.uncheck(e);
}
return null;
}
// 找到对应的方法 bean.method(...)
public Method resolveMethod(Class<?> beanClass, String methodName, Class<?>[] parameterTypes) {
String key = getPrivateCacheKeyName(beanClass.getName(), methodName, parameterTypes);
// lookup cache
Method method = bean_method_cache.get(key);
if (method == null) {
synchronized (bean_method_cache) {
method = MethodFinderUtils.lookupMethod(beanClass, methodName, parameterTypes);
if (method != null) {
bean_method_cache.put(key, method);
}
}
}
return method;
}
// 找到对应的方法 Tool.method(bean, ...)
public Method resolveToolMethod(Class<?> beanClass, String methodName, Class<?>[] parameterTypes) {
List<Method> methods = methodMap1.get(methodName);
if (methods == null) return null;
Class<?>[] targetParameterTypes = new Class<?>[1 + parameterTypes.length];
targetParameterTypes[0] = beanClass;
for (int i = 0; i < parameterTypes.length; i++) {
targetParameterTypes[i + 1] = parameterTypes[i];
}
String key = getPrivateCacheKeyName(null, methodName, targetParameterTypes);
// lookup cache
Method method = static_tool_method_cache.get(key);
if (method == null) {
synchronized (static_tool_method_cache) {
method = MethodFinderUtils.lookupBestMethod(methods, methodName, targetParameterTypes);
if (method != null) {
static_tool_method_cache.put(key, method);
}
}
}
return method;
}
// 找到对应的方法 Tool.method(bean, JetPageContext, ...)
public Method resolveToolMethod_advanced(Class<?> beanClass, String methodName, Class<?>[] parameterTypes) {
List<Method> methods = methodMap2.get(methodName);
if (methods == null) return null;
Class<?>[] targetParameterTypes = new Class<?>[2 + parameterTypes.length];
targetParameterTypes[0] = beanClass;
targetParameterTypes[1] = JetPageContext.class;
for (int i = 0; i < parameterTypes.length; i++) {
targetParameterTypes[i + 2] = parameterTypes[i];
}
String key = getPrivateCacheKeyName(null, methodName, targetParameterTypes);
// lookup cache
Method method = static_tool_method_cache.get(key);
if (method == null) {
synchronized (static_tool_method_cache) {
method = MethodFinderUtils.lookupBestMethod(methods, methodName, targetParameterTypes);
if (method != null) {
static_tool_method_cache.put(key, method);
}
}
}
return method;
}
// 找到对应的方法 function(...)
public Method resolveFunction(String methodName, Class<?>[] parameterTypes) {
List<Method> methods = functionMap1.get(methodName);
if (methods == null) return null;
String key = getPrivateCacheKeyName(null, methodName, parameterTypes);
// lookup cache
Method method = static_function_cache.get(key);
if (method == null) {
synchronized (static_function_cache) {
method = MethodFinderUtils.lookupBestMethod(methods, methodName, parameterTypes);
if (method != null) {
static_function_cache.put(key, method);
}
}
}
return method;
}
// 找到对应的方法 function(JetPageContext, ...)
public Method resolveFunction_advanced(String methodName, Class<?>[] parameterTypes) {
List<Method> methods = functionMap2.get(methodName);
if (methods == null) return null;
Class<?>[] targetParameterTypes = new Class<?>[1 + parameterTypes.length];
targetParameterTypes[0] = JetPageContext.class;
for (int i = 0; i < parameterTypes.length; i++) {
targetParameterTypes[i + 1] = parameterTypes[i];
}
String key = getPrivateCacheKeyName(null, methodName, targetParameterTypes);
// lookup cache
Method method = static_function_cache.get(key);
if (method == null) {
synchronized (static_function_cache) {
method = MethodFinderUtils.lookupBestMethod(methods, methodName, targetParameterTypes);
if (method != null) {
static_function_cache.put(key, method);
}
}
}
return method;
}
// 找到对应的构造函数
public Constructor<?> resolveConstructor(Class<?> beanClass, Class<?>[] parameterTypes) {
String key = getPrivateCacheKeyName(beanClass.getName(), "<init>", parameterTypes);
// lookup cache
Constructor<?> constructor = bean_constructor_cache.get(key);
if (constructor == null) {
synchronized (bean_constructor_cache) {
constructor = MethodFinderUtils.lookupConstructor(beanClass, parameterTypes);
if (constructor != null) {
bean_constructor_cache.put(key, constructor);
}
}
}
return constructor;
}
// 找到对应的Tag tag(...)
public Method resolveTagMethod(String methodName, Class<?>[] parameterTypes) {
List<Method> methods = tagMap.get(methodName);
if (methods == null) return null;
String key = getPrivateCacheKeyName(null, methodName, parameterTypes);
// lookup cache
Method method = static_tag_method_cache.get(key);
if (method == null) {
synchronized (static_tag_method_cache) {
method = MethodFinderUtils.lookupBestMethod(methods, methodName, parameterTypes);
if (method != null) {
static_tag_method_cache.put(key, method);
}
}
}
return method;
}
public Field resolveStaticField(Class<?> beanClass, String name) {
String key = getPrivateCacheKeyName(beanClass.getName(), name, null);
// lookup cache
Field field = static_field_cache.get(key);
if (field == null) {
synchronized (static_field_cache) {
try {
field = beanClass.getField(name);
if (field != null) {
if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) {
static_field_cache.put(key, field);
}
}
} catch (Exception e) {
return null;
}
}
}
return field;
}
public Method resolveStaticMethod(Class<?> beanClass, String name, Class<?>[] parameterTypes) {
String key = getPrivateCacheKeyName(beanClass.getName(), name, parameterTypes);
// lookup cache
Method method = static_method_cache.get(key);
if (method == null) {
synchronized (static_method_cache) {
try {
method = MethodFinderUtils.lookupMethod(beanClass, name, parameterTypes);
if (method != null) {
if (Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers())) {
static_method_cache.put(key, method);
}
}
} catch (Exception e) {
return null;
}
}
}
return method;
}
// 生成一个字符串作为 Method Cache 的 Key
private static String getPrivateCacheKeyName(String clazzName, String methodName, Class<?>[] parameterTypes) {
StringBuilder sb = new StringBuilder(32);
if (clazzName != null) {
sb.append(clazzName);
sb.append('/');
}
sb.append(methodName);
if (parameterTypes != null) {
for (Class<?> type : parameterTypes) {
sb.append('/');
sb.append(type == null ? "<null>" : type.getName());
}
}
return sb.toString();
}
}