package org.theonefx.wcframework.ioc;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.theonefx.wcframework.core.TypeConverter;
import org.theonefx.wcframework.core.exception.BeanCreationException;
import org.theonefx.wcframework.core.exception.BeansException;
import org.theonefx.wcframework.ioc.val.ManagedArray;
import org.theonefx.wcframework.ioc.val.ManagedList;
import org.theonefx.wcframework.ioc.val.ManagedMap;
import org.theonefx.wcframework.ioc.val.ManagedProperties;
import org.theonefx.wcframework.ioc.val.ManagedSet;
import org.theonefx.wcframework.ioc.val.TypedStringValue;
import org.theonefx.wcframework.utils.ClassUtils;
import org.theonefx.wcframework.utils.StringUtils;
/**
* BeanDefinition的值解析器
* BeanFactory实现过程中的一个工具类,用于将BeanDefinition中的值定义转换成应用于目标对象的真实值。
*/
class BeanDefinitionValueResolver {
private final AbstractBeanFactory beanFactory;
private final String beanName;
private final TypeConverter typeConverter;
public BeanDefinitionValueResolver(AbstractBeanFactory beanFactory, String beanName, BeanDefinition beanDefinition, TypeConverter typeConverter) {
this.beanFactory = beanFactory;
this.beanName = beanName;
this.typeConverter = typeConverter;
}
public Object resolveValueIfNecessary(Object argName, Object value) {
// We must check each value to see whether it requires a runtime
// reference
// to another bean to be resolved.
if (value instanceof AutoFoundRuntimeBeanReference) {
AutoFoundRuntimeBeanReference aref = (AutoFoundRuntimeBeanReference) value;
return resolveAutoFoundReference(argName, aref);
} else if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
} else if (value instanceof ManagedArray) {
// May need to resolve contained runtime references.
ManagedArray array = (ManagedArray) value;
Class<?> elementType = array.resolvedElementType;
if (elementType == null) {
String elementTypeName = array.getElementTypeName();
if (StringUtils.isNotBlank(elementTypeName)) {
try {
elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
array.resolvedElementType = elementType;
} catch (Throwable ex) {
// Improve the message by showing the context.
throw new BeanCreationException(this.beanName, "Error resolving array type for " + argName, ex);
}
} else {
elementType = Object.class;
}
}
return resolveManagedArray(argName, (List<?>) value, elementType);
} else if (value instanceof ManagedList) {
// May need to resolve contained runtime references.
return resolveManagedList(argName, (List<?>) value);
} else if (value instanceof ManagedSet) {
// May need to resolve contained runtime references.
return resolveManagedSet(argName, (Set<?>) value);
} else if (value instanceof ManagedMap) {
// May need to resolve contained runtime references.
return resolveManagedMap(argName, (Map<?, ?>) value);
} else if (value instanceof ManagedProperties) {
Properties original = (Properties) value;
Properties copy = new Properties();
for (Map.Entry<?, ?> propEntry : original.entrySet()) {
Object propKey = propEntry.getKey();
Object propValue = propEntry.getValue();
if (propKey instanceof TypedStringValue) {
propKey = evaluate((TypedStringValue) propKey);
}
if (propValue instanceof TypedStringValue) {
propValue = evaluate((TypedStringValue) propValue);
}
copy.put(propKey, propValue);
}
return copy;
} else if (value instanceof TypedStringValue) {
// Convert value to target type here.
TypedStringValue typedStringValue = (TypedStringValue) value;
Object valueObject = evaluate(typedStringValue);
try {
Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
if (resolvedTargetType != null) {
return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
} else {
return valueObject;
}
} catch (Throwable ex) {
// Improve the message by showing the context.
throw new BeanCreationException(this.beanName, "Error converting typed String value for " + argName, ex);
}
} else {
return value;
}
}
/**
* Evaluate the given value as an expression, if necessary.
* @param value the candidate value (may be an expression)
* @return the resolved value
*/
protected Object evaluate(TypedStringValue value) {
return value.getValue();
}
/**
* Resolve the target type in the given TypedStringValue.
* @param value the TypedStringValue to resolve
* @return the resolved target type (or <code>null</code> if none specified)
* @throws ClassNotFoundException if the specified type cannot be resolved
* @see TypedStringValue#resolveTargetType
*/
protected Class<?> resolveTargetType(TypedStringValue value) throws ClassNotFoundException {
if (value.hasTargetType()) {
return value.getTargetType();
}
return value.resolveTargetType(this.beanFactory.getBeanClassLoader());
}
/**
* Resolve a reference to another bean in the factory.
*/
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
try {
String refName = ref.getBeanId();
Object bean = this.beanFactory.getBean(refName);
return bean;
} catch (BeansException ex) {
throw new BeanCreationException(this.beanName, "Cannot resolve reference to bean '" + ref.getBeanId() + "' while setting " + argName, ex);
}
}
private Object resolveAutoFoundReference(Object argName, AutoFoundRuntimeBeanReference ref) {
try {
String refName = ref.getFieldName();
if (this.beanFactory.containsBean(refName)) {
Object bean = this.beanFactory.getBean(refName);
return bean;
} else if (this.beanFactory.containsBean(ref.getValueType())) {
Object bean = this.beanFactory.getBean(ref.getValueType());
return bean;
} else {
throw new BeanCreationException(this.beanName, "没有合适的值可被注入,属性:" + argName);
}
} catch (BeansException ex) {
throw new BeanCreationException(this.beanName, "Cannot resolve reference to bean '" + ref.getBeanId() + "' while setting " + argName, ex);
}
}
/**
* For each element in the managed array, resolve reference if necessary.
*/
private Object resolveManagedArray(Object argName, List<?> ml, Class<?> elementType) {
Object resolved = Array.newInstance(elementType, ml.size());
for (int i = 0; i < ml.size(); i++) {
Array.set(resolved, i,
resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));
}
return resolved;
}
/**
* For each element in the managed list, resolve reference if necessary.
*/
private List<?> resolveManagedList(Object argName, List<?> ml) {
List<Object> resolved = new ArrayList<Object>(ml.size());
for (int i = 0; i < ml.size(); i++) {
resolved.add(
resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));
}
return resolved;
}
/**
* For each element in the managed set, resolve reference if necessary.
*/
private Set<?> resolveManagedSet(Object argName, Set<?> ms) {
Set<Object> resolved = new LinkedHashSet<Object>(ms.size());
int i = 0;
for (Object m : ms) {
resolved.add(resolveValueIfNecessary(new KeyedArgName(argName, i), m));
i++;
}
return resolved;
}
/**
* For each element in the managed map, resolve reference if necessary.
*/
private Map<?,?> resolveManagedMap(Object argName, Map<?, ?> mm) {
Map<Object, Object> resolved = new LinkedHashMap<Object, Object>(mm.size());
for (Map.Entry<?,?> entry : mm.entrySet()) {
Object resolvedKey = resolveValueIfNecessary(argName, entry.getKey());
Object resolvedValue = resolveValueIfNecessary(
new KeyedArgName(argName, entry.getKey()), entry.getValue());
resolved.put(resolvedKey, resolvedValue);
}
return resolved;
}
/**
* Holder class used for delayed toString building.
*/
private static class KeyedArgName {
private final Object argName;
private final Object key;
public KeyedArgName(Object argName, Object key) {
this.argName = argName;
this.key = key;
}
@Override
public String toString() {
return this.argName + " with key " + BeanWrapper.PROPERTY_KEY_PREFIX + this.key + BeanWrapper.PROPERTY_KEY_SUFFIX;
}
}
}