Property Editors


If you add an integer or string property to a bean, then that property is automatically displayed in the bean's property inspector. But what happens if you add a property whose values cannot easily be edited in a text field, for example, a date or a Color? Then, you need to provide a separate component by which the user can specify the property value. Such components are called property editors. For example, a property editor for a date object might be a calendar that lets the user scroll through the months and pick a date. A property editor for a Color object would let the user select the red, green, and blue components of the color.

Actually, NetBeans already has a property editor for colors. Also, of course, there are property editors for basic types such as String (a text field) and boolean (a checkbox). These property editors are registered with the property editor manager.

The process for supplying a new property editor is slightly involved. First, you create a bean info class to accompany your bean. Override the getPropertyDescriptors method. That method returns an array of PropertyDescriptor objects. You create one object for each property that should be displayed on a property editor, even those for which you just want the default editor.

You construct a PropertyDescriptor by supplying the name of the property and the class of the bean that contains it.

 PropertyDescriptor descriptor = new PropertyDescriptor("titlePosition", ChartBean.class); 

Then you call the setPropertyEditorClass method of the PropertyDescriptor class.

 descriptor.setPropertyEditorClass(TitlePositionEditor.class); 

Next, you build an array of descriptors for properties of your bean. For example, the chart bean that we discuss in this section has five properties:

  • A Color property, graphColor

  • A String property, title

  • An int property, titlePosition

  • A double[] property, values

  • A boolean property, inverse

The code in Example 8-3 shows the ChartBeanBeanInfo class that specifies the property editors for these properties. It achieves the following:

  1. The getPropertyDescriptors method returns a descriptor for each property. The title and graphColor properties are used with the default editors, that is, the string and color editors that come with the builder tool.

  2. The titlePosition, values, and inverse properties use special editors of type TitlePositionEditor, DoubleArrayEditor, and InverseEditor, respectively.

Figure 8-10 shows the chart bean. You can see the title on the top. Its position can be set to left, center, or right. The values property specifies the graph values. If the inverse property is true, then the background is colored and the bars of the chart are white. Example 8-4 lists the code for the chart bean; the bean is simply a modification of the chart applet in Volume 1, Chapter 10.

Figure 8-10. The chart bean


The static registerEditor method of the PropertyEditorManager class sets a property editor for all properties of a given type. Here is an example:

 PropertyEditorManager.registerEditor(Date.class, CalendarSelector.class); 

NOTE

You should not call the registerEditor method in your beansthe default editor for a type is a global setting that is properly the responsibility of the builder environment.


You use the findEditor method in the PropertyEditorManager class to check whether a property editor exists for a given type in your builder tool. That method does the following:

  1. It looks first to see which property editors are already registered with it. (These are the editors supplied by the builder tool and by calls to the registerEditor method.)

  2. Then, it looks for a class with a name that consists of the name of the type plus the word Editor.

  3. If neither lookup succeeds, then findEditor returns null.

For example, if a CalendarSelector class is registered for java.util.Date objects, then it would be used to edit a Date property. Otherwise, a java.util.DateEditor would be searched.

Example 8-3. ChartBeanBeanInfo.java
  1. package com.horstmann.corejava;  2.  3. import java.beans.*;  4.  5. /**  6.    The bean info for the chart bean, specifying the property editors.  7. */  8. public class ChartBeanBeanInfo extends SimpleBeanInfo  9. { 10.    public PropertyDescriptor[] getPropertyDescriptors() 11.    { 12.       try 13.       { 14.          PropertyDescriptor titlePositionDescriptor 15.             = new PropertyDescriptor("titlePosition", ChartBean.class); 16.          titlePositionDescriptor.setPropertyEditorClass(TitlePositionEditor.class); 17.          PropertyDescriptor inverseDescriptor 18.             = new PropertyDescriptor("inverse", ChartBean.class); 19.          inverseDescriptor.setPropertyEditorClass(InverseEditor.class); 20.          PropertyDescriptor valuesDescriptor 21.             = new PropertyDescriptor("values", ChartBean.class); 22.          valuesDescriptor.setPropertyEditorClass(DoubleArrayEditor.class); 23. 24.          return new PropertyDescriptor[] 25.          { 26.             new PropertyDescriptor("title", ChartBean.class), 27.             titlePositionDescriptor, 28.             valuesDescriptor, 29.             new PropertyDescriptor("graphColor", ChartBean.class), 30.             inverseDescriptor 31.          }; 32.       } 33.       catch (IntrospectionException e) 34.       { 35.          e.printStackTrace(); 36.          return null; 37.       } 38.    } 39. } 

Example 8-4. ChartBean.java
   1. package com.horstmann.corejava;   2.   3. import java.awt.*;   4. import java.awt.font.*;   5. import java.awt.geom.*;   6. import java.util.*;   7. import java.beans.*;   8. import java.io.*;   9. import javax.swing.*;  10.  11. /**  12.    A bean to draw a bar chart.  13. */  14. public class ChartBean extends JPanel  15. {  16.    public void paint(Graphics g)  17.    {  18.       Graphics2D g2 = (Graphics2D) g;  19.  20.       if (values == null || values.length == 0) return;  21.       double minValue = 0;  22.       double maxValue = 0;  23.       for (int i = 0; i < values.length; i++)  24.       {  25.          if (minValue > getValues(i)) minValue = getValues(i);  26.          if (maxValue < getValues(i)) maxValue = getValues(i);  27.       }  28.       if (maxValue == minValue) return;  29.  30.       Dimension d = getSize();  31.       Rectangle2D bounds = getBounds();  32.       double clientWidth = bounds.getWidth();  33.       double clientHeight = bounds.getHeight();  34.       double barWidth = clientWidth / values.length;  35.  36.       g2.setPaint(inverse ? color : Color.white);  37.       g2.fill(bounds);  38.       g2.setPaint(Color.black);  39.  40.       Font titleFont = new Font("SansSerif", Font.BOLD, 20);  41.       FontRenderContext context = g2.getFontRenderContext();  42.       Rectangle2D titleBounds = titleFont.getStringBounds(title, context);  43.  44.       double titleWidth = titleBounds.getWidth();  45.       double y = -titleBounds.getY();  46.       double x;  47.       if (titlePosition == LEFT) x = 0;  48.       else if (titlePosition == CENTER) x = (clientWidth - titleWidth) / 2;  49.       else x = clientWidth - titleWidth;  50.  51.       g2.setFont(titleFont);  52.       g2.drawString(title, (float) x, (float) y);  53.  54.       double top = titleBounds.getHeight();  55.       double scale = (clientHeight - top) / (maxValue - minValue);  56.       y = clientHeight;  57.  58.       for (int i = 0; i < values.length; i++)  59.       {  60.          double x1 = i * barWidth + 1;  61.          double y1 = top;  62.          double value = getValues(i);  63.          double height =  value * scale;  64.          if (value >= 0)  65.             y1 += (maxValue - value) * scale;  66.          else  67.          {  68.             y1 += (int)(maxValue * scale);  69.             height = -height;  70.          }  71.  72.          g2.setPaint(inverse ? Color.white : color);  73.          Rectangle2D bar = new Rectangle2D.Double(x1, y1, barWidth - 2, height);  74.          g2.fill(bar);  75.          g2.setPaint(Color.black);  76.          g2.draw(bar);  77.       }  78.    }  79.  80.    /**  81.       Sets the title property.  82.       @param t the new chart title.  83.    */  84.    public void setTitle(String t) { title = t; }  85.  86.    /**  87.       Gets the title property.  88.       @return the chart title.  89.    */  90.    public String getTitle() { return title; }  91.  92.    /**  93.       Sets the indexed values property.  94.       @param v the values to display in the chart.  95.    */  96.    public void setValues(double[] v) { values = v; }  97.  98.    /**  99.       Gets the indexed values property. 100.       @return the values to display in the chart. 101.    */ 102.    public double[] getValues() { return values; } 103. 104.    /** 105.       Sets the indexed values property. 106.       @param i the index of the value to set 107.       @param value the new value for that index 108.    */ 109.    public void setValues(int i, double value) 110.    { 111.       if (0 <= i && i < values.length) values[i] = value; 112.    } 113. 114.    /** 115.       Gets the indexed values property. 116.       @param i the index of the value to get 117.       @return the value for that index 118.    */ 119.    public double getValues(int i) 120.    { 121.       if (0 <= i && i < values.length) return values[i]; 122.       return 0; 123.    } 124. 125.    /** 126.       Sets the inverse property. 127.       @param b true if the display is inverted (white bars 128.       on colored background) 129.    */ 130.    public void setInverse(boolean b) { inverse = b; } 131. 132.    /** 133.       Gets the inverse property. 134.       @return true if the display is inverted 135.    */ 136.    public boolean isInverse() { return inverse; } 137. 138.    /** 139.       Sets the titlePosition property. 140.       @param p LEFT, CENTER, or RIGHT 141.    */ 142.    public void setTitlePosition(int p) { titlePosition = p; } 143. 144.    /** 145.       Gets the titlePosition property. 146.       @return LEFT, CENTER, or RIGHT 147.    */ 148.    public int getTitlePosition() { return titlePosition; } 149. 150.    /** 151.       Sets the graphColor property. 152.       @param c the color to use for the graph 153.    */ 154.    public void setGraphColor(Color c) { color = c; } 155. 156.    /** 157.       Gets the graphColor property. 158.       @param c the color to use for the graph 159.    */ 160.    public Color getGraphColor() { return color; } 161. 162.    public Dimension getPreferredSize() 163.    { 164.       return new Dimension(XPREFSIZE, YPREFSIZE); 165.    } 166. 167.    private static final int LEFT = 0; 168.    private static final int CENTER = 1; 169.    private static final int RIGHT = 2; 170. 171.    private static final int XPREFSIZE = 300; 172.    private static final int YPREFSIZE = 300; 173.    private double[] values = { 1, 2, 3 }; 174.    private String title = "Title"; 175.    private int titlePosition = CENTER; 176.    private boolean inverse; 177.    private Color color = Color.red; 178. } 


 java.beans.PropertyEditorManager 1.1 

  • static PropertyEditor findEditor(Class targetType)

    returns a property editor for the given type, or null if none is registered.

    Parameters:

    targetType

    The Class object for the type to be edited, such as Color.class


  • static void registerEditor(Class targetType, Class editorClass)

    registers an editor class to edit values of the given type.

    Parameters:

    targetType

    The Class object for the type to be edited

     

    editorClass

    The Class object for the editor class (null unregisters the current editor)



 java.beans.PropertyDescriptor 1.1 

  • PropertyDescriptor(String name, Class beanClass)

    constructs a PropertyDescriptor object.

    Parameters:

    name

    The name of the property

     

    beanClass

    The class of the bean to which the property belongs


  • void setPropertyEditorClass(Class editorClass)

    sets the class of the property editor to be used with this property.


 java.beans.BeanInfo 1.1 

  • PropertyDescriptor[] getPropertyDescriptors()

    returns a descriptor for each property that should be displayed in the property inspector for the bean.

