📄 beanwrapperimpl.java
字号:
String nestedProperty = propertyPath.substring(0, pos);
String nestedPath = propertyPath.substring(pos + 1);
BeanWrapperImpl nestedBw = getNestedBeanWrapper(nestedProperty);
return nestedBw.getBeanWrapperForPropertyPath(nestedPath);
}
else {
return this;
}
}
/**
* Recursively navigate to return a BeanWrapper for the nested property path.
* In case of an indexed or mapped property, all BeanWrappers that apply will
* be returned.
* @param propertyPath property property path, which may be nested
* @return a BeanWrapper for the target bean
*/
private List getBeanWrappersForPropertyPath(String propertyPath) {
List beanWrappers = new ArrayList();
int pos = propertyPath.indexOf(NESTED_PROPERTY_SEPARATOR);
// handle nested properties recursively
if (pos > -1) {
String nestedProperty = propertyPath.substring(0, pos);
String nestedPath = propertyPath.substring(pos + 1);
if (nestedProperty.indexOf('[') == -1) {
Class propertyType = getPropertyDescriptor(nestedProperty).getPropertyType();
if (propertyType.isArray()) {
Object[] array = (Object[]) getPropertyValue(nestedProperty);
for (int i = 0; i < array.length; i++) {
beanWrappers.addAll(
getBeanWrappersForNestedProperty(propertyPath, nestedProperty + "[" + i + "]", nestedPath));
}
return beanWrappers;
}
else if (List.class.isAssignableFrom(propertyType)) {
List list = (List) getPropertyValue(nestedProperty);
for (int i = 0; i < list.size(); i++) {
beanWrappers.addAll(
getBeanWrappersForNestedProperty(propertyPath, nestedProperty + "[" + i + "]", nestedPath));
}
return beanWrappers;
}
else if (Map.class.isAssignableFrom(propertyType)) {
Map map = (Map) getPropertyValue(nestedProperty);
for (Iterator it = map.keySet().iterator(); it.hasNext();) {
beanWrappers.addAll(
getBeanWrappersForNestedProperty(propertyPath, nestedProperty + "[" + it.next() + "]", nestedPath));
}
return beanWrappers;
}
}
beanWrappers.addAll(getBeanWrappersForNestedProperty(propertyPath, nestedProperty, nestedPath));
return beanWrappers;
}
else {
beanWrappers.add(this);
return beanWrappers;
}
}
private List getBeanWrappersForNestedProperty(String propertyPath, String nestedProperty, String nestedPath) {
logger.debug("Navigating to nested property '" + nestedProperty + "' of property path '" + propertyPath + "'");
BeanWrapperImpl nestedBw = getNestedBeanWrapper(nestedProperty);
return nestedBw.getBeanWrappersForPropertyPath(nestedPath);
}
/**
* Retrieve a BeanWrapper for the given nested property.
* Create a new one if not found in the cache.
* <p>Note: Caching nested BeanWrappers is necessary now,
* to keep registered custom editors for nested properties.
* @param nestedProperty property to create the BeanWrapper for
* @return the BeanWrapper instance, either cached or newly created
*/
private BeanWrapperImpl getNestedBeanWrapper(String nestedProperty) {
if (this.nestedBeanWrappers == null) {
this.nestedBeanWrappers = new HashMap();
}
// get value of bean property
String[] tokens = getPropertyNameTokens(nestedProperty);
Object propertyValue = getPropertyValue(tokens[0], tokens[1], tokens[2]);
String canonicalName = tokens[0];
if (propertyValue == null) {
throw new NullValueInNestedPathException(getWrappedClass(), canonicalName);
}
// lookup cached sub-BeanWrapper, create new one if not found
BeanWrapperImpl nestedBw = (BeanWrapperImpl) this.nestedBeanWrappers.get(canonicalName);
if (nestedBw == null) {
logger.debug("Creating new nested BeanWrapper for property '" + canonicalName + "'");
nestedBw = new BeanWrapperImpl(propertyValue, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR);
// inherit all type-specific PropertyEditors
if (this.customEditors != null) {
for (Iterator it = this.customEditors.keySet().iterator(); it.hasNext();) {
Object key = it.next();
if (key instanceof Class) {
Class requiredType = (Class) key;
PropertyEditor propertyEditor = (PropertyEditor) this.customEditors.get(key);
nestedBw.registerCustomEditor(requiredType, null, propertyEditor);
}
}
}
this.nestedBeanWrappers.put(canonicalName, nestedBw);
}
else {
logger.debug("Using cached nested BeanWrapper for property '" + canonicalName + "'");
}
return nestedBw;
}
private String[] getPropertyNameTokens(String propertyName) {
String actualName = propertyName;
String key = null;
int keyStart = propertyName.indexOf('[');
if (keyStart != -1 && propertyName.endsWith("]")) {
actualName = propertyName.substring(0, keyStart);
key = propertyName.substring(keyStart + 1, propertyName.length() - 1);
if (key.startsWith("'") && key.endsWith("'")) {
key = key.substring(1, key.length() - 1);
}
else if (key.startsWith("\"") && key.endsWith("\"")) {
key = key.substring(1, key.length() - 1);
}
}
String canonicalName = actualName;
if (key != null) {
canonicalName += "[" + key + "]";
}
return new String[] {canonicalName, actualName, key};
}
public Object getPropertyValue(String propertyName) throws BeansException {
if (isNestedProperty(propertyName)) {
BeanWrapper nestedBw = getBeanWrapperForPropertyPath(propertyName);
return nestedBw.getPropertyValue(getFinalPath(propertyName));
}
String[] tokens = getPropertyNameTokens(propertyName);
return getPropertyValue(tokens[0], tokens[1], tokens[2]);
}
private Object getPropertyValue(String propertyName, String actualName, String key) {
PropertyDescriptor pd = getPropertyDescriptor(actualName);
Method readMethod = pd.getReadMethod();
if (readMethod == null) {
throw new FatalBeanException("Cannot get property '" + actualName + "': not readable", null);
}
if (logger.isDebugEnabled())
logger.debug("About to invoke read method [" + readMethod +
"] on object of class [" + this.object.getClass().getName() + "]");
try {
Object value = readMethod.invoke(this.object, null);
if (key != null) {
if (value == null) {
throw new FatalBeanException("Cannot access indexed value in property referenced in indexed property path '" +
propertyName + "': returned null");
}
else if (value.getClass().isArray()) {
return Array.get(value, Integer.parseInt(key));
}
else if (value instanceof List) {
List list = (List) value;
return list.get(Integer.parseInt(key));
}
else if (value instanceof Set) {
// apply index to Iterator in case of a Set
Set set = (Set) value;
int index = Integer.parseInt(key);
Iterator it = set.iterator();
for (int i = 0; it.hasNext(); i++) {
Object elem = it.next();
if (i == index) {
return elem;
}
}
throw new FatalBeanException("Cannot get element with index " + index + " from Set of size " +
set.size() + ", accessed using property path '" + propertyName + "'");
}
else if (value instanceof Map) {
Map map = (Map) value;
return map.get(key);
}
else {
throw new FatalBeanException("Property referenced in indexed property path '" + propertyName +
"' is neither an array nor a List nor a Map; returned value was [" + value + "]");
}
}
else {
return value;
}
}
catch (InvocationTargetException ex) {
throw new FatalBeanException("Getter for property '" + actualName + "' threw exception", ex);
}
catch (IllegalAccessException ex) {
throw new FatalBeanException("Illegal attempt to get property '" + actualName + "' threw exception", ex);
}
catch (IndexOutOfBoundsException ex) {
throw new FatalBeanException("Index of out of bounds in property path '" + propertyName + "'", ex);
}
catch (NumberFormatException ex) {
throw new FatalBeanException("Invalid index in property path '" + propertyName + "'");
}
}
public void setPropertyValue(String propertyName, Object value) throws BeansException {
if (isNestedProperty(propertyName)) {
try {
BeanWrapper nestedBw = getBeanWrapperForPropertyPath(propertyName);
nestedBw.setPropertyValue(new PropertyValue(getFinalPath(propertyName), value));
return;
}
catch (NullValueInNestedPathException ex) {
// let this through
throw ex;
}
catch (FatalBeanException ex) {
// error in the nested path
throw new NotWritablePropertyException(propertyName, getWrappedClass(), ex);
}
}
String[] tokens = getPropertyNameTokens(propertyName);
setPropertyValue(tokens[0], tokens[1], tokens[2], value);
}
private void setPropertyValue(String propertyName, String actualName, String key, Object value)
throws BeansException {
if (key != null) {
Object propValue = getPropertyValue(actualName);
if (propValue == null) {
throw new FatalBeanException("Cannot access indexed value in property referenced in indexed property path '" +
propertyName + "': returned null");
}
else if (propValue.getClass().isArray()) {
Object newValue = doTypeConversionIfNecessary(propertyName, propertyName, null, value,
propValue.getClass().getComponentType());
Array.set(propValue, Integer.parseInt(key), newValue);
}
else if (propValue instanceof List) {
Object newValue = doTypeConversionIfNecessary(propertyName, propertyName, null, value, null);
List list = (List) propValue;
int index = Integer.parseInt(key);
if (index < list.size()) {
list.set(index, newValue);
}
else if (index >= list.size()) {
for (int i = list.size(); i < index; i++) {
try {
list.add(null);
}
catch (NullPointerException ex) {
throw new FatalBeanException("Cannot set element with index " + index + " in List of size " +
list.size() + ", accessed using property path '" + propertyName +
"': List does not support filling up gaps with null elements");
}
}
list.add(newValue);
}
}
else if (propValue instanceof Map) {
Object newValue = doTypeConversionIfNecessary(propertyName, propertyName, null, value, null);
Map map = (Map) propValue;
map.put(key, newValue);
}
else {
throw new FatalBeanException("Property referenced in indexed property path '" + propertyName +
"' is neither an array nor a List nor a Map; returned value was [" + value + "]");
}
}
else {
if (!isWritableProperty(propertyName)) {
throw new NotWritablePropertyException(propertyName, getWrappedClass());
}
PropertyDescriptor pd = getPropertyDescriptor(propertyName);
Method writeMethod = pd.getWriteMethod();
Object newValue = null;
try {
// old value may still be null
newValue = doTypeConversionIfNecessary(propertyName, propertyName, null, value, pd.getPropertyType());
if (pd.getPropertyType().isPrimitive() &&
(newValue == null || "".equals(newValue))) {
throw new IllegalArgumentException("Invalid value [" + value + "] for property '" +
pd.getName() + "' of primitive type [" + pd.getPropertyType() + "]");
}
if (logger.isDebugEnabled()) {
logger.debug("About to invoke write method [" + writeMethod +
"] on object of class [" + object.getClass().getName() + "]");
}
writeMethod.invoke(this.object, new Object[] { newValue });
if (logger.isDebugEnabled()) {
String msg = "Invoked write method [" + writeMethod + "] with value ";
// only cause toString invocation of new value in case of simple property
if (newValue == null || BeanUtils.isSimpleProperty(pd.getPropertyType())) {
logger.debug(msg + "[" + newValue + "]");
}
else {
logger.debug(msg + "of type [" + pd.getPropertyType().getName() + "]");
}
}
}
catch (InvocationTargetException ex) {
// TODO could consider getting rid of PropertyChangeEvents as exception parameters
// as they can never contain anything but null for the old value as we no longer
// support event propagation.
PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(this.object, this.nestedPath + propertyName,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -