diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java b/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java new file mode 100644 index 0000000000..5e766690af --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java @@ -0,0 +1,174 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans; + +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.util.Enumeration; + +import org.springframework.util.ObjectUtils; + +/** + * Common delegate methods for Spring's internal {@link PropertyDescriptor} implementations. + * + * @author Chris Beams + * @author Juergen Hoeller + */ +class PropertyDescriptorUtils { + + /** + * See {@link java.beans.FeatureDescriptor}. + */ + public static void copyNonMethodProperties(PropertyDescriptor source, PropertyDescriptor target) + throws IntrospectionException { + + target.setExpert(source.isExpert()); + target.setHidden(source.isHidden()); + target.setPreferred(source.isPreferred()); + target.setName(source.getName()); + target.setShortDescription(source.getShortDescription()); + target.setDisplayName(source.getDisplayName()); + + // Copy all attributes (emulating behavior of private FeatureDescriptor#addTable) + Enumeration keys = source.attributeNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + target.setValue(key, source.getValue(key)); + } + + // See java.beans.PropertyDescriptor#PropertyDescriptor(PropertyDescriptor) + target.setPropertyEditorClass(source.getPropertyEditorClass()); + target.setBound(source.isBound()); + target.setConstrained(source.isConstrained()); + } + + /** + * See {@link java.beans.PropertyDescriptor#findPropertyType}. + */ + public static Class findPropertyType(Method readMethod, Method writeMethod) throws IntrospectionException { + Class propertyType = null; + + if (readMethod != null) { + Class[] params = readMethod.getParameterTypes(); + if (params.length != 0) { + throw new IntrospectionException("Bad read method arg count: " + readMethod); + } + propertyType = readMethod.getReturnType(); + if (propertyType == Void.TYPE) { + throw new IntrospectionException("Read method returns void: " + readMethod); + } + } + + if (writeMethod != null) { + Class params[] = writeMethod.getParameterTypes(); + if (params.length != 1) { + throw new IntrospectionException("Bad write method arg count: " + writeMethod); + } + if (propertyType != null) { + if (propertyType.isAssignableFrom(params[0])) { + // Write method's property type potentially more specific + propertyType = params[0]; + } + else if (params[0].isAssignableFrom(propertyType)) { + // Proceed with read method's property type + } + else { + throw new IntrospectionException( + "Type mismatch between read and write methods: " + readMethod + " - " + writeMethod); + } + } + else { + propertyType = params[0]; + } + } + + return propertyType; + } + + /** + * See {@link java.beans.IndexedPropertyDescriptor#findIndexedPropertyType}. + */ + public static Class findIndexedPropertyType(String name, Class propertyType, + Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException { + + Class indexedPropertyType = null; + + if (indexedReadMethod != null) { + Class params[] = indexedReadMethod.getParameterTypes(); + if (params.length != 1) { + throw new IntrospectionException("Bad indexed read method arg count: " + indexedReadMethod); + } + if (params[0] != Integer.TYPE) { + throw new IntrospectionException("Non int index to indexed read method: " + indexedReadMethod); + } + indexedPropertyType = indexedReadMethod.getReturnType(); + if (indexedPropertyType == Void.TYPE) { + throw new IntrospectionException("Indexed read method returns void: " + indexedReadMethod); + } + } + + if (indexedWriteMethod != null) { + Class params[] = indexedWriteMethod.getParameterTypes(); + if (params.length != 2) { + throw new IntrospectionException("Bad indexed write method arg count: " + indexedWriteMethod); + } + if (params[0] != Integer.TYPE) { + throw new IntrospectionException("Non int index to indexed write method: " + indexedWriteMethod); + } + if (indexedPropertyType != null) { + if (indexedPropertyType.isAssignableFrom(params[1])) { + // Write method's property type potentially more specific + indexedPropertyType = params[1]; + } + else if (params[1].isAssignableFrom(indexedPropertyType)) { + // Proceed with read method's property type + } + else { + throw new IntrospectionException("Type mismatch between indexed read and write methods: " + + indexedReadMethod + " - " + indexedWriteMethod); + } + } + else { + indexedPropertyType = params[1]; + } + } + + if (propertyType != null && (!propertyType.isArray() || + propertyType.getComponentType() != indexedPropertyType)) { + throw new IntrospectionException("Type mismatch between indexed and non-indexed methods: " + + indexedReadMethod + " - " + indexedWriteMethod); + } + + return indexedPropertyType; + } + + /** + * Compare the given {@code PropertyDescriptors} and return {@code true} if + * they are equivalent, i.e. their read method, write method, property type, + * property editor and flags are equivalent. + * @see java.beans.PropertyDescriptor#equals(Object) + */ + public static boolean equals(PropertyDescriptor pd, PropertyDescriptor otherPd) { + return (ObjectUtils.nullSafeEquals(pd.getReadMethod(), otherPd.getReadMethod()) && + ObjectUtils.nullSafeEquals(pd.getWriteMethod(), otherPd.getWriteMethod()) && + ObjectUtils.nullSafeEquals(pd.getPropertyType(), otherPd.getPropertyType()) && + ObjectUtils.nullSafeEquals(pd.getPropertyEditorClass(), otherPd.getPropertyEditorClass()) && + pd.isBound() == otherPd.isBound() && pd.isConstrained() == otherPd.isConstrained()); + } + +}