Writing a Property Editor

Before we begin showing you how to write a property editor, we want to point out that although each property editor works with a value of one specific type, it can nonetheless be quite elaborate. For example, a font property editor (which edits an object of type Font) could show font samples to allow the user to pick a font in a more congenial way.

Next, any property editor you write must implement the PropertyEditor interface, an interface with 12 methods. As with the BeanInfo interface, you will not want to do this directly. Instead, it is far more convenient to extend the convenience PropertyEditorSupport class that is supplied with the standard library. This support class comes with methods to add and remove property change listeners, and with default versions of all other methods of the PropertyEditor interface. For example, our editor for editing the title position of a chart in our chart bean starts out like this:

 // property editor class for title position class TitlePositionEditor extends PropertyEditorSupport {    . . . } 

Note that if a property editor class has a constructor, it must also supply a default constructor, that is, one without arguments.

Finally, before we get into the mechanics of actually writing a property editor, we should point out that the editor is under the control of the builder, not the bean. The builder adheres to the following procedure to display the current value of the property:

1.

It instantiates property editors for each property of the bean.

2.

It asks the bean to tell it the current value of the property.

3.

It then asks the property editor to display the value.

The property editor can use either text-based or graphics-based methods to actually display the value. We discuss these methods next.

Simple Property Editors

Simple property editors work with text strings. You override the setAsText and getAsText methods. For example, our chart bean has a property that lets you set where the title should be displayed: Left, Center, or Right. These choices are implemented as integer constants.

 private static final int LEFT = 0; private static final int CENTER = 1; private static final int RIGHT = 2; 

