Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Writing JavaBean Property Editors


Feb99: Writing JavaBean Property Editors

Morgan is a programmer at IBM and is currently a member of the IBM WebSphere Studio development team. He can be contacted at [email protected].


JavaBeans are reusable software components that can be manipulated by visual programming tools. Tools such as Symantec's Visual Café or IBM's VisualAge for Java let users manipulate the properties of a Bean via a Bean's property sheet. The property must be both readable and writable to allow editing. Visual tools typically provide property editors for the basic data types built into the language as well as some other common types like color and font that can be used by third-party Beans. Additionally, the JavaBeans spec (http://www.javasoft.com/beans/docs/spec.html) defines a set of classes that lets you provide their own property editors. In this article, I'll focus on how to build property editors and the relationships between the visual tool, property editor, and Bean. For more information on creating beans, see Cliff Berg's article, "How Do I Create a JavaBean?" (DDJ, September 1997).

The java.beans package contains the key classes for implementing property editors, and essentially provides for three types of editors -- string, list, and custom. With the exception of the custom editor, the visual tool handles all aspects of the user interface via the Bean's property sheet, so you need only implement logic to validate and maintain the property's current value. The PropertyEditor interface defines the methods that each property editor must implement. The methods that need full implementations vary with the type of property editor being built. A PropertyEditorSupport class that has default implementations of all these methods is also provided. Property editors normally subclass PropertyEditorSupport.

The PropertyEditor Interface

The getValue and setValue(Object value) methods are used by the visual tool to set and retrieve the value of the property. The parameter of setValue is an Object, indicating that properties maintained by the Bean as primitives (int, for instance) are sent to the property editor as an instance of their corresponding wrapper class (Integer). The getAsText and setAsText methods perform the same function using strings to represent the property's value. Most property editors must be able to provide the value of the property as a string. The setAsText method must be able to receive any string that is returned from getAsText and convert it to the proper property value.

Some visual tools, such as JavaSoft's Java-Beans Developers Kit (BDK), maintain the state of the Bean and its properties via serialization. Others, such as IBM's VisualAge for Java, recreate the state of a Bean at run time by generating Java code that sets the Bean's properties to the values that were specified at edit time. The getJavaInitializationString method supports the latter category of tool by returning a snippet of Java code that, when executed, results in the current value of the property. For example, if a Bean had a property declared as int foo, and its current value was 2, this method would return the string "2." The tool uses this string to generate code like beanInstance.setFoo(2) to set the property's value.

The getTags method returns an array of Strings. When this method returns null, the visual tool uses a String editor in its property sheet to allow users to manipulate the property's value. If your property assumes a limited set of values, you can return an array containing a string for each possible value. Visual tools will present this array as a drop-down list.

isPaintable and paintValue(Graphics g, Rectangle r) are used by property editors that paint the current value of the property in the property sheet themselves. When isPaintable returns True, most tools invoke the property editor's paintValue method when the property needs to be updated in the property sheet. This technique is used for properties such as Color, which are best rendered visually as opposed to textually.

The supportsCustomEditor and getCustomEditor methods are used to provide a custom user interface for manipulating the property that is presented to users in a dialog. These methods are normally used for complex properties.

Since visual tools reflect the value of the property in the Bean's property sheet, every property editor must implement an addPropertyChangeListener and removePropertyChangeListener method. The tool registers itself as a property-change listener so that it will be aware of changes in the property's value. The property editor will send the propertyChange(PropertyChangeEvent event) message to each registered listener whenever the property's value changes. You will normally use the PropertyEditorSupport default implementations of these methods.

CaseAwareTextDisplay Bean

Listing One is a Bean called CaseAwareTextDisplay. It is a subclass of java.awt.Canvas and displays text. It has several properties of various sorts. The font property is a simple (unbound) property that is inherited from Canvas. The other properties inherited from Canvas are foreground and background. This Bean overrides the property setters, converting them to bound and constrained properties.

