package org.springframework.beans;
Default
BeanWrapper implementation that should be sufficient
for all typical use cases. Caches introspection results for efficiency.
Note: Auto-registers default property editors from the
org.springframework.beans.propertyeditors package, which apply
in addition to the JDK's standard PropertyEditors. Applications can call
the PropertyEditorRegistrySupport.registerCustomEditor(java.lang.Class,java.beans.PropertyEditor) method
to register an editor for a particular instance (i.e. they are not shared
across the application). See the base class
PropertyEditorRegistrySupport for details.
BeanWrapperImpl will convert collection and array values
to the corresponding target collections or arrays, if necessary. Custom
property editors that deal with collections or arrays can either be
written via PropertyEditor's setValue, or against a
comma-delimited String via setAsText, as String arrays are
converted in such a format if the array itself is not assignable.
NOTE: As of Spring 2.5, this is - for almost all purposes - an
internal class. It is just public in order to allow for access from
other framework packages. For standard application access purposes, use the
PropertyAccessorFactory.forBeanPropertyAccess(java.lang.Object) factory method instead.
We'll create a lot of these objects, so we don't want a new logger every time.
Cached introspections results for this object, to prevent encountering
the cost of JavaBeans introspection every time.
Map with cached nested BeanWrappers: nested path -> BeanWrapper instance.
Create new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards.
Registers default editors.
Create new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards.
- Parameters:
registerDefaultEditors whether to register default editors
(can be suppressed if the BeanWrapper won't need any type conversion)- See also:
setWrappedInstance(java.lang.Object)
if (registerDefaultEditors) { Create new BeanWrapperImpl for the given object.
- Parameters:
object object wrapped by this BeanWrapper
Create new BeanWrapperImpl, wrapping a new instance of the specified class.
- Parameters:
clazz class to instantiate and wrap
Create new BeanWrapperImpl for the given object,
registering a nested path that the object is in.
- Parameters:
object object wrapped by this BeanWrappernestedPath the nested path of the objectrootObject the root object at the top of the path
Create new BeanWrapperImpl for the given object,
registering a nested path that the object is in.
- Parameters:
object object wrapped by this BeanWrappernestedPath the nested path of the objectsuperBw the containing BeanWrapper (must not be null)
Switch the target object, replacing the cached introspection results only
if the class of the new object is different to that of the replaced object.
- Parameters:
object the new target object
Switch the target object, replacing the cached introspection results only
if the class of the new object is different to that of the replaced object.
- Parameters:
object the new target objectnestedPath the nested path of the objectrootObject the root object at the top of the path
Assert.notNull(object, "Bean object must not be null");
this.nestedPath = (nestedPath != null ? nestedPath : "");
Return the nested path of the object wrapped by this BeanWrapper.
Return the root object at the top of the path of this BeanWrapper.
Return the class of the root object at the top of the path of this BeanWrapper.
Set the class to introspect.
Needs to be called when the target object changes.
- Parameters:
clazz the class to introspect
Obtain a lazily initializted CachedIntrospectionResults instance
for the wrapped object.
Assert.state(this.object != null, "BeanWrapper does not hold a bean instance");
"No property '" + propertyName + "' found");
Internal version of
getPropertyDescriptor(java.lang.String):
Returns
null if not found rather than throwing an exception.
- Parameters:
propertyName the property to obtain the descriptor for- Returns:
- the property descriptor for the specified property,
or
null if not found - Throws:
BeansException in case of introspection failure
Assert.notNull(propertyName, "Property name must not be null");
if (editorType != null) { Convert the given value for the specified property to the latter's type.
This method is only intended for optimizations in a BeanFactory.
Use the convertIfNecessary methods for programmatic conversion.
- Parameters:
value the value to convertpropertyName the target property
(note that nested or indexed properties are not supported here)- Returns:
- the new value, possibly the result of type conversion
- Throws:
TypeMismatchException if type conversion failed
"No property '" + propertyName + "' found");
Get the last component of the path. Also works if not nested.
- Parameters:
bw BeanWrapper to work onnestedPath property path we know is nested- Returns:
- last component of the path (the property on the target bean)
Recursively navigate to return a BeanWrapper for the nested property path.
- Parameters:
propertyPath property property path, which may be nested- Returns:
- a BeanWrapper for the target bean
Retrieve a BeanWrapper for the given nested property.
Create a new one if not found in the cache.
Note: Caching nested BeanWrappers is necessary now,
to keep registered custom editors for nested properties.
- Parameters:
nestedProperty property to create the BeanWrapper for- Returns:
- the BeanWrapper instance, either cached or newly created
String canonicalName = tokens.canonicalName;
if (propertyValue == null) { logger.trace("Creating new nested BeanWrapper for property '" + canonicalName + "'"); logger.trace("Using cached nested BeanWrapper for property '" + canonicalName + "'"); Create a new nested BeanWrapper instance.
Default implementation creates a BeanWrapperImpl instance.
Can be overridden in subclasses to create a BeanWrapperImpl subclass.
- Parameters:
object object wrapped by this BeanWrappernestedPath the nested path of the object- Returns:
- the nested BeanWrapper instance
Parse the given property name into the corresponding property name tokens.
- Parameters:
propertyName the property name to parse- Returns:
- representation of the parsed property tokens
while (searchIndex != -1) { if (actualName == null) { actualName = propertyName.substring(0, keyStart);
tokens.actualName = (actualName != null ? actualName : propertyName);
tokens.canonicalName = tokens.actualName;
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
if (tokens.keys != null) { for (int i = 0; i < tokens.keys.length; i++) { "Cannot access indexed value of property referenced in indexed " +
"property path '" + propertyName + "': returned null");
else if (value instanceof List) { else if (value instanceof Set) { if (index < 0 || index >= set.size()) { "Cannot get element with index " + index + " from Set of size " +
set.size() + ", accessed using property path '" + propertyName + "'");
for (int j = 0; it.hasNext(); j++) { else if (value instanceof Map) { value = map.get(convertedMapKey);
"Property referenced in indexed property path '" + propertyName +
"' is neither an array nor a List nor a Set nor a Map; returned value was [" + value + "]");
"Getter for property '" + actualName + "' threw exception", ex);
"Illegal attempt to get property '" + actualName + "' threw exception", ex);
"Index of out of bounds in property path '" + propertyName + "'", ex);
"Invalid index in property path '" + propertyName + "'", ex);
"Nested property in path '" + propertyName + "' does not exist", ex);
"Nested property in path '" + propertyName + "' does not exist", ex);
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
if (tokens.keys != null) { 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);
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "'", ex);
String key = tokens.keys[tokens.keys.length - 1];
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "': returned null");
oldValue = Array.get(propValue, arrayIndex);
propertyName, oldValue, pv.getValue(), requiredType);
Array.set(propValue, Integer.parseInt(key), convertedValue);
"Invalid array index in property path '" + propertyName + "'", ex);
else if (propValue instanceof List) { Class requiredType = null;
oldValue = list.get(index);
propertyName, oldValue, pv.getValue(), requiredType);
if (index < list.size()) { list.set(index, convertedValue);
else if (index >= list.size()) { for (int i = list.size(); i < index; i++) { "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) { Class mapValueType = null;
Object convertedMapKey = null;
Object convertedMapValue = null;
oldValue = map.get(convertedMapKey);
propertyName, oldValue, pv.getValue(), mapValueType, null,
map.put(convertedMapKey, convertedMapValue);
"Property referenced in indexed property path '" + propertyName +
"' is neither an array nor a List nor a Map; returned value was [" + pv.getValue() + "]");
Object valueToApply = originalValue;
logger.debug("Could not read previous value of property '" + sb.append(": no wrapped object set");