But of course, we don't want them to appear as numbers 0, 1, 2 in the text fieldunless we are trying to enter the User Interface Hall of Horrors. Instead, we define a property editor whose getAsText method returns the value as a string. The method calls the getValue method of the PropertyEditor to find the value of the property. Because this is a generic method, the value is returned as an Object. If the property type is a basic type, we need to return a wrapper object. In our case, the property type is int, and the call to getValue returns an Integer.

 class TitlePositionEditor extends PropertyEditorSupport {    public String getAsText()    {       int value = (Integer) getValue();       return options[value];    }    . . .    private String[] options = { "Left", "Center", "Right" }; } 

Now, the text field displays one of these fields. When the user edits the text field, this triggers a call to the setAsText method to update the property value by invoking the setValue method. It, too, is a generic method whose parameter is of type Object. To set the value of a numeric type, we need to pass a wrapper object.

 public void setAsText(String s) {    for (int i = 0; i < options.length; i++)    {       if (options[i].equals(s))       {          setValue(i);          return;       }    } } 

Actually, this property editor is not a good choice for the titlePosition property, unless, of course, we are also competing for entry into the User Interface Hall of Shame. The user may not know what the legal choices are. It would be better to display all valid settings (see Figure 8-11). The PropertyEditorSupport class gives a simple method to display the selections in a property editor. We simply write a getTags method that returns an array of strings.

 public String[] getTags() { return options; } 

Figure 8-11. Custom property editors at work


The default getTags method returns null. By returning a non-null value, we indicate a choice field instead of a text field.

We still need to supply the getAsText and setAsText methods. The getTags method simply specifies the values to be displayed in a combo box. The getAsText/setAsText methods translate between the strings and the data type of the property (which may be a string, an integer, or a completely different type).

Example 8-5 lists the complete code for this property editor.

Example 8-5. TitlePositionEditor.java
  1. package com.horstmann.corejava;  2.  3. import java.beans.*;  4.  5. /**  6.    A custom editor for the titlePosition property of the  7.    ChartBean. The editor lets the user choose between  8.    Left, Center, and Right  9. */ 10. public class TitlePositionEditor 11.    extends PropertyEditorSupport 12. { 13.    public String[] getTags() { return options; } 14.    private String[] options = { "Left", "Center", "Right" }; 15.    public String getJavaInitializationString() { return "" + getValue(); } 16. 17.    public String getAsText() 18.    { 19.       int value = (Integer) getValue(); 20.       return options[value]; 21.    } 22. 23.    public void setAsText(String s) 24.    { 25.       for (int i = 0; i < options.length; i++) 26.       { 27.          if (options[i].equals(s)) 28.          { 29.             setValue(i); 30.             return; 31.          } 32.       } 33.    } 34. } 


 java.beans.PropertyEditorSupport 1.1 

  • Object getValue()

    returns the current value of the property. Basic types are wrapped into object wrappers.

  • void setValue(Object newValue)

    sets the property to a new value. Basic types must be wrapped into object wrappers.

    Parameters:

    newValue

    The new value of the object; should be a newly created object that the property can own


  • String getAsText()

    Override this method to return a string representation of the current value of the property. The default returns null to indicate that the property cannot be represented as a string.

  • void setAsText(String text)

    Override this method to set the property to a new value that is obtained by parsing the text. May throw an IllegalArgumentException if the text does not represent a legal value or if this property cannot be represented as a string.

  • String[] getTags()

    Override this method to return an array of all possible string representations of the property values so they can be displayed in a Choice box. The default returns null to indicate that there is not a finite set of string values.

GUI-Based Property Editors

More sophisticated property types can't be edited as text. Instead, they are represented in two ways. The property inspector contains a small area (which otherwise would hold a text box or combo box) onto which the property editor will draw a graphical representation of the current value. When the user clicks on that area, a custom editor dialog box pops up (see Figure 8-12). The dialog box contains a component to edit the property values, supplied by the property editor, and various buttons, supplied by the builder environment.

Figure 8-12. A custom editor dialog box


To build a GUI-based property editor:

  1. Tell the builder tool that you will paint the value and not use a string.

  2. "Paint" the value the user enters onto the GUI.

  3. Tell the builder tool that you will be using a GUI-based property editor.

  4. Build the GUI.

  5. Write the code to validate what the user tries to enter as the value.

For the first step, you override the getAsText method in the PropertyEditor interface to return null and the isPaintable method to return true.

 public String getAsText() { return null; } public boolean isPaintable() { return true; } 

