Zat Home
Documentation | JavaBeans | Customization

Bean Customization Options

The JavaBeans component architecture provides a number of ways that the bean designer can customize the interface provided by a bean tool for interacting with a bean. This section describes these options.

PropertyEditors

The JavaBeans architecture uses the java.beans.PropertyEditor interface to allow you to provide customized information about how specific property types should be edited. When a builder tool user wants to edit a property that has an associated PropertyEditor, the tool

  1. supplies the PropertyEditor with the current property value,
  2. allows the PropertyEditor to interact with the user to edit this value,
  3. retrieves the the result of the editing from the PropertyEditor, and finally
  4. sets the bean's property with this result.

Throughout this process, the PropertyEditor stores an object that is the same type as its property. This stored value is what is changed as the result of interaction with the user.

The PropertyEditorSupport class

java.beans.PropertyEditorSupport is a convenience class that implements the PropertyEditor interface. Extending PropertyEditorSupport is usually the easiest way to write a PropertyEditor. The class implements each of the interface methods with reasonable default behaviors, so you only need to implement the methods you need.

The reader should take a look at the methods defined by the PropertyEditor interface before continuing.

The setValue and getValue methods

All PropertyEditors must provide access to their stored values with the following two methods:

  1. public void setValue(Object value): sets the value that the PropertyEditor will store.
  2. public Object getValue(): returns the value stored in the PropertyEditor.

The PropertyEditorSupport class provides good implementations for both of these methods. Classes that extend PropertyEditorSupport can use super.setValue and super.getValue to manage the stored property value. Both the MyLineTransparencyEditor and MyLineColorEditor use this approach.

PropertyEditors for Primitive Properties

You may have noticed that the getValue and setValue methods both deal with java.lang.Objects. This means that properties whose values are java primitives (i.e., characters and numbers) must be wrapped in their java.lang wrapper classes (java.lang.Character and the subclasses of java.lang.Number) before being passed through these methods.

Builder tools will perform this conversion automatically before calling setValue and after calling getValue. Therefore, PropertyEditors for primitive properties must behave as if the property type is that of the wrapper class, which can be confusing. See the MyLineTransparencyEditor class for an example of how to deal with this conversion.

Writing a PropertyEditor

There are two very different ways that PropertyEditors can perform their function. One option is String-based, and the other is visual.

String-based PropertyEditors

String-based PropertyEditors provide the builder tool with a translation mechanism for converting property values into descriptive Strings. These PropertyEditors do not interact directly with the user. The builder tool either lets the user choose from a list of descriptions provided by the PropertyEditor, or it allows the user to type a String that the PropertyEditor can translate.

Providing a String-to-value conversion system is useful when a property value is easily conveyed by a textual description. Properties whose values are enumerated constants are especially well suited to this kind of PropertyEditor. The Transparency property on the MyLine example bean is an example of this sort of property: Transparency is an integer property whose value is either 0 or 1. (Yes, this property would be better implemented as a boolean, but we needed an example of an enumerated property...). These numbers are meaningless to the user without interpretation. The MyLineTransparencyEditor provides labels (or tags) for each of the values, giving the builder tool the option to have the user choose between the words "Opaque" and "Transparent".

The PropertyEditor methods that are used in String-based PropertyEditors are:

  1. public void setAsText(String description)
  2. public String getAsText()
  3. public String[] getTags()

The getTags method is usually only useful when the property has a fixed set of possible values. If getTags returns null, it will be ignored.

When the user wishes to edit a property with an associated String-based PropertyEditor, a builder tool will perform a procedure something like the following:

  1. initialize the PropertyEditor by calling setValue with the current property value,
  2. call getAsText to obtain the description of this current value to show the user,
  3. call getTags to obtain a list of all possible property value descriptions, and
  4. call setAsText with the String provided by the user in Step #3, and finally
  5. call getValue to obtain the newly translated value to set as the property value.

The following is an example of a text-based PropertyEditor, from the MyLineTransparencyEditor:

    // The labels for the possible property values:
    protected static final String    OPAQUE_TAG        = "Opaque",
                                    TRANSPARENT_TAG    = "Transparent";

    // These are the possible property values. Note that they are Integers, not
    // ints. The unwrapping of these Objects is performed by the builder tool
    // before they are passed into the bean.
    protected static final Integer OPAQUE_VALUE = new Integer( MyLine.OPAQUE ),
                                TRANSPARENT_VALUE = new Integer( MyLine.TRANSPARENT );    

    // setAsText translates the supplied String to a property value, and caches the 
    // result for later retrieval by getValue().
    public void setAsText( String tag ) {
        if (tag.equals( OPAQUE_TAG )) {
            setValue( OPAQUE_VALUE );
        } else if (tag.equals( TRANSPARENT_TAG )) {
            setValue( TRANSPARENT_VALUE );
        } else {
            throw new IllegalArgumentException();
        }
    }

    // getAsText returns the label for the current value stored by the PropertyEditor.
    public String getAsText() {
        // PropertyEditorSupport.getValue() returns an Object, so we coerce.
        Integer value = (Integer)getValue();
        if (value.equals( OPAQUE_VALUE )) {
            return OPAQUE_TAG;
        } else {
            return TRANSPARENT_TAG;
        }
    }

    // returns an array of labels for all of the possible property values.
    public String[] getTags() {
        return tags;
    }

