Writing property editors

A property editor is an editor for changing property values at design time. You can see several different types of property editors in JBuilder's Component Inspector. For example, for some properties, you simply type in a value in the Component Inspector, and by so doing, change the value of the property. This is the simplest type of property editor. For other properties, you use a choice menu (drop-down list) to display all the possible values and you select the value you want from that list. Colors and fonts have property editors that are actually dialog boxes you can use to set their values.

The JavaBeans Component Library supplies a few property editors for the primitive Java data types. When you create your own components, however, you might create your own property classes and want to have editors capable of editing their values.

This following sections will get you started writing your own property editors. These topics are covered:

Implementing the PropertyEditor interface

Every property editor must implement the PropertyEditor interface. You can implement PropertyEditor directly, or you can start by extending the PropertyEditorSupport class. PropertyEditorSupport implements the PropertyEditor interface.

To begin creating a property editor class, create a class that implements the PropertyEditor interface.

You can use the PropertyEditorSupport class as a starting point, or implement PropertyEditor directly in the class you create. This is the recommended naming convention:

class <propertyName>Editor

Getting a Java initialization string

Each property editor must implement the getJavaInitializationString() method. getJavaInitializationString() returns a string representation of the property value that JBuilder uses in code it generates. This is the getJavaInitializationString() declaration:
public String getJavaInitializationString()
The returned string must be suitable for a Java assignment. Here are some examples:

Selecting a display style

The PropertyEditor interface provides several ways of updating and displaying property values. A property editor seldom needs to support all of them. Property values can display in a property editor as either text or any other object. How property values are displayed determines which methods of the PropertyEditor interface you need to use.

Displaying the value as text

The simplest kind of property editor displays the property value as text and allows the user to change the property value by typing in a new text string. This doesn't mean that the data type of the property must be a string, but that the value is represented by a string.

To permit the display and editing of a property value as text, write code that implements the getAsText() and setAsText() methods. Here are their declarations:

public String getAsText()

public void setAsText(String text) throws IllegalArgumentException
setAsText() throws the IllegalArgumentException exception if the string isn't formatted properly or if the property can't be represented as a text string.

Displaying the value as a list of text strings

Another commonly used property editor type presents the user a list of choices in a choice menu. The user can enter one of these values only, eliminating the possibility of an invalid property data type. For example, the alignment property of JBuilder's ListControl uses a choice menu of values. boolean values are usually presented this way.

To permit the display and editing of a property value as a list of text values,

  1. Write code that implements the getAsText() and setAsText() methods.
  2. Write code that implements the getTags() method.
This is the declaration of getTags():
public String[] getTags()
getTags() returns an array of tags that are used to represent enumerated values of the property. These tags are the strings the user sees in a choice menu (drop-down list). The user chooses the desired value from the list.

Here's an example of a getTags() method that presents the user a choice of beverages:

public String[] getTags() {
    String[] choices = new String {"Coffee", "Tea", "Milk", "Orange juice"};
    return choices;
}
The getAsText() method must be able to display one of the tagged values, and the setAsText() method must be able to set the property's value using one of the tagged values.

Displaying the value as a graphical representation

Property editors can display values in forms other than text. Two methods are used to represent the property value graphically: isPaintable() and paintValue().
public boolean isPaintable()
public void paintValue(Graphics gfx, Rectangle box)
To make your property editor capable of painting a property value,

  1. Implement isPaintable() and have it return true. If the property value uses getAsText() and setAsText(), set isPaintable() to false.

  2. Write the paintValue() method that paints the representation of the property value.

  3. The box argument is the rectanglar area that surrounds the data item in which the painting occurs. The gfx argument is the Graphics object the painting takes place on. If the property editor displays and sets values as text only, leave the body of the paintValue() method empty.

Providing a custom editor

While the three ways of displaying and setting property values will meet most of your needs, there are times when you might want to use more specialized and elaborate property editors. For example, ListControl uses dialog boxes to allow the user to select font attributes for its font property, and to select color attributes for its background and foreground properties.

There are two methods you must write: supportsCustomEditor() and getCustomEditor().

public boolean supportsCustomEditor()
public Component getCustomEditor()
To provide a custom editor for your property editor class,

  1. Implement supportsCustomEditor() and have it return true. If the property editor doesn't use a custom editor, supportsCustomEditor() must return false.

  2. Write the code for the getCustomEditor().

Note that getCustomEditor() returns a Component. You can create just about any kind of property editor you want. You must take on the task of connecting the custom editor with the property editor.

Notifying listeners of changes in a property value

When a property editor is used to change a property value, the editor can generate a PropertyChange event on all registered objects that are interested in a change in the property value. To register and unregister themselves as listeners of property change events, the objects call the addPropertyChangeListener() and removePropertyChangeListener() methods:
public void addPropertyChangeListener(PropertyChangeListener listener)
public void removePropertyChangeListener(PropertyChangeListener listener)
By implementing these methods, the property editor can notify all listeners that a change in a property value has occurred.

See also:
Working with events

A property editor example