The CaseAwareTextDisplay Bean also implements some new bound properties: text, topMargin, leftMargin, and textCase. The text property contains the text to be shown starting at the pixel offsets indicated by the topMargin and leftMargin properties. The text is painted using the inherited font, foreground, and background properties. The textCase property is maintained by the Bean as an int and indicates whether the text is to be displayed as typed -- in uppercase, lowercase, or initial caps. I'll focus on the textCase property by providing different property editors for it.

A Bean can have an optional BeanInfo class, which describes the properties of the Bean and identifies their associated property editors. When a property editor is not explicitly identified in BeanInfo, the tool uses the property editors it provides for those properties whose data types it understands. Therefore, you do not need to provide a property editor for many basic data types built into Java.

The CaseAwareTextDisplayBeanInfo class (Listing Two) describes the Case-AwareTextDisplay Bean. Its getIcon method returns a 32×32 color icon for the Bean, which is used by many tools as a visual representation of the Bean. Its superclass, SimpleBeanInfo returns null from its getMethodDescriptors and getEventSetDescriptors methods, indicating that the tool should use default introspection for discovering its methods and events. The getAdditionalBeanInfo method returns the superclass's BeanInfo so the visual tool will be aware of the inherited font property.

The getPropertyDescriptors method is the heart of the CaseAwareTextDisplayBean-Info class. It returns an array containing a property descriptor for the text, foreground, background, topMargin, leftMargin, and textCase properties. The lack of a statement like pd.setPropertyEditorClass(aClass) when initializing these property descriptors indicates that the visual tool should use its default property editor for the property's data type. The method also merges in property descriptors (in this case the font property descriptor) from its superclass. Normally, this logic is not needed, but it is included here to circumvent a problem in Lotus BeanMachine 1.1, which never invokes getAdditionalBeanInfo and would therefore be unaware of the Bean's font property.

The getTextCasePropertyDescriptor method builds the property descriptor for the textCase property. This is implemented as a separate method in the sample to ease subclassing and demonstrate different property editors. The property's getter and setter methods are identified when the property-descriptor constructor is invoked. The descriptor is also set to indicate that this property is bound, not constrained, is not an expert property, and is not hidden. Finally, this method sets the display name of the property to "case," which will be used to name the property when it is shown in a property sheet. I'll use case and textCase property interchangeably from this point, depending on the context. Keep in mind these refer to the same property.

Although a BeanInfo is technically not required for the CaseAwareTextDisplay Bean, it illustrates how to implement a BeanInfo that uses the tool's default property editors.

Building and Using the Beans

The Beans presented here were all developed with JDK 1.1.4 on Windows NT 4.0. They were tested with JavaSoft BDK 1.0 - Nov '97, IBM VisualAge for Java Professional Edition, Lotus BeanMachine 1.1, and Symantec Visual Café for Java PDE 2.0. To compile the Beans you will need JDK 1.1.4 or later, which can be downloaded from JavaSoft (http:// www.javasoft.com/products/jdk/1.1/index.html). To compile the Beans, issue the following command in a DOS window: javac -verbose -deprecation -d . *.java.

Once you have successfully compiled, you can construct a .jar file using this command in a DOS window: jar -cfm Case-AwareTextDisplay.jar Manifest.mf mybeans\*.class mybeans\*.gif. This command uses the Manifest.mf file in Listing Three that identifies which classes in the .jar are Beans. You can test the CaseAwareTextDisplay Bean using BeanBox by copying the .jar file to the bdk\jars subdirectory and launching BeanBox from a DOS window. Select the CaseAwareTextDisplay Bean and drop it in the BeanBox. Now edit the case property in the Property window. Change the value to 1 and the text will appear in uppercase; change the value to 2 and the text will appear in lowercase; change the value to 3 and the text will appear with the first letter of each word in uppercase. Using any other value will cause the text to appear as it was typed in the Property window. Notice that the Property window is using the default property editor for the case property, which in BeanBox is a simple TextField. Although the Bean uses an int internally to represent this property, that is not the best choice for manipulating the value in a property sheet. The property can only take on a limited set of values and without documentation users have no idea which values are allowed. This can be improved upon by using a String property editor.

