Bean Property Types


A sophisticated bean will have lots of different kinds of properties that it should expose in a builder tool for a user to set at design time or get at run time. It can also trigger both standard and custom events. Properties can be as simple as the fileName property that you saw in ImageViewerBean and FileNameBean or as sophisticated as a color value or even an array of data pointswe encounter both of these cases later in this chapter. Furthermore, properties can fire events, as you will see in this section.

Getting the properties of your beans right is probably the most complex part of building a bean because the model is quite rich. The JavaBeans specification allows four types of properties, which we illustrate by various examples.

Simple Properties

A simple property is one that takes a single value such as a string or a number. The fileName property of the ImageViewer is an example of a simple property. Simple properties are easy to program: Just use the set/get naming convention we indicated earlier. For example, if you look at the code in Example 8-1, you can see that all it took to implement a simple string property is the following:

 public void setFileName(String f) {    fileName = f;    image = . . .    repaint(); } public String getFileName() {    if (file == null) return null;    else return file.getPath(); } 

Notice that, as far as the JavaBeans specification is concerned, we also have a read-only property of this bean because we have a method with this signature inside the class

 public Dimension getPreferredSize() 

without a corresponding setPreferredSize method. You would not normally be able to see read-only properties at design time in a property inspector.

Indexed Properties

An indexed property is one that gets or sets an array. A chart bean (see below) would use an indexed property for the data points. With an indexed property, you supply two pairs of get and set methods: one for the array and one for individual entries. They must follow this pattern:


Type[] getPropertyName()
void setPropertyName(Type[] x)
Type getPropertyName(int i)
void setPropertyName(int i, Type x)

Here's an example of the indexed property we use in the chart bean that you will see later in this chapter.

 public double[] getValues() { return values; } public void setValues(double[] v) { values = v; } public double getValues(int i) {    if (0 <= i && i < values.length) return values[i];    return 0; } public void setValues(int i, double value) {    if (0 <= i && i < values.length) values[i] = value; } . . . private double[] values; 

The


setPropertyName(int i, Type x)

method cannot be used to grow the array. To grow the array, you must manually build a new array and then pass it to this method:


setPropertyName(Type[] x)

NOTE

As we write this, NetBeans does not support indexed properties in the property inspector. You see later in this chapter how to overcome this limitation by supplying a custom property editor for arrays.


Bound Properties

Bound properties tell interested listeners that their value has changed. For example, the fileName property in FileNameBean is a bound property. When the file name changes, then ImageViewerBean is automatically notified and it loads the new file.

To implement a bound property, you must implement two mechanisms.

  1. Whenever the value of the property changes, the bean must send a PropertyChange event to all registered listeners. This change can occur when the set method is called or when the program user carries out an action, such as editing text or selecting a file.

  2. To enable interested listeners to register themselves, the bean has to implement the following two methods:

     void addPropertyChangeListener(PropertyChangeListener listener) void removePropertyChangeListener(PropertyChangeListener listener) 

The java.beans package has a convenience class, called PropertyChangeSupport, that manages the listeners for you. To use this convenience class, your bean must have a data field of this class that looks like this:

 private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this); 

You delegate the task of adding and removing property change listeners to that object.

 public void addPropertyChangeListener(PropertyChangeListener listener) {    changeSupport.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) {    changeSupport.removePropertyChangeListener(listener); } 

Whenever the value of the property changes, use the firePropertyChange method of the PropertyChangeSupport object to deliver an event to all the registered listeners. That method has three parameters: the name of the property, the old value, and the new value. For example,

 changeSupport.firePropertyChange("fileName", oldValue, newValue); 

The values must be objects. If the property type is not an object, then you must use an object wrapper. For example,

 changeSupport.firePropertyChange("running", false, true); 

TIP

If your bean extends any Swing class that ultimately extends the JComponent class, then you do not need to implement the addPropertyChangeListener and removePropertyChangeListener methods. These methods are already implemented in the JComponent superclass.

To notify the listeners of a property change, simply call the firePropertyChange method of the JComponent superclass:

 firePropertyChange("propertyName", oldValue, newValue); 

For your convenience, that method is overloaded for the types boolean, byte, char, double, float, int, long, and short. If oldValue and newValue belong to these types, you do not need to use object wrappers.


Other beans that want to be notified when the property value changes must implement the PropertyChangeListener interface. That interface contains only one method:

 void propertyChange(PropertyChangeEvent event) 

The code in the propertyChange method is triggered whenever the property value changes, provided, of course, that you have added the recipient to the property change listeners of the bean that generates the event. The PropertyChangeEvent object encapsulates the old and new value of the property, obtainable with

 Object oldValue = event.getOldValue(); Object newValue = event.getNewValue(); 

If the property type is not a class type, then the returned objects are the usual wrapper types. For example, if a boolean property is changed, then a Boolean is returned and you need to retrieve the Boolean value with the booleanValue method.

Thus, a listening object must follow this model:

 class Listener {    public Listener()    {       bean.addPropertyChangeListener(new          PropertyChangeListener()          {             void propertyChange(PropertyChangeEvent event)             {                Object newValue = event.getNewValue();                . . .             }          });    }    . . . } 

Constrained Properties

A constrained property is constrained by the fact that any listener can "veto" proposed changes, forcing it to revert to the old setting. The Java library contains only a few examples of constrained properties. One of them is the closed property of the JInternalFrame class. If someone tries to call setClosed(true) on an internal frame, then all of its VetoableChangeListeners are notified. If any of them throws a PropertyVetoException, then the closed property is not changed, and the setClosed method throws the same exception. For example, a VetoableChangeListener may veto closing the frame if its contents have not been saved.

To build a constrained property, your bean must have the following two methods to manage VetoableChangeListener objects:

 public void addVetoableChangeListener(VetoableChangeListener listener); public void removeVetoableChangeListener(VetoableChangeListener listener); 

Just as there is a convenience class to manage property change listeners, there is a convenience class, called VetoableChangeSupport, that manages vetoable change listeners. Your bean should contain an object of this class.

 private VetoableChangeSupport vetoSupport = new VetoableChangeSupport(this); 

Adding and removing listeners should be delegated to this object. For example:

 public void addVetoableChangeListener(VetoableChangeListener listener) {    vetoSupport.addVetoableChangeListener(listener); } public void removeVetoableChangeListener(VetoableChangeListener listener) {    vetoSupport.removeVetoableChangeListener(listener); } 

TIP

The JComponent class has some support for constrained properties, but it is not as extensive as that for bound properties. The JComponent class keeps a single listener list for vetoable change listeners, not a separate list for each property. And the fireVetoableChange method is not overloaded for basic types. If your bean extends JComponent and has a single constrained property, then the listener support of the JComponent superclass is entirely adequate, and you do not need a separate VetoableChangeSupport object.


To update a constrained property value, a bean uses the following three-phase approach:

  1. Notify all vetoable change listeners of the intent to change the property value. (Use the fireVetoableChange method of the VetoableChangeSupport class.)

  2. If none of the vetoable change listeners has thrown a PropertyVetoException, then update the value of the property.

  3. Notify all property change listeners to confirm that a change has occurred.

For example,


public void setValue(Type newValue) throws PropertyVetoException
{
   Type oldValue = getValue();
   vetoSupport.fireVetoableChange("value", oldValue, newValue);
   // survived, therefore no veto
   value = newValue;
   changeSupport.firePropertyChange("value", oldValue, newValue);
}

It is important that you don't change the property value until all the registered vetoable change listeners have agreed to the proposed change. Conversely, a vetoable change listener should never assume that a change that it agrees to is actually happening. The only reliable way to get notified when a change is actually happening is through a property change listener.

We end our discussion of JavaBeans properties by showing the full code for FileNameBean (see Example 8-2). The FileNameBean has a constrained filename property. Because FileNameBean extends the JPanel class, we did not have to explicitly use a PropertyChangeSupport object. Instead, we rely on the ability of the JPanel class to manage property change listeners.

