Package java.beans

Source Code of java.beans.PropertyDescriptor

/*
* @(#)PropertyDescriptor.java  1.73 05/11/17
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* SUN 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;

/**
* A PropertyDescriptor describes one property that a Java Bean
* exports via a pair of accessor methods.
*/
public class PropertyDescriptor extends FeatureDescriptor {

    private Reference propertyTypeRef;
    private Reference readMethodRef;
    private Reference writeMethodRef;
    private Reference 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,
       "is" + capitalize(propertyName),
       "set" + 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);
  }
 
    }

    /**
     * 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 Class object for the property.
     *
     * @return The Java type info for the property.  Note that
     * the "Class" object may describe a built-in Java type such as "int".
     * The result may be "null" if this is an indexed property that
     * does not support non-indexed access.
     * <p>
     * This is the type that will be returned by the ReadMethod.
     */
    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) {
  propertyTypeRef = createReference(type);
    }

    private Class getPropertyType0() {
  return (Class)getObject(propertyTypeRef);
    }

    /**
     * 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 = Introspector.findMethod(cls, readMethodName, 0);
      if (readMethod == null) {
    readMethodName = "get" + getBaseName();
    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.
     */
    public synchronized void setReadMethod(Method readMethod)
        throws IntrospectionException {
  if (readMethod == null) {
      readMethodName = null;
      readMethodRef = null;
      return;
  }
  // The property type is determined by the read method.
  setPropertyType(findPropertyType(readMethod, getWriteMethod0()));
  setClass0(readMethod.getDeclaringClass());

  readMethodName = readMethod.getName();
  readMethodRef = createReference(readMethod, true);
    }

    /**
     * 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 = getWriteMethod0();
  if (writeMethod == null) {
      Class cls = getClass0();
      if (cls == null || (writeMethodName == null && writeMethodRef == null)) {
    // 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 = "set" + getBaseName();
      }

      writeMethod = Introspector.findMethod(cls, writeMethodName, 1,
           (type == null) ? null : new Class[] { type });
      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.
     */
    public synchronized void setWriteMethod(Method writeMethod)
        throws IntrospectionException {
  if (writeMethod == null) {
      writeMethodName = null;
      writeMethodRef = null;
      return;
  }
  // Set the property type - which validates the method
  setPropertyType(findPropertyType(getReadMethod(), writeMethod));
  setClass0(writeMethod.getDeclaringClass());

  writeMethodName = writeMethod.getName();
  writeMethodRef = createReference(writeMethod, true);

    }

    private Method getReadMethod0() {
  return (Method)getObject(readMethodRef);
    }

    private Method getWriteMethod0() {
  return (Method)getObject(writeMethodRef);
    }

    /**
     * Overridden to ensure that a super class doesn't take precedent
     */
    void setClass0(Class clz) {
  if (getClass0() != null && clz.isAssignableFrom(getClass0())) {
      // dont 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) {
  propertyEditorClassRef = createReference(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 (Class)getObject(propertyEditorClassRef);
    }

    /**
     * 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;

  Class cls = getPropertyEditorClass();
  if (cls != null) {
      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) {
    // A serious error has occured.
    // Proably due to an invalid property editor.
    throw new RuntimeException("PropertyEditor not instantiated",
             ex);
      }
  }
  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 (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;
    }

    /*
     * 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;
  readMethodRef = old.readMethodRef;
  writeMethodRef = old.writeMethodRef;
  propertyEditorClassRef = old.propertyEditorClassRef;

  writeMethodName = old.writeMethodName;
  readMethodName = old.readMethodName;
  baseName = old.baseName;

  bound = old.bound;
  constrained = old.constrained;
    }

    /**
     * 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 = 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 " +
          readMethod.getName() + " returns void");
    }
      }
      if (writeMethod != null) {
    Class params[] = writeMethod.getParameterTypes();
    if (params.length != 1) {
        throw new IntrospectionException("bad write method arg count: "
                 + writeMethod);
    }
    if (propertyType != null && propertyType != params[0]) {
        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 = capitalize(getName());
  }
  return baseName;
    }

    /*
    public String toString() {
  String message = "name=" + getName();
  message += ", class=" + getClass0();
  message += ", type=" + getPropertyType();

  message += ", writeMethod=";
  message += writeMethodName;

  message += ", readMethod=";
  message += readMethodName;

  message += ", bound=" + bound;
  message += ", constrained=" + constrained;

  return message;
    }
    */
TOP

Related Classes of java.beans.PropertyDescriptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.