String Property Editor

For illustrative purposes, and convenience, I implemented another Bean named CaseAwareTextDisplayT (which, along with many of the other Beans discussed in this article, is available electronically; see "Resource Center," page 5), that will use a string property editor. This Bean subclasses CaseAwareTextDisplay and does nothing. It is simply a way to create a new Bean class that can have a new associated BeanInfo class. CaseAwareTextDisplayTBeanInfo subclasses CaseAwareTextDisplayBeanInfo and overrides the getTextCasePropertyDescriptor method. It sends setPropertyEditorClass(Class.forName("mybeans.TextCaseTextPropertyEditor")) to the property descriptor to indicate that the TextCaseTextPropertyEditor class should be used to edit the textCase property.

This property editor subclasses the PropertyEditorSupport class and implements only four methods. The tags class constant is an array of strings that are valid values for the property. The getTags method returns null to indicate to the visual tool that a String editor should be used in the property sheet when editing this property. The setAsText method is called by the visual tool whenever the user changes the value of the property. The string users enter is passed as the parameter. If the string is valid, the editor instance sends setValue to itself, passing the position of the string in the array as an Integer object. The superclass's implementation of setValue updates the value of the property and notifies listeners (the tool) of the property change. If the string is invalid, setAsText throws an IllegalArgumentException. Some tools will catch this exception and display a message to users. The getAsText method simply uses the current value of the property as an index into the tags array and returns the string found there, thus converting the property value to a string. Finally, the getJavaInitializationString method returns the current value of the property as a string by sending toString to the value. Figure 1 shows the complete message flow.

After compiling and rebuilding the .jar file, if necessary, try dropping the Case-AwareTextDisplayT Bean in BeanBox. You should be able to enter the following strings for the case property and see the appropriate results: As Is, Upper Case, Lower Case, and First Letter Capitalized. Although still not the best solution for the case property, this type of editor would be a good approach for a property where users must enter a string that is intuitively known beforehand, such as time-of-day.

As an aside, JavaSoft's BDK never sends getJavaInitializationString because it uses serialization. I've seen a fair number of bad Beans that failed to implement this method and therefore do not work in tools such as VisualAge for Java, BeanMachine, and Visual Café. I suspect that the developers of these Beans only tested in BeanBox. Be sure to test your Beans and property editors against multiple tools, particularly if your Beans are being developed for the commercial market.

List Property Editor

The ideal property editor for the textCase property would show a list of human readable values that the property can assume and let users select from the list. Providing such a property editor is done quite easily by subclassing the TextCaseTextPropertyEditor class. The CaseAwareTextDisplayL Bean and the CaseAwareTextDisplayLBeanInfo class perform the same function as their corresponding, aforementioned classes. In this case, the BeanInfo identifies the TextCaseListPropertyEditor class and subclasses TextCaseTextPropertyEditor as the property editor class. This editor overrides the getTags method and returns the array of Strings containing the valid values the property can take. Because an array of Strings is returned, the visual tool will use an instance of java.awt.Choice in its property sheet. Figure 2 illustrates the complete message flow for this property editor.

Custom Property Editor

Another acceptable editor for the textCase property is a custom property editor, which provides its own graphical user interface for manipulating the property value. Because it provides its own user interface, a custom editor is an excellent choice for complex Bean properties. This should not be confused with a Bean customizer, which generally lets users manipulate all the properties of a Bean. The CaseAwareTextDisplayC Bean and the CaseAwareTextDisplayCBeanInfo class perform the usual subclassing.

TextCaseCustomPropertyEditor is the property editor class, which uses an instance of the TextCaseCustomPropertyEditorGUI class and is a subclass of java.awt.Panel, to provide the user interface. The constructor instantiates the user interface and registers itself as a PropertyChangeListener so it is aware of user interactions. It returns True from its supportsCustomEditor method, which notifies the tool that the editor provides its own user interface. When users attempt to edit the property's value, the tool creates a dialog frame, adds to it a Done button and the instance of TextCaseCustomPropertyEditorGUI returned from getCustomEditor. The tool then makes the dialog visible to users, and users can manipulate the property value, clicking the Done button when finished.

The editor also creates an instance of the CaseAwareTextDisplayC Bean, which is used to paint itself in the visual tool's property sheet. This is a common and convenient technique for user interface Beans since they already know how to paint themselves. This class returns true from isPaintable and implements the paintValue method, indicating that the editor can do its own painting within the property sheet. The tool provides both a Graphics object and a Rectangle in which the paintValue method may paint.

The TextCaseCustomPropertyEditorGUI constructor creates a panel with a group of four checkboxes, one for each possible value the property can assume, and adds itself as an item listener to each checkbox. Its setValue method updates the user interface to reflect the new value and informs any property-change listeners of the new value. Constant forwarding of property-change events is prevented by logic in the java.beans.PropertyChangeSupport class, which does not invoke the listener's propertyChange method when the old value matches the new value. The getValue method simply returns the index of the currently selected checkbox in the array of checkboxes the class maintains. When users select a different checkbox the itemStateChanged method is invoked, and the TextCaseCustomPropertyEditorGUI instance sends itself a setValue passing the index of the newly selected checkbox. This causes the property editor to be notified of the change that, in turn, notifies the visual tool of the change. The visual tool will then normally invoke paintValue to update its property sheet. Figure 3 is a typical message flow for a custom editor.

The TextCaseCustomPropertyEditor class also implements the now familiar getAsText and setAsText methods. Not all tools support paintable properties. Visual Café 2.0 is an example of such a tool. However, by providing the getAsText and setAsText methods, the editor works properly in Visual Café, which uses these methods when updating its property sheet.

Conclusion

Given the property editor architecture as defined in the BeanSpec, and in particular the capabilities provided by custom property editors, it's possible to write usable property editors to meet a wide range of needs. Implementing property editors that are portable between visual tools is a bit more difficult due to anomalies and different levels of support among the tools. However, it is generally possible to circumvent these problems so that your property editors will work in most tools. To ensure you've addressed all the tool anomalies, be certain to test your Beans and property editors with the visual tools you plan to support.

DDJ

Listing One

package mybeans;import java.awt.*;


</p>
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;


</p>
import java.io.Serializable;


