📄 beanwrapperimpl.java
字号:
}
// Check to see if there is a custom editor,
// which might give an indication on the desired target type.
Class editorType = guessPropertyTypeFromEditors(propertyName);
if (editorType != null) {
return editorType;
}
}
}
catch (InvalidPropertyException ex) {
// Consider as not determinable.
}
return null;
}
/**
* Get the last component of the path. Also works if not nested.
* @param bw BeanWrapper to work on
* @param nestedPath property path we know is nested
* @return last component of the path (the property on the target bean)
*/
private String getFinalPath(BeanWrapper bw, String nestedPath) {
if (bw == this) {
return nestedPath;
}
return nestedPath.substring(PropertyAccessorUtils.getLastNestedPropertySeparatorIndex(nestedPath) + 1);
}
/**
* Recursively navigate to return a BeanWrapper for the nested property path.
* @param propertyPath property property path, which may be nested
* @return a BeanWrapper for the target bean
*/
protected BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath) throws BeansException {
int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
// handle nested properties recursively
if (pos > -1) {
String nestedProperty = propertyPath.substring(0, pos);
String nestedPath = propertyPath.substring(pos + 1);
BeanWrapperImpl nestedBw = getNestedBeanWrapper(nestedProperty);
return nestedBw.getBeanWrapperForPropertyPath(nestedPath);
}
else {
return this;
}
}
/**
* 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) throws BeansException {
if (this.nestedBeanWrappers == null) {
this.nestedBeanWrappers = new HashMap();
}
// Get Value of bean property-
PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty);
String canonicalName = tokens.canonicalName;
Object propertyValue = getPropertyValue(tokens);
if (propertyValue == null) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + canonicalName);
}
// Lookup cached sub-BeanWrapper, create new one if not found.
BeanWrapperImpl nestedBw = (BeanWrapperImpl) this.nestedBeanWrappers.get(canonicalName);
if (nestedBw == null || nestedBw.getWrappedInstance() != propertyValue) {
if (logger.isDebugEnabled()) {
logger.debug("Creating new nested BeanWrapper for property '" + canonicalName + "'");
}
nestedBw = newNestedBeanWrapper(propertyValue, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR);
// Inherit all type-specific PropertyEditors.
copyDefaultEditorsTo(nestedBw);
copyCustomEditorsTo(nestedBw, canonicalName);
this.nestedBeanWrappers.put(canonicalName, nestedBw);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Using cached nested BeanWrapper for property '" + canonicalName + "'");
}
}
return nestedBw;
}
/**
* Create a new nested BeanWrapper instance.
* <p>Default implementation creates a BeanWrapperImpl instance.
* Can be overridden in subclasses to create a BeanWrapperImpl subclass.
* @param object object wrapped by this BeanWrapper
* @param nestedPath the nested path of the object
* @return the nested BeanWrapper instance
*/
protected BeanWrapperImpl newNestedBeanWrapper(Object object, String nestedPath) {
return new BeanWrapperImpl(object, nestedPath, this);
}
/**
* Parse the given property name into the corresponding property name tokens.
* @param propertyName the property name to parse
* @return representation of the parsed property tokens
*/
private PropertyTokenHolder getPropertyNameTokens(String propertyName) {
PropertyTokenHolder tokens = new PropertyTokenHolder();
String actualName = null;
List keys = new ArrayList(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);
}
return tokens;
}
//---------------------------------------------------------------------
// Implementation of PropertyAccessor interface
//---------------------------------------------------------------------
public Object getPropertyValue(String propertyName) throws BeansException {
BeanWrapperImpl nestedBw = getBeanWrapperForPropertyPath(propertyName);
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
return nestedBw.getPropertyValue(tokens);
}
private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
PropertyDescriptor pd = getPropertyDescriptorInternal(tokens.actualName);
if (pd == null || pd.getReadMethod() == null) {
throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName);
}
Method readMethod = pd.getReadMethod();
if (logger.isDebugEnabled()) {
logger.debug("About to invoke read method [" + readMethod + "] on object of class [" +
this.object.getClass().getName() + "]");
}
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(this.object, (Object[]) null);
if (tokens.keys != null) {
// apply indexes and map keys
for (int i = 0; i < tokens.keys.length; i++) {
String key = tokens.keys[i];
if (value == null) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value of property referenced in indexed " +
"property path '" + propertyName + "': returned null");
}
else if (value.getClass().isArray()) {
value = Array.get(value, Integer.parseInt(key));
}
else if (value instanceof List) {
List list = (List) value;
value = 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);
if (index < 0 || index >= set.size()) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Cannot get element with index " + index + " from Set of size " +
set.size() + ", accessed using property path '" + 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;
value = map.get(key);
}
else {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Property referenced in indexed property path '" + propertyName +
"' is neither an array nor a List nor a Set nor a Map; returned value was [" + value + "]");
}
}
}
return value;
}
catch (InvocationTargetException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Getter for property '" + actualName + "' threw exception", ex);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Illegal attempt to get property '" + actualName + "' threw exception", ex);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Index of out of bounds in property path '" + propertyName + "'", ex);
}
catch (NumberFormatException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid index in property path '" + propertyName + "'", ex);
}
}
public void setPropertyValue(String propertyName, Object value) throws BeansException {
BeanWrapperImpl nestedBw = null;
try {
nestedBw = getBeanWrapperForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
nestedBw.setPropertyValue(tokens, value);
}
private void setPropertyValue(PropertyTokenHolder tokens, Object newValue) throws BeansException {
String propertyName = tokens.canonicalName;
if (tokens.keys != null) {
// Apply indexes and map keys: fetch value for all keys but the last one.
PropertyTokenHolder getterTokens = new PropertyTokenHolder();
getterTokens.canonicalName = tokens.canonicalName;
getterTokens.actualName = tokens.actualName;
getterTokens.keys = new String[tokens.keys.length - 1];
System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
Object propValue = null;
try {
propValue = getPropertyValue(getterTokens);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "'", ex);
}
// Set value for last key.
String key = tokens.keys[tokens.keys.length - 1];
if (propValue == null) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "': returned null");
}
else if (propValue.getClass().isArray()) {
Class requiredType = propValue.getClass().getComponentType();
int arrayIndex = Integer.parseInt(key);
Object oldValue = null;
try {
if (this.extractOldValueForEditor) {
oldValue = Array.get(propValue, arrayIndex);
}
Object convertedValue =
doTypeConversionIfNecessary(propertyName, propertyName, oldValue, newValue, requiredType);
Array.set(propValue, Integer.parseInt(key), convertedValue);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, requiredType, ex);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid array index in property path '" + propertyName + "'", ex);
}
}
else if (propValue instanceof List) {
List list = (List) propValue;
int index = Integer.parseInt(key);
Object oldValue = null;
if (this.extractOldValueForEditor && index < list.size()) {
oldValue = list.get(index);
}
Object convertedValue =
doTypeConversionIfNecessary(propertyName, propertyName, oldValue, newValue, null);
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(getRootClass(), this.nestedPath + propertyName,
"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(convertedValue);
}
}
else if (propValue instanceof Map) {
Map map = (Map) propValue;
Object oldValue = null;
if (this.extractOldValueForEditor) {
oldValue = map.get(key);
}
Object convertedValue =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -