HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

JavaBeans, now in its third year, is proving to be a powerful component model. Whether it's the Java e-commerce framework or the Java platform for the enterprise, JavaBeans is at the heart of many new and exciting technologies. The JavaBeans model provides a framework to build, customize, run and deploy Java software components. While there are numerous books and articles on JavaBeans, not many of them deal with the customization aspect of JavaBeans.

An object that conforms to the JavaBeans specs is called a JavaBean or, more generally, a bean, which at runtime is like any other object. What distinguishes it from other Java objects is that it can be manipulated with visual builder tools. This process includes customization and connection, which means that you can visually customize beans and connect them to assemble an application.

Let's discuss customization using the Pie Chart bean (part of the visualization bean suite developed for my book, The Awesome Power of JavaBeans) shown in Figure 1. If you want to use this bean, you'll probably customize it to suit your application, which may include changing the size and position of the pie as well as the location of the legends. You can customize the bean visually by using a bean builder tool. Once you customize it, you may want to save the configuration for future use by using the Java serialization feature. Sun's BeanBox, which comes with the BDK, is an example of a visual builder tool that allows you to customize, serialize and connect beans. Most of the Java IDEs provide similar tools.

Figure 1
Figure 1:

Property Sheets
To customize a bean visually, you need a tool to view and edit its properties —builder tools typically provide property sheets for this purpose. Figure 2 shows the property sheet generated by the BeanBox for the Pie Chart bean. Every time you insert a bean or click on the existing bean in the BeanBox frame, the BeanBox generates a new property sheet. To generate a property sheet, the builder tool obtains the property names and their valuesfrom the bean using a mechanism called introspection.

Figure 2
Figure 2:

Introspection
According to the JavaBean specs, three basic features describe a bean: properties, methods and events. The introspection mechanism analyzes the bean classes to obtain these features. A bean author can help the introspection process by providing a design-time class, BeanInfo, that is specific to a runtime bean class. For example, PieChartBeanInfo is the BeanInfo class for PieChart. Bean authors can provide descriptions of bean features in the BeanInfo class. In the case of a property, its description may include display name, access methods and short text.

How does introspection work? The process first checks the BeanInfo class to obtain a bean's feature (property, method and event) descriptions. If the bean author doesn't provide any information about properties (or other features) in the BeanInfo class, the introspection process uses the low-level reflection API (java.lang.reflect and java.lang.Class) to obtain it. Using these APIs, it probes the bean class to obtain its fields and methods, then deduces a bean's feature descriptions by matching the method signatures with the naming conventions defined by the JavaBeans specs.

The naming conventions for properties define the signatures for setter and getter methods. The former sets the property and the latter reads it. Here is a naming convention defined for a simple property:

  • The setter method: public void set(PropertyType t)

  • The getter method: public <Property Type> get<PropertyName) {}
The following example shows the setter and getter methods for the Graph Color property:

