Java Beans: Continued again

BeanInfo, Property Editors and Customization

Customization of Beans allows you as the Beans developer to control what a Bean-integrator will see when they use a builder tool. By default, a builder tool uses reflection to determine what to display when designing programs with Beans. In most cases, this is sufficient. However, there are times when you want to provide different functionality. For instance, if you want to provide your own customization interface, instead of using the default property sheet, you can implement the Customizer interface and provide a custom Panel. The ExplicitButton Bean in the Beans Development Kit (BDK) provides a Customizer. (demo: edit menu option and the custom editor)
(Note source code from the beanbox demos are in bdk\demo\sunw\demo)

On the other hand, if you want to provide an alternative input mechanism for one property (like an enumerated list of valid choices), you can extend the PropertyEditorSupport class. The Molecule Bean in the BDK uses a property editor to provide a list of molecules it can display. (demo: edit menu option and the custom editor)

Using either of these mechanisms requires your Bean to provide a supporting class, along with the Bean itself. This second class implements the BeanInfo interface.

Bean Info

The BeanInfo class lets the BeanBox and other application construction tools uncover information you specify about the Bean. You have control over how your Bean is presented to programmers inside these application builders. Some examples:

The BeanInfo interface allows you to describe your Bean in greater detail then reflection alone can do. You usually want to do this to provide your Bean integrator with less options, or more restrictive options, so the Bean is easier to work with. In order for the system to locate the BeanInfo for a Bean, you must name it after the Bean, with BeanInfo at the end of the Bean's classname. For instance, if your Bean was called Foo, the BeanInfo for the Foo Bean would be called FooBeanInfo. It isn't necessary to implement the entire BeanInfo yourself. The SimpleBeanInfo class provides a basis, then you just selectively override methods.

It's easy to get confused about the relationship between information provided by BeanInfo and information that's available through reflection.

HOWEVER

An Aside: Design-time vs. Run-time Beans "mode"

Beans must be able to operate in a running application as well as inside a builder. It is certainly possible that you would want to design a Bean that behaved differently at design-time than it does at run-time. At design-time, Beans must provide the design information necessary to edit properties and customize behavior. A development tool can call the setDesignTime() method on the class to indicate whether or not the system is running in design mode. Also it has to expose methods and events so a builder tool can create code that interacts with the Bean at run-time. The Beans.isDesignTime method allows you to check for the current mode.

Also, it is important to note that this information on BeanInfo (and the rest of the following material) is particular to the design mode. The classes that we implement here are not intended for use by the application developer. These classes are used by the beanbox tool that manipulates the bean. The bean class itself does not refer to any of these auxiliary beanbox classes so that it is not dependent on them and they do not have to be shipped with the bean in finished products.


More on BeanInfo