Example 8-2. FileNameBean.java
   1. package com.horstmann.corejava;   2.   3. import java.awt.*;   4. import java.awt.event.*;   5. import java.beans.*;   6. import java.io.*;   7. import javax.swing.*;   8.   9. /**  10.    A bean for specifying file names.  11. */  12. public class FileNameBean extends JPanel  13. {  14.    public FileNameBean()  15.    {  16.       dialogButton = new JButton("...");  17.       nameField = new JTextField(30);  18.  19.       chooser = new JFileChooser();  20.  21.       chooser.setFileFilter(new  22.          javax.swing.filechooser.FileFilter()  23.          {  24.             public boolean accept(File f)  25.             {  26.                String name = f.getName().toLowerCase();  27.                return name.endsWith("." + defaultExtension) || f.isDirectory();  28.             }  29.             public String getDescription()  30.             {  31.                return defaultExtension + " files";  32.             }  33.          });  34.  35.       setLayout(new GridBagLayout());  36.       GridBagConstraints gbc = new GridBagConstraints();  37.       gbc.weightx = 100;  38.       gbc.weighty = 100;  39.       gbc.anchor = GridBagConstraints.WEST;  40.       gbc.fill = GridBagConstraints.BOTH;  41.       gbc.gridwidth = 1;  42.       gbc.gridheight = 1;  43.       add(nameField, gbc);  44.  45.       dialogButton.addActionListener(  46.          new ActionListener()  47.          {  48.             public void actionPerformed(ActionEvent event)  49.             {  50.                int r = chooser.showOpenDialog(null);  51.                if(r == JFileChooser.APPROVE_OPTION)  52.                {  53.                   File f = chooser.getSelectedFile();  54.                   try  55.                   {  56.                      String name = f.getCanonicalPath();  57.                      setFileName(name);  58.                   }  59.                   catch (IOException e)  60.                   {  61.                   }  62.                }  63.             }  64.          });  65.       nameField.setEditable(false);  66.  67.       gbc.weightx = 0;  68.       gbc.anchor = GridBagConstraints.EAST;  69.       gbc.fill = GridBagConstraints.NONE;  70.       gbc.gridx = 1;  71.       add(dialogButton, gbc);  72.    }  73.  74.    /**  75.       Sets the fileName property.  76.       @param newValue the new file name  77.    */  78.    public void setFileName(String newValue)  79.    {  80.       String oldValue = nameField.getText();  81.       nameField.setText(newValue);  82.       firePropertyChange("fileName", oldValue, newValue);  83.    }  84.  85.    /**  86.       Gets the fileName property.  87.       @return the name of the selected file  88.    */  89.    public String getFileName()  90.    {  91.       return nameField.getText();  92.    }  93.  94.    /**  95.       Sets the defaultExtension property.  96.       @param s the new default extension  97.    */  98.    public void setDefaultExtension(String s)  99.    { 100.       defaultExtension = s; 101.    } 102. 103.    /** 104.       Gets the defaultExtension property. 105.       @return the default extension in the file chooser 106.    */ 107.    public String getDefaultExtension() 108.    { 109.       return defaultExtension; 110.    } 111. 112.    public Dimension getPreferredSize() 113.    { 114.       return new Dimension(XPREFSIZE, YPREFSIZE); 115.    } 116. 117.    private static final int XPREFSIZE = 200; 118.    private static final int YPREFSIZE = 20; 119.    private JButton dialogButton; 120.    private JTextField nameField; 121.    private JFileChooser chooser; 122.    private String defaultExtension = "gif"; 123. } 


 java.beans.PropertyChangeListener 1.1 

  • void propertyChange(PropertyChangeEvent event)

    is called when a property change event is fired.

    Parameters:

    event

    The property change event



 java.beans.PropertyChangeSupport 1.1 

  • PropertyChangeSupport(Object sourceBean)

    constructs a PropertyChangeSupport object that manages listeners for bound property changes of the given bean.

  • void addPropertyChangeListener(PropertyChangeListener listener)

  • void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) 1.2

    register an interested listener for changes in all bound properties, or only the named bound property.

  • void removePropertyChangeListener(PropertyChangeListener listener)

  • void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) 1.2

    remove a previously registered property change listener.

  • void firePropertyChange(String propertyName, Object oldValue, Object newValue)

  • void firePropertyChange(String propertyName, int oldValue, int newValue) 1.2

  • void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) 1.2

    send a PropertyChangeEvent to registered listeners.

  • void fireIndexedPropertyChange(String propertyName, int index, Object oldValue, Object newValue) 5.0

  • void fireIndexedPropertyChange(String propertyName, int index, int oldValue, int newValue) 5.0

  • void fireIndexedPropertyChange(String propertyName, int index, boolean oldValue, boolean newValue) 5.0

    send an IndexedPropertyChangeEvent to registered listeners.

  • PropertyChangeListener[] getPropertyChangeListeners() 1.4

  • PropertyChangeListener[] getPropertyChangeListeners(String propertyName) 1.4

    get the listeners for changes in all bound properties, or only the named bound property.


 java.beans.PropertyChangeEvent 1.1 

  • PropertyChangeEvent(Object sourceBean, String propertyName, Object oldValue, Object newValue)

    constructs a new PropertyChangeEvent object, describing that the given property has changed from oldValue to newValue.

  • Object getNewValue()

    returns the new value of the property.

  • Object getOldValue();

    returns the previous value of the property.

  • String getPropertyName()

    returns the name of the property.


 java.beans.IndexedPropertyChangeEvent 5.0 

  • IndexedPropertyChangeEvent(Object sourceBean, String propertyName, int index, Object oldValue, Object newValue)

    constructs a new IndexedPropertyChangeEvent object, describing that the given property has changed from oldValue to newValue at the given index.

  • int getIndex()

    returns the index at which the change occurred.


 java.beans.VetoableChangeListener 1.1 

  • void vetoableChange(PropertyChangeEvent event)

    is called when a property is about to be changed. It should throw a PropertyVetoException if the change is not acceptable.

    Parameters:

    event

    The event object describing the property change



 java.beans.VetoableChangeSupport 1.1 

  • VetoableChangeSupport(Object sourceBean)

    constructs a PropertyChangeSupport object that manages listeners for constrained property changes of the given bean.

  • void addVetoableChangeListener(VetoableChangeListener listener)

  • void addVetoableChangeListener(String propertyName, VetoableChangeListener listener) 1.2

    register an interested listener for changes in all constrained properties, or only the named constrained property.

  • void removeVetoableChangeListener(VetoableChangeListener listener)

  • void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener) 1.2

    remove a previously registered vetoable change listener.

  • void fireVetoableChange(String propertyName, Object oldValue, Object newValue)

  • void fireVetoableChange(String propertyName, int oldValue, int newValue) 1.2

  • void fireVetoableChange(String propertyName, boolean oldValue, boolean newValue) 1.2

    send a VetoableChangeEvent to registered listeners.

  • VetoableChangeListener[] getVetoableChangeListeners() 1.4

  • VetoableChangeListener[] getVetoableChangeListeners(String propertyName) 1.4

    get the listeners for changes in all constrained properties, or only the named bound property.


 javax.swing.JComponent 1.2 

  • void addPropertyChangeListener(PropertyChangeListener listener)

  • void addPropertyChangeListener(String propertyName, PropertyChangeListener listener)

    register an interested listener for changes in all bound properties, or only the named bound property.

  • void removePropertyChangeListener(PropertyChangeListener listener)

  • void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) 1.2

    remove a previously registered property change listener.

  • void firePropertyChange(String propertyName, Object oldValue, Object newValue)

    sends a PropertyChangeEvent to registered listeners.

  • void addVetoableChangeListener(VetoableChangeListener listener)

    registers an interested listener for changes in all constrained properties, or only the named constrained property.

  • void removeVetoableChangeListener(VetoableChangeListener listener)

    removes a previously registered vetoable change listener.

  • void fireVetoableChange(String propertyName, Object oldValue, Object newValue)

    sends a VetoableChangeEvent to registered listeners.


 java.beans.PropertyVetoException 1.1 

  • PropertyVetoException(String reason, PropertyChangeEvent event)

    creates a new PropertyVetoException.

    Parameters:

    reason

    A string that describes the reason for the veto

     

    event

    The PropertyChangeEvent for the constrained property you want to veto


  • PropertyChangeEvent getPropertyChangeEvent()

    returns the PropertyChangeEvent used to construct the exception.



    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

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