public void setGraphColor(Color color){//code } public Color getGraphColor(){// code}

The other types of properties, which include indexed and boolean, have similar naming conventions.

Editing Properties
Once the builder tool obtains the exposable properties, it creates a suitable edit field for each one. For example, the edit field for the plotTitle property in the Pie Chart bean is a text field (TextField or JTextField). Text fields are adequate as long as a property is simple, that is, of Java primitive type (short, int, byte, char, etc.). When a property is of enumerated type, the property sheets typically provide combo boxes (Choice in AWT; JComboBox in JFC).

To edit a property, each property type needs to be associated with a property editor. For simple properties, a property value can be expressed as a text string. The property editor converts the property value obtained from the bean to a text string so as to display it in the text field. When the user inputs a value in the text field, it converts the inputted text string to the corresponding primitive type.

When you start writing real-world beans, you'll realize that Java primitive types aren't always adequate to represent different types of bean attributes. Take the background and foreground colors in the Pie Chart bean, for example. In this case the property type is Color, which is a class in the AWT. You can't enter a color name in the edit field because the text type property editor can't convert a color name to a Color object. What you need is a special property editor that does this conversion. Builder tools often provide custom-written property editors for commonly used properties such as Font and Color. If the property type is a class defined by the bean author, then she/he needs to write a property editor for that class.

Property Sheet Limitations
While the property sheet concept is a vast improvement over manual customization (through programming), it's inadequate to customize complex beans. Here are some limitations:

  • Bean authors can't choose the GUI components they like for editing properties. Consider the Pie Chart bean example again. Let's say you want to move the pie to the right. You can do so by entering a value in the Pie Center X Increment field. It's hard to guess the right increment the first time. Before you get the pie position right, you typically enter the increment values a few times. For this kind of operation you'd probably prefer a different component to move the pie.
  • Property sheets are static. Edit fields for properties can't be added to or removed from the sheet depending on the value entered in another edit field.
  • Notice that the property sheet for the Pie Chart bean displays its properties in a random order. Before you start editing the properties, you may have to mentally group the properties along the functional lines. This problem gets worse when there are a large number of properties. Imagine a bean that has more than a hundred properties - not uncommon in complex beans. You'd have to scroll the property sheet many times to get an overall picture. Even with the Pie Chart bean, which has fewer properties, customizing is easier when properties are grouped together.
  • It's not always easy to capture the semantics of the customization process just by looking at the property sheet. Sometimes the process may require properties to be entered in a certain order.
  • You often have to configure a bean at runtime. But property sheets are available at design time only because a builder tool is needed to construct the sheet.
Customizers
The JavaBeans model provides an alternative to property sheets. It specifies an interface called Customizer to enable bean authors to implement a bean-specific customizer. Such a customizer can be a simple panel with related properties grouped together or a sophisticated wizard that allows the user to navigate through a hierarchy of screens. Since builder tools recognize any customizer that implements the Customizer interface, it can be launched from any builder tool that has the bean installed.

Customizer Interface
The Customizer interface is simple and has only three methods:

  1. setObject(Object bean): The builder tool calls this method to pass the target bean instance to the customizer. It is called only once, which is before the builder tool launches the customizer.
  2. addPropertyChangeListener(PropertyChangeListener listener): This method adds a propertyChange event listener. By registering as a listener, the target object(s) (typically the builder tool and property sheet at design time or bean or bean context at runtime) can receive notification and the modified value of the property. When the target object receives a propertyChange event, it retrieves the setter Method object and the property value from the PropertyChangeEvent object. Using this Method object and the property value, the target object then invokes the setter method in the bean to modify the property.
  3. removePropertyChangeListener (PropertyChangeListener listener): This removes a property change event listener.
Builder Tool Interaction
To run the customizer at design time, the builder tool first fetches the customizer class from the BeanInfo class associated with a bean. Using the customizer class, it creates an instance of the customizer object by calling the newInstance() method. The builder tool then embeds the customizer object in a dialog box and launches it.

Before the builder tool launches a customizer, it calls the setObject(Object bean) method in the customizer object and passes the bean instance as the parameter. It also registers for the propertyChange events. The customizer has to fire these events whenever a property is modified. When the customizer fires the event, it sends the new property value encapsulated in the PropertyChangeEvent object.

Designing Customizers
Choosing the right graphical user interface for presenting and editing properties is an important part of the design. Besides being user-friendly and interactive, the GUI should capture the semantics of customization. In other words, it should be intuitive for a user to start using it just by looking at the GUI.

If you intend to provide a customizer for your bean, start the customizer design while you're designing the bean itself. Design your bean's properties to be compatible with the customizer GUI you have in mind. For example, I wanted to have "+" and "-" buttons to increment and decrement the pie size in the Pie Chart bean (see Figure 3). I originally intended to have just one property, Pie Diameter, to represent the pie size. In order to develop a more interactive customizer GUI, I created one more property, Pie Diameter Increment, to represent the increments or decrements relative to Pie Diameter. Thus, when you click the "+" button, the pie diameter is incremented by a certain amount. Likewise, when you click the "-" button, it is decremented by the same amount. With the Pie Position property, I added two more properties: Pie Position X Increment and Pie Position Y Increment to represent the increments (decrements) to the pie position in X direction and Y direction, respectively.

Figure 3
Figure 3:

Figure 3 shows the Pie Chart bean customizer, which provides a better graphical interface compared to its property sheet. You can adjust the size and position of the pie smoothly by clicking the "+" and "-" buttons. Similarly, you can adjust the position of the legend by clicking the appropriate "+" and "-" buttons.

Implementing a Customizer
Once you design the customizer GUI, implementing the customizer class is just a matter of writing the code for the user interface and reading/writing properties. Building a sophisticated customizer may require a lot of programming, but you can develop it in such a way that most of the code can be reused.

A customizer class has to meet the following requirements to enable builder tools to launch it:

  1. It must implement the Customizer interface. This is to enable the builder tool to recognize the customizer and to obtain the bean instance, which can be passed on to the customizer. Using this bean instance, the customizer can fetch and modify a bean's properties.
  2. It has to be a subclass (direct or derived) of the java.awt.Component class. This is to enable builder tools to embed the customizer in a dialog box.
  3. It must have a constructor with no arguments. This is to enable the builder tool to launch it. If constructors with arguments are allowed, builder tools wouldn't know which constructor to call (in case there are many) or what values to pass as parameters.

The customizer UI will have different types of GUI components depending on your design. The event-handling code for these components should fetch and set property values. Since the customizer object gets the bean instance, it can directly call any public methods in the bean. This means that the customizer can directly invoke the getter and setter method to get and set a property in the bean. While you can build a customizer easily this way, it's not a good solution for the following reasons:

  • Property changes in the customizer aren't reflected in the property sheet. In the majority of bean builder tools, when the customizer is launched, the property sheet doesn't go away. Changes made to the property in the customizer have to be reflected in the property sheet as well because the customizer can't access the property sheet directly.
  • Code isn't reusable because getter and setter methods are used explicitly.
  • Code may require maintenance as you may need to change it in a number of places if a property name or type changes.
An appropriate solution would be to use the reflection API to obtain the getter and setter methods of a property and fire the propertyChange events to set its value. The Pie Chart bean customizer uses this approach.

Pie Chart Customizer
The Pie Chart bean is part of a plotter bean suite. The other beans in this suite include XY Plot, Histogram, Line Chart and Bar Chart beans. The plotter bean customizers have many similarities. To capture the behavior common to all the chart bean customizers, I developed two common classes: CustomizerImpl and CustomizerUtil. The former implements the Customizer interface and the latter has a number of factory methods that create edit fields for different types of properties. The Pie Chart bean customizer class extends the CustomizerImpl class and uses the methods in CustomizerUtil class. The same is true for other plotter beans.

CustomizerImpl Class
As mentioned before, the customizer component is expected to be contained in a dialog box, so a customizer class can't be a subclass of Frame (JFrame) or Dialog (JDialog). Furthermore, the customizer class should be a container because it needs to house a number of components for editing. The logical choice would be to subclass Panel (JPanel). The CustomizerImpl class shown in Listing 1 does just that. It extends JPanel and implements the Customizer interface.

As the listing shows, the CustomizerImpl class has three instance variables:

  1. bean: Holds the target bean instance. Since the setObject() method is called only once, this variable saves the bean instance.
  2. pcs: Holds the PropertyChangeSupport instance. The propertyChange event registration methods call the corresponding methods in pcs.
  3. title: Holds the customizer title. The addNotify() method, which is called only when the component receives a parent, sets the customizer title in the parent.
The instance variables, bean and pcs, are set in the setObject() method. The createGUI() method creates the GUI components and is called only after the target bean instance is received. It is an abstract method, which means that subclasses of CustomizerImpl provide the actual code.

CustomizerUtil Class
The CustomizerUtil class has a number of utility methods that help to create edit field components, which include JTextField and JComboBox. This class uses the reflection API to get and set a property value.

As an example, see Listing 2, which shows the createJTextField() method. This method first constructs a JTextField component. It then obtains the actual property value from the bean by calling the getProperty() method (see Listing 3). Using the property name string, getProperty() method first constructs the Method object for the property's getter method. The Method object executes invoke() to invoke the getter method.

The createJTextField() method also has the code to set the property. When the user types a value and hits Enter, the JTextField fires an action event. To handle this event, createJTextField() registers with the JTextField to receive action events. The event-handling code for the action event is in an anonymous class in which the actionPerformed() method first fetches the text entered by the user from the JTextField and converts the value to an appropriate object. It then calls the firePropertyChangeEvent() method in pcs.

Listing 4 shows the code to create "+" and "-" buttons for incrementing and decrementing a property. This method uses a highly reusable class, IncrButtonAdapter, which is an adapter class for the action event (see Listing 5). An IncrButtonAdapter instance is constructed for each button. The property name and increment intervals are passed as arguments to this constructor. The actionPerformed() method, which is called whenever the increment button is clicked, first fetches the property value from the bean, then adds the increment to this value and calls the firePropertyChangeEvent() in pcs. The IncrButtonAdapter class is used by pie and legend properties in the Pie Chart bean customizer.

PieChartCustomizer Class
Listing 6 shows part of the PieChartCustomizer class, which extends the CustomizerImpl class. It overrides the createGUI() method, which calls createLabelsPanel() and createColorPanel() methods to create labels and color panels. It also calls the createIncrPanel() method to create increment panels for pies and legends. As you can see from the listing, these methods call static methods in the CustomizerUtil class to create components for editing.

Registering the Customizer
The customizer has to be registered for the builder tool to launch it. The BeanDescriptor class is the only class that holds the customizer class. You can set the customizer class by using one of its constructors. The BeanDescriptor class has two constructors:

  • BeanDescriptor(Class beanClass)
  • BeanDescriptor(Class beanClass, Class customizerClass)
The second constructor is used to register the customizer. If you use the first constructor, the builder tool assumes that the bean has no customizer. The code to register the customizer goes in the BeanInfo class. The example below shows how the Pie Chart bean customizer is registered. This code is taken from the PieChartBeanInfo class.

public BeanDescriptor getBeanDescriptor(){
BeanDescriptor bd = new BeanDescriptor(PieChart.class, PieChartCustomizer.class);
// Other stuff
return bd;
}

A bean's own BeanInfo class is the only place where you can register a customizer. This means that building a BeanInfo class for that bean class is a must. This is because builder tools won't recognize the customizer even if the customizer is registered in a bean's superclass BeanInfo.

Customizer at Runtime
Although the availability of customizers at runtime is often desirable, the JavaBeans specification makes no mention of runtime customizers. You can provide this facility, however, by adding a few more methods to your bean. The program that launches the customizer can reside in the bean itself or in the bean context (container).

To launch the customizer, you can use the button, pop-up menu or any suitable component depending on your bean and the application. The Graphics Viewport bean shown in Figure 4 provides a pop-up menu that can be triggered by right-clicking. (You can download this bean from my Web site at www.execpc.com/~larryhr/gvbean.html and use it to draw a variety of shapes and text.) This menu contains the items customize and serialize.

Figure 4
Figure 4:

The customizer class itself is a property in the Graphics Viewport bean. This means that the class can be set by the setter method and fetched by the getter method. The bean context or application that uses the Graphics Viewport bean can call these methods to register and launch the customizer.

Listing 7 shows the event-handling code (which is in the Graphics Viewport bean itself) for the Customize menu item. As you can see, the actionPerformed() method first fetches the customizer class and passes it as a parameter to launchCustomizer() to launch the customizer.

Launching the Customizer
Listing 8 shows the launchCustomizer() method, which:

  1. Creates an instance of the customizer object
  2. Calls setObject() method to pass the bean instance to the customizer
  3. Registers for the propertyChange event
In the event-handling method it retrieves the property values from the PropertyChangeEvent object and calls the setProperty() method (see Listing 9). This method uses the Method class to invoke the setter method.

Bean as a Standalone Application
Figure 5 shows the customizer for the Graphics Viewport bean. This customizer is much more comprehensive than the Pie Chart customizer. With it you can set the drawing parameters and also draw shapes and text. Since this customizer can be launched at runtime, you can use the Graphics Viewport bean as a standalone application.

Figure 5
Figure 5:

To make a bean run as a standalone application, you need to add the main() method. Starting with Java 2, a jar file can be made executable. This means that you can run a bean that has the main() method by executing its JAR file. For example, to run the Graphics Viewport bean, just type java -jar grbean.jar.

To make a JAR executable, you need to include an attribute called Main-Class in the JAR manifest file. Following is the example from the grbean.jar manifest file.

Main-Class: com.vistech.viewport.GraphicsViewport

Name: com/vistech/viewport/GraphicsViewport.class
Java-Bean: True

This brings up another issue: Should the runtime and design-time customizers be the same? There is nothing to prevent you from having two customizers - one for design time and one for runtime. You can use the same Customizer interface to implement these customizers.

Conclusion
Property sheets are simple and don't require a lot of programming effort. They're inadequate, however, when beans are complex and have a large number of properties. We can overcome most of the limitations of the property sheet by building a special-purpose customizer. The Customizer interface is a simple but powerful API that can be used to develop user-friendly customizers to configure complicated beans. However, writing a good customizer requires a lot of programming effort. Unlike property editors, which are property specific, a customizer is specific to a bean. So if you make changes to the target bean, you may have to modify the customizer as well.

References

  1. Rodrigues, L. (1998). The Awesome Power of JavaBeans. Manning Publications.
  2. Sun Microsystems, JavaBeans API Specification Version 1.01, July 1997.
  3. Rodrigues, L. (1997). "Java, The Next Generation: JavaBeans." Java Developer's Journal, Vol. 2, Issue 1, January.
About the Author
Lawrence Rodrigues, author of The Awesome Power of Java Beans, is the lead architect at a large industrial automation company. You can reach him through his "Bean man's home page" at www.execpc.com/~larryhr or via e-mail at [email protected]

	

Listing 1. The CustomizerImpl class.
 
abstract public class CustomizerImpl extends 
         JPanel implements Customizer{ 
  public Object bean; 
  public PropertyChangeSupport pcs; 
  public String title = "Customizer"; 
  //This method is called by the builder tool to 
  //pass object instance to the target bean. 
  public void setObject(Object bnObj){ 
   bean = bnObj; 
   pcs = new PropertyChangeSupport(bean); 
   createGUI(); 
  } 
  
  //Title read/write methods 
  public void setTitle(String title){this.title = title;} 
  public String getTitle(){return title;} 

  //This method is overriden so as to set the customizer title. 
  public void addNotify(){ 
   super.addNotify(); 
   Component cmp = getParent(); 
   if(cmp instanceof Dialog) ((Dialog)cmp).setTitle(title); 
   if(cmp instanceof JFrame) ((JFrame)cmp).setTitle(title); 
   if(cmp instanceof JDialog)((JDialog)cmp).setTitle(title); 
  } 

  //Adds property change event listeners 
  public void addPropertyChangeListener(PropertyChangeListener p){ 
   //Delegate the event firing job to pcs. 
   pcs.addPropertyChangeListener(p); 
  } 

  //Removes property change listeners 
  public void removePropertyChangeListener(PropertyChangeListener p){ 
   pcs.removePropertyChangeListener(p); 
  } 

  //All the subclasses have to implement this method. 
  abstract protected void createGUI(); 
} 

Listing 2. Creating a JTextField component.
 
/* Creates an edit field for any primitive type of property. 
 * Uses the relection API to invoke the getter method. Since the 
 * JTextField’s input and output is only text, the property 
 * type is needed to convert its input and output. 
 * @param obj the bean object. 
 * @param pc the property change support object. 
 * @param name the property name. 
 * @param clType the property class type. 
 */ 
public static JTextField createJTextField(Object obj, 
                     PropertyChangeSupport pc, 
                     String name, 
                     Class clType){ 
  final Object bean = obj; 
  final PropertyChangeSupport pcs = pc; 
  final String propName = name; 
  final Class type = clType; 
  JTextField jtf = new JTextField(5); 
  String str = "get"+propName; 
  //Get the current property value as an object 
  Object probject= getProperty(bean, propName); 
  //Display it 
  jtf.setText(probject.toString()); 
  jtf.addActionListener( 
   new ActionListener() { 
    public void actionPerformed(ActionEvent e){ 
      try{ 
       //Get the modified property as a text string 
       String txt =((JTextField)(e.getSource())).getText(); 
       //Convert the text string to actual property value. 
       Object propValue = convertStringToObject(type,txt); 
       //delegates firing of property change event to pcs 
       pcs.firePropertyChange(propName, null, propValue); 
      }catch(Exception x){ x.printStackTrace(); } 
    } 
   } 
  ); 
  return jtf; 
} 

Listing 3. Getting the property value.
 
/** This method fetches the current value of a property. 
 * @param bean the target bean. 
 * @param propName the prperty name. 
 */ 
public static Object getProperty(Object bean, 
                 String propName){ 
  try{ 
    Class bcl = bean.getClass(); 
    //Construct the "getter" method name. 
    String str = "get"+propName; 
    //First create the Method object for the "getter" method 
    //No arguments for getter method 
    Method getM = bcl.getMethod(str, null); 
    //Use the Method object to invoke the "getter"method 
    Object args[] = { }; 
    return getM.invoke(bean, args); 
  } catch(Exception e){e.printStackTrace();} 
} 

Listing 4. Creating an increment/decrement button panel.
 
/** Creates a panel with "+" and "-" buttons. 
 * @param prName the property name. 
 * @param JLabelStr the JLabel string. 
 */ 
public static JPanel createIncrPanel(Object target, 
                   PropertyChangeSupport pc, 
                   String prName, 
                   String JLabelStr){ 
  JButton incr = new JButton("+"); 
  IncrButtonAdapter incrada = new IncrButtonAdapter(target, 
                           pc, 
                           prName, 2); 
  incr.addActionListener(incrada); 

  JButton decr = new JButton("-"); 
  IncrButtonAdapter decrada = new IncrButtonAdapter(target, 
                           pc, 
                           prName, -2); 
  decr.addActionListener(decrada); 

  JLabel inclab = new JLabel(JLabelStr); 
  JPanel pan = new JPanel(); 
  pan.add(inclab); 
  pan.add(incr); 
  pan.add(decr); 
  return pan; 
} 

Listing 5. The adapter class for "+" and "-" buttons.
 
//This is an inner class in CustomizerUtil.java. 
static class IncrButtonAdapter implements ActionListener{ 
  int increment; 
  String propName; 
  Object bean; 
  PropertyChangeSupport psc; 
  public IncrButtonAdapter(Object target, 
              PropertyChangeSupport pc, 
              String pr, 
              int incr) { 
   bean = target; 
   psc = pc; 
   increment = incr; 
   propName = pr; 
  } 
  
  public void actionPerformed(ActionEvent e){ 
   try{ 
    Integer num = (Integer)getProperty(bean,propName); 
    Object invarg[] = {new Integer(num.intValue()+ increment)}; 
    psc.firePropertyChange(propName, null, invarg[0]); 
   } catch(Exception x){System.out.println(x);} 
  } 
} 

Listing 6. The PieChartCustomizer class
 
public class PieChartCustomizer extends CustomizerImpl{ 
  public PieChart target; 
  public PieChartCustomizer(){setTitle("Pie Chart Customizer");} 
  protected void createGUI(){ 
   target = (PieChart)bean; 
   JPanel pan = new JPanel(); 
   JPanel colorspan = createColorsPanel(); 
   JPanel JLabelspan = createLabelsPanel(); 
   JPanel incrpan = new JPanel(); 
   JPanel diapan = CustomizerUtil.createIncrPanel( 
                         target, 
                         pcs, 
                         "PieDiaIncr", 
                         "Diameter"); 
   JPanel centpan = CustomizerUtil.createXYIncrPanel( 
                         target, 
                         pcs, 
                         "PieCenterXIncr", 
                         "PieCenterYIncr", 
                         "Center"); 
   incrpan.add(diapan); 
   incrpan.add(centpan); 
   incrpan.setBorder(BorderFactory.createTitledBorder("Pie")); 

   JPanel legpan = new JPanel(); 
   JPanel legpospan = CustomizerUtil.createXYIncrPanel( 
                         target, 
                         pcs, 
                         "LegendPosXIncr", 
                         "LegendPosYIncr", 
                         "Position"); 
   JPanel leggappan = CustomizerUtil.createIncrPanel( 
                         target, 
                         pcs, 
                         "LegendGapIncr", 
                         "Gap"); 
   legpan.add(leggappan); 
   legpan.add(legpospan); 
   legpan.setBorder(BorderFactory.createTitledBorder("Legend")); 
   // Gridbag Layout code 
   // ... 
  } 

  // Creates a panel for the colors. 
  public JPanel createColorsPanel(){ 
   JLabel bklab = new JLabel("Background"); 
   JComboBox bk = CustomizerUtil.createColorComboBox(target, 
                            pcs, 
                            "Background"); 
   JLabel fglab = new JLabel("Foreground"); 
   JComboBox fg = CustomizerUtil.createColorComboBox(target, 
                            pcs, 
                            "Foreground"); 
   Component comp[] = {bklab,bk,fglab,fg}; 
   JPanel pan = CustomizerUtil.doGridbagLayout(comp, 2); 
   pan.setBorder(BorderFactory.createTitledBorder("Colors")); 
   return pan; 
  } 
  //Creates a panel for the lables. 
  public JPanel createLabelsPanel(){ 
   JLabel tlab = new JLabel("Title"); 
   JTextField tl = CustomizerUtil.createJTextField(target,pcs, 
                           "TitleString", 
                           String.class); 
   Component comp[] = {tlab,tl}; 
   JPanel pan = CustomizerUtil.doGridbagLayout(comp,2); 
   pan.setBorder(BorderFactory.createTitledBorder("Labels")); 
   return pan; 
  } 
} 

Listing 7. The pop-up menu event handling code.
 
JMenuItem custitem = new JMenuItem("Customize"); 
custitem.addActionListener( 
  new ActionListener(){ 
   public void actionPerformed(ActionEvent e){ 
    try{ 
      Class cust = getCustomizerClass(); 
      launchCustomizer(cust); 
    } catch(Exception e1) {} 
   } 
  } 
); 

Listing 8. Launching a custimizer at runtime.
 
protected void launchCustomizer(Class cl) 
   throws InstantiationException, IllegalAccessException { 
  final Object bean = this; 
  Component cmp = (Component)cl.newInstance(); 
  if(!(cmp instanceof java.beans.Customizer)) return; 
  Customizer customizer = ((java.beans.Customizer)cmp); 
  customizer.setObject(bean); 
  customizer.addPropertyChangeListener( 
   new PropertyChangeListener() { 
     public void propertyChange(PropertyChangeEvent e) { 
      Object newVal = e.getNewValue(); 
      String propName = e.getPropertyName(); 
      CustomizerUtil.setProperty(bean, propName, newVal); 
     } 
   } 
  ); 
  //rest of the code 
  // ... 
} 

Listing 9. Setting the value of a property.
 
/** This method sets the value of a property. 
 * @param bean the target object 
 * @param propName the property name 
 * @param propValue the value of the property as text. 
 */ 
public static void setProperty(Object bean, 
              String propName, 
              Object propValue){ 
  try{ 
    Class bcl = bean.getClass(); 
    //Construct the "setter" method. 
    String str = "set"+propName; 
    //Create the parameter Class objects 
    Class cls = propValue.getClass(); 
    Class type = getType(propValue); 
    Class[] paraobj = {type}; 
    //Create the "setter" method object. 
    Method setM = bcl.getMethod(str, paraobj); 
    //Construct the argument objects 
    Object args[] = {propValue}; 
    //Invoke the "setter" method in the bean 
    setM.invoke(bean, args); 
  } catch(Exception e){e.printStackTrace();} 
} 




  

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.