Visual PropertyEditors

Visual PropertyEditors provide a more complete interface for editing property values. The PropertyEditor methods that are used by visual PropertyEditors are

  1. public boolean supportsCustomEditor()
  2. public java.awt.Component getCustomEditor()
  3. public boolean isPaintable()
  4. public void paintValue(java.awt.Graphics g, java.awt.Rectangle rect)

When a builder tool user wants to edit a property with a visual PropertyEditor, the bean tool first checks to make sure that supportsCustomEditor returns true, then:

  1. calls setValue with the current property value,
  2. calls getCustomEditor to obtain the custom Component,
  3. displays this Component for the user, and finally
  4. calls getValue to get the result of the editing to set the property.

Bean Editors

[Not finished yet]

The BeanInfo Interface: Tying it All Together

The java.beans.BeanInfo interface is the entrypoint for builder tools to learn about the customized features of a specific JavaBean. When a builder tool loads a bean, it will check to see if a BeanInfo class with a particular name is in the same package with the bean. In order to be associated with a bean class, the BeanInfo class must meet these specifications:

The simplest way to write a BeanInfo class is to extend java.beans.SimpleBeanInfo. This class implements each of the BeanInfo interface methods with minimal default behaviors. You may override just the methods you want to implement.

Providing an icon for your bean

Builder tools often represent beans with icons. The getIcon method from the BeanInfo interface lets bean designers specify custom icons to represent their beans. Here is the signature of this method:

The iconKind parameter will be one of the constants defined by the BeanInfo interface, such as BeanInfo.ICON_COLOR_16x16. The Image that is returned by this method should have the size and color-depth specified by this constant. If an appropriate Image cannot be obtained, it's ok for getIcon to return null (this is the default behavior).

Loading icon images from files

The SimpleBeanInfo convenience class supplies a utility method, loadImage, to simplify obtaining images stored in files in the bean's .jar file:

The resourceName parameter for this method should be the pathname to a GIF file, expressed relative to the location of the BeanInfo class file in the .jar file.

Here is an example implementation of the getIcon method, from the MyLineBeanInfo class:

    public Image getIcon(int iconKind) {
        if (iconKind == BeanInfo.ICON_COLOR_16x16) {
            return loadImage("MyLineIconColor16.gif");            
        }
        return null;
    }

This method returns the Image obtained from the "MyLineIconColor16.gif" file if the request is for a colored icon that is 16x16 pixels, and null otherwise.

PropertyDescriptors

A BeanInfo class can also provide details about specific properties. This is accomplished using the BeanInfo.getPropertyDescriptors method:

  1. public PropertyDescriptor[] getPropertyDescriptors()

This method should return an array that contains a java.beans.PropertyDescriptor object for each property defined by the bean. Here is an example implementation of getPropertyDescriptors, from the MyLineBeanInfo class:

    private static PropertyDescriptor[] pdArray = null;

    // Note that the PropertyDescriptor constructor used in the 
    // getPropertyDescriptors() method performs introspection on the specified
    // class to verify that the name really is a property, and to find its accessor
    // methods. If any of these PropertyDescriptors cannot be constructed, we
    // punt to the superclass to provide the default.
    public PropertyDescriptor[] getPropertyDescriptors() {
        if ( pdArray == null ) {
            try {
                pdArray = new PropertyDescriptor[] {
                    new PropertyDescriptor( "color", MyLine.class ),
                    new PropertyDescriptor( "transparency", MyLine.class ),
                    new PropertyDescriptor( "point_A", MyLine.class ),
                    new PropertyDescriptor( "point_B", MyLine.class ),
                    new PropertyDescriptor( "clickableDistance", MyLine.class )
                };
                
                // register the editor classes for those properties that have them
                pdArray[0].setPropertyEditorClass( MyLineColorEditor.class );
                pdArray[1].setPropertyEditorClass( MyLineTransparencyEditor.class );

            } catch ( IntrospectionException error ) {
                pdArray = super.getPropertyDescriptors();
            }
        }

        return pdArray;
    }

BeanDescriptors

The use of the java.beans.BeanDescriptor class is similar to that of PropertyDescriptors, except that there can be only one BeanDescriptor per bean. The only information that can be communicated via a BeanDescriptor is the customizer class for the bean.

To indicate that there is a customizer for your bean, you must pass the customizer class to the constructor of a BeanDescriptor for the bean class, and return this BeanDescriptor from the BeanInfo.getBeanDescriptor method:

    public BeanDescriptor getBeanDescriptor {
        return new BeanDescriptor(myBeanClass, myCustomizerClass);
    }