/**
  * 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);
 }
  /**
   * 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 = getReadMethod0();
    if (readMethod == null) {
      Class cls = getClass0();
      if (cls == null || (readMethodName == null && readMethodRef == null)) {
        // The read method was explicitly set to null.
        return null;
      }
      if (readMethodName == null) {
        Class type = getPropertyType0();
        if (type == boolean.class || type == null) {
          readMethodName = "is" + getBaseName();
        } else {
          readMethodName = "get" + getBaseName();
        }
      }

      // 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 = MethodUtils.findAccessibleMethodIncludeInterfaces(cls, readMethodName, 0, null);
      if (readMethod == null) {
        readMethodName = "get" + getBaseName();
        readMethod =
            MethodUtils.findAccessibleMethodIncludeInterfaces(cls, readMethodName, 0, null);
      }
      try {
        setReadMethod(readMethod);
      } catch (IntrospectionException ex) {
        // fall
      }
    }
    return readMethod;
  }
  /**
   * 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 (yr != null && yr.getDeclaringClass() == getClass0()) {
        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()
        && xr.getReturnType() == boolean.class
        && yr.getReturnType() == boolean.class
        && xr.getName().indexOf("is") == 0
        && yr.getName().indexOf("get") == 0) {
      try {
        setReadMethod(xr);
      } catch (IntrospectionException ex) {
        // fall through
      }
    }

    Method xw = x.getWriteMethod();
    Method yw = y.getWriteMethod();

    try {
      if (yw != null && yw.getDeclaringClass() == getClass0()) {
        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;
  }