📄 beanwrapperimpl.java
字号:
case PROPERTY_KEY_PREFIX_CHAR:
case PROPERTY_KEY_SUFFIX_CHAR:
inKey = !inKey;
break;
case NESTED_PROPERTY_SEPARATOR_CHAR:
if (!inKey) {
return i;
}
}
if (last) i--; else i++;
}
return -1;
}
/**
* 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(getNestedPropertySeparatorIndex(nestedPath, true) + 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 = getNestedPropertySeparatorIndex(propertyPath, false);
// 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
String[] tokens = getPropertyNameTokens(nestedProperty);
Object propertyValue = getPropertyValue(tokens[0], tokens[1], tokens[2]);
String canonicalName = tokens[0];
String propertyName = tokens[1];
if (propertyValue == null) {
throw new NullValueInNestedPathException(getWrappedClass(), 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 = new BeanWrapperImpl(
propertyValue, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR, this);
// inherit all type-specific PropertyEditors
if (this.customEditors != null) {
for (Iterator it = this.customEditors.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
if (entry.getKey() instanceof Class) {
Class requiredType = (Class) entry.getKey();
PropertyEditor editor = (PropertyEditor) entry.getValue();
nestedBw.registerCustomEditor(requiredType, editor);
}
else if (entry.getKey() instanceof String) {
String editorPath = (String) entry.getKey();
int pos = getNestedPropertySeparatorIndex(editorPath, false);
if (pos != -1) {
String editorNestedProperty = editorPath.substring(0, pos);
String editorNestedPath = editorPath.substring(pos + 1);
if (editorNestedProperty.equals(canonicalName) || editorNestedProperty.equals(propertyName)) {
CustomEditorHolder editorHolder = (CustomEditorHolder) entry.getValue();
nestedBw.registerCustomEditor(
editorHolder.getRegisteredType(), editorNestedPath, editorHolder.getPropertyEditor());
}
}
}
}
}
this.nestedBeanWrappers.put(canonicalName, nestedBw);
}
else {
if (logger.isDebugEnabled()) {
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(PROPERTY_KEY_PREFIX);
if (keyStart != -1 && propertyName.endsWith(PROPERTY_KEY_SUFFIX)) {
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 += PROPERTY_KEY_PREFIX + key + PROPERTY_KEY_SUFFIX;
}
return new String[] {canonicalName, actualName, key};
}
public Object getPropertyValue(String propertyName) throws BeansException {
BeanWrapperImpl nestedBw = getBeanWrapperForPropertyPath(propertyName);
String[] tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
return nestedBw.getPropertyValue(tokens[0], tokens[1], tokens[2]);
}
protected Object getPropertyValue(String propertyName, String actualName, String key) throws BeansException {
PropertyDescriptor pd = getPropertyDescriptorInternal(actualName);
if (pd == null || pd.getReadMethod() == null) {
throw new NotReadablePropertyException(getWrappedClass(), this.nestedPath + propertyName);
}
if (logger.isDebugEnabled())
logger.debug("About to invoke read method [" + pd.getReadMethod() + "] on object of class [" +
this.object.getClass().getName() + "]");
try {
Object value = pd.getReadMethod().invoke(this.object, null);
if (key != null) {
if (value == null) {
throw new NullValueInNestedPathException(
getWrappedClass(), this.nestedPath + propertyName,
"Cannot access indexed value of 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 InvalidPropertyException(
getWrappedClass(), this.nestedPath + propertyName,
"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 InvalidPropertyException(
getWrappedClass(), this.nestedPath + propertyName,
"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 InvalidPropertyException(
getWrappedClass(), this.nestedPath + propertyName,
"Getter for property '" + actualName + "' threw exception", ex);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(
getWrappedClass(), this.nestedPath + propertyName,
"Illegal attempt to get property '" + actualName + "' threw exception", ex);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(
getWrappedClass(), this.nestedPath + propertyName,
"Index of out of bounds in property path '" + propertyName + "'", ex);
}
catch (NumberFormatException ex) {
throw new InvalidPropertyException(
getWrappedClass(), 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(
getWrappedClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
String[] tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
nestedBw.setPropertyValue(tokens[0], tokens[1], tokens[2], value);
}
protected void setPropertyValue(String propertyName, String actualName, String key, Object value)
throws BeansException {
if (key != null) {
Object propValue = null;
try {
propValue = getPropertyValue(actualName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(
getWrappedClass(), this.nestedPath + propertyName,
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "'", ex);
}
if (propValue == null) {
throw new NullValueInNestedPathException(
getWrappedClass(), 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();
Object newValue = doTypeConversionIfNecessary(propertyName, propertyName, null, value, requiredType);
try {
Array.set(propValue, Integer.parseInt(key), newValue);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(
this.object, this.nestedPath + propertyName, null, newValue);
throw new TypeMismatchException(pce, requiredType, ex);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(
getWrappedClass(), this.nestedPath + propertyName,
"Invalid array index in property path '" + propertyName + "'", ex);
}
}
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 InvalidPropertyException(
getWrappedClass(), 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(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 InvalidPropertyException(
getWrappedClass(), this.nestedPath + propertyName,
"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(getWrappedClass(), this.nestedPath + propertyName);
}
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 [" +
this.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 + PROPERTY_KEY_PREFIX + newValue + PROPERTY_KEY_SUFFIX);
}
else {
logger.debug(msg + "of type [" + pd.getPropertyType().getName() + "]");
}
}
}
catch (InvocationTargetException ex) {
PropertyChangeEvent propertyChangeEvent =
new PropertyChangeEvent(this.object, this.nestedPath + propertyName, null, value);
if (ex.getTargetException() instanceof ClassCastException) {
throw new TypeMismatchException(propertyChangeEvent, pd.getPropertyType(), ex.getTargetException());
}
else {
throw new MethodInvocationException(propertyChangeEvent, ex.getTargetException());
}
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.object, this.nestedPath + propertyName, null, value);
throw new TypeMismatchException(pce, pd.getPropertyType(), ex);
}
catch (IllegalAccessException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.object, this.nestedPath + propertyName, null, value);
throw new MethodInvocationException(pce, ex);
}
}
}
public void setPropertyValue(PropertyValue pv) throws BeansException {
setPropertyValue(pv.getName(), pv.getValue());
}
/**
* Bulk update from a Map.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -