You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
751 lines
27 KiB
751 lines
27 KiB
/*
|
|
* Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package java.beans;
|
|
|
|
import java.lang.ref.Reference;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Constructor;
|
|
import sun.reflect.misc.ReflectUtil;
|
|
|
|
/**
|
|
* A PropertyDescriptor describes one property that a Java Bean
|
|
* exports via a pair of accessor methods.
|
|
*/
|
|
public class PropertyDescriptor extends FeatureDescriptor {
|
|
|
|
private Reference<? extends Class<?>> propertyTypeRef;
|
|
private final MethodRef readMethodRef = new MethodRef();
|
|
private final MethodRef writeMethodRef = new MethodRef();
|
|
private Reference<? extends Class<?>> propertyEditorClassRef;
|
|
|
|
private boolean bound;
|
|
private boolean constrained;
|
|
|
|
// The base name of the method name which will be prefixed with the
|
|
// read and write method. If name == "foo" then the baseName is "Foo"
|
|
private String baseName;
|
|
|
|
private String writeMethodName;
|
|
private String readMethodName;
|
|
|
|
/**
|
|
* Constructs a PropertyDescriptor for a property that follows
|
|
* the standard Java convention by having getFoo and setFoo
|
|
* accessor methods. Thus if the argument name is "fred", it will
|
|
* assume that the writer method is "setFred" and the reader method
|
|
* is "getFred" (or "isFred" for a boolean property). Note that the
|
|
* property name should start with a lower case character, which will
|
|
* be capitalized in the method names.
|
|
*
|
|
* @param propertyName The programmatic name of the property.
|
|
* @param beanClass The Class object for the target bean. For
|
|
* example sun.beans.OurButton.class.
|
|
* @exception IntrospectionException if an exception occurs during
|
|
* introspection.
|
|
*/
|
|
public PropertyDescriptor(String propertyName, Class<?> beanClass)
|
|
throws IntrospectionException {
|
|
this(propertyName, beanClass,
|
|
Introspector.IS_PREFIX + NameGenerator.capitalize(propertyName),
|
|
Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName));
|
|
}
|
|
|
|
/**
|
|
* This constructor takes the name of a simple property, and method
|
|
* names for reading and writing the property.
|
|
*
|
|
* @param propertyName The programmatic name of the property.
|
|
* @param beanClass The Class object for the target bean. For
|
|
* example sun.beans.OurButton.class.
|
|
* @param readMethodName The name of the method used for reading the property
|
|
* value. May be null if the property is write-only.
|
|
* @param writeMethodName The name of the method used for writing the property
|
|
* value. May be null if the property is read-only.
|
|
* @exception IntrospectionException if an exception occurs during
|
|
* introspection.
|
|
*/
|
|
public PropertyDescriptor(String propertyName, Class<?> beanClass,
|
|
String readMethodName, String writeMethodName)
|
|
throws IntrospectionException {
|
|
if (beanClass == null) {
|
|
throw new IntrospectionException("Target Bean class is null");
|
|
}
|
|
if (propertyName == null || propertyName.length() == 0) {
|
|
throw new IntrospectionException("bad property name");
|
|
}
|
|
if ("".equals(readMethodName) || "".equals(writeMethodName)) {
|
|
throw new IntrospectionException("read or write method name should not be the empty string");
|
|
}
|
|
setName(propertyName);
|
|
setClass0(beanClass);
|
|
|
|
this.readMethodName = readMethodName;
|
|
if (readMethodName != null && getReadMethod() == null) {
|
|
throw new IntrospectionException("Method not found: " + readMethodName);
|
|
}
|
|
this.writeMethodName = writeMethodName;
|
|
if (writeMethodName != null && getWriteMethod() == null) {
|
|
throw new IntrospectionException("Method not found: " + writeMethodName);
|
|
}
|
|
// If this class or one of its base classes allow PropertyChangeListener,
|
|
// then we assume that any properties we discover are "bound".
|
|
// See Introspector.getTargetPropertyInfo() method.
|
|
Class[] args = { PropertyChangeListener.class };
|
|
this.bound = null != Introspector.findMethod(beanClass, "addPropertyChangeListener", args.length, args);
|
|
}
|
|
|
|
/**
|
|
* This constructor takes the name of a simple property, and Method
|
|
* objects for reading and writing the property.
|
|
*
|
|
* @param propertyName The programmatic name of the property.
|
|
* @param readMethod The method used for reading the property value.
|
|
* May be null if the property is write-only.
|
|
* @param writeMethod The method used for writing the property value.
|
|
* May be null if the property is read-only.
|
|
* @exception IntrospectionException if an exception occurs during
|
|
* introspection.
|
|
*/
|
|
public PropertyDescriptor(String propertyName, Method readMethod, Method writeMethod)
|
|
throws IntrospectionException {
|
|
if (propertyName == null || propertyName.length() == 0) {
|
|
throw new IntrospectionException("bad property name");
|
|
}
|
|
setName(propertyName);
|
|
setReadMethod(readMethod);
|
|
setWriteMethod(writeMethod);
|
|
}
|
|
|
|
/**
|
|
* Creates <code>PropertyDescriptor</code> for the specified bean
|
|
* with the specified name and methods to read/write the property value.
|
|
*
|
|
* @param bean the type of the target bean
|
|
* @param base the base name of the property (the rest of the method name)
|
|
* @param read the method used for reading the property value
|
|
* @param write the method used for writing the property value
|
|
* @exception IntrospectionException if an exception occurs during introspection
|
|
*
|
|
* @since 1.7
|
|
*/
|
|
PropertyDescriptor(Class<?> bean, String base, Method read, Method write) throws IntrospectionException {
|
|
if (bean == null) {
|
|
throw new IntrospectionException("Target Bean class is null");
|
|
}
|
|
setClass0(bean);
|
|
setName(Introspector.decapitalize(base));
|
|
setReadMethod(read);
|
|
setWriteMethod(write);
|
|
this.baseName = base;
|
|
}
|
|
|
|
/**
|
|
* Returns the Java type info for the property.
|
|
* Note that the {@code Class} object may describe
|
|
* primitive Java types such as {@code int}.
|
|
* This type is returned by the read method
|
|
* or is used as the parameter type of the write method.
|
|
* Returns {@code null} if the type is an indexed property
|
|
* that does not support non-indexed access.
|
|
*
|
|
* @return the {@code Class} object that represents the Java type info,
|
|
* or {@code null} if the type cannot be determined
|
|
*/
|
|
public synchronized Class<?> getPropertyType() {
|
|
Class<?> type = getPropertyType0();
|
|
if (type == null) {
|
|
try {
|
|
type = findPropertyType(getReadMethod(), getWriteMethod());
|
|
setPropertyType(type);
|
|
} catch (IntrospectionException ex) {
|
|
// Fall
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
|
|
private void setPropertyType(Class<?> type) {
|
|
this.propertyTypeRef = getWeakReference(type);
|
|
}
|
|
|
|
private Class<?> getPropertyType0() {
|
|
return (this.propertyTypeRef != null)
|
|
? this.propertyTypeRef.get()
|
|
: null;
|
|
}
|
|
|
|
/**
|
|
* Gets the method that should be used to read the property value.
|
|
*
|
|
* @return The method that should be used to read the property value.
|
|
* May return null if the property can't be read.
|
|
*/
|
|
public synchronized Method getReadMethod() {
|
|
Method readMethod = this.readMethodRef.get();
|
|
if (readMethod == null) {
|
|
Class<?> cls = getClass0();
|
|
if (cls == null || (readMethodName == null && !this.readMethodRef.isSet())) {
|
|
// The read method was explicitly set to null.
|
|
return null;
|
|
}
|
|
String nextMethodName = Introspector.GET_PREFIX + getBaseName();
|
|
if (readMethodName == null) {
|
|
Class<?> type = getPropertyType0();
|
|
if (type == boolean.class || type == null) {
|
|
readMethodName = Introspector.IS_PREFIX + getBaseName();
|
|
} else {
|
|
readMethodName = nextMethodName;
|
|
}
|
|
}
|
|
|
|
// Since there can be multiple write methods but only one getter
|
|
// method, find the getter method first so that you know what the
|
|
// property type is. For booleans, there can be "is" and "get"
|
|
// methods. If an "is" method exists, this is the official
|
|
// reader method so look for this one first.
|
|
readMethod = Introspector.findMethod(cls, readMethodName, 0);
|
|
if ((readMethod == null) && !readMethodName.equals(nextMethodName)) {
|
|
readMethodName = nextMethodName;
|
|
readMethod = Introspector.findMethod(cls, readMethodName, 0);
|
|
}
|
|
try {
|
|
setReadMethod(readMethod);
|
|
} catch (IntrospectionException ex) {
|
|
// fall
|
|
}
|
|
}
|
|
return readMethod;
|
|
}
|
|
|
|
/**
|
|
* Sets the method that should be used to read the property value.
|
|
*
|
|
* @param readMethod The new read method.
|
|
* @throws IntrospectionException if the read method is invalid
|
|
*/
|
|
public synchronized void setReadMethod(Method readMethod)
|
|
throws IntrospectionException {
|
|
this.readMethodRef.set(readMethod);
|
|
if (readMethod == null) {
|
|
readMethodName = null;
|
|
return;
|
|
}
|
|
// The property type is determined by the read method.
|
|
setPropertyType(findPropertyType(readMethod, this.writeMethodRef.get()));
|
|
setClass0(readMethod.getDeclaringClass());
|
|
|
|
readMethodName = readMethod.getName();
|
|
setTransient(readMethod.getAnnotation(Transient.class));
|
|
}
|
|
|
|
/**
|
|
* Gets the method that should be used to write the property value.
|
|
*
|
|
* @return The method that should be used to write the property value.
|
|
* May return null if the property can't be written.
|
|
*/
|
|
public synchronized Method getWriteMethod() {
|
|
Method writeMethod = this.writeMethodRef.get();
|
|
if (writeMethod == null) {
|
|
Class<?> cls = getClass0();
|
|
if (cls == null || (writeMethodName == null && !this.writeMethodRef.isSet())) {
|
|
// The write method was explicitly set to null.
|
|
return null;
|
|
}
|
|
|
|
// We need the type to fetch the correct method.
|
|
Class<?> type = getPropertyType0();
|
|
if (type == null) {
|
|
try {
|
|
// Can't use getPropertyType since it will lead to recursive loop.
|
|
type = findPropertyType(getReadMethod(), null);
|
|
setPropertyType(type);
|
|
} catch (IntrospectionException ex) {
|
|
// Without the correct property type we can't be guaranteed
|
|
// to find the correct method.
|
|
return null;
|
|
}
|
|
}
|
|
|
|
if (writeMethodName == null) {
|
|
writeMethodName = Introspector.SET_PREFIX + getBaseName();
|
|
}
|
|
|
|
Class<?>[] args = (type == null) ? null : new Class<?>[] { type };
|
|
writeMethod = Introspector.findMethod(cls, writeMethodName, 1, args);
|
|
if (writeMethod != null) {
|
|
if (!writeMethod.getReturnType().equals(void.class)) {
|
|
writeMethod = null;
|
|
}
|
|
}
|
|
try {
|
|
setWriteMethod(writeMethod);
|
|
} catch (IntrospectionException ex) {
|
|
// fall through
|
|
}
|
|
}
|
|
return writeMethod;
|
|
}
|
|
|
|
/**
|
|
* Sets the method that should be used to write the property value.
|
|
*
|
|
* @param writeMethod The new write method.
|
|
* @throws IntrospectionException if the write method is invalid
|
|
*/
|
|
public synchronized void setWriteMethod(Method writeMethod)
|
|
throws IntrospectionException {
|
|
this.writeMethodRef.set(writeMethod);
|
|
if (writeMethod == null) {
|
|
writeMethodName = null;
|
|
return;
|
|
}
|
|
// Set the property type - which validates the method
|
|
setPropertyType(findPropertyType(getReadMethod(), writeMethod));
|
|
setClass0(writeMethod.getDeclaringClass());
|
|
|
|
writeMethodName = writeMethod.getName();
|
|
setTransient(writeMethod.getAnnotation(Transient.class));
|
|
}
|
|
|
|
/**
|
|
* Overridden to ensure that a super class doesn't take precedent
|
|
*/
|
|
void setClass0(Class<?> clz) {
|
|
if (getClass0() != null && clz.isAssignableFrom(getClass0())) {
|
|
// don't replace a subclass with a superclass
|
|
return;
|
|
}
|
|
super.setClass0(clz);
|
|
}
|
|
|
|
/**
|
|
* Updates to "bound" properties will cause a "PropertyChange" event to
|
|
* get fired when the property is changed.
|
|
*
|
|
* @return True if this is a bound property.
|
|
*/
|
|
public boolean isBound() {
|
|
return bound;
|
|
}
|
|
|
|
/**
|
|
* Updates to "bound" properties will cause a "PropertyChange" event to
|
|
* get fired when the property is changed.
|
|
*
|
|
* @param bound True if this is a bound property.
|
|
*/
|
|
public void setBound(boolean bound) {
|
|
this.bound = bound;
|
|
}
|
|
|
|
/**
|
|
* Attempted updates to "Constrained" properties will cause a "VetoableChange"
|
|
* event to get fired when the property is changed.
|
|
*
|
|
* @return True if this is a constrained property.
|
|
*/
|
|
public boolean isConstrained() {
|
|
return constrained;
|
|
}
|
|
|
|
/**
|
|
* Attempted updates to "Constrained" properties will cause a "VetoableChange"
|
|
* event to get fired when the property is changed.
|
|
*
|
|
* @param constrained True if this is a constrained property.
|
|
*/
|
|
public void setConstrained(boolean constrained) {
|
|
this.constrained = constrained;
|
|
}
|
|
|
|
|
|
/**
|
|
* Normally PropertyEditors will be found using the PropertyEditorManager.
|
|
* However if for some reason you want to associate a particular
|
|
* PropertyEditor with a given property, then you can do it with
|
|
* this method.
|
|
*
|
|
* @param propertyEditorClass The Class for the desired PropertyEditor.
|
|
*/
|
|
public void setPropertyEditorClass(Class<?> propertyEditorClass) {
|
|
this.propertyEditorClassRef = getWeakReference(propertyEditorClass);
|
|
}
|
|
|
|
/**
|
|
* Gets any explicit PropertyEditor Class that has been registered
|
|
* for this property.
|
|
*
|
|
* @return Any explicit PropertyEditor Class that has been registered
|
|
* for this property. Normally this will return "null",
|
|
* indicating that no special editor has been registered,
|
|
* so the PropertyEditorManager should be used to locate
|
|
* a suitable PropertyEditor.
|
|
*/
|
|
public Class<?> getPropertyEditorClass() {
|
|
return (this.propertyEditorClassRef != null)
|
|
? this.propertyEditorClassRef.get()
|
|
: null;
|
|
}
|
|
|
|
/**
|
|
* Constructs an instance of a property editor using the current
|
|
* property editor class.
|
|
* <p>
|
|
* If the property editor class has a public constructor that takes an
|
|
* Object argument then it will be invoked using the bean parameter
|
|
* as the argument. Otherwise, the default constructor will be invoked.
|
|
*
|
|
* @param bean the source object
|
|
* @return a property editor instance or null if a property editor has
|
|
* not been defined or cannot be created
|
|
* @since 1.5
|
|
*/
|
|
public PropertyEditor createPropertyEditor(Object bean) {
|
|
Object editor = null;
|
|
|
|
final Class<?> cls = getPropertyEditorClass();
|
|
if (cls != null && PropertyEditor.class.isAssignableFrom(cls)
|
|
&& ReflectUtil.isPackageAccessible(cls)) {
|
|
Constructor<?> ctor = null;
|
|
if (bean != null) {
|
|
try {
|
|
ctor = cls.getConstructor(new Class<?>[] { Object.class });
|
|
} catch (Exception ex) {
|
|
// Fall through
|
|
}
|
|
}
|
|
try {
|
|
if (ctor == null) {
|
|
editor = cls.newInstance();
|
|
} else {
|
|
editor = ctor.newInstance(new Object[] { bean });
|
|
}
|
|
} catch (Exception ex) {
|
|
// Fall through
|
|
}
|
|
}
|
|
return (PropertyEditor)editor;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compares this <code>PropertyDescriptor</code> against the specified object.
|
|
* Returns true if the objects are the same. Two <code>PropertyDescriptor</code>s
|
|
* are the same if the read, write, property types, property editor and
|
|
* flags are equivalent.
|
|
*
|
|
* @since 1.4
|
|
*/
|
|
public boolean equals(Object obj) {
|
|
if (this == obj) {
|
|
return true;
|
|
}
|
|
if (obj != null && obj instanceof PropertyDescriptor) {
|
|
PropertyDescriptor other = (PropertyDescriptor)obj;
|
|
Method otherReadMethod = other.getReadMethod();
|
|
Method otherWriteMethod = other.getWriteMethod();
|
|
|
|
if (!compareMethods(getReadMethod(), otherReadMethod)) {
|
|
return false;
|
|
}
|
|
|
|
if (!compareMethods(getWriteMethod(), otherWriteMethod)) {
|
|
return false;
|
|
}
|
|
|
|
if (getPropertyType() == other.getPropertyType() &&
|
|
getPropertyEditorClass() == other.getPropertyEditorClass() &&
|
|
bound == other.isBound() && constrained == other.isConstrained() &&
|
|
writeMethodName == other.writeMethodName &&
|
|
readMethodName == other.readMethodName) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Package private helper method for Descriptor .equals methods.
|
|
*
|
|
* @param a first method to compare
|
|
* @param b second method to compare
|
|
* @return boolean to indicate that the methods are equivalent
|
|
*/
|
|
boolean compareMethods(Method a, Method b) {
|
|
// Note: perhaps this should be a protected method in FeatureDescriptor
|
|
if ((a == null) != (b == null)) {
|
|
return false;
|
|
}
|
|
|
|
if (a != null && b != null) {
|
|
if (!a.equals(b)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Package-private constructor.
|
|
* Merge two property descriptors. Where they conflict, give the
|
|
* second argument (y) priority over the first argument (x).
|
|
*
|
|
* @param x The first (lower priority) PropertyDescriptor
|
|
* @param y The second (higher priority) PropertyDescriptor
|
|
*/
|
|
PropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) {
|
|
super(x,y);
|
|
|
|
if (y.baseName != null) {
|
|
baseName = y.baseName;
|
|
} else {
|
|
baseName = x.baseName;
|
|
}
|
|
|
|
if (y.readMethodName != null) {
|
|
readMethodName = y.readMethodName;
|
|
} else {
|
|
readMethodName = x.readMethodName;
|
|
}
|
|
|
|
if (y.writeMethodName != null) {
|
|
writeMethodName = y.writeMethodName;
|
|
} else {
|
|
writeMethodName = x.writeMethodName;
|
|
}
|
|
|
|
if (y.propertyTypeRef != null) {
|
|
propertyTypeRef = y.propertyTypeRef;
|
|
} else {
|
|
propertyTypeRef = x.propertyTypeRef;
|
|
}
|
|
|
|
// Figure out the merged read method.
|
|
Method xr = x.getReadMethod();
|
|
Method yr = y.getReadMethod();
|
|
|
|
// Normally give priority to y's readMethod.
|
|
try {
|
|
if (isAssignable(xr, yr)) {
|
|
setReadMethod(yr);
|
|
} else {
|
|
setReadMethod(xr);
|
|
}
|
|
} catch (IntrospectionException ex) {
|
|
// fall through
|
|
}
|
|
|
|
// However, if both x and y reference read methods in the same class,
|
|
// give priority to a boolean "is" method over a boolean "get" method.
|
|
if (xr != null && yr != null &&
|
|
xr.getDeclaringClass() == yr.getDeclaringClass() &&
|
|
getReturnType(getClass0(), xr) == boolean.class &&
|
|
getReturnType(getClass0(), yr) == boolean.class &&
|
|
xr.getName().indexOf(Introspector.IS_PREFIX) == 0 &&
|
|
yr.getName().indexOf(Introspector.GET_PREFIX) == 0) {
|
|
try {
|
|
setReadMethod(xr);
|
|
} catch (IntrospectionException ex) {
|
|
// fall through
|
|
}
|
|
}
|
|
|
|
Method xw = x.getWriteMethod();
|
|
Method yw = y.getWriteMethod();
|
|
|
|
try {
|
|
if (yw != null) {
|
|
setWriteMethod(yw);
|
|
} else {
|
|
setWriteMethod(xw);
|
|
}
|
|
} catch (IntrospectionException ex) {
|
|
// Fall through
|
|
}
|
|
|
|
if (y.getPropertyEditorClass() != null) {
|
|
setPropertyEditorClass(y.getPropertyEditorClass());
|
|
} else {
|
|
setPropertyEditorClass(x.getPropertyEditorClass());
|
|
}
|
|
|
|
|
|
bound = x.bound | y.bound;
|
|
constrained = x.constrained | y.constrained;
|
|
}
|
|
|
|
/*
|
|
* Package-private dup constructor.
|
|
* This must isolate the new object from any changes to the old object.
|
|
*/
|
|
PropertyDescriptor(PropertyDescriptor old) {
|
|
super(old);
|
|
propertyTypeRef = old.propertyTypeRef;
|
|
this.readMethodRef.set(old.readMethodRef.get());
|
|
this.writeMethodRef.set(old.writeMethodRef.get());
|
|
propertyEditorClassRef = old.propertyEditorClassRef;
|
|
|
|
writeMethodName = old.writeMethodName;
|
|
readMethodName = old.readMethodName;
|
|
baseName = old.baseName;
|
|
|
|
bound = old.bound;
|
|
constrained = old.constrained;
|
|
}
|
|
|
|
void updateGenericsFor(Class<?> type) {
|
|
setClass0(type);
|
|
try {
|
|
setPropertyType(findPropertyType(this.readMethodRef.get(), this.writeMethodRef.get()));
|
|
}
|
|
catch (IntrospectionException exception) {
|
|
setPropertyType(null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the property type that corresponds to the read and write method.
|
|
* The type precedence is given to the readMethod.
|
|
*
|
|
* @return the type of the property descriptor or null if both
|
|
* read and write methods are null.
|
|
* @throws IntrospectionException if the read or write method is invalid
|
|
*/
|
|
private Class<?> findPropertyType(Method readMethod, Method writeMethod)
|
|
throws IntrospectionException {
|
|
Class<?> propertyType = null;
|
|
try {
|
|
if (readMethod != null) {
|
|
Class<?>[] params = getParameterTypes(getClass0(), readMethod);
|
|
if (params.length != 0) {
|
|
throw new IntrospectionException("bad read method arg count: "
|
|
+ readMethod);
|
|
}
|
|
propertyType = getReturnType(getClass0(), readMethod);
|
|
if (propertyType == Void.TYPE) {
|
|
throw new IntrospectionException("read method " +
|
|
readMethod.getName() + " returns void");
|
|
}
|
|
}
|
|
if (writeMethod != null) {
|
|
Class<?>[] params = getParameterTypes(getClass0(), writeMethod);
|
|
if (params.length != 1) {
|
|
throw new IntrospectionException("bad write method arg count: "
|
|
+ writeMethod);
|
|
}
|
|
if (propertyType != null && !params[0].isAssignableFrom(propertyType)) {
|
|
throw new IntrospectionException("type mismatch between read and write methods");
|
|
}
|
|
propertyType = params[0];
|
|
}
|
|
} catch (IntrospectionException ex) {
|
|
throw ex;
|
|
}
|
|
return propertyType;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a hash code value for the object.
|
|
* See {@link java.lang.Object#hashCode} for a complete description.
|
|
*
|
|
* @return a hash code value for this object.
|
|
* @since 1.5
|
|
*/
|
|
public int hashCode() {
|
|
int result = 7;
|
|
|
|
result = 37 * result + ((getPropertyType() == null) ? 0 :
|
|
getPropertyType().hashCode());
|
|
result = 37 * result + ((getReadMethod() == null) ? 0 :
|
|
getReadMethod().hashCode());
|
|
result = 37 * result + ((getWriteMethod() == null) ? 0 :
|
|
getWriteMethod().hashCode());
|
|
result = 37 * result + ((getPropertyEditorClass() == null) ? 0 :
|
|
getPropertyEditorClass().hashCode());
|
|
result = 37 * result + ((writeMethodName == null) ? 0 :
|
|
writeMethodName.hashCode());
|
|
result = 37 * result + ((readMethodName == null) ? 0 :
|
|
readMethodName.hashCode());
|
|
result = 37 * result + getName().hashCode();
|
|
result = 37 * result + ((bound == false) ? 0 : 1);
|
|
result = 37 * result + ((constrained == false) ? 0 : 1);
|
|
|
|
return result;
|
|
}
|
|
|
|
// Calculate once since capitalize() is expensive.
|
|
String getBaseName() {
|
|
if (baseName == null) {
|
|
baseName = NameGenerator.capitalize(getName());
|
|
}
|
|
return baseName;
|
|
}
|
|
|
|
void appendTo(StringBuilder sb) {
|
|
appendTo(sb, "bound", this.bound);
|
|
appendTo(sb, "constrained", this.constrained);
|
|
appendTo(sb, "propertyEditorClass", this.propertyEditorClassRef);
|
|
appendTo(sb, "propertyType", this.propertyTypeRef);
|
|
appendTo(sb, "readMethod", this.readMethodRef.get());
|
|
appendTo(sb, "writeMethod", this.writeMethodRef.get());
|
|
}
|
|
|
|
private boolean isAssignable(Method m1, Method m2) {
|
|
if (m1 == null) {
|
|
return true; // choose second method
|
|
}
|
|
if (m2 == null) {
|
|
return false; // choose first method
|
|
}
|
|
if (!m1.getName().equals(m2.getName())) {
|
|
return true; // choose second method by default
|
|
}
|
|
Class<?> type1 = m1.getDeclaringClass();
|
|
Class<?> type2 = m2.getDeclaringClass();
|
|
if (!type1.isAssignableFrom(type2)) {
|
|
return false; // choose first method: it declared later
|
|
}
|
|
type1 = getReturnType(getClass0(), m1);
|
|
type2 = getReturnType(getClass0(), m2);
|
|
if (!type1.isAssignableFrom(type2)) {
|
|
return false; // choose first method: it overrides return type
|
|
}
|
|
Class<?>[] args1 = getParameterTypes(getClass0(), m1);
|
|
Class<?>[] args2 = getParameterTypes(getClass0(), m2);
|
|
if (args1.length != args2.length) {
|
|
return true; // choose second method by default
|
|
}
|
|
for (int i = 0; i < args1.length; i++) {
|
|
if (!args1[i].isAssignableFrom(args2[i])) {
|
|
return false; // choose first method: it overrides parameter
|
|
}
|
|
}
|
|
return true; // choose second method
|
|
}
|
|
}
|