| /* | |
| * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package java.beans; | |
| import java.lang.ref.Reference; | |
| import java.lang.reflect.Method; | |
| import java.lang.reflect.Constructor; | |
| import java.util.Map.Entry; | |
| import com.sun.beans.introspect.PropertyInfo; | |
| /** | |
| * A PropertyDescriptor describes one property that a Java Bean | |
| * exports via a pair of accessor methods. | |
| * @since 1.1 | |
| */ | |
| 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. | |
| * @throws 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. | |
| * @throws 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. | |
| * @throws 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} from the specified property info. | |
| * | |
| * @param entry the pair of values, | |
| * where the {@code key} is the base name of the property (the rest of the method name) | |
| * and the {@code value} is the automatically generated property info | |
| * @param bound the flag indicating whether it is possible to treat this property as a bound property | |
| * | |
| * @since 9 | |
| */ | |
| PropertyDescriptor(Entry<String,PropertyInfo> entry, boolean bound) { | |
| String base = entry.getKey(); | |
| PropertyInfo info = entry.getValue(); | |
| setName(Introspector.decapitalize(base)); | |
| setReadMethod0(info.getReadMethod()); | |
| setWriteMethod0(info.getWriteMethod()); | |
| setPropertyType(info.getPropertyType()); | |
| setConstrained(info.isConstrained()); | |
| setBound(bound && info.is(PropertyInfo.Name.bound)); | |
| boolean isExpert = info.is(PropertyInfo.Name.expert); | |
| setValue(PropertyInfo.Name.expert.name(), isExpert); // compatibility | |
| setExpert(isExpert); | |
| boolean isHidden = info.is(PropertyInfo.Name.hidden); | |
| setValue(PropertyInfo.Name.hidden.name(), isHidden); // compatibility | |
| setHidden(isHidden); | |
| setPreferred(info.is(PropertyInfo.Name.preferred)); | |
| boolean isRequired = info.is(PropertyInfo.Name.required); | |
| setValue(PropertyInfo.Name.required.name(), isRequired); | |
| boolean visual = info.is(PropertyInfo.Name.visualUpdate); | |
| setValue(PropertyInfo.Name.visualUpdate.name(), visual); | |
| Object description = info.get(PropertyInfo.Name.description); | |
| if (description != null) { | |
| setShortDescription(description.toString()); | |
| } | |
| Object values = info.get(PropertyInfo.Name.enumerationValues); | |
| if (values == null) { | |
| values = new Object[0]; | |
| } | |
| setValue(PropertyInfo.Name.enumerationValues.name(), values); | |
| 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 | |
| * @since 1.2 | |
| */ | |
| public synchronized void setReadMethod(Method readMethod) | |
| throws IntrospectionException { | |
| // The property type is determined by the read method. | |
| setPropertyType(findPropertyType(readMethod, this.writeMethodRef.get())); | |
| setReadMethod0(readMethod); | |
| } | |
| private void setReadMethod0(Method readMethod) { | |
| this.readMethodRef.set(readMethod); | |
| if (readMethod == null) { | |
| readMethodName = null; | |
| return; | |
| } | |
| 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 | |
| * @since 1.2 | |
| */ | |
| public synchronized void setWriteMethod(Method writeMethod) | |
| throws IntrospectionException { | |
| // Set the property type - which validates the method | |
| setPropertyType(findPropertyType(getReadMethod(), writeMethod)); | |
| setWriteMethod0(writeMethod); | |
| } | |
| private void setWriteMethod0(Method writeMethod) { | |
| this.writeMethodRef.set(writeMethod); | |
| if (writeMethod == null) { | |
| writeMethodName = null; | |
| return; | |
| } | |
| 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 | |
| */ | |
| @SuppressWarnings("deprecation") | |
| public PropertyEditor createPropertyEditor(Object bean) { | |
| Object editor = null; | |
| final Class<?> cls = getPropertyEditorClass(); | |
| if (cls != null && PropertyEditor.class.isAssignableFrom(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} against the specified object. | |
| * Returns true if the objects are the same. Two {@code PropertyDescriptor}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 instanceof PropertyDescriptor other) { | |
| 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()); | |
| } | |
| 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 | |
| } | |
| } |