Then, you implement the paintValue method. It receives a Graphics context and the coordinates of the rectangle inside which you can paint. Note that this rectangle is typically small, so you can't have a very elaborate representation. To graphically represent the inverse property, we draw the string "Inverse" in white letters with a black background, or the string "Normal" in black letters with a white background (see Figure 8-11).

 public void paintValue(Graphics g, Rectangle box) {    Graphics2D g2 = (Graphics2D) g;    boolean isInverse = (Boolean) getValue();    String s = isInverse ? "Inverse" : "Normal";    g2.setColor(isInverse ? Color.black : Color.white);    g2.fill(box);    g2.setColor(isInverse ? Color.white : Color.black);    // compute string position to center string    . . .    g2.drawString(s, (float) x, (float) y); } 

Of course, this graphical representation is not editable. The user must click on it to pop up a custom editor.

You indicate that you will have a custom editor by overriding the supportsCustomEditor in the PropertyEditor interface to return true.

 public boolean supportsCustomEditor() { return true; } 

Now, you write the code that builds up the component that will hold the custom editor. You must build a separate custom editor class for every property. For example, associated to our InverseEditor class is an InverseEditorPanel class (see Example 8-7) that describes a GUI with two radio buttons to toggle between normal and inverse mode. That code is straightforward. However, the GUI actions must update the property values. We did this as follows:

  1. Have the custom editor constructor receive a reference to the property editor object and store it in a variable editor.

  2. To read the property value, we have the custom editor call editor.getValue().

  3. To set the object value, we have the custom editor call editor.setValue(newValue) followed by editor.firePropertyChange().

Next, the getCustomEditor method of the PropertyEditor interface constructs and returns an object of the custom editor class.

 public Component getCustomEditor() { return new InverseEditorPanel(this); } 

Finally, property editors should implement the getJavaInitializationString method.With this method, you can give the builder tool the Java code that sets a property to its current value. The builder tool uses this string for automatic code generation. For example, here is the method for the InverseEditor:

 public String getJavaInitializationString() { return "" + getValue(); } 

This method returns the string "false" or "TRue". try it out in NetBeans: If you edit the inverse property, NetBeans inserts code such as

 chartBean1.setInverse(true); 

NOTE

If a property has a custom editor that does not implement the getJavaInitializationString method, NetBeans does not know how to generate code and produces a setter with parameter ???.


Example 8-6 shows the complete code for the InverseEditor that displays the current setting in the property inspector. Example 8-7 lists the code implementing the pop-up editor panel.

The code for the property editor class (shown in Example 8-8) is almost identical to that of the InverseEditor, except that we simply paint a string consisting of the first few array values, followed by . . . in the paintValue method. And, of course, we return a different custom editor in the getCustomEditor method.

The other custom editor that we built for the chart bean class lets you edit a double[] array. NetBeans cannot edit array properties. We developed this custom editor to fill this obvious gap. Figure 8-13 shows the custom editor in action. All array values are shown in the list box, prefixed by their array index. Clicking on an array value places it into the text field above it, and you can edit it. You can also resize the array. The code for the DoubleArrayPanel class that implements the GUI is listed in Example 8-9.

Figure 8-13. The custom editor dialog box for editing an array


These examples complete the code for the chart bean.

NOTE

Unfortunately, we have to paint the array values. It would be more convenient to return a string with the getAsText method. However, some builder environments (including NetBeans) get confused when both getAsText and getCustomEditor return non-null values.


Example 8-6. InverseEditor.java
  1. package com.horstmann.corejava;  2.  3. import java.awt.*;  4. import java.awt.font.*;  5. import java.awt.geom.*;  6. import java.beans.*;  7.  8. /**  9.    The property editor for the inverse property of the ChartBean. 10.    The inverse property toggles between colored graph bars 11.    and colored background. 12. */ 13. public class InverseEditor extends PropertyEditorSupport 14. { 15.    public Component getCustomEditor() { return new InverseEditorPanel(this); } 16.    public boolean supportsCustomEditor() { return true; } 17.    public boolean isPaintable() { return true; } 18.    public String getAsText() { return null; } 19.    public String getJavaInitializationString() { return "" + getValue(); } 20. 21.    public void paintValue(Graphics g, Rectangle box) 22.    { 23.       Graphics2D g2 = (Graphics2D) g; 24.       boolean isInverse = (Boolean) getValue(); 25.       String s = isInverse ? "Inverse" : "Normal"; 26.       g2.setPaint(isInverse ? Color.black : Color.white); 27.       g2.fill(box); 28.       g2.setPaint(isInverse ? Color.white : Color.black); 29.       FontRenderContext context = g2.getFontRenderContext(); 30.       Rectangle2D stringBounds = g2.getFont().getStringBounds(s, context); 31.       double w = stringBounds.getWidth(); 32.       double x = box.x; 33.       if (w < box.width) x += (box.width - w) / 2; 34.       double ascent = -stringBounds.getY(); 35.       double y = box.y + (box.height - stringBounds.getHeight()) / 2 + ascent; 36.       g2.drawString(s, (float) x, (float) y); 37.    } 38. } 

Example 8-7. InverseEditorPanel.java
  1. package com.horstmann.corejava;  2.  3. import java.awt.*;  4. import java.awt.event.*;  5. import java.text.*;  6. import java.lang.reflect.*;  7. import java.beans.*;  8. import javax.swing.*;  9. 10. /** 11.    The panel for setting the inverse property. It contains 12.    radio buttons to toggle between normal and inverse coloring. 13. */ 14. public class InverseEditorPanel extends JPanel 15. { 16.    public InverseEditorPanel(PropertyEditorSupport ed) 17.    { 18.       editor = ed; 19.       ButtonGroup g = new ButtonGroup(); 20.       boolean isInverse = (Boolean) editor.getValue(); 21.       normal = new JRadioButton("Normal", !isInverse); 22.       inverse = new JRadioButton("Inverse", isInverse); 23. 24.       g.add(normal); 25.       g.add(inverse); 26.       add(normal); 27.       add(inverse); 28. 29.       ActionListener buttonListener = 30.          new ActionListener() 31.          { 32.             public void actionPerformed(ActionEvent event) 33.             { 34.                editor.setValue( 35.                   new Boolean(inverse.isSelected())); 36.                editor.firePropertyChange(); 37.             } 38.          }; 39. 40.       normal.addActionListener(buttonListener); 41.       inverse.addActionListener(buttonListener); 42.    } 43. 44.    private JRadioButton normal; 45.    private JRadioButton inverse; 46.    private PropertyEditorSupport editor; 47. } 

Example 8-8. DoubleArrayEditor.java
  1. package com.horstmann.corejava;  2.  3. import java.awt.*;  4. import java.awt.font.*;  5. import java.awt.geom.*;  6. import java.beans.*;  7.  8. /**  9.    A custom editor for an array of floating point numbers. 10. */ 11. public class DoubleArrayEditor extends PropertyEditorSupport 12. { 13.    public Component getCustomEditor() { return new DoubleArrayEditorPanel(this); } 14.    public boolean supportsCustomEditor() { return true; } 15.    public boolean isPaintable() { return true; } 16.    public String getAsText() { return null; } 17. 18.    public void paintValue(Graphics g, Rectangle box) 19.    { 20.       Graphics2D g2 = (Graphics2D) g; 21.       double[] values = (double[]) getValue(); 22.       StringBuilder s = new StringBuilder(); 23.       for (int i = 0; i < 3; i++) 24.       { 25.          if (values.length > i) s.append(values[i]); 26.          if (values.length > i + 1) s.append(", "); 27.       } 28.       if (values.length > 3) s.append("..."); 29. 30.       g2.setPaint(Color.white); 31.       g2.fill(box); 32.       g2.setPaint(Color.black); 33.       FontRenderContext context = g2.getFontRenderContext(); 34.       Rectangle2D stringBounds = g2.getFont().getStringBounds(s.toString(), context); 35.       double w = stringBounds.getWidth(); 36.       double x = box.x; 37.       if (w < box.width) x += (box.width - w) / 2; 38.       double ascent = -stringBounds.getY(); 39.       double y = box.y + (box.height - stringBounds.getHeight()) / 2 + ascent; 40.       g2.drawString(s.toString(), (float) x, (float) y); 41.    } 42. 43.    public String getJavaInitializationString() 44.    { 45.       double[] values = (double[]) getValue(); 46.       StringBuilder s = new StringBuilder(); 47.       s.append("new double[] {"); 48.       for (int i = 0; i < values.length; i++) 49.       { 50.          if (i > 0) s.append(", "); 51.          s.append(values[i]); 52.       } 53.       s.append("}"); 54.       return s.toString(); 55.    } 56. } 

Example 8-9. DoubleArrayEditorPanel.java

[View full width]

   1. package com.horstmann.corejava;   2.   3. import java.awt.*;   4. import java.awt.event.*;   5. import java.text.*;   6. import java.lang.reflect.*;   7. import java.beans.*;   8. import javax.swing.*;   9. import javax.swing.event.*;  10.  11. /**  12.    The panel inside the DoubleArrayEditor. It contains  13.    a list of the array values, together with buttons to  14.    resize the array and change the currently selected list value.  15. */  16. public class DoubleArrayEditorPanel extends JPanel  17. {  18.    public DoubleArrayEditorPanel(PropertyEditorSupport ed)  19.    {  20.       editor = ed;  21.       setArray((double[])ed.getValue());  22.  23.       setLayout(new GridBagLayout());  24.  25.       add(sizeField, new GBC(0, 0, 1, 1).setWeight(100, 0).setFill(GBC.HORIZONTAL));  26.       add(valueField, new GBC(0, 1, 1, 1).setWeight(100, 0).setFill(GBC.HORIZONTAL));  27.       add(sizeButton, new GBC(1, 0, 1, 1).setWeight(100, 0));  28.       add(valueButton, new GBC(1, 1, 1, 1).setWeight(100, 0));  29.       add(new JScrollPane(elementList), new GBC(0, 2, 2, 1).setWeight(100, 100) .setFill(GBC.BOTH));  30.  31.       sizeButton.addActionListener(new  32.          ActionListener()  33.          {  34.             public void actionPerformed(ActionEvent event) { changeSize(); }  35.          });  36.  37.       valueButton.addActionListener(new  38.          ActionListener()  39.          {  40.             public void actionPerformed(ActionEvent event) { changeValue(); }  41.          });  42.  43.  44.       elementList.setSelectionMode(  45.          ListSelectionModel.SINGLE_SELECTION);  46.  47.       elementList.addListSelectionListener(new  48.          ListSelectionListener()  49.          {  50.             public void valueChanged(ListSelectionEvent event)  51.             {  52.                int i = elementList.getSelectedIndex();  53.                if (i < 0) return;  54.                valueField.setText("" + array[i]);  55.             }  56.          });  57.  58.       elementList.setModel(model);  59.       elementList.setSelectedIndex(0);  60.    }  61.  62.    /**  63.       This method is called when the user wants to change  64.       the size of the array.  65.    */  66.    public void changeSize()  67.    {  68.       fmt.setParseIntegerOnly(true);  69.       int s = 0;  70.       try  71.       {  72.          s = fmt.parse(sizeField.getText()).intValue();  73.          if (s < 0) throw new ParseException("Out of bounds", 0);  74.       }  75.       catch (ParseException e)  76.       {  77.          JOptionPane.showMessageDialog(this, "" + e, "Input Error", JOptionPane .WARNING_MESSAGE);  78.          sizeField.requestFocus();  79.          return;  80.       }  81.       if (s == array.length) return;  82.       setArray((double[])arrayGrow(array, s));  83.       editor.setValue(array);  84.       editor.firePropertyChange();  85.    }  86.  87.    /**  88.       This method is called when the user wants to change  89.       the currently selected array value.  90.    */  91.    public void changeValue()  92.    {  93.       double v = 0;  94.       fmt.setParseIntegerOnly(false);  95.       try  96.       {  97.          v = fmt.parse(valueField.getText()).doubleValue();  98.       }  99.       catch (ParseException e) 100.       { 101.          JOptionPane.showMessageDialog(this, "" + e, "Input Error", JOptionPane .WARNING_MESSAGE); 102.          valueField.requestFocus(); 103.          return; 104.       } 105.       int currentIndex = elementList.getSelectedIndex(); 106.       setArray(currentIndex, v); 107.       editor.firePropertyChange(); 108.    } 109. 110.    /** 111.       Sets the indexed array property. 112.       @param v the array to edit 113.    */ 114.    public void setArray(double[] v) 115.    { 116.       if (v == null) array = new double[0]; 117.       else array = v; 118.       model.setArray(array); 119.       sizeField.setText("" + array.length); 120.       if (array.length > 0) 121.       { 122.          valueField.setText("" + array[0]); 123.          elementList.setSelectedIndex(0); 124.       } 125.       else 126.          valueField.setText(""); 127.    } 128. 129.    /** 130.       Gets the indexed array property. 131.       @return the array being edited 132.    */ 133.    public double[] getArray() 134.    { 135.       return (double[]) array.clone(); 136.    } 137. 138.    /** 139.       Sets the indexed array property. 140.       @param i the index whose value to set 141.       @param value the new value for the given index 142.    */ 143.    public void setArray(int i, double value) 144.    { 145.       if (0 <= i && i < array.length) 146.       { 147.          model.setValue(i, value); 148.          elementList.setSelectedIndex(i); 149.          valueField.setText("" + value); 150.       } 151.    } 152. 153.    /** 154.       Gets the indexed array property. 155.       @param i the index whose value to get 156.       @return the value at the given index 157.    */ 158.    public double getArray(int i) 159.    { 160.       if (0 <= i && i < array.length) return array[i]; 161.       return 0; 162.    } 163. 164.    /** 165.       Resizes an array 166.       @param a the array to grow 167.       @param newLength the new length 168.       @return an array with the given length and the same 169.       elements as a in the common positions 170.    */ 171.    private static Object arrayGrow(Object a, int newLength) 172.    { 173.       Class cl = a.getClass(); 174.       if (!cl.isArray()) return null; 175.       Class componentType = a.getClass().getComponentType(); 176.       int length = Array.getLength(a); 177. 178.       Object newArray = Array.newInstance(componentType, newLength); 179.       System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength)); 180.       return newArray; 181.    } 182. 183.    private PropertyEditorSupport editor; 184.    private double[] array; 185.    private NumberFormat fmt = NumberFormat.getNumberInstance(); 186.    private JTextField sizeField = new JTextField(4); 187.    private JTextField valueField = new JTextField(12); 188.    private JButton sizeButton = new JButton("Resize"); 189.    private JButton valueButton = new JButton("Change"); 190.    private JList elementList = new JList(); 191.    private DoubleArrayListModel model = new DoubleArrayListModel(); 192. } 193. 194. /** 195.    The list model for the element list in the editor. 196. */ 197. class DoubleArrayListModel extends AbstractListModel 198. { 199.    public int getSize() { return array.length; } 200.    public Object getElementAt(int i) { return "[" + i + "] " + array[i]; } 201. 202.    /** 203.       Sets a new array to be displayed in the list. 204.       @param a the new array 205.    */ 206.    public void setArray(double[] a) 207.    { 208.       int oldLength = array == null ? 0 : array.length; 209.       array = a; 210.       int newLength = array == null ? 0 : array.length; 211.       if (oldLength > 0) fireIntervalRemoved(this, 0, oldLength); 212.       if (newLength > 0) fireIntervalAdded(this, 0, newLength); 213.    } 214. 215.    /** 216.       Changes a value in the array to be displayed in the list. 217.       @param i the index whose value to change 218.       @param value the new value for the given index 219.    */ 220.    public void setValue(int i, double value) 221.    { 222.       array[i] = value; 223.       fireContentsChanged(this, i, i); 224.    } 225. 226.    private double[] array; 227. } 

Summing Up

For every property editor you write, you have to choose one of three ways to display and edit the property value:

  • As a text string (define getAsText and setAsText)

  • As a choice field (define getAsText, setAsText, and getTags)

  • Graphically, by painting it (define isPaintable, paintValue, supportsCustomEditor, and getCustomEditor)

You saw examples of all three cases in the chart bean.


 java.beans.PropertyEditorSupport 1.1 

  • boolean isPaintable()

    should be overridden to return true if the class uses the paintValue method to display the property.

  • void paintValue(Graphics g, Rectangle box)

    should be overridden to represent the value by drawing into a graphics context in the specified place on the component used for the property inspector.

    Parameters:

    g

    The graphics object to draw onto

     

    box

    A rectangle object that represents where on the property inspector component to draw the value


  • boolean supportsCustomEditor()

    should be overridden to return TRue if the property editor has a custom editor.

  • Component getCustomEditor()

    should be overridden to return the component that contains a customized GUI for editing the property value.

  • String getJavaInitializationString()

    should be overridden to return a Java code string that can be used to generate code that initializes the property value. Examples are "0", "new Color(64, 64, 64)".



    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