</p>
// CaseAwareTextDisplay
public class CaseAwareTextDisplay extends Canvas implements Serializable
{
  //--------------------------------------------------------------------
  // Class constants
  public static final int AS_IS         = 0;
  public static final int UPPERCASE     = 1;
  public static final int LOWERCASE     = 2;
  public static final int FIRST_IN_CAPS = 3;


</p>
  public static final String TEXT         = "text";
  public static final String TOP_MARGIN   = "topMargin";
  public static final String LEFT_MARGIN  = "leftMargin";
  public static final String FOREGROUND   = "foreground";
  public static final String BACKGROUND   = "background";
  public static final String TEXT_CASE    = "textCase";
  public static final String FONT         = "font";


</p>
  //--------------------------------------------------------------------
  // Instance variables
  // The instance variables for font, foreground and background color
  // are inherited from Canvas.
  protected String text = "default text";
  protected int topMargin = 4;
  protected int leftMargin = 4;
  protected int textCase = AS_IS;


</p>
  protected PropertyChangeSupport propertyListenerSupport;
  protected VetoableChangeSupport vetoListenerSupport;


</p>
  //--------------------------------------------------------------------
  // Constructor. All beans must have a no-argument constructor.
  public CaseAwareTextDisplay() {
    this.setSize(180, 30);
    propertyListenerSupport = new PropertyChangeSupport(this);
    vetoListenerSupport     = new VetoableChangeSupport(this);
  }
  //--------------------------------------------------------------------
  // Getters and setters. All properties (except font - which is inherited 
  // from Canvas) are bound. Foreground and background color are both bound
  // and constrained.
  public String getText() {
    return text;
  }
  public void setText(String text) {
    String oldValue = this.text;
    this.text = text;
    firePropertyChange(TEXT, oldValue, this.text);
  }
  public int getTopMargin() {
    return topMargin;
  }
  public void setTopMargin(int pixels) {
    int oldValue = this.topMargin;
    this.topMargin = pixels;
    firePropertyChange(TOP_MARGIN, new Integer(oldValue), 
                                           new Integer(this.topMargin));
  }
  public int getLeftMargin() {
    return leftMargin;
  }
  public void setLeftMargin(int pixels) {
    int oldValue = this.leftMargin;
    this.leftMargin = pixels;
    firePropertyChange(LEFT_MARGIN, new Integer(oldValue), 
                                            new Integer(this.leftMargin));
  }
  public void setForeground (Color newColor) {
    Color oldValue = getForeground();
    try {
      fireVetoableChange(FOREGROUND, oldValue, newColor);
      // Set new color only when change not vetoed.
      super.setForeground(newColor);
      // Inform bound beans of property change
      firePropertyChange(FOREGROUND, oldValue, newColor);
      repaint();
    }
    catch (PropertyVetoException e) {}
  }
  public void setBackground (Color newColor) {
    Color oldValue = getBackground();
    try {
      fireVetoableChange(BACKGROUND, oldValue, newColor);
      // Set new color only when change not vetoed.
      super.setBackground(newColor);
      // Inform bound beans of property change
      firePropertyChange(BACKGROUND, oldValue, newColor);
      repaint();
    }
    catch (PropertyVetoException e) {}
  }
  public int getTextCase() {
    return textCase;
  }
  public void setTextCase(int textCase) {
    int oldValue = this.textCase;
    this.textCase = textCase;
    firePropertyChange(TEXT_CASE, new Integer(oldValue), 
                                          new Integer(this.textCase));
    repaint();
  }
  //--------------------------------------------------------------------
  // Paint method overrides paint in Canvas.
  public void paint (Graphics g) {
    // Convert the text to the proper case
    String convertedText = getText();
    if (convertedText == null)
      convertedText = " ";
    switch (textCase) {
      case UPPERCASE:
        convertedText = convertedText.toUpperCase();
        break;
      case LOWERCASE:
        convertedText = convertedText.toLowerCase();
        break;
      case FIRST_IN_CAPS:
        convertedText = convertedText.toLowerCase();
        char [] temp = convertedText.toCharArray();
        char previous = ' ';
        for (int i = 0; i < temp.length; i ++)
        {
          if (previous == ' ')
            temp[i] = Character.toUpperCase(temp[i]);
          previous = temp[i];
        }
        convertedText = new String(temp);
        break;
      case AS_IS:
      default:
        // Text should be explicitly left as is, or the value for property
        // is unknown, so make no change to the case of the text.
    }
    // Paint the text
    Rectangle r = getBounds();
    Font font = getFont();
    if (font == null)
      font = new Font("Dialog", Font.PLAIN, 12);
FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font);


</p>
    int x = leftMargin;
    int y = topMargin + fm.getAscent();


</p>
    g.setColor(getBackground());
    g.clearRect(r.x, r.y, r.width, r.height);


</p>
    g.setColor(getForeground());
    g.setFont(getFont());
    g.drawString(convertedText, x, y);
  }
  //--------------------------------------------------------------------
  // Methods to support bound properties.
  public void addPropertyChangeListener(PropertyChangeListener listener) {
    propertyListenerSupport.addPropertyChangeListener(listener);
  }
  public void removePropertyChangeListener(PropertyChangeListener listener) {
    propertyListenerSupport.removePropertyChangeListener(listener);
  }
  protected void firePropertyChange(String propertyName,
                                    Object oldValue, Object newValue) {
   propertyListenerSupport.firePropertyChange(propertyName,oldValue,newValue);
  }
  //--------------------------------------------------------------------
  // Methods to support constrained properties.
  public void addVetoableChangeListener(VetoableChangeListener vetoListener) {
    vetoListenerSupport.addVetoableChangeListener(vetoListener);
  }
  public void removeVetoableChangeListener(VetoableChangeListener vetoListener) {
    vetoListenerSupport.removeVetoableChangeListener(vetoListener);
  }
  protected void fireVetoableChange(String propertyName, Object oldValue,
                            Object newValue) throws PropertyVetoException {
    vetoListenerSupport.fireVetoableChange(propertyName, oldValue, newValue);
  }
}


</p>

Back to Article

Listing Two

package mybeans;
import java.awt.Image;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.beans.SimpleBeanInfo;
// CaseAwareTextDisplayBeanInfo
public class CaseAwareTextDisplayBeanInfo extends SimpleBeanInfo
{
  public static final String BEAN_CLASS_NAME="mybeans.CaseAwareTextDisplay";
  //------------------------------------------------------------
  // Define an icon for the bean. Image must be in the bean's subdirectory
  // in the jar, but the subdirectory is not referred to here since its known 
  // from the package (fully qualified class name).
  public Image getIcon(int iconKind) {
    if (iconKind == BeanInfo.ICON_COLOR_32x32)
      return loadImage(getBeanIconString());
    else
      return null;
  }
  //------------------------------------------------------------
  // Return the property descriptors
  public PropertyDescriptor[] getPropertyDescriptors() {


</p>
    PropertyDescriptor[] pd = new PropertyDescriptor[6];
    PropertyDescriptor[] superPd;
    PropertyDescriptor[] finalPd;


</p>
    try {
      // Build the property descriptor for the text property
      pd[0] =
        new PropertyDescriptor( CaseAwareTextDisplay.TEXT,
                      Class.forName(BEAN_CLASS_NAME), "getText", "setText");
      pd[0].setBound(true);
      pd[0].setConstrained(false);
      pd[0].setDisplayName("text");
      pd[0].setExpert(false);
      pd[0].setHidden(false);


</p>
      pd[0].setShortDescription("The text to be displayed.");


</p>
      // Build the property descriptor for the topMargin property
      pd[1] =
        new PropertyDescriptor( CaseAwareTextDisplay.TOP_MARGIN,
             Class.forName(BEAN_CLASS_NAME),"getTopMargin", "setTopMargin");
      pd[1].setBound(true);
      pd[1].setConstrained(false);
      pd[1].setDisplayName("top margin");
      pd[1].setExpert(false);
      pd[1].setHidden(false);
      pd[1].setShortDescription("The top margin in pixels.");


</p>
      // Build the property descriptor for the leftMargin property
      pd[2] =
        new PropertyDescriptor( CaseAwareTextDisplay.LEFT_MARGIN,
           Class.forName(BEAN_CLASS_NAME), "getLeftMargin", "setLeftMargin");
      pd[2].setBound(true);
      pd[2].setConstrained(false);
      pd[2].setDisplayName("left margin");
      pd[2].setExpert(false);
      pd[2].setHidden(false);
      pd[2].setShortDescription("The left margin in pixels.");


</p>
      // Build the property descriptor for the foreground property
      pd[3] =
        new PropertyDescriptor( CaseAwareTextDisplay.FOREGROUND,
          Class.forName(BEAN_CLASS_NAME), "getForeground", "setForeground");
      pd[3].setBound(true);
      pd[3].setConstrained(true);
      pd[3].setDisplayName("foreground");
      pd[3].setExpert(false);
      pd[3].setHidden(false);
      pd[3].setShortDescription("The foreground color for the text.");


</p>
      // Build the property descriptor for the background property
      pd[4] =
        new PropertyDescriptor( CaseAwareTextDisplay.BACKGROUND,
          Class.forName(BEAN_CLASS_NAME), "getBackground", "setBackground");
      pd[4].setBound(true);
      pd[4].setConstrained(true);
      pd[4].setDisplayName("background");
      pd[4].setExpert(false);
      pd[4].setHidden(false);
      pd[4].setShortDescription("The background color for the text.");


</p>
      // Build the property descriptor for the textCase property
      pd[5] = getTextCasePropertyDescriptor();
    }
    catch (Throwable t) {
      t.printStackTrace();
      return null;      // use default design patterns
    }
    // Get the property descriptors of the superclass. The font property is 
    // implemented by Canvas. This logic is needed because some tools 
    // do not call getAdditionalBeanInfo.
    try {
    BeanInfo superBeanInfo = 
    Introspector.getBeanInfo(Class.forName(BEAN_CLASS_NAME).getSuperclass());
    superPd = superBeanInfo.getPropertyDescriptors();
    }
    catch (Throwable t) {
      t.printStackTrace();
      return null;      // use default design patterns
    }
    // Add the two sets of property descriptors to a single array
    finalPd = new PropertyDescriptor[superPd.length + pd.length];
    for (int i = 0; i < superPd.length; i++)
      finalPd[i] = superPd[i];
    for (int i = superPd.length; i < (superPd.length + pd.length); i++)
      finalPd[i] = pd[i - superPd.length];
    // Return the array of property descriptors
    return finalPd;
  }
  //-------------------------------------------------------------------------
  // Return the bean info for the superclasses, excluding the Object class.
  // This handles the font property which is implemented in the Canvas class.
  public BeanInfo[] getAdditionalBeanInfo() {
    try {
     BeanInfo[] bi = new BeanInfo[1];
     bi[0] = 
    Introspector.getBeanInfo(Class.forName(BEAN_CLASS_NAME).getSuperclass());
     return bi;
    }
    catch (Throwable t) {
    }
    return null;
  }
  //-------------------------------------------------------------------------
  // Returns a property descriptor for the textCase property. This is
  // implemented as a separate method solely for convenience in subclassing.
  protected PropertyDescriptor getTextCasePropertyDescriptor()
  throws IntrospectionException, ClassNotFoundException {
    PropertyDescriptor pd;
    pd = new PropertyDescriptor(CaseAwareTextDisplay.TEXT_CASE,
               Class.forName(BEAN_CLASS_NAME), "getTextCase", "setTextCase");
    pd.setBound(true);
    pd.setConstrained(false);
    pd.setDisplayName("case");
    pd.setExpert(false);
    pd.setHidden(false);
    pd.setShortDescription("Case to use for displaying the text.");
    return pd;
  }
  //-------------------------------------------------------------------------
  // Returns the name of the file containing the beans icon. This is
  // implemented as a separate method solely for convenience in subclassing.
  protected String getBeanIconString() {
    return "Earth.gif";
  }
}


</p>

Back to Article

Listing Three

Manifest-Version: 1.0


</p>
Name: mybeans/CaseAwareTextDisplay.class
Java-Bean: true 


</p>
Name: mybeans/CaseAwareTextDisplayC.class
Java-Bean: true 


</p>
Name: mybeans/CaseAwareTextDisplayL.class
Java-Bean: true 


</p>
Name: mybeans/CaseAwareTextDisplayT.class
Java-Bean: true 


</p>
Name: mybeans/ColorConstrainer.class
Java-Bean: true 

Back to Article


Copyright © 1999, Dr. Dobb's Journal

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.