The YesNoDialogBeanInfo class in "Java in a Nutshell", specifies a number of pieces of information: For more on these classes

  1. An icon that can be used to represent the bean

  2. A BeanDescriptor object, which includes a reference to a Customizer class for the bean.

  3. A list of the supported properties of the bean, along with a short description of each one

  4. A method that returns the most commonly customized property of the bean - this is called the "default" property

  5. References to PropertyEditor classes for two of the properties.

  6. A list of methods supported by the bean, again with a short description of each

  7. (not in this beanInfo but I listed anyway) Provides information about the events it generates and specify a default event When you implement this interface to describe your bean, a builder tool will look to the methods from the BeanInfo interface to tell it information about the properties, events, and methods your bean supports. The BeanInfo is supposed to free you from the tyranny of naming patterns. One benefit of using this interface is that it gives you a way to have members of your bean that are not exposed in a builder environment yet are still public.

    As mentioned earlier, one usually doesn't implement the entire interface, but rather uses the SimpleBeanInfo convenience class that has empty implementations (returning null.)

    A list of methods defined by the BeanInfo interface with a short description of each one:
    METHOD Description
    getAdditionalBeanInfo() Returns any additional BeanInfo objects that provide additional information on the current bean.
    getBeanDescriptor() Returns the Bean descriptor object
    getDefaultEventIndex() Returns the default event index
    getDefaultPropertyIndex() Returns the default property index
    getEventSetDescriptors() Returns the event set descriptors
    getIcon(int) Returns the specified icon for the Bean
    getMethodDescriptors() Returns the method descriptors
    getPropertyDescriptors() Returns the property descriptors

    The key to using any of the more advanced features of the BeanInfo class is the FeatureDescriptor class and its various subclasses. As its name suggests, the FeatureDescriptor class provides information about a feature. Features are properties, methods, events, and so on. The subclasses of FeatureDescriptor:

    These classes all work basically the same. You create a descriptor object for each member you are trying to describe, and you collect all descriptors of a feature set in an array and return it as the return value of one of the BeanInfo methods. Thus, if you want to restrict a builder tool to display less choices, you can override the default behavior and implement your own BeanInfo.

    Suppose you've created a SizedTextField Bean, the SizedTextFieldBeanInfo class would provide the Bean's BeanInfo. If you only wanted to display one property, length, you could define that class as such:

      import java.beans.*; public class SizedTextFieldBeanInfo extends SimpleBeanInfo { private final static Class beanClass = SizedTextFieldBeanInfo.class; public PropertyDescriptor[] getPropertyDescriptors() { try { PropertyDescriptor length = new PropertyDescriptor("length", beanClass); PropertyDescriptor rv[] = {length}; return rv; } catch (IntrospectionException e) { throw new Error(e.toString()); } } }

    Then, instead of the 17 properties of TextField, the only property displayed by the tool, would be the new length property. The SizedTextField class still needs to implement the set/getLength methods. Also, creating a custom BeanInfo does not prevent someone from calling the methods of the now hidden properties. The property accessor methods are still public. So, by using the Reflection API (or just knowing the method names), you can still invoke all the public methods. (Unless you also defined getMethodDescriptors() to limit these.)

    Also, if you wanted to have the Bean have a space delimited name, instead of being all crunched together, you could add the following to the SizedTextFieldBeanInfo definition:

    And, to wrap things up, you could even supply an icon for your Bean with the help of an image file and the following added to your BeanInfo definition (yes, they have to be a small size "The ones that would load were around 1.5" tall by 1.5" wide or smaller."):

    For another simple example of PropertyDescriptor, see the Nervous06 tutorial example

    Since these descriptors are basically the same technique, I will not go into depth here.

    Actually, this one can serve as a demo both for MethodDescriptors and Property Editor, so let's see a demo of them: the editor the use of MethodDescriptors: method choices by looking at the NervousText07.jar. In the above example, notice the Property Editor with PropertyEditorSupport and setPropertyEditorClass. PropertyEditors allow you to edit properties whose values cannot easily be changed in a text field (e.g., a property that is a class rather than a string and you want to provide options for instance choices) (or where you just want to be fancy).

    Now let's see another demo of the use of MethodDescriptors: method choices

    Note that to add a Customizer to your bean, you must supply a beanInfo class and override the getBeanDescriptor method as shown in both of the next two examples. The Customizer lets you set several properties at once (no matter how fancy your property editor is, it is still only responsible for allowing the user to set one property at a time.)

    For your interest: Notice the difference in implementation of the PropertyDescriptor between the YesNoDialogBeanInfo.java: in Nutshell with its special editors:

    and ChartBean ( newer ChartBean ) with
    ChartBeanBeanInfo ( newer) in Core Java V2 with its special editors:

    In summary: When any feature of your bean differs from the standard naming pattern, you must


    Other interesting things and cleaning up

    This probably should have gone with our discussion on Events in Beans. But it didn't, so here it is now.

    Acme07Bean Do demo and show debug property (Also note the use of prinln for debugging)

    Debug method (for debugging features): Example for Events

    When an event is fired, the event source (the bean button) iterates over the list of listeners, sending each a notification of the ActionEvent.

    Now you can define other required instance variables. The debug instance variable can be used to control the printing of stub information when handleEvent is called. By making debug a property, you can change it's value inside a builder tool. This is very handy in BeanBox where you can turn debugging on and off for each individual button to get just the amount of feedback you need for diagnosing a particular problem.

    To make debug a property, simply define setDebug and getDebug methods.

    Making debug a property lets you alter the reporting of events through a println stub on the fly by changing the value of the property from false to true using a property sheet editor. When debug is true calls to fireAction are reported to the users by printing the label of the button that fired the event. The newly defined bean can now act as an event source for ActionEvent objects. You can verify that this is true, by adding the bean to the BeanBox component palette, then trying to hook up the bean's ActionEvent to start or stop the Duke Juggler bean.

    Introspection

    Also see JDC Tech Tips No. 5

    To enable a builder tool to learn about what Events a bean can generate, Properties that are exposed by a bean, and Methods it exports the bean must use "introspection". To accomplish this JavaBeans use a two-level approach:

    The difference between a Bean and a Class:

    Tips for Bean Development

    Technology Comparison to ActiveX/COM

    Recently, JavaWorld contained two articles comparing the technologies. The first article is a strategic analysis of the two. The second article is a head-to-head comparison.

    JavaBeans Benefit Analysis

    Write Once, Run Anywheretm java.beans package part of Core API
    Component Reusability Reuse Everywhere -- across platforms/tools/solutions
    Example: 3D Charting Bean - drop into any container, regardless of platform tool
    Interoperability Communicate with other component architectures
    Beans-ActiveX Bridge now beta, Beans-OpenDoc under development

    Beans References: