JavaBeans
Overview
The JavaBeans API makes it possible to write component software. Components expose their public methods and events to builder tools for visual manipulation. JavaBean components are known as Beans.Builder tools maintain Beans in palettes. These beans can be dragged and dropped onto forms, modified, and saved, without writing code.
Builder tools discover a Bean's properties, methods, and events by a process known as introspection, which uses the Introspector class. which, in turn, relies on the Reflection API.
By explicitly providing property, method, and event information with a related Bean Information class. A Bean information class implements the BeanInfo interface. A BeanInfo class explicitly lists those Bean features that are to be exposed to application builder tools.
Properties are a Bean's appearance and behavior characteristics that can be changed at design time. Builder tools introspect on a Bean to discover its properties, and expose those properties for manipulation.
Beans expose properties so they can be customized at design time. Customization is supported in two ways: By using property editors, or by using more sophisticated Bean customizers.
Beans use events to communicate with other Beans. A Bean that wants to receive events (a listener Bean) registers its interest with the Bean that fires the event (a source Bean). Builder tools can examine a Bean and determine which events that Bean can fire (send) and which it can handle (receive).
Persistence enables Beans to save and restore their state. Once you've changed a Beans properties, you can save the state of the Bean and restore that Bean at a later time, property changes intact. JavaBeans uses Java Object Serialization to support persistence.
A Bean's methods are no different than Java methods, and can be called from other Beans or a scripting environment. By default all public methods are exported.
The Beans Development Kit
The BDK is delivered separately from the JDK. You can download the BDK freely from the JavaBeans web site.This site contains instructions for installing the BDK on your system. Here is a general description of the BDK files and directories:
- README.html contains an entry point to the BDK documentation
- LICENSE.html contains the BDK license agreement
- GNUmakefile and Makefile are Unix and Windows makefiles (.gmk and .mk suffixes) for building the demos and the BeanBox, and for running the BeanBox
- beans/apis contains
- a java directory containing JavaBeans source files
- a sun directory containing property editor source files
- beans/beanbox contains
- makefiles for building the BeanBox
- scripts for running the BeanBox
- a classes directory containing the BeanBox class files
- a lib directory containing a BeanBox support jar file used by MakeApplet's produced code
- sun and sunw directories containing BeanBox source (.java) files
- a tmp directory containing automatically generated event adapter source and class files, .ser files, and applet files automatically generated by MakeApplet
- beans/demos contains
- makefiles for building the demo Beans
- an HTML directory containing an applet wrapper demonstration that must be run in appletviewer, HotJava, or JDK1.1-compliant browsers
- a sunw directory containing
- a wrapper directory containing a Bean applet wrapper
- a demos directory containing demo source file
- beans/doc contains
- demos documentation
- a javadoc directory containing JavaBeans and JavaBeans-related class and interface documentation
- miscellaneous documentation
- beans/jars contains jar files for demo Beans
Writing a Simple Bean
In this section you will learn more about Beans and the BeanBox by
- Creating a simple Bean
- Compiling and saving the Bean into a Java Archive (JAR) file
- Loading the Bean into the ToolBox
- Dropping a Bean instance into the BeanBox
- Inspecting the Bean's properties, methods, and events
- Generating an introspection report
Your Bean will be named SimpleBean. Here are the steps to create it and view it in the BeanBox:
- Write the SimpleBean code. Put it in a file named SimpleBean.java, in the directory of your choice. Here's the code:
import java.awt.*; import java.io.Serializable; public class SimpleBean extends Canvas implements Serializable { //Constructor sets inherited properties public SimpleBean(){ setSize(60,40); setBackground(Color.red); } }SimpleBean extends the java.awt.Canvas component. SimpleBean also implements the java.io.Serializable interface, a requirement for all Beans. SimpleBean sets the background color and component size.
- Make sure the CLASSPATH environment variable is set to point to all needed .class (or .jar) files. Here are some URLs that will help you to set CLASSPATH correctly:
- The JDK Tool Reference Page provides complete CLASSPATH information for both Windows and Solaris platforms
- Compile the Bean:
javac SimpleBean.javaThis produces the class file SimpleBean.class
- Create a manifest file. Use your favorite text editor to create a file, we'll call it manifest.tmp, that contains the following text:
Name: SimpleBean.class Java-Bean: True- Create the JAR file. The JAR file will contain the manifest and the SimpleBean class file:
jar cfm SimpleBean.jar manifest.tmp SimpleBean.class- Load the JAR file into the ToolBox. Select the File|LoadJar... menu item. This will bring up a file browser. Navigate to the SimpleBean.jar location and select it. SimpleBean will appear at the bottom of the ToolBox. (Note that when the BeanBox is started, all Beans in JAR files in the beans/jars directory are automatically loaded into the ToolBox).
- Drop a SimpleBean instance into the BeanBox. Click on the word SimpleBean in the ToolBox. The cursor will change to a crosshair. Move the cursor to a spot within the BeanBox and click. SimpleBean will appear as a painted rectangle with a hatched border. This border means that SimpleBean is selected. The SimpleBean properties will appear in the Properties sheet.
You can resize SimpleBean, because it inherits from Canvas, by dragging a corner. You will see the cursor change to a right angle when over a corner. You can also reposition SimpleBean within the BeanBox by dragging on any non-corner portion of the hatched border. You will see the cursor change to crossed arrows when in position to move the Bean.
SimpleBean Makefiles
Below are two makefiles (Unix and Windows) set up to create SimpleBean.
# gnumake file CLASSFILES= SimpleBean.class JARFILE= SimpleBean.jar all: $(JARFILE) # Create a JAR file with a suitable manifest. $(JARFILE): $(CLASSFILES) $(DATAFILES) echo "Name: SimpleBean.class" >> manifest.tmp echo "Java-Bean: True" >> manifest.tmp jar cfm $(JARFILE) manifest.tmp *.class @/bin/rm manifest.tmp # Compile the sources %.class: %.java export CLASSPATH; CLASSPATH=. ; \ javac $< # make clean clean: /bin/rm -f *.class /bin/rm -f $(JARFILE)
Here is the Windows nmake version:
# nmake file CLASSFILES= simplebean.class JARFILE= simplebean.jar all: $(JARFILE) # Create a JAR file with a suitable manifest. $(JARFILE): $(CLASSFILES) $(DATAFILES) jar cfm $(JARFILE) <<manifest.tmp *.class Name: SimpleBean.class Java-Bean: True << .SUFFIXES: .java .class {sunw\demo\simple}.java{sunw\demo\simple}.class : set CLASSPATH=. javac $< clean: -del sunw\demo\simple\*.class -del $(JARFILE)You can use these makefiles as templates for creating your own Bean makefiles. The example Bean makefiles, in the beans/demo directory, also show you how to use makefiles to build and maintain your Beans.
Inspecting SimpleBean Properties and Events
The Properties sheet displays the selected Bean's properties. With SimpleBean selected, the Properties sheet displays four propeties: foreground, background, font, and name. We declared no properties in SimpleBean , so these are properties inherited from Canvas. Clicking on each property brings up a property editor. The BeanBox provides default property editors for the primitive types, plus Font and Color types. You can find the sources for these property editors in beans/apis/sun/beans/editors.
Beans communicate with other Beans by sending and receiving event notifications. To see which events SimpleBean can send, choose the Edit|Events BeanBox menu item. A list of events, grouped by the Java interface in which the event method is declared, will be displayed. Under each interface group is a list of event methods. These are all inherited from Canvas.
You will learn more about properties and events in upcoming sections.
Generating Bean Introspection Reports
Introspection is the process of discovering a Bean's design-time features by one of two methods:
- Low-level reflection, which uses design patterns to discover your Bean's features
- By examining an associated bean information class that explicitly describes your Bean's features.
You can generate a Bean introspection report by choosing the the Edit|Report menu item. The report lists Bean events, properties, and methods, and their characteristics.
By default Bean reports are sent to the java interpreter's standard output, which is the window where you started the BeanBox. You can redirect the report to a file by changing the java interpreter command in beanbox/run.sh or run.bat to:
java sun.beanbox.BeanBoxFrame > beanreport.txt
Simple Properties
Properties are aspects of a Bean's appearance and behavior that are changeable at design time. Properties are private values accessed through getter and setter methods. Property getter and setter method names follow specific rules, called design patterns. By using these design pattern-based method names, JavaBeans-enabled builder tools (and the BeanBox) can
- Discover a Bean's properties
- Determine the properties' read/write attributes
- Determine the properties' types
- Locate an appropriate property editor for each property type
- Display the properties' (usually in a property sheet)
- Alter those properties (at design time)
For example, a builder tool, on introspecting your Bean, discovers two methods:
public Color getColor() { ... } public void setColor Color(c) { ... }From this the builder tool infers that a property named color exists, that it is readable and writeable, and that it's type is Color. Further, the builder tool can attempt to locate a property editor for that type, and display the property (usually in a property sheet) so it can be edited.
Adding a Color Property to SimpleBean
Make the following changes to SimpleBean.java to add a color property:
- Create and initialize a private instance variable.
private Color color = Color.green;- Write a public getter method.
public Color getColor(){ return color; }- Write a public setter method.
public void setColor Color(newColor){ color = newColor; repaint(); }- Override the paint() method inherited from Canvas.
public void paint Graphics(g) { g.setColor(color); g.fillRect(20, 5, 20, 30); }- Compile the Bean, load it in the ToolBox, and create an instance in the BeanBox.
The results are:
- SimpleBean will be displayed with a green centered rectangle.
- The Properties sheet will contain a new Color property. The introspection mechanism will also search for a Color property editor. A Color property editor is one of the default editors supplied with the BeanBox, and is assigned as SimpleBean's Color property editor. Click on the Color property in the Properties sheet to run this editor. Section 9.2 of the JavaBeans API Specification
The following figure shows the revised SimpleBean instance within the BeanBox, SimpleBean's new Color property within the Properties sheet, and the Color property editor shipped with the BeanBox.
You can change the color property by menu, or by RGB value. Try changing colors.
Here is the complete SimpleBean source code, revised to add a color property.
package sunw.demo.simple; import java.awt.*; import java.io.Serializable; public class SimpleBean extends Canvas implements Serializable{ private Color color = Color.green; //property getter method public Color getColor(){ return color; } //property setter method. Sets new SimpleBean //color and repaints. public void setColor Color(newColor){ color = newColor; repaint(); } public void paint Graphics(g) { g.setColor(color); g.fillRect(20, 5, 20, 30); } //Constructor sets inherited properties public SimpleBean(){ setSize(60,40); setBackground(Color.red); } }
Bound Properties
Sometimes when a Bean property changes, another object may want to be notified of the change, and react to the change. Whenever a bound property changes, notification of the change is sent to interested listeners.
A Bean containing a bound property must maintain a list of property change listeners, and alert those listeners when the bound property changes. The convenience class PropertyChangeSupport implements methods that add and remove PropertyChangeListener objects from a list, and fires PropertyChangeEvent objects at those listeners when the bound property changes. Your Beans can inherit from this class, or use it as an inner class.
An object that wants to listen for property changes must be able to add and remove itself from the listener list on the Bean containing the bound property, and respond to the event notification method that signals a property change. By implementing the PropertyChangeListener interface the listener can be added to the list maintained by the bound property Bean, and because it implements the PropertyChangeListener.propertyChange method, the listener can respond to property change notifications.
The PropertyChangeEvent class encapsulates property change information, and is sent from the property change event source to each object in the property change listener list via the propertyChange method.
The following sections provide the details of implementing bound properties.
Implementing Bound Property Support Within a Bean
To implement a bound property, take the following steps:
- Import the java.beans package. This gives you access to the PropertyChangeSupport class.
- Instantiate a PropertyChangeSupport object:
This object maintains the property change listener list and fires property change events. You can also make your class a PropertyChangeSupport subclass.private PropertyChangeSupport changes = new PropertyChangeSupport(this);
- Implement methods to maintain the property change listener list. Since PropertyChangeSupport implements these methods, you merely wrap calls to the property-change support object's methods:
public void addPropertyChangeListener( PropertyChangeListener l) { changes.addPropertyChangeListener(l); } public void removePropertyChangeListener( PropertyChangeListener l) { changes.removePropertyChangeListener(l); }- Modify a property's setter method to fire a property change event when the property is changed. OurButton's setLabel method looks like this:
public void setLabel(String newLabel) { String oldLabel = label; label = newLabel; sizeToFit(); changes.firePropertyChange("label", oldLabel, newLabel); }Note that setLabel stores the old label value, because both the old and new labels must be passed to firePropertyChange.
public void firePropertyChange(String propertyName, Object oldValue, Object newValue)The firePropertyChange method bundles its parameters into a PropertyChangeEvent object, and calls propertyChange(PropertyChangeEvent pce) on each registered listener. The old and new values are treated as Object values. If your property values are primitive types such as int, you must use the object wrapper version such as java.lang.Integer. Also, property change events are fired after the property has changed.
When the BeanBox (or Beans-aware builder tool) recognizes the design patterns for bound properties within your Bean, you will see a propertyChange interface item when you select the Edit|Events menu.
Now that you have given your Bean the ability to broadcast events when a bound property has changed, the next step is to create a listener.
Implementing Bound Property Listeners
To listen for property change events, your listener Bean must implement the PropertyChangeListener interface. This interface contains one method:
This is the notification method that the source Bean calls on all property change listeners in its property change listener list.public abstract void propertyChange(PropertyChangeEvent evt)So to make your class able to listen and respond to property change events, you must:
- Implement the PropertyChangeListener interface.
public class MyClass implements java.beans.PropertyChangeListener, java.io.Serializable
- Implement the propertyChange method in the listener. This method needs to contain the code that handles what you need to do when the listener receives property change event. Very often, for example, this is a call to a setter method in the listener class: a property change in the source Bean propagates a change to a property in a listener Bean.
To register interest in receiving notification about a Bean property change, the listener Bean calls the listener registration method on the source Bean. For example:
Or, you can use an adapter class to catch the property change event, and subsequently call the correct method within the listener object. Here is an example taken from comments in the beans/demo/sunw/demo/misc/ChangeReporter.java file.button.addPropertyChangeListener(aButtonListener);OurButton button = new OurButton(); ... PropertyChangeAdapter adapter = new PropertyChangeAdapter(); ... button.addPropertyChangeListener(adapter); ... class PropertyChangeAdapter implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent e) { reporter.reportChange(e); } }
Bound Properties in the BeanBox
The BeanBox handles bound property events as it handles all events: by using an event hookup adapter. Event hookup adapters classes are generated by builder tools when you connect an event source Bean to an event listener Bean. These objects interpose between event sources and event listeners to provide control and filtering over event delivery. Since an event listener can register with multiple listeners that fire the same event type, event hookup adapters can be used to intercept an event from a particular event source, and forward it to the correct event listener. This saves the event listener from implementing code that would examine each event to determine if it is from the correct source. See section 6.7 of the JavaBeans API Specification for a complete discussion of event hookup adapters.
The OurButton and ChangeReporter Beans can be used to illustrate this technique. To see how this works, take the following steps:
- Drop OurButton and ChangeReporter instances on the BeanBox.
- Select the OurButton instance and choose the Edit|Events|propertyChange|propertyChange menu item.
- Connect the rubber band line to the ChangeReporter instance. The EventTargetDialog will be displayed.
- Choose reportChange from the EventTargetDialog. The event hookup adapter source will be generated and compiled
- Select OurButton and change some of it's properties. You will see change reports in ChangeReporter.
Behind the scenes the BeanBox generated the event hookup adapter. This adapter implements the PropertyChangeListener interface, and also generates a propertyChange method implementation that calls the ChangeReporter.reportChange method. Here's the generated adapter source code:
// Automatically generated event hookup file. package tmp.sunw.beanbox; import sunw.demo.misc.ChangeReporter; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; public class ___Hookup_14636f1560 implements java.beans.PropertyChangeListener, java.io.Serializable { public void setTarget(sunw.demo.misc.ChangeReporter t) { target = t; } public void propertyChange(java.beans.PropertyChangeEvent arg0) { target.reportChange(arg0); } private sunw.demo.misc.ChangeReporter target; }The ChangeReporter Bean need not implement the PropertyChangeListener interface; instead, the BeanBox generated adapter class implements PropertyChangeListener, and the adapter's propertyChange method calls the appropriate method in the target object (ChangeReporter).
The BeanBox puts the event adapter classes in the beans/beanbox/tmp/sunw/beanbox directory. When an adapter class is generated, you can view the adapter source in that directory.
Constrained Properties
A Bean property is constrained when any change to that property can be vetoed. Usually an outside object exercises the right to veto, but the Bean itself can also veto a property change.
The JavaBeans API provides an event mechanism, very similar to the bound property mechanism, that allows objects to veto a Bean's property changes.
There are three parts to constrained property implementations:
- A source Bean containing one or more constrained properties.
- Listener objects that implement the VetoableChangeListener interface. This object accepts or rejects proposed changes to a constrained property in the source Bean.
- A PropertyChangeEvent object containing the property name, and its old and new values. This is the same class used for bound properties.
A Bean containing constrained properties must
- Allow VetoableChangeListener objects to register and unregister their interest in receiving notification that a property change is proposed.
- Fire property change events at those interested listeners when a property change is proposed. The event should be fired before the actual property change takes place. This gives each listener a chance to veto the proposed change. The PropertyChangeEvent is fired by a call to each listeners vetoableChange method.
- If a listener vetoes, then make sure that any other listeners can revert to the old value. This means reissuing the vetoableChange call to all the listeners, with a PropertyChangeEvent containing the old value.
The VetoableChangeSupport utility class is provided to implement these capabilities. This class implements methods to add and remove VetoableChangeListener objects to a listener list, and a method that fires property change events at each listener in that list when a property change is proposed. This method will also catch any vetoes, and resend the property change event with the original property value. Your Bean can either inherit from VetoableChangeSupport, or use an instance of it.
Note that, in general, constrained properties should also be bound properties. When a constrained property change does occur, a PropertyChangeEvent can be sent via PropertyChangeListener.propertyChange to signal all VetoableChangeListener Beans that the change has taken effect. This lets all the vetoable change listeners know that the change was not vetoed by any listener.
The JellyBean demo Bean has a constrained property. We will its code to illustrate the steps in implementing constrained properties. Here's the steps to implement constrained properties in your Beans:
- Import the java.beans package. This gives you access to the VetoableChangeSupport class.
- Instantiate a VetoableChangeSupport object within your Bean:
VetoableChangeSupport manages a list of VetoableChangeListener objects, and fires property change events at each object in the list when a change occurs to a constrained property.private VetoableChangeSupport vetos = new VetoableChangeSupport(this);
- Implement methods to maintain the property change listener list. These merely wrap calls to the VetoableChangeSupport object's methods:
public void addVetoableChangeListener( VetoableChangeListener l) { vetos.addVetoableChangeListener(l); } public void removeVetoableChangeListener( VetoableChangeListener l) { vetos.removeVetoableChangeListener(l); }- Write a property's setter method to fire a property change event when the property is changed. This includes adding a throws clause to the setter method's signature. JellyBean's setPriceInCents method looks like this:
public void setPriceInCents(int newPriceInCents) throws PropertyVetoException { int oldPriceInCents = ourPriceInCents; // First tell the vetoers about the change. // If anyone objects, we don't catch the exception // but just let if pass on to our caller. vetos.fireVetoableChange("priceInCents", new Integer(oldPriceInCents), new Integer(newPriceInCents)); // No-one vetoed, so go ahead and make the change. ourPriceInCents = newPriceInCents; changes.firePropertyChange("priceInCents", new Integer(oldPriceInCents), new Integer(newPriceInCents)); }Note that setPriceInCents stores the old price, because both the old and new prices must be passed to fireVetoableChange. Also note that the primitive int prices are converted to Integer objects.
public void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws PropertyVetoExceptionThese values are then bundled into a PropertyChangeEvent object sent to each listener. The old and new values are treated as Object values, so if they are primitive types such as int, you must use the object version such as java.lang.Integer.
Now you are ready to implement a Bean that listens for constrained property changes.
Implementing Constrained Property Listeners
To listen for property change events, your listener Bean must implement the VetoableChangeListener interface. This interface contains one method:
void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException;So to make your class able to listen and respond to property change events, your listener class must:
- Implement the VetoableChangeListener interface.
- Implement the vetoableChange method. This is the method that will be called by the source Bean on each object in the listener list (maintained by the VetoableChangeSupport object). This is also the method that exercises veto power. A property change is vetoed by throwing the PropertyVetoException.
Note that the VetoableChangeListener object is often an adapter class. The adapter class implements the VetoableChangeListener interface and the vetoableChange method. This adapter is added to the constrained Bean's listener list, intercepts the vetoableChange call, and calls the target Bean method that exercises veto power. You'll see an example of this in the next section.
Constrained Properties in the BeanBox
When the BeanBox recognizes the design patterns for constrained properties within your Bean, you will see a vetoableChange interface item when you pull down the Edit|Events menu.
As with any event hookup, the BeanBox generates an adapter class when you hook up a Bean with a constrained property to another Bean. To see how this works, take the following steps:
- Drop Voter and JellyBean instances into the BeanBox.
- Select the JellyBean instance, and choose the Edit|Events|vetoableChange|vetoableChange menu item.
- Connect the rubber band line to the Voter Bean. This brings up the EventTargetDialog panel.
- Choose the Voter Bean's vetoableChange method, and push the OK button. This generates an event adapter. You can view this adapter in the beans/beanbox/tmp/sunw/beanbox directory.
- Test the constrained property. Select the JellyBean and edit its priceInCents property in the Properties sheet. A PropertyVetoException is thrown, and an error dialog pops up.
Behind the scenes the BeanBox generated the event hookup adapter. This adapter implements the VetoableChangeListener interface, and also generates a vetoableChange method implementation that calls the Voter.vetoableChange method. Here's the generated adapter source code:
// Automatically generated event hookup file. package tmp.sunw.beanbox; import sunw.demo.misc.Voter; import java.beans.VetoableChangeListener; import java.beans.PropertyChangeEvent; public class ___Hookup_1475dd3cb5 implements java.beans.VetoableChangeListener, java.io.Serializable { public void setTarget(sunw.demo.misc.Voter t) { target = t; } public void vetoableChange(java.beans.PropertyChangeEvent arg0) throws java.beans.PropertyVeto Exception { target.vetoableChange(arg0); } private sunw.demo.misc.Voter target; }The Voter Bean need not implement the VetoableChangeListener interface; instead, the generated adapter class implements VetoableChangeListener. The adapter's vetoableChange method calls the appropriate method in the target object (Voter).
Per Property Constraint
As with bound property support, JavaBeans has design pattern support for adding and removing VetoableChangeListener objects that are tied to a specific named property:
As an alternative, for each constrained property a Bean can provide methods with the following signature to register and unregister vetoable change listeners on a per property basis:void addVetoableChangeListener(String propertyName, VetoableChangeListener listener); void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener);void add<PropertyName>Listener(VetoableChangeListener p); void remove<PropertyName>Listener(VetoableChangeListener p);
Indexed Properties
Indexed properties represent collections of values accessed, like an array, by index. The indexed property design patterns are
Conforming to these patterns lets builder tools know that your Bean contains an indexed property.//Methods to access the entire indexed property array public <PropertyType>[] get<PropertyName>(); public void set<PropertyName>(<PropertyType>[] value); //Methods to access individual values public <PropertyType> get<PropertyName>(int index); public void set<PropertyName>(int index, <PropertyType> value);The OurListBox demo Bean illustrates how to use an indexed property. OurListBox extends the List class to provide a Bean that presents the user with a list of choices: Choices that you can provide and change at design time. Here's an illustration of an OurListBox instance:
OurListBox exposes the item indexed property with the following accessor methods: public void setItems(String[] indexprop) { String[] oldValue=fieldIndexprop; fieldIndexprop=indexprop; populateListBox(); support.firePropertyChange("items",oldValue, indexprop); } public void setItems(int index, String indexprop) { String[] oldValue=fieldIndexprop; fieldIndexprop[index]=indexprop; populateListBox(); support.firePropertyChange("Items",oldValue, fieldIndexprop); } public String[] getItems() { return fieldIndexprop; } public String getItems(int index) { return getItems()[index]; }When an item is set by one of the setItems() methods, OurListBox is populated with the contents of a String array.
Indexed properties are almost as easily exposed to builder tools as simple properties. Writing an indexed property editor, though, requires writing a custom property editor.
Indexed Property Editors
The OurListBox demo Bean provides an associated IndexPropertyEditor which is a good example of how to implement an indexed property editor. The following illustration shows an OurListBox instance in the BeanBox, the Properties sheet which contains an entry for the indexed property items, and the IndexPropertyEditor which pops up when the items property entry is clicked:
Implementing IndexPropertyEditor is the same as implementing any custom property editor:
- Implement the PropertyEditor interface:
You can use the PropertyEditorSupport class, either by subclassing or as an inner class.public class IndexPropertyEditor extends Panel implements PropertyEditor, ActionListener {- Denote the custom editor in a related BeanInfo class. OurListBox has a related OurListBoxBeanInfo class that contains the following code:
itemsprop.setPropertyEditorClass( IndexPropertyEditor.class);
- Make the property editor a source for bound property events. The property editor will register property listeners, and fire property change events at those listeners. This is how the property changes are propagated back to the Bean (via the property sheet). So IndexPropertyEditor instantiates an inner PropertyChangeSupport class:
Provides the ability for objects to register their interest in being notified when a property is edited:private PropertyChangeSupport support = new PropertyChangeSupport(this);And fires property change events at those listeners:public void addPropertyChangeListener( PropertyChangeListener l) { support.addPropertyChangeListener(l); } public void removePropertyChangeListener( PropertyChangeListener l) { support.removePropertyChangeListener(l); }public void actionPerformed(ActionEvent evt) { if (evt.getSource() == addButton) { listBox.addItem(textBox.getText()); textBox.setText(""); support.firePropertyChange("", null, null); } else if (evt.getSource()== textBox) { listBox.addItem(textBox.getText()); textBox.setText(""); support.firePropertyChange("",null,null); } ... }IndexPropertyEditor maintains listbox as a proxy for OurListBox. When a change is made to listbox, a property change event is fired to all listeners.
When the Properties sheet, which is registered as an IndexPropertyEditor listener, receives a property change event from IndexPropertyEditor, the Properties sheet calls IndexPropertyEditor.getValue to retrieve the new or changed items and update the Bean.
How the BeanBox Discovers a Beans Event Capabilities
The BeanBox uses either design pattern introspection or a BeanInfo class to discover what events a Bean can fire.
Using Introspection to Discover the Events A Bean Fires
JavaBeans provides event-oriented design patterns to give introspecting tools the ability to discover what events a Bean can fire. For a Bean to be the source of an event, it must implement methods that add and remove listener objects for that type of event. The design patterns for these methods are
These methods let a source Bean know where to fire events. The source Bean then fires events at those listener Beans using the methods for those particular interfaces. For example, if a source Bean registers ActionListener objects, it will fire events at those objects by calling the actionPerformed method on those listeners.public void add<EventListenerType>(<EventListenerType> a) public void remove<EventListenerType>(<EventListenerType> a)To see events discovered using design patterns, drop an instance of OurButton into the BeanBox and pull down the Edit|Events menu. This displays a list of event interfaces to which OurButton can fire events. Note that OurButton itself only adds and removes two of these interfaces; the rest are inherited from the base class.
Using BeanInfo to Define the Events a Bean Fires
You can explicitly "publish" the events a Bean fires by using a class that implements the BeanInfo interface. The ExplicitButton demo Bean subclasses OurButton, and provides an associated ExplicitButtonBeanInfo class. ExplicitButtonBeanInfo implements the following method to explicitly define interfaces to which ExplicitButton fires events.
Drop an ExplicitButton instance in the BeanBox, and pull down the Edit|Events menu. Notice that only those interfaces explicitly exposed in the ExplicitButtonBeanInfo class are listed. No inherited capabilities are exposed.public EventSetDescriptor[] getEventSetDescriptors() { try { EventSetDescriptor push = new EventSetDescriptor(beanClass, "actionPerformed", java.awt.event.ActionListener.class, "actionPerformed"); EventSetDescriptor changed = new EventSetDescriptor(beanClass, "propertyChange", java.beans.PropertyChangeListener.class, "propertyChange"); push.setDisplayName("button push"); changed.setDisplayName("bound property change"); EventSetDescriptor[] rv = { push, changed}; return rv; } catch (IntrospectionException e) { throw new Error(e.toString()); } }
Viewing a Bean's Events in the BeanBox
If you select an OurButton Bean in the BeanBox, then pull down the Edit|Events menu, you will see a list of interfaces that OurButton can fire events at. Each interface item will, when selected, display the methods that fire different events at those interfaces. These correspond to all the events that OurButton can fire.
Hooking Up Events in the BeanBox
In this example you will use two OurButton Bean instances to stop and start an instance of the animated Juggler Bean. You will label the buttons "start" and "stop"; make the start button, when pressed, invoke the Juggler Bean's startJuggling method; and make the stop button, when pressed, invoke the Juggler Bean's stopJuggling method.
- Start the BeanBox.
- Drop a Juggler Bean and two OurButton bean instances into the BeanBox.
- Select an OurButton instance. In the Properties sheet, change the label property to "start". Select the second OurButton instance and change its label to "stop".
- Select the start button. Choose the Edit|Events|action|actionPerformed menu item. This causes a rubber band line to track between the start button and the cursor. Click on the Juggler instance. This brings up the EventTargetDialog:
This list contains Juggler methods that take no arguments, or arguments of type actionPerformed. - Select the startJuggling method and press OK. You will see a message that the BeanBox is generating adapter classes.
- Do the above two steps on the stop button, except choose the stopJuggling method in the EventTargetDialog.
Clicking on the start and stop buttons will now start and stop the Juggler. Here is a general description of what happened:
- The start and stop buttons, are event sources. Event sources fire events at event targets. In this example the Juggler Bean is the event target.
- When you select the start button and choose an event method (via the Edit|Event menu item), you are choosing the type of event the event source will fire.
- When you connect the rubber band line to another Bean, you are selecting the event target Bean.
- The EventTargetDialog lists methods that can accept that type of event or that take no parameters. When you choose a method in the EventTargetDialog, you are specifying the method that will receive the fired event, and act on it.
Use the File|Save menu item to save this example to a file of your choice.
Event Adapter Classes
The BeanBox generates an adapter class that interposes between the event source and target. The adapter class implements the appropriate event listener interface (and so is the actual listener), catches the event fired by the button, and then calls the selected target Bean method. Here is the BeanBox-generated adapter class that interposes between the start button and the JugglerBean:
// Automatically generated event hookup file. package tmp.sunw.beanbox; import sunw.demo.juggler.Juggler; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; public class ___Hookup_1474c0159e implements java.awt.event.ActionListener, java.io.Serializable { public void setTarget(sunw.demo.juggler.Juggler t) { target = t; } public void actionPerformed(java.awt.event.ActionEvent arg0) { target.startJuggling(arg0); } private sunw.demo.juggler.Juggler target; }The adapter implements the ActionListener interface that you selected in the BeanBox's Edit|Events menu. ActionListener declares one method, actionPerformed(), which is implemented by the adapter to call the target Bean method (startJuggling()) that you selected. The adapter's setTarget() method is called by the BeanBox to set the actual target Bean, in this case Juggler. See section 6.7 of the JavaBeans specification for a complete discussion of event adapters.
The EventMonitor Demo Bean
The EventMonitor Bean (beans/demo/sunw/demo/encapsulatedEvents) prints out source Bean event reports, as they occur, in a scrolling listbox. To see how this works, take the following steps:
When the first event is delivered, EventMonitor analyzes the source Bean to discover all the events it fires, creates and registers an event listener for each event type, and then reports whenever any event is fired. This is useful for debugging. Try connecting other demo Beans to EventMonitor to observer their events.
- Drop OurButton and EventMonitor instances in the BeanBox. You might want to resize the EventMonitor (and the BeanBox) to accommodate viewing the event reports.
- Select the OurButton instance, and choose any event method in the Edit|Events menu.
- Connect the rubber band line to the EventMonitor, and choose its initiateEventSourcMonitoring in the EventTargetDialog.
- Select the OurButton Bean. You will begin seeing event reports in the EventMonitor.
Bean Persistence
A Bean persists by having its properties, fields, and state information saved and restored to and from storage. The mechanism that makes persistence possible is called serialization. When a Bean instance is serialized, it is converted into a data stream and written to storage. Any applet, application, or tool that uses that Bean can then "reconstitute" it by deserialization. JavaBeans uses the JDK's Object Serialization API for its serialization needs.
All Beans must persist. To persist, your Beans must support serialization by implementing either the java.io.Serializable interface, or the java.io.Externalizable interface. These interfaces offer you the choice between automatic serialization, and "roll your own". As long as one class in a class's inheritance hierarchy implements Serializable or Externalizable, that class is serializable.
Controlling Serialization
You can control the level of serialization that your Beans undergo:
- Automatic: implement Serializable. Everything gets serialized by the Java serialization software.
- Selectively exclude fields you do not want serialized by marking with the transient (or static) modifier.
- Writing Beans to a specific file format: implement Externalizable, and its two methods.
Default Serialization: The Serializable Interface
The Serializable interface provides automatic serialization by using the Java Object Serialization tools. Serializable declares no methods; it acts as a marker, telling the Object Serialization tools that your Bean class is serializable. Marking your class with Serializable means you are telling the Java Virtual Machine (JVM) that you have made sure your class will work with default serialization. Here are some important points about working with the Serializable interface:
- Classes that implement Serializable must have a no-argument constructor. This constructor will be called when an object is "reconstituted" from a .ser file.
- You don't need to implement Serializable in your class if if it is already implemented in a superclass (but you do need to make sure works correctly and as you expect with default serialization).
- All fields but static and transient are serialized. Use the transient modifier to specify fields you do not want serialized, and to specify classes that are not serializable.
The BeanBox writes serialized Beans to a file with a .ser extension.
The OurButton demo Bean uses default serialization to make its properties persist. OurButton only added Serializable to its class definition to make use of default serialization:
public class OurButton extends Component implements Serializable,...If you drop an OurButton instance into the BeanBox, the properties sheet displays OurButton's properties. To ascertain that serialization is working
The OurButton instance will appear in the BeanBox with your property changes intact. By implementing Serializable in your class, simple, primitive properties and fields can be serialized. For more complex class members, different techniques must be used, as described in the following sections.
- Change some OurButton properties. For example change the font size and colors.
- Serialize the changed OurButton instance by selecting the File|SerializeComponent... BeanBox menu item. A file browser will pop up.
- Put the .ser file in a JAR file with a suitable manifest.
- Clear the BeanBox form by selecting the File|Clear menu item.
- Reload the serialized instance by selecting the File|LoadJar menu item.
Selective Serialization Using the transient Keyword
To exclude fields from serialization in a Serializable object from serialization, mark the fields with the transient modifier.
Default serialization will not serialize transient and static fields.transient int Status;If your serializable class contains either of the following two methods (the signatures must be exact), then the default serialization will not take place.
private void writeObject(java.io.ObjectOutputStream out) throws IOException; private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;You can control how more complex objects are serialized, by writing your own implementations of the writeObject and readObject methods. Implement writeObject when you need to exercise greater control over what gets serialized, when you need to serialize objects that default serialization cannot handle, or when you need to add data to the serialization stream that is not an object data member. Implement readObject to reconstruct the data stream you wrote with writeObject.
Example: The Molecule Demo Bean
The Molecule demo keeps a version number in a static field. Since static fields are not serialized by default, writeObject and readObject are implemented to serialize this field. Here is the writeObject and readObject implementations in Molecule.java:
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.writeInt(ourVersion); s.writeObject(moleculeName); } private void readObject(java.io.ObjectInputStream s) throws java.lang.ClassNotFoundException, java.io.IOException { // Compensate for missing constructor. reset(); if (s.readInt() != ourVersion) { throw new IOException("Molecule.readObject: version mismatch"); } moleculeName = (String) s.readObject(); }These implementations limit the fields serialized to ourVersion and moleculeName. Any other data in the class will not be serialized.
It is best to use the ObjectInputStream's defaultWriteObject and defaultReadObject before doing your own specific stream writing. For example:
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { //First write out defaults s.defaultWriteObject(); //... } private void readObject(java.io.ObjectInputStream s) throws java.lang.ClassNotFoundException, java.io.IOException { //First read in defaults s.defaultReadObject(); //... }
The Externalizable Interface
Use the Externalizable interface when you need complete control over your Bean's serialization (for example, when writing and reading a specific file format). You need to implement two methods: readExternal and writeExternal. Externalizable classes must also have a no-argument constructor.
Example: The BlueButton and OrangeButton Demo Beans
When you run the BeanBox, you will see two Beans named BlueButton and OrangeButton in the ToolBox. These two Beans are actually serialized instances of the ExternalizableButton class.
ExternalizableButton implements the Externalizable interface. This means it does all its own serialization, by implementing Externalizable.readExternal and Externalizable.writeExternal. The BlueButtonWriter program is used by the buttons makefile to create an ExternalizableButton instance, change its background property to blue, and write the Bean out to the file BlueButton.ser. Another button, OrangeButton, is created the same way using OrangeButtonWriter. The button makefile then puts these .ser files in buttons.jar, where the ToolBox can find and reconstitute them.
New JavaBeans Features
Here is a list of upcoming Beans and Beans-related features:Check the JavaBeans web site for the latest information.
- The Java Activation Framework (JAF). The JAF is a data typing and command registry API. With the JAF you can discover an arbitrary data object's type, and look command applications or Beans that can process that data type, and activate the appropriate command at a user gesture. For example, a browser can identify a file's data type, and then launch the appropriate plug-in to view or edit the file. The JAF is a Java standard extension. You can get a copy from the JavaBeans Glasgow web site.
- The Extensible Runtime Containment and Services Protocol, also known as beancontext. Previous to this protocol a Bean only knew about and had access to the Java Virtual Machine (JVM) in which the Bean ran, and the core Java APIs. This protocol introduces a standard way for a Bean to discover and access attributes or services provided by a Bean's enclosing environment, and for a Bean's enclosing environment to discover and access a Bean's attributes and services. The java.beans.beancontext API introduces the ability to nest Beans and Bean contexts within a hierarchical structure. At runtime, a Bean can obtain services from its containing enviroment; the Bean can then make use of those services, or propagate those services to any Beans that it contains.
NEW!:
- Drag and Drop Support. The java.awt.dnd API provides support for drag and drop between Java applications and native platform applications.
The BeanInfo Interface
Use the java.beans.Introspector class to examine Beans and expose their properties, events, and methods. The Introspector class uses the JDK core reflection API to discover a Bean's methods, and then applies the JavaBeans design patterns to discover the Beans features. This discovery process is named introspection.Alternatively, you can explicitly expose a Bean's features in a separate, associated class that implements the BeanInfo interface. By associating a BeanInfo class with your Bean, you can:
- Expose only those features you want to expose.
- Rely on BeanInfo to expose some Bean features while relying on low-level reflection to expose others.
- Associate an icon with the target Bean.
- Specify a customizer class.
- Segregate features into normal and expert categories.
- Provide a more descriptive display name, or additional information about a Bean feature.
BeanInfo defines methods that return descriptors for each property, method, or event that you want exposed. Here's the prototypes for these methods:
Each of these methods returns an array of descriptors for each feature.PropertyDescriptor[] getPropertyDescriptors(); MethodDescriptor[] getMethodDescriptors(); EventSetDescriptor[] getEventSetDescriptors();
Feature Descriptors
BeanInfo classes contain descriptors that precisely describe the target Bean's features. The BDK implements the following descriptor classes:
- FeatureDescriptor is the base class for the other descriptor classes. It declares the aspects common to all descriptor types.
- BeanDescriptor describes the target Bean's class type and name, and describes the target Bean's customizer class if it exists.
- PropertyDescriptor describes the target Bean's properties.
- IndexedPropertyDescriptor is a subclass of PropertyDescriptor, and describes the target Bean's indexed properties.
- EventSetDescriptor describes the events the target Bean fires.
- MethodDescriptor describes the target Bean's methods.
- ParameterDescriptor describes method parameters.
The BeanInfo interface declares methods that return arrays of the above descriptors.
Creating a BeanInfo Class
This section uses the ExplicitButtonBeanInfo demo class to illustrate creating a BeanInfo class. Here are the general steps to make a BeanInfo class:
- Name your BeanInfo class. You must append the string "BeanInfo" to the target class name. If the target class name is ExplicitButton, then its associated Bean information class must be named ExplicitButtonBeanInfo
- Subclass SimpleBeanInfo. This is a convenience class that implements BeanInfo methods to return null, or an equivalent no-op value.
Using SimpleBeanInfo saves you from implementing all the BeanInfo methods; you only have to override those methods you need.public class ExplicitButtonBeanInfo extends SimpleBeanInfo {
- Override the appropriate methods to return the properties, methods, or events that you want exposed. ExplicitButtonBeanInfo overrides the getPropertyDescriptors() method to return four properties:
public PropertyDescriptor[] getPropertyDescriptors() { try { PropertyDescriptor background = new PropertyDescriptor("background", beanClass); PropertyDescriptor foreground = new PropertyDescriptor("foreground", beanClass); PropertyDescriptor font = new PropertyDescriptor("font", beanClass); PropertyDescriptor label = new PropertyDescriptor("label", beanClass); background.setBound(true); foreground.setBound(true); font.setBound(true); label.setBound(true); PropertyDescriptor rv[] = {background, foreground, font, label}; return rv; } catch (IntrospectionException e) { throw new Error(e.toString()); } }There are two important things to note here:
- If you leave a descriptor out, that property, event or method will not be exposed. In other words, you can selectively expose properties, events, or methods by leaving out those you don't want exposed.
- If a feature's getter (for example, getMethodDescriptor()) method returns null, low-level reflection is then used for that feature. This means, for example, that you can explicitly specify properties, and let low-level reflection discover the methods. If you don't override the SimpleBeanInfo default method, which returns null, low-level reflection will be used for that feature.
- Optionally associate an icon with the target Bean.
The BeanBox displays this icon next to the Bean name in the ToolBox. You can expect builder tools to do the same.public java.awt.Image getIcon(int iconKind) { if (iconKind == BeanInfo.ICON_MONO_16x16 || iconKind == BeanInfo.ICON_COLOR_16x16 ) { java.awt.Image img = loadImage("ExplicitButtonIcon16.gif"); return img; } if (iconKind == BeanInfo.ICON_MONO_32x32 || iconKind == BeanInfo.ICON_COLOR_32x32 ) { java.awt.Image img = loadImage("ExplicitButtonIcon32.gif"); return img; } return null; }
- Specify the target Bean class, and, if the Bean has a customizer, specify it also.
public BeanDescriptor getBeanDescriptor() { return new BeanDescriptor(beanClass, customizerClass); } ... private final static Class beanClass = ExplicitButton.class; private final static Class customizerClass = OurButtonCustomizer.class;Keep the BeanInfo class in the same directory as its target class. The BeanBox first searches for a target Bean's BeanInfo class in the target Bean's package path. If no BeanInfo is found, then the Bean information package search path (maintained by the Introspector) is searched. The default Bean information search path is sun.beans.infos. If no BeanInfo class is found, then low-level reflection is used to discover a Bean's features.
Using BeanInfo to Control What Features are Exposed
If you rely on low-level reflection to discover your Bean's features, all those properties, methods, and events that conform to the appropriate design patterns will be exposed in a builder tool. This includes any features in all base classes. If the BeanBox finds an associated BeanInfo class, then that information is used instead, and no more base classes are examined using reflection. In other words, BeanInfo information overrides low-level reflection information, and prevents base class examination.
By using a BeanInfo class, you can expose subsets of a particular Bean feature. For example, by not returning a method descriptor for a particular method, that method will not be exposed in a builder tool.
When you use a BeanInfo class:
- Base class features will not be exposed. You can retrieve base class features by using the BeanInfo.getAdditionalBeanInfo method.
- Properties, events, or methods that have no descriptor will not be exposed. For a particular feature, only those items returned in the descriptor array will be exposed. For example, if you return descriptors for all your Bean methods except foo, then foo will not be exposed.
- Low-level reflection will be used for features with getter methods returning null. For example if your BeanInfo class contains this method implementation:
public MethodDescriptor[] getMethodDescriptors() { return null; }Then low-level reflection will be used to discover your Bean's public methods.
Locating BeanInfo Classes
Before examining a Bean, the Introspector will attempt to find a BeanInfo class associated with the Bean. By default, the Introspector takes the target Bean's fully qualified package name, and appends "BeanInfo" to form a new class name. For example, if the target Bean is sunw.demo.buttons.ExplicitButton, then the Introspector will attempt to locate sunw.demo.buttons.ExplicitButtonBeanInfo.
If that fails, then each package in the BeanInfo search path is searched. The BeanInfo search path is maintained by Introspector.setBeanInfoSearchPath() and Introspector.getBeanInfoSearchPath().
See Also
JavaBeans API Specification
Core Reflection Documentation:
Beans API Documentation:
- BeanInfo
- SimpleBeanInfo
- Introspector
- FeatureDescriptor
- BeanDescriptor
- EventSetDescriptor
- PropertyDescriptor
- IndexedPropertyDescriptor
- MethodDescriptor
- ParameterDescriptor
Bean Customization
A Bean's appearance and behavior can be customized at design time within Beans-compliant builder tools. Typically there are two ways to customize a Bean:
- By using a property editor. Each Bean property has its own property editor. A builder tool usually displays a Bean's property editors in a property sheet. A property editor is associated with, and edits a particular property type.
- By using customizers. Customizers give you complete GUI control over Bean customization. Customizers are used where property editors are not practical or applicable. Unlike a property editor, which is associated with a property, a customizer is associated with a Bean.
Property Editors
A property editor is a tool for customizing a particular property type. Property editors are displayed in, or activated from property sheets. A property sheet will determine a property's type, search for a relevant property editor, and display the property's current value in a relevant way.
Property editors must implement the PropertyEditor interface. PropertyEditor provides methods that specify how a property should be displayed in a property sheet.
Here is the BeanBox's Properties sheet containing OurButton properties:
You begin the process of editing these properties by clicking on the property entry in the sheet.
- The label and fontSize properties are displayed in an editable text box. Changes can be made in place.
- The largeFont and debug properties are selection boxes with discrete choices.
- Clicking on the foreground, background, and font entries brings up separate panels.
How each of these is displayed depends on which PropertyEditor methods you implement to return non-null (or equivalent) values.
For example, the int property editor implements the setAsText method. This indicates to the property sheet that the property can be displayed as a String, hence an editable text box will be used.
The Color and Font property editors use a separate panel, and merely use the property sheet to display the current property value. The editor is displayed by clicking on that value.
To display the current property value "sample" within the property sheet you need to override isPaintable to return true, and override paintValue to paint the current property value in a rectangle in the property sheet. Here's how ColorEditor implements paintValue:
public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) { Color oldColor = gfx.getColor(); gfx.setColor(Color.black); gfx.drawRect(box.x, box.y, box.width-3, box.height-3); gfx.setColor(color); gfx.fillRect(box.x+1, box.y+1, box.width-4, box.height-4); gfx.setColor(oldColor); }To support the custom property editor, you need to override two more methods: Override supportsCustomEditor to return true, and override getCustomEditor to return a custom editor instance. ColorEditor.getCustomEditor returns this.
Additionally, the PropertyEditorSupport class maintains a PropertyChangeListener list, and fires property change event notifications to those listeners when a bound property is changed.
How Property Editors are Associated with Properties
Property editors are discovered and associated with a given property by
- Explicit association via a BeanInfo object. The Molecule demo Bean uses this technique. Within the MoleculeBeanInfo class, the Molecule Bean's property editor is set with the following line of code:
pd.setPropertyEditorClass(MoleculeNameEditor.class);
- Explicit registration via java.Beans.PropertyEditorManager.registerEditor. This method takes a pair of arguments: The class type, and the editor to be associated with that type.
- Name search. If a class has no explicitly associated property editor, then the PropertyEditorManager searchs for that class's property editor by:
- Appending "Editor" to the fully qualified class name. For example, for the java.beans.ComplexNumber class, the property editor manager would search for the java.beans.ComplexNumberEditor class.
- Appending "Editor" to the class name and searching a class search path. The default class path for the BeanBox is sun.beans.editors.
The BDK Property Editors
The BDK provides property editors for the primitive data types like int, boolean, and float, and Color and Font class types. The source code for these property editors is in beans/apis/sun/beans/editors. These sources make a good starting point for writing your own property editors. Some things to note about the BDK property editors:
- All the "number" properties are represented as String objects. The IntEditor overrides PropertyEditorSupport.setAsText.
- The boolean property editor is a menu of discrete choices that overrides the PropertyEditorSupport.getTags method to return a String[] containing "True" and "False":
public String[] getTags() { String result[] = { "True", "False" }; return result; }
- The Color and Font property editors implement custom property editors. Because these objects require a more sophisticated interface to be easily edited a separate component pops up to do the property editing. Overriding supportsCustomEditor to return true signals the property sheet that this property's editor is a custom component. The isPaintable and paintValue methods are also overridden to provide color and font painting in the editors property sheet sample areas.
Note that if no property editor is found for a property, the BeanBox will not display that property in the Properties sheet.
Customizers
When you use a Bean Customizer, you get complete control over how to configure or edit a Bean. A Customizer is like an application that specifically targets a Bean's customization. Sometimes properties are insufficient for representing a Bean's configurable attributes. Customizers are used where sophisticated instructions would be needed to change a Bean, and where property editors are too primitive to achieve Bean customization.
All customizers must:
- Extend java.awt.Component or one of its subclasses.
- Implement the java.beans.Customizer interface This means implementing methods to register PropertyChangeListener objects, and firing property change events at those listeners when a change to the target Bean has occurred.
- Implement a default constructor.
- Associate the customizer with its target class via BeanInfo.getBeanDescriptor.
If a Bean that has an associated Customizer is dropped into the BeanBox, you will notice a "Customize..." item on the Edit menu.
BDK Customizers
The OurButtonCustomizer serves as an example that demonstrates the mechanics of building a customizer. OurButtonCustomizer:
- Extends java.awt.Panel (a Component subclass).
- Implements the Customizer interface, and uses a PropertyChangeSupport object to manage PropertyChangeListener registration and notification. See the bound property section for a PropertyChangeSupport description.
- Implements a default constructor:
public OurButtonCustomizer() { setLayout(null); }
- Is associated with its target class, ExplicitButton, by the following ExplicitButtonBeanInfo code:
public BeanDescriptor getBeanDescriptor() { return new BeanDescriptor(beanClass, customizerClass); } ... private final static Class customizerClass = OurButtonCustomizer.class;
The BridgeTester and JDBC Select demo Beans also have customizers.