Customization: Providing Custom

   

Customization: Providing Custom PropertyEditors and GUI Interfaces

So far, you have seen how to create a Bean; how to expose its properties, methods , and events; and how to tweak the introspection process. You might have noticed from the figures that the properties of a Bean have a PropertyEditor. For example, look back at Figure 29.3. In the PropertySheet window, next to the "Text String" label, there's a TextField component already filled with the value of the OutputText property. You didn't supply any code for this component, so how did Sun's BeanBox know to provide it? The answer is that the BeanBox application asked the java.beans.PropertyEditorManager what the default PropertyEditor was for an object of type java.lang.String and displayed it.

Just because PropertyEditor s and Customizer s require a GUI environment doesn't mean a Bean can't function without one. For example, a Bean designed to run on a server might not use (or need) a GUI environment at all. The java.beans.Beans class and the java.beans.Visibility interface allow Beans to exhibit different behavior in GUI and non-GUI environments.

PropertyEditor s and the PropertyEditorManager

The class java.beans.PropertyEditorManager provides default PropertyEditor s for the majority of the Java class types. So, if you use only native Java data types and objects, you're all set. But what if you design a Bean that has a property for which there's no default PropertyEditor? You'll run into this problem any time you design a custom property type. For those cases where there is no default PropertyEditor, you have to provide your own. Actually, you could redesign all the default PropertyEditor s, too, if you choose, but you would only do this in rare cases, so this won't be discussed here. This means that you have to provide an additional class, by appending Editor to the class name , that the PropertyEditorManager can use. In most cases, you provide a subclass of java.awt.Component. The property sheet for your component will then pop up your custom PropertyEditor to allow your custom property to be edited. You won't actually design a custom PropertyEditor here because the majority of Beans won't require it, but an explanation of how to do it will be included. The requirements of a PropertyEditor are as follows :

  1. Custom PropertyEditor s must inherit from java.awt.Component so that they can be displayed in a property sheet. Note that this could mean inheriting from an AWT component such as java.awt.TextField.

  2. Custom PropertyEditor s must derive their class name by postfixing Editor to the property class name unless they register themselves with the PropertyEditorManager for their container (see step 3). For example, the PropertyEditor for a custom property type CustomProperty.class must be named CustomPropertyEditor.class.

  3. For custom PropertyEditor s that do not follow the standard naming convention in step 2, the custom property type must register itself with the container's PropertyEditorManager by calling the registerEditor() method.

  4. Custom PropertyEditor s always must fire a PropertyChange event to update the custom property. This is a must! Otherwise, the container has no way of knowing to update the component.

You can provide your own property sheet; in fact, for complex Beans, this is absolutely imperative. Property sheets by nature are simple and relatively not user -friendly. The following section discusses how to override the property sheet mechanism to provide your own customization dialog boxes.

Customization Editor

All application builders have to implement some method of customizing the Beans placed into their containers. Thus, the PropertyEditor mechanism and the idea of a property sheet were born. But what about the special cases where a Bean can be customized several different ways, or there are dozens of properties? The solution to this problem is called customizers. Bean developers can optionally supply customizer classes with their Beans to be used in place of standard property sheets. Even though the property sheet mechanism works just fine for the TextReader Bean, you'll create a customizer class anyway, to learn how it's done.

To implement a customizer class, a Bean must also provide a BeanInfo class. The class name of a Bean's customizer class is determined from a call to the getBeanDescriptor() method of the java.beans.BeanInfo interface. This is a little bit different from what you've encountered so far. There is no default introspection design pattern for customizers; you must provide a BeanInfo class, even if the only information it provides is a BeanDescriptor. In fact, this is what you do for the TextReaderBeanInfo.class shown in Listing 29.8. Notice how the class inherits from java.beans.SimpleBeanInfo; the parent class implements the java.beans.BeanInfo class, and you override the getBeanDescriptor() method so that it returns something meaningful.