JBuilder's JavaBeans controls use property editors. You can see them in the Component Inspector. This section presents the code for IntegerTagEditor, the engine that displays property values that evaluate to integers in a choice menu. Next you'll find the code for the TreeStyleEditor, the property editor for the style property of TreeControl. TreeStyleEditor extends the IntegerTagEditor class.
package borland.jbcl.editors;

import java.beans.*;

import borland.jbcl.util.*;

public class IntegerTagEditor implements PropertyEditor
{
  int[] values;               // the array of values (null counts from 0:n)
  String[] resourceStrings;   // strings the user will see in the drop down
  String[] sourceCodeStrings; // strings this will generate in source code

  public IntegerTagEditor(int[] values, String[] resourceStrings, String[] sourceCodeStrings) {
    // A null list of integer values assumes an incrementing enumeration from 0
    if (values == null) {
      values = new int[resourceStrings.length];
      for (int i = 0; i < values.length; ++i)

        values[i] = i;
    }
    this.values = values;
    this.resourceStrings = resourceStrings;
    // A null set of sourceCode Strings assumes the resourceStrings are the same
    this.sourceCodeStrings = (sourceCodeStrings != null) ? sourceCodeStrings : resourceStrings;
  }

  // PropertyEditor Implementation

  public void setValue(Object o) {
    value = o;
    fire();
  }

  public Object getValue() {
    return value;
  }

  public boolean isPaintable() {
    // the property editor does support painting the property value
    return false;                    
  }

  public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {
    // Leave empty.             // no painting occurs
  }


  private String getAsText(boolean forSourceCode) {
    int iVal = (value == null || !(value instanceof Integer))
                  ? values[0]
                  : ((Integer)value).intValue();
    int iPos;
    for (iPos = 0; iPos < values.length; ++iPos)
      if (values[iPos] == iVal)
        break;
    if (iPos >= values.length)
      return "";
    return (forSourceCode) ? sourceCodeStrings[iPos] : resourceStrings[iPos];
  }
  
  public String getAsText() {
    // returns one of the strings the user sees in the choice menu
    return getAsText(false);  
  }

  public String getJavaInitializationString() {
    // returns the one of the source code strings
    return getAsText(true);    
  }

  public void setAsText(String text) throws java.lang.IllegalArgumentException {
    int iPos = 0;
    if (text != null) {
      for (; iPos < resourceStrings.length; ++iPos)
        if (text.equals(resourceStrings[iPos]))
          break;
      if (iPos >= resourceStrings.length)
        throw new java.lang.IllegalArgumentException();
      value = new Integer(values[iPos]);

      // generates a PropertyChange event
      fire();    
    }
  }

  public String[] getTags() {
    // returns the strings the user sees in the choice menu
    return resourceStrings;      
  }

  public java.awt.Component getCustomEditor() {
     // no custom editor available
    return null;                       
  }

  public boolean supportsCustomEditor() {
    // doesn't support a custom editor

    return false;                    }

  private void fire() {
    // calls the propertyChange() method in the listener and passes it a PropertyChangeEvent
    if (listener != null) {
      listener.propertyChange(new PropertyChangeEvent(this, "???", null/*???*/, value));  
    }
  }

   addPropertyChangeListener(PropertyChangeListener l) {
    // registers the listener for property change events
    listener = l;                      
  }

  public void removePropertyChangeListener(PropertyChangeListener l) {
    // removes the listener for property change events
    listener = null;               
  }

  private PropertyChangeListener listener;
  private Object value;
}

The TreeStyleEditor class

Several of JBuilder's property editors extend the IntegerTagEditor class. TreeStyleEditor is one of them.
package borland.jbcl.editors;

public class TreeStyleEditor extends IntegerTagEditor
{
  public TreeStyleEditor() {
    // creates the values array
    super(new int[] {
            borland.jbcl.view.TreeView.STYLE_PLUSES,
            borland.jbcl.view.TreeView.STYLE_ARROWS},

          // creates the resourceStrings array
          new String[] {
            "Pluses",
            "Arrows"},

          // creates the array that holds the strings JBuilder uses when generating source code
          new String[] {
            "borland.jbcl.view.TreeView.STYLE_PLUSES", 
            "borland.jbcl.view.TreeView.STYLE_ARROWS"}); 
  }
}

Specifying a property editor for a property

After you've created your property editor for your property class, you must specify the name of the property editor in the component's BeanInfo class.

JBuilder provides several default property editors that are based on property types. If you fail to specify a property editor for a property in the BeanInfo class, JBuilder attempts to use one of these default editors. For example, it supplies a string type editor for entering and editing a string value such as the value of the text property for LabelControl. Another example is the int type editor for entering and editing integer values such as the value of the itemWidth property of ListControl.

If JBuilder can't find an editor that can edit the property type, it uses a scoped object list editor, which displays each of the objects in scope that match the given property type. For example, the dataSet property for the various JBuilder controls use the scoped object list editor and lists all the objects in scope of the type DataSet.

Preparing to use a new property editor

Whether you modified an existing property editor or wrote a new one, you must exit JBuilder and then restart JBuilder before the Component Inspector reflects the changes you made.