package org.theonefx.wcframework.ioc;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.theonefx.wcframework.core.ClassWrapper;
import org.theonefx.wcframework.core.TypeConverterDelegate;
import org.theonefx.wcframework.core.exception.BeansException;
import org.theonefx.wcframework.core.inject.InjectByField;
import org.theonefx.wcframework.core.inject.InjectBySetter;
import org.theonefx.wcframework.core.inject.Injector;
import org.theonefx.wcframework.core.propertyeditor.PropertyTypeDescriptor;
import org.theonefx.wcframework.core.propertyeditor.TypeDescriptor;
import org.theonefx.wcframework.ioc.exception.ConversionException;
import org.theonefx.wcframework.ioc.exception.ConversionNotSupportedException;
import org.theonefx.wcframework.ioc.exception.ConverterNotFoundException;
import org.theonefx.wcframework.ioc.exception.InvalidPropertyException;
import org.theonefx.wcframework.ioc.exception.MethodInvocationException;
import org.theonefx.wcframework.ioc.exception.NotReadablePropertyException;
import org.theonefx.wcframework.ioc.exception.NotWritablePropertyException;
import org.theonefx.wcframework.ioc.exception.NullValueInNestedPathException;
import org.theonefx.wcframework.ioc.exception.TypeMismatchException;
import org.theonefx.wcframework.ioc.val.GenericCollectionTypeResolver;
import org.theonefx.wcframework.ioc.val.MethodParameter;
import org.theonefx.wcframework.utils.Assert;
import org.theonefx.wcframework.utils.BeanUtils;
import org.theonefx.wcframework.utils.ObjectUtils;
import org.theonefx.wcframework.utils.StringUtils;
public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWrapper {
private static final Log log = LogFactory.getLog(BeanWrapperImpl.class);
private Object object;
private TypeConverterDelegate typeConverterDelegate;
private AccessControlContext acc;
private CachedIntrospectionResults cachedIntrospectionResults;
public BeanWrapperImpl() {
this.typeConverterDelegate = new TypeConverterDelegate(this);
}
public BeanWrapperImpl(Object object) {
setWrappedInstance(object);
}
// ---------------------------------------------------------------------
// BeanWrapper接口的实现
// ---------------------------------------------------------------------
public void setWrappedInstance(Object object) {
Assert.notNull(object, "Bean object must not be null");
if (log.isDebugEnabled()) {
log.debug("设置BeanWrapperImpl的包装Bean:" + object);
}
this.object = object;
this.typeConverterDelegate = new TypeConverterDelegate(this, object);
setIntrospectionClass(object.getClass());
}
public final Object getWrappedInstance() {
return this.object;
}
public final Class<?> getWrappedClass() {
return (this.object != null ? this.object.getClass() : null);
}
/**
* 设置调用封装的bean实例的方法时使用的安全上下文. 可以设置null.
*/
public void setSecurityContext(AccessControlContext acc) {
this.acc = acc;
}
/**
* 返回调用封装的bean实例的方法时使用的安全上下文. 可能是null.
*/
public AccessControlContext getSecurityContext() {
return this.acc;
}
/**
* 设置内省的Class.
* 需要在目标对象改变的时候调用.
*/
protected void setIntrospectionClass(Class<?> clazz) {
if (this.cachedIntrospectionResults != null && !clazz.equals(this.cachedIntrospectionResults.getBeanClass())) {
this.cachedIntrospectionResults = null;
}
}
/**
* 获得一个延迟初始化封装对象的CachedIntrospectionResults实例
*/
private CachedIntrospectionResults getCachedIntrospectionResults() {
Assert.state(this.object != null, "BeanWrapper does not hold a bean instance");
if (this.cachedIntrospectionResults == null) {
this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass());
}
return this.cachedIntrospectionResults;
}
public PropertyDescriptor[] getPropertyDescriptors() {
return getCachedIntrospectionResults().getPropertyDescriptors();
}
public PropertyDescriptor getPropertyDescriptor(String propertyName) throws BeansException {
PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName);
if (pd == null) {
throw new InvalidPropertyException(getWrappedClass(), propertyName, "找不到名称为 '" + propertyName + "' 的属性");
}
return pd;
}
/**
* {@link #getPropertyDescriptor}的内部版本: 如果没有找到对应的属性则返回 <code>null</code>而不是抛出异常.
*
* @param propertyName 需要获得PropertyDescriptor的属性的名称
* @return 指定属性的PropertyDescriptor, 如果找不到则返回<code>null</code>
* @throws BeansException 内省失败时抛出此异常
*/
protected PropertyDescriptor getPropertyDescriptorInternal(String propertyName) throws BeansException {
return getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
}
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
} catch (ConverterNotFoundException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
} catch (ConversionException ex) {
throw new TypeMismatchException(value, requiredType, ex);
} catch (IllegalStateException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
} catch (IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
private Object convertIfNecessary(String propertyName, Object oldValue, Object newValue, Class<?> requiredType, TypeDescriptor td) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
} catch (ConverterNotFoundException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.object, propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, requiredType, ex);
} catch (ConversionException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.object, propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, requiredType, ex);
} catch (IllegalStateException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.object, propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, requiredType, ex);
} catch (IllegalArgumentException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.object, propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, requiredType, ex);
}
}
/**
* 将给定的value装换为与指定属性最相近的类型
* @param value 需要被转换的值
* @param propertyName 目标属性的名称
* @return 新的值
* @throws TypeMismatchException 类型转换失败则抛出此异常
*/
public Object convertForProperty(Object value, String propertyName) throws TypeMismatchException {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
return convertForProperty(propertyName, null, value, pd);
}
/**
* 属性转换的内部实现
* @param propertyName 属性名称
* @param oldValue 属性原值
* @param newValue 属性新值
* @param pd JavaBean的属性描述
* @return 转换完成后的对象
* @throws TypeMismatchException 类型不匹配时发生此异常,即无法完成转换
*/
private Object convertForProperty(String propertyName, Object oldValue, Object newValue, PropertyDescriptor pd) throws TypeMismatchException {
TypeDescriptor descriptor = null;
Class<?> propertyType = null;
if (pd != null) {
Method method = pd.getWriteMethod();
if (method != null) {
Type[] types = method.getGenericParameterTypes();
if (types != null && types.length > 0) {
propertyType = method.getParameterTypes()[0];
}
descriptor = new PropertyTypeDescriptor(pd, BeanUtils.getWriteMethodParameter(pd));
}
}
if (descriptor == null) {
ClassWrapper<?> wrapper = ClassWrapper.getWrapper(getWrappedClass());
Field field = wrapper.getField(propertyName);
propertyType = field.getType();
descriptor = new TypeDescriptor(field);
}
return convertIfNecessary(propertyName, oldValue, newValue, propertyType, descriptor);
}
// ---------------------------------------------------------------------
// PropertyAccessor接口的实现
// ---------------------------------------------------------------------
@Override
public Object getPropertyValue(String propertyName) throws BeansException {
PropertyTokenHolder tokens = getPropertyNameTokens(propertyName);
return getPropertyValue(tokens);
}
/**
* 解析给定的属性名称,并转换为对应的属性名称标记
* @param propertyName 需要解析的属性名称
* @return 代表解析出来的属性的标记
*/
private PropertyTokenHolder getPropertyNameTokens(String propertyName) {
PropertyTokenHolder tokens = new PropertyTokenHolder();
String actualName = null;
List<String> keys = new ArrayList<String>(2);
int searchIndex = 0;
while (searchIndex != -1) {
int keyStart = propertyName.indexOf(PROPERTY_KEY_PREFIX, searchIndex);
searchIndex = -1;
if (keyStart != -1) {
int keyEnd = propertyName.indexOf(PROPERTY_KEY_SUFFIX, keyStart + PROPERTY_KEY_PREFIX.length());
if (keyEnd != -1) {
if (actualName == null) {
actualName = propertyName.substring(0, keyStart);
}
String key = propertyName.substring(keyStart + PROPERTY_KEY_PREFIX.length(), keyEnd);
if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) {
key = key.substring(1, key.length() - 1);
}
keys.add(key);
searchIndex = keyEnd + PROPERTY_KEY_SUFFIX.length();
}
}
}
tokens.actualName = (actualName != null ? actualName : propertyName);
tokens.canonicalName = tokens.actualName;
if (!keys.isEmpty()) {
tokens.canonicalName += PROPERTY_KEY_PREFIX + StringUtils.collectionToDelimitedString(keys, PROPERTY_KEY_SUFFIX + PROPERTY_KEY_PREFIX) + PROPERTY_KEY_SUFFIX;
tokens.keys = StringUtils.toStringArray(keys);
}
// 例如:
// actualName = propertya
// canonicalName = propertya[x][y]
// keys = [x,y]
return tokens;
}
/**
* 根据属性名称标记获取属性值
* @param tokens 属性名称标记
* @return 属性值
* @throws BeansException 可能在获取过程中发生的异常
*/
private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
// 规范名称
String propertyName = tokens.canonicalName;
// 真是属性名
String actualName = tokens.actualName;
// 根据真实属性名获取PropertyDescriptor
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
// 该属性必须有PropertyDescriptor 同时还得有读取方法
if (pd == null || pd.getReadMethod() == null) {
throw new NotReadablePropertyException(getWrappedClass(), propertyName);
}
final Method readMethod = pd.getReadMethod();
try {
// 调用readMethod读取属性值
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()) && !readMethod.isAccessible()) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
readMethod.setAccessible(true);
return null;
}
});
} else {
readMethod.setAccessible(true);
}
}
Object value;
if (System.getSecurityManager() != null) {
try {
value = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
return readMethod.invoke(object, (Object[]) null);
}
}, acc);
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
} else {
value = readMethod.invoke(object, (Object[]) null);
}
// 此时已经获取了object中属性值,处理规范名称与属性名称不一致的情况(也就是说,需要获取的是一个集合/Map中的某一个值而非集合/Map本身)
if (tokens.keys != null) {
if (value == null) {
throw new NullValueInNestedPathException(getWrappedClass(), propertyName, "属性'" + propertyName + "'为null,无法再根据名称中的path获取内部的值");
}
// 应用索引号和Map键
for (int i = 0; i < tokens.keys.length; i++) {
String key = tokens.keys[i];
if (value == null) {
throw new NullValueInNestedPathException(getWrappedClass(), propertyName, "属性'" + propertyName + "'为null,无法再根据名称中的path获取内部的值");
} else if (value.getClass().isArray()) {
int index = Integer.parseInt(key);
value = Array.get(value, index);
} else if (value instanceof List) {
int index = Integer.parseInt(key);
List<?> list = (List<?>) value;
value = list.get(index);
} else if (value instanceof Set) {
// 由于Set本身没有基于索引号获取内容的方法
// 这里只能使用便利找到对应位置的元素
Set<?> set = (Set<?>) value;
int index = Integer.parseInt(key);
if (index < 0 || index >= set.size()) {
throw new InvalidPropertyException(getWrappedClass(), propertyName, "无法在大小为 " + set.size()
+ " 的Set中获取索引号为 " + index + "的元素, 属性名称为 '" + propertyName + "'");
}
Iterator<?> it = set.iterator();
for (int j = 0; it.hasNext(); j++) {
Object elem = it.next();
if (j == index) {
value = elem;
break;
}
}
} else if (value instanceof Map) {
Map<?, ?> map = (Map<?, ?>) value;
Class<?> mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(readMethod, i + 1);
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, new PropertyTypeDescriptor(pd, new MethodParameter(readMethod, -1), mapKeyType));
value = map.get(convertedMapKey);
} else {
// 不是数组、不是集合、不是map。。。天知道是什么东西
throw new InvalidPropertyException(getWrappedClass(), propertyName, "无法读取名为 '" + propertyName + "' 值为 '"+ value+"' 的属性,因为他既不是一个数组、也不是一个List或者Set或者Map");
}
}
}
return value;
} catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getWrappedClass(), propertyName, "路径 '" + propertyName + "' 的属性索引号超出范围", ex);
} catch (NumberFormatException ex) {
throw new InvalidPropertyException(getWrappedClass(), propertyName, "属性路径 '" + propertyName + "' 中的索引号无效", ex);
} catch (TypeMismatchException ex) {
throw new InvalidPropertyException(getWrappedClass(), propertyName, "属性路径 '" + propertyName + "' 中的索引号无效", ex);
} catch (InvocationTargetException ex) {
throw new InvalidPropertyException(getWrappedClass(), propertyName, "获得名称为 '" + actualName + "' 的属性时发生异常", ex);
} catch (Exception ex) {
throw new InvalidPropertyException(getWrappedClass(), propertyName, "尝试获取名称为 '" + actualName + "' 的属性时发生异常", ex);
}
}
@Override
public void setPropertyValue(String propertyName, Object value) throws BeansException {
PropertyTokenHolder tokens = getPropertyNameTokens(propertyName);
setPropertyValue(tokens, new PropertyValue(propertyName, value));
}
@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
if (tokens == null) {
String propertyName = pv.getName();
tokens = getPropertyNameTokens(propertyName);
pv.getOriginalPropertyValue().resolvedTokens = tokens;
}
setPropertyValue(tokens, pv);
}
@SuppressWarnings("unchecked")
private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
if (tokens.keys != null) {
// keys不为空,代表需要设置的属性是一个嵌套属性,需要根据key据需向内部查找
PropertyTokenHolder getterTokens = new PropertyTokenHolder();
getterTokens.canonicalName = tokens.canonicalName;
getterTokens.actualName = tokens.actualName;
getterTokens.keys = new String[tokens.keys.length - 1];
// 跳过最后一个key,目的是为了获取最后一个key的上层值
System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
Object propValue;
try {
// 这个propValue是目标属性的上层属性值
propValue = getPropertyValue(getterTokens);
} catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getWrappedClass(), propertyName, "无法设置属性路径为 '" + propertyName + "'的值,因为他的上层不是可读的", ex);
}
// 设置最后一个key的值
String key = tokens.keys[tokens.keys.length - 1];
// 首先需要保证最后一个key的上层属性值不为null,否则无法设置最有一个key的值
if (propValue == null) {
throw new NullValueInNestedPathException(getWrappedClass(), propertyName, "无法设置属性路径为 '" + propertyName + "'的值,因为他的上层属性值为null");
} else if (propValue.getClass().isArray()) {
// 是一个数组
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
Class<?> requiredType = propValue.getClass().getComponentType();
int arrayIndex = Integer.parseInt(key);
Object oldValue = null;
try {
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType,
new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), requiredType));
Array.set(propValue, arrayIndex, convertedValue);
} catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getWrappedClass(), propertyName, "路径为'" + propertyName + "'的属性其索引号是无效的", ex);
}
} else if (propValue instanceof List) {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
Class<?> requiredType = GenericCollectionTypeResolver.getCollectionReturnType(pd.getReadMethod(), tokens.keys.length);
List<Object> list = (List<Object>) propValue;
int index = Integer.parseInt(key);
Object oldValue = null;
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType,
new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), requiredType));
if (index < list.size()) {
list.set(index, convertedValue);
} else if (index >= list.size()) {
for (int i = list.size(); i < index; i++) {
try {
list.add(null);
} catch (NullPointerException ex) {
throw new InvalidPropertyException(getWrappedClass(), propertyName, "无法设置索引号为 " + index
+ ",大小为" + list.size() + ",属性路径为 '" + propertyName
+ "'的list的元素: 该list不支持null为其元素");
}
}
list.add(convertedValue);
}
} else if (propValue instanceof Map) {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
Class<?> mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(pd.getReadMethod(), tokens.keys.length);
Class<?> mapValueType = GenericCollectionTypeResolver.getMapValueReturnType(pd.getReadMethod(), tokens.keys.length);
Map<Object, Object> map = (Map<Object, Object>) propValue;
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), mapKeyType));
Object oldValue = null;
Object convertedMapValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), mapValueType, new TypeDescriptor(new MethodParameter(pd.getReadMethod(), -1, tokens.keys.length + 1)));
map.put(convertedMapKey, convertedMapValue);
} else {
throw new InvalidPropertyException(getWrappedClass(), propertyName, "无法设置名为 '" + propertyName + "' 值为 '"+ pv.getValue()+"' 的属性,因为他既不是一个数组、也不是一个List或者Set或者Map");
}
} else {
// keys为null,代表着actualName与canonicalName相同,也就是说,actualName就是我们需要设置的属性
PropertyDescriptor pd = pv.resolvedDescriptor;
if (pd == null || !pd.getWriteMethod().getDeclaringClass().isInstance(this.object)) {
pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
if (pd != null) {
pv.getOriginalPropertyValue().resolvedDescriptor = pd;
}
}
Injector injector = pv.resolvedInjector;
if (injector == null) {
if (pd == null || pd.getWriteMethod() == null) {
// 没有对应的Set方法
injector = new InjectByField(ClassWrapper.getWrapper(getWrappedClass()).getField(pv.getName()));
} else {
// 有对应的Set方法
Method writeMethod = (pd instanceof GenericTypeAwarePropertyDescriptor ? ((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess() : pd.getWriteMethod());
injector = new InjectBySetter(writeMethod);
}
pv.getOriginalPropertyValue().resolvedInjector = injector;
}
Object oldValue = null;
try {
Object originalValue = pv.getValue();
Object valueToApply = originalValue;
// 检查是否需要类型转换
// pv.conversionNecessary的默认值是null,也就是说,只要不是false都会进行转换
// 完成了第一次转换以后会对转换前后的值进行对比,以确定pv.conversionNecessary的真正的值
if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
if (pv.isConverted()) {
valueToApply = pv.getConvertedValue();
} else {
valueToApply = convertForProperty(propertyName, oldValue, originalValue, pd);
}
// 这里是一个原型模式
// 只有在第一次尝试转换以后才能知道以后这个PropertyValue是否还需要转换
pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
}
injector.inject(object, valueToApply, acc);
} catch (TypeMismatchException ex) {
throw ex;
} catch (Exception ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.object, propertyName, oldValue, pv.getValue());
throw new MethodInvocationException(pce, ex);
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getName());
if (this.object != null) {
sb.append(": wrapping object [").append(ObjectUtils.identityToString(this.object)).append("]");
} else {
sb.append(": no wrapped object set");
}
return sb.toString();
}
// ---------------------------------------------------------------------
// 内部使用的内部类
// ---------------------------------------------------------------------
private static class PropertyTokenHolder {
/**
* 规范名称
*/
public String canonicalName;
/**
* 真实名称
*/
public String actualName;
public String[] keys;
}
}