Listing 29.8 TextReaderBeanInfo.java ” The BeanInfo Class for the TextReader Bean Showing How to Provide Customizer Class Information
 import java.beans.*; public class TextReaderBeanInfo extends SimpleBeanInfo {    // override the getBeanDescriptor method to provide a customizer.    public BeanDescriptor getBeanDescriptor() {       return(new BeanDescriptor(BeanClass, CustomizerClass));    }    private Class BeanClass = TextReader.class;    private Class CustomizerClass = TextReaderCustomizer.class; } 

Although there isn't a design pattern for it, it's customary to name a customizer class by postfixing Customizer to the class name. Notice that you named the TextReader customizer TextReaderCustomizer.class. This is a good habit to get into.

The programmer has a tremendous amount of freedom when designing customizer classes. There are only two restrictions: The class must inherit from java.awt.Component, so that it can be placed in a Panel or Dialog, and it must implement the java.beans.Customizer interface. The customizer class is given a reference to the target component through a call to the setObject() method. After this point, what the customizer class does is its business, for the most part. Remember, however, that you'll be required (by the compiler) to acknowledge constrained properties because their accessor methods might throw PropertyVetoException s. Finally, the java.beans.Customizer interface includes functionality for PropertyChangeListener s. Because the Bean's container might register itself as a listener with the customizer class, any property updates should be followed by a call to firePropertyChange(). The easiest way to do this is by using a java.beans.PropertyChangeSupport class as was done when discussing bound properties earlier.

Listing 29.9 shows most of the code for the TextReaderCustomizer class. Some of the AWT-specific code was removed for clarity. Take a look at the handleEvent() method. This method is called by AWT when the user enters data. Notice how you were forced to catch ProperyVetoException s for the setWidth() method? You can also see how the PropertyChangeListener methods are used appropriately. Figure 29.4 shows what the customizer looks like when called up from within Sun's BeanBox.

Figure 29.4. Sun's BeanBox shows the TextReader Bean and its customizer dialog box.

graphics/29fig04.gif

Listing 29.9 TextReaderCustomizer.java ” The Code from TextReaderCustomizer.java Showing How to Implement a Customizer Class
 import java.awt.*; import java.beans.*; public class TextReaderCustomizer extends Panel implements Customizer {    public TextReaderCustomizer() {       setLayout(new BorderLayout());    }    public void setObject(Object target) {       component = (TextReader)target;       // generate the User Interface (code removed for clarity)    }    public void processEvent(AWTEvent event) {       if (event.getID() == Event.KEY_RELEASE &&        event.getSource() == InputText) {          String old_text = component.getInputText();          String text = InputText.getText();          component.setInputText(text);          changeAgent.firePropertyChange("inputText", old_text, text);       }  else if (event.getID() == Event.KEY_RELEASE &&        event.getSource() == Width) {          int old_width, width;          old_width = component.getWidth();          try {             width = Integer.parseInt(Width.getText());             try {                component.setWidth(width);                changeAgent.firePropertyChange("width",                  new Integer(old_width), new Integer(width));             }  catch(PropertyVetoException e) {                // do nothing wait for acceptable data.             }          }  catch(NumberFormatException e) {             // do nothing wait for better data.          }       }       super.processEvent(event);    }    public void addPropertyChangeListener(PropertyChangeListener l) {       changeAgent.addPropertyChangeListener(l);    }    public void removePropertyChangeListener(PropertyChangeListener l) {       changeAgent.removePropertyChangeListener(l);    }    private TextReader component;    private TextField InputText, Width;    private PropertyChangeSupport changeAgent =       new PropertyChangeSupport(this); } 

Providing Alternative Behavior in Non-GUI Environments

Unfortunately, a GUI interface is not always available to a Bean. The most likely reason for this is that the Bean is being run in the background or on a server. Whatever the case, Beans that need to provide alternative or additional behavior in non-GUI environments can do so by using the java.beans.Beans class and the java.beans.Visibility interface.

The static methods isDesignTime() and isGuiAvailable() in the java.beans.Beans class can be used to check whether the Bean is being used in an application builder and a GUI environment is available. The method isDesignTime() returns true if the Bean is in an application builder and false if not. The method isGuiAvailable() returns true if a GUI environment is available to the Bean, and false if not.

Just because a GUI environment is available doesn't necessarily mean a container wants a Bean to use it. Similarly, a container might want to know whether a Bean isn't using the GUI environment, or even whether it needs one. A Bean and its container can communicate these things by implementing the java.beans.Visibility interface. The vast majority of Beans have no need for this interface, and it isn't necessary to implement it unless a Bean plans to use it. There are four methods in the interface:

 public abstract boolean avoidingGui() 

This method is called by a container to ask whether a Bean is currently avoiding the GUI environment. A Bean should return true for this method if it is actively avoiding the GUI environment. Notice that this is not the same as indicating that it doesn't need the GUI environment. For example, a container might use this information to free up resources being used by the GUI environment if a call to this method returns true.

 public abstract void dontUseGui() 

This method is called by the container to tell the Bean that even though a GUI environment might be available, the Bean shouldn't use it. For example, a container using a Bean on a server would call this method to tell the Bean there's no point in using the GUI environment. If a Bean chooses to comply with this method (and it should), the Bean should return true for subsequent calls to avoidingGui().

 public abstract boolean needsGui() 

This method is called by the container to ask whether a Bean absolutely has to have a GUI environment. If a Bean can function in a non-GUI environment, it should return false. Note that it's safe to return true and then never use the GUI environment, but it's not safe to return false and use it anyway.

 public abstract void okToUseGui() 

This method is called by a container to tell a Bean that a GUI environment is available and the Bean can use it. This method might also be called after dontUseGui() to indicate that a previously unavailable GUI environment is available again. Note that a call to this method in no way implies that a Bean should use the GUI environment, for example, if it wasn't planning to.

   


Special Edition Using Java 2 Standard Edition
Special Edition Using Java 2, Standard Edition (Special Edition Using...)
ISBN: 0789724685
EAN: 2147483647
Year: 1999
Pages: 353

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net