📄 beanwrapperimpl.java
字号:
/**
* Return the class of the root object at the top of the path of this BeanWrapper.
* @see #getNestedPath
*/
public Class getRootClass() {
return (this.rootObject != null ? this.rootObject.getClass() : null);
}
/**
* Set the class to introspect.
* Needs to be called when the target object changes.
* @param clazz the class to introspect
*/
protected void setIntrospectionClass(Class clazz) {
if (this.cachedIntrospectionResults == null ||
!this.cachedIntrospectionResults.getBeanClass().equals(clazz)) {
this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(clazz);
}
}
public void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor) {
registerCustomEditor(requiredType, null, propertyEditor);
}
public void registerCustomEditor(Class requiredType, String propertyPath, PropertyEditor propertyEditor) {
if (requiredType == null && propertyPath == null) {
throw new IllegalArgumentException("Either requiredType or propertyPath is required");
}
if (this.customEditors == null) {
this.customEditors = new HashMap();
}
if (propertyPath != null) {
this.customEditors.put(propertyPath, new CustomEditorHolder(propertyEditor, requiredType));
}
else {
this.customEditors.put(requiredType, propertyEditor);
}
}
public PropertyEditor findCustomEditor(Class requiredType, String propertyPath) {
if (this.customEditors == null) {
return null;
}
if (propertyPath != null) {
// check property-specific editor first
PropertyEditor editor = getCustomEditor(propertyPath, requiredType);
if (editor == null) {
List strippedPaths = new LinkedList();
addStrippedPropertyPaths(strippedPaths, "", propertyPath);
for (Iterator it = strippedPaths.iterator(); it.hasNext() && editor == null;) {
String strippedPath = (String) it.next();
editor = getCustomEditor(strippedPath, requiredType);
}
}
if (editor != null) {
return editor;
}
else if (requiredType == null) {
requiredType = getPropertyType(propertyPath);
}
}
// no property-specific editor -> check type-specific editor
return getCustomEditor(requiredType);
}
/**
* Get custom editor that has been registered for the given property.
* @return the custom editor, or null if none specific for this property
*/
private PropertyEditor getCustomEditor(String propertyName, Class requiredType) {
CustomEditorHolder holder = (CustomEditorHolder) this.customEditors.get(propertyName);
return (holder != null ? holder.getPropertyEditor(requiredType) : null);
}
/**
* Get custom editor for the given type. If no direct match found,
* try custom editor for superclass (which will in any case be able
* to render a value as String via <code>getAsText</code>).
* @see java.beans.PropertyEditor#getAsText
* @return the custom editor, or null if none found for this type
*/
private PropertyEditor getCustomEditor(Class requiredType) {
if (requiredType != null) {
PropertyEditor editor = (PropertyEditor) this.customEditors.get(requiredType);
if (editor == null) {
for (Iterator it = this.customEditors.keySet().iterator(); it.hasNext();) {
Object key = it.next();
if (key instanceof Class && ((Class) key).isAssignableFrom(requiredType)) {
editor = (PropertyEditor) this.customEditors.get(key);
}
}
}
return editor;
}
return null;
}
/**
* Add property paths with all variations of stripped keys and/or indexes.
* Invokes itself recursively with nested paths
* @param strippedPaths the result list to add to
* @param nestedPath the current nested path
* @param propertyPath the property path to check for keys/indexes to strip
*/
private void addStrippedPropertyPaths(List strippedPaths, String nestedPath, String propertyPath) {
int startIndex = propertyPath.indexOf(PROPERTY_KEY_PREFIX_CHAR);
if (startIndex != -1) {
int endIndex = propertyPath.indexOf(PROPERTY_KEY_SUFFIX_CHAR);
if (endIndex != -1) {
String prefix = propertyPath.substring(0, startIndex);
String key = propertyPath.substring(startIndex, endIndex + 1);
String suffix = propertyPath.substring(endIndex + 1, propertyPath.length());
// strip the first key
strippedPaths.add(nestedPath + prefix + suffix);
// search for further keys to strip, with the first key stripped
addStrippedPropertyPaths(strippedPaths, nestedPath + prefix, suffix);
// search for further keys to strip, with the first key not stripped
addStrippedPropertyPaths(strippedPaths, nestedPath + prefix + key, suffix);
}
}
}
/**
* Determine the first (or last) nested property separator in the
* given property path, ignoring dots in keys (like "map[my.key]").
* @param propertyPath the property path to check
* @param last whether to return the last separator rather than the first
* @return the index of the nested property separator, or -1 if none
*/
private int getNestedPropertySeparatorIndex(String propertyPath, boolean last) {
boolean inKey = false;
int i = (last ? propertyPath.length()-1 : 0);
while ((last && i >= 0) || i < propertyPath.length()) {
switch (propertyPath.charAt(i)) {
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
PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty);
Object propertyValue = getPropertyValue(tokens);
String canonicalName = tokens.canonicalName;
String propertyName = tokens.actualName;
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 = 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 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 = key.substring(1, key.length() - 1);
}
else if (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 = (String[]) keys.toArray(new String[keys.size()]);
}
return tokens;
}
public Object getPropertyValue(String propertyName) throws BeansException {
BeanWrapperImpl nestedBw = getBeanWrapperForPropertyPath(propertyName);
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
return nestedBw.getPropertyValue(tokens);
}
protected 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);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -