Chapter 24. Java Beans in Practice

Java Beans in Practice

In this chapter we develop two independent Java beans then integrate them using the beanbox. We’ll start with the program specifications for what we want our two Java beans to do. That’s the eventual goal we are aiming at, and we will get there in steps. We’ll write as much as we can of our two components without using anything specific to beans. Then we will add the beans conventions and support framework piece by piece until we have a full working example. You’ll want to refer to the example in Chapter 23 for reminders on how to use the beanbox. At each step we will make clear what problem we are trying to solve, and how the code we added helps us get there.

The Specification for Two Beans

You will get the most benefit if you can work along on your computer as you read. Type in the code as it evolves, compile and run it in the beanbox, and you’ll quickly get a good understanding of how this all works. Here is the specification for the first of two Java beans that we will develop in this chapter. These beans are independent software components. We will develop the code for them, and then integrate them in the beanbox.

The point of component software is to write self-contained chunks of code that do a single thing well. Then any of the class files can be snapped together to form a bigger system. Beans must do all their communication using events or the bean framework. That way, any bean can be connected with any other bean that agrees on the events sent. Beans should not have any dependencies on or knowledge of each other at coding time. Of course, you can usually write the same classes, and probably more quickly, if you let them reference each other directly. But then they are not beans. By avoiding dependencies at code time, you make your beans more flexible and general-purpose.

The second bean that we will develop is much simpler (has much less behavior). It will be a button that will tell some other component that it is time to “do” something. Hence, we call it a “DoIt” button. The specification follows.

The Code for the ConverterField Bean

From reading the specification, here are the parts of the ConverterField. We can write almost all these pieces immediately without needing any component features. For one piece we need to learn more about beans. The pieces of the code that we can write are shown below, interleaved with explanations of what they do. Here are the properties that our specification calls for:

Properties of the ConverterField Bean

ConverterField

  • conversion rate value

  • font size and style

  • whether the display is currently a valid number

The ConverterField class should clearly be based on javax.swing.JTextField, since that supports entering and displaying text. In JDK 1.4, we could use the new javax.swing.JFormattedTextField, which adds the hooks for automatically formatting arbitrary kinds of values. However, it is overkill for what we want here, so we’ll stick with JTextField.

import javax.swing.*;
import java.awt.Color;
import java.awt.event.*;
public class ConverterField extends javax.swing.JTextField
            implements KeyListener {
     public ConverterField() {  // no-arg constructor
           super("0", 16);  // default display is 16 digits long
           setBackground(Color.white);
           addKeyListener(this);
    } 

We can easily write some code to do the “validate as a number” action. The code gets the string from the text field, tries to parse it as a double length floating point number, and sets the background of the text field to either white (valid) or red (invalid).

    public void validateNumber() {
    // gets a double from the text field, and
    // sets the textfield color to red if invalid.
    String s = this.getText();
    try {
        n = Double.parseDouble(s);
          // success -- n holds the number
        setBackground(Color.white);
        setValidNum(true);
    } catch (NumberFormatException nfe) {
          // failed to parse TextField as number
          // set background to red!
        setValidNum(false);
        requestFocus();
        setBackground(Color.red);
        n = Double.NaN;
    }
}
private double n; 

We want to validate the text field when the user hits “enter.” How do we know when that happens? We will make the component be a listener for keystrokes that take place over it. When “enter” is pressed, we will call the validate number method. We provide methods for all three methods in the key listener interface, even though we only use one of them. The work takes place completely in this class and has no impact on other beans.

public void keyPressed(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {
  // ConverterField generates these events, and
  // validates the number when user hits "enter".
    if (e.getKeyChar() == KeyEvent.VK_ENTER) {
        validateNumber();
    }
} 

We need to keep track of the “rate” property and acquire its initial value from the property sheet editor. The design tool will have the ability to set and change the value of properties without requiring any coding on your part. So the code to implement a rate property is this:

// holds the rate that we will multiply with the TextField
// the rate will be set at design time, and doesn't change
private double rate = 1.0;
public double getRate() {  return rate; }
public void setRate(double d) {
    rate = d;
    this.setText( "rate set to " + Double.toString(d) );
} 

We need to keep track of whether the text in the display represents a valid number or not. In fact, this should be a boolean property of the component. Let us call it “validNum.” That also fixes the names of the getter and setter methods because of the naming conventions. When validNum changes from true-to-false or false-to-true, we need to be able to make other beans aware of it somehow. The button is going to use that property change as a command to hide or reveal itself.

private boolean validNum = true;
public boolean isValidNum() {  return validNum; }
public void setValidNum(boolean b) {  validNum = b; } 

We can write the routine that validates the number in the text field and does the multiplication. We already saw in the previous chapter how to use the beanbox to connect a button to another bean such that a button press triggers some action in the other bean. That is how we will get the button press to cause the validate and multiply in this bean. There is no code to add to the ConverterField class. The beanbox will create it for us.

public void validateAndMultiply() {
    // we do this when another bean tells us to.
    validateNumber();
    if ( !(isvalidNum()))  return;
    n = n * rate;
    setText( Double.toString(n) );
} 

Pretty much all the other needed characteristics (such as font) can be inherited from the parent class. A complete listing of the ConverterField class is at the end of the chapter. Take a moment to look it over. Every reader who has got this far in the book already has the ability to write this ConverterField code and other code like it. We have created 99% of a Java bean without having to learn a single new thing.

There is one tiny part of ConverterField that is beans-specific, and that we have not been given the information to write yet. We have not yet seen how to write the code that informs other beans that the validNum property has changed. As you probably guessed, this will be done by sending an event from ConverterField each time validNum changes between valid and invalid. There will be an expanded explanation later in this chapter, but for now note that a property in one bean that is connected to another bean is known as a “bound property.”

The beanbox will take care of generating the event handler and registering beans as listeners for the property. There is a special event type that represents a change in value of a bean property. It is known as a PropertyChangeEvent, and the class is part of the java.beans package. The visual design tool will automatically generate Java code for other classes that express interest in listening for the PropertyChangeEvent for the ConverterField validNum property. The programmer must manually write the code (in the bean with the property) to fire the event when the property changes. The obvious place to put the code is in the method that sets the property. This code is shown in bold below.

public void setValidNum(boolean newVal) {
    boolean oldVal = validNum;
    firePropertyChange( "validNum", oldVal, newVal);
    validNum = newVal;
} 

You do not have to check if the old and new values are actually different because firePropertyChange does that for you. The method firePropertyChange() is part of all Swing components, by virtue of being present in javax.swing.JComponent. The ConverterField bean is now 100% complete.

Summary of ConverterField Bean

Let’s summarize what we did in this section. We created the ConverterField class based on JTextField. We added a method to read the string from its text field and check if it is a valid floating point number, setting the textfield background to various colors accordingly. We made the class a key listener, so that the text field can be validated based on each character entered.

The program specification told us the bean must have three properties. In our implementation, one of them is inherited from the parent class, and we wrote code to implement the other two. The validNum property uses a boolean set by the validate routine. An alternative and equally good approach would have it call the validate routine.

Finally, the specification makes it clear that the validNum property is going to be bound to other beans. So, in the only bit of beans-specific code in this example, we added some code to make ConverterField fire an event when the validNum property changes. That is the ConverterField bean finished and 100% complete. And the good news is that the DoItButton is even simpler than this.

The Code for DoItButton Bean

The DoItButton specification makes clear the properties of the component.

Properties of the DoItButton Bean

DoItButton

  • label on button

  • font size and style

  • background color of the button

  • whether the button is visible or invisible

Here are the tasks we need to code for the DoItButton:

  • The DoItButton class should clearly be based on javax.swing.JButton, since that gives us the button behavior we want.

  • JButton has methods to keep track of label, font, and color. These properties can be customized using the property sheet, without any code needed in the class.

  • We already saw in the previous chapter how to use the beanbox to connect a button to another bean so that a button press triggers some action in the other bean. That is how we will get the button press to cause the validate and multiply in the ConverterField bean. There is no code to add to the DoIt button class. The beanbox will create it for us.

  • JButton has methods to keep track of whether a button is visible or invisible. We haven’t yet seen how to connect a property (validNum) to a property (the “visible” property) in some other bean. We know how to make the button disappear. You can make any Swing component disappear by setting its “visible” property to false. We haven’t yet seen how to do that when the ConverterField decides that the validNum property has changed to invalid. In fact, the beanbox can make this connection between the two beans and automatically generate the glue code, just as with the button press. So there is no code impact here.

  • All other needed characteristics are inherited from the parent class.

    The DoItButton code looks like this:

import javax.swing.*;
public class DoItButton extends JButton {
    public DoItButton() {
        super("press for service");
    }
} 

As we go on to develop these two classes into Java beans, you may be surprised to learn that no further changes or additions whatsoever are needed in DoItButton. That is our finished Java bean right there. That is a formidable statement about the lightweight, non-intrusive nature of Java component software.

It’s easy enough to compile these two classes in the usual way. The next task to tackle is how to put them in a jar file and get them into the beanbox. We will show that in the next section. We still need to write the two bean info classes that accompany these beans and describe them to the visual design tool. That makes up the rest of the chapter as we gradually develop the bean info code. You should recompile, re-jar, and retry each time to see the effect of each change.

Compile, Jar, and Load Classes into Beanbox

The next task is to compile these two bean classes and put them in a jar file along with a manifest file. Then move the jar file to the jars subdirectory of the BDK and start the beanbox. Here are the steps to get your beans and related classes into the beanbox.

We need a way to bundle all the classes, images, audio files, etc., that make up a bean and pass them around in one file (exactly like a zip archive). Fortunately, there is already something that does that: a jar file.

Beans are Best Carried Around in Jars

(I have been waiting a long time for the chance to make that pun.) When you have written and compiled your beans, use your favorite editor to create an ASCII text manifest file that contains two lines for each class that is a bean and that you want to show up in the beanbox. The lines give the filename of the class file and state that it is a Java bean. Here is the content of the manifest file for our example.

Beans manifest file, e.g., mybeans.mf

Name: ConverterField.class
Java-Bean: true

Name: DoItButton.class
Java-Bean: true 

Be sure to have a blank line between each bean and to include a carriage return at the end of the last line. Next, put the classfiles and any other resources that your beans will use (other classes of yours, gif files to represent a bean as an icon for display in the design tool, etc.) into a jar file, along with this manifest file. If you called this manifest file “mybeans.mf,” the jar command would look like:

jar cfvm  mybeans.jar   mybeans.mf   *.class  *.gif 

“jar” is one of the java utilities in the JDK, like “java” and “javac.” So you need to make sure the JDK bin directory containing those executables is in your path. The mysterious “cfvm” string is the options to the jar program:

c = create a new jar file

f = the name of the output file is supplied on the command line

v = do the work in verbose mode

m = the name of the manifest file is supplied on the command line

Make sure that your jar filename and your manifest filename appear on the command line in the same order as their option switches, i.e., if “f” appears before “m” in the options, the jar filename must come before the manifest filename in the rest of the command line. This pitfall catches people again and again. The jar code should really be made a bit more robust by Sun.

It’s a good idea to use the “v” (verbose) option so that the jar tool prints out the name of each file it processes. That lets you check that it is dealing with the files that you intended. Now move the jar file to the jars subdirectory of the BDK, or load it using the “file | load jar” menu item on the beanbox window.

move mybeans.jar  c:dk1.1jars 

Now start the beanbox again:

cd c:dk1.1eanbox
run 

You should now see the names of your beans from the manifest file appear in the ToolBox window. At this point, you should experiment with connecting events and methods as we did with the juggler bean in the previous chapter.

Bean Icons

It’s a good idea to provide icons that represent your beans at design time. The visual aspect is a big part of component software. The best way to make the association between a GIF icon file and a Java class would be in the manifest file that describes the contents of the jar file where they are all stored. After all, that’s where you provide the information about whether a given class is a bean at all. Unfortunately, one of the beans designers decided it should instead be done in the bean info class.

The bean info class is the subject of most of the rest of the chapter. You will typically code a bean info class for each of your beans to provide information about the bean at design time. Your bean info class can contain a method called getIcon( ) that the design tool invokes to retrieve an icon file and turn it into an image. An example of the getIcon method in a bean info class follows. The design tool passes in a parameter saying what size and shade icon it would like, and the method tries to provide it.

public java.awt.Image getIcon(int iconKind) {
 if     (iconKind == BeanInfo.ICON_MONO_16x16 ||
         iconKind == BeanInfo.ICON_COLOR_16x16 ) {
         java.awt.Image img = loadImage("JellyBeanIconColor16.gif");
         return img;
}
 if     (iconKind == BeanInfo.ICON_MONO_32x32 ||
         iconKind == BeanInfo.ICON_COLOR_32x32 ) {
         java.awt.Image img = loadImage("JellyBeanIconColor32.gif");
         return img;
}
 return null;
} 

Just add this method to your bean info classes and change the filename string to a gif file that you have. Some icon gif files can be found in the directories under c:dk1.1demosunwdemo. Icon gif files can be in color or black and white, either 32 pixels or 16 pixels square. There are large numbers of icon gifs available for free download on the web. Sun has made a number of them available for free download at developer.java.sun.com/developer/techDocs/hi/repository/. You can also reuse the gif files that come with the beanbox for your own programs, as I have done here with the jellybean icon.

You should type this code in, put it in a class called ConverterFieldBeanInfo, compile it, jar it with a manifest, and then load it into the beanbox, as shown in the previous section. You should see that your bean is now represented by a small icon in the toolbox window of the beanbox. In the next section, we will add several more methods to the ConverterFieldBeanInfo class to provide more information about the bean to the design tool.

The Bean Info Class

Let’s consider the features a software component architecture needs to offer so it can allow code to be manipulated visually. These are the requirements:

  • The visual builder tools must be able to look at a bean class file and know what fields and methods it has, and what the parameters and return type are. With this knowledge, the tool can connect one bean with another.

  • The tools must be able to configure the appearance or behavior of a bean, typically by setting new initial values or defaults. An example would be setting the text on a button. We dealt with this in the previous chapter, under “customization.”

  • The tools should be able to save an instance of a bean object to a disk file, and later restore it with the same state and values. For example, once we have decided what text will appear on a button, we need to keep that information. That will allow the tool to remember new defaults and settings that are given to beans. Customized components can be saved and loaded from the beanbox file menu.

These three requirements are design time needs. The computer science terms for them are introspection, customization, and persistence, respectively. In this section you will see how to write a bean info class that accompanies your bean into the beanbox, and fulfills the first requirement by publishing the bean internal details to the visual design tool. You do not have to write a bean info class. Some or all of the information can be derived by the Java runtime system and the visual design tool using introspection. However, it’s good practice to provide a bean info class to help with the discovery of properties and other class information.

The Bean Info Class

Note: No further code needed for the beans. To avoid confusion, we emphasize that both our bean classes are 100% complete at this point. All further code that appears in this chapter (with the sole exception of revisiting firePropertyChange) relates to the Bean Info classes for the beans at design time, not the beans themselves.

A bean info class for each of your beans makes the design time task much easier. Instead of the visual builder tool showing absolutely every method, property, and event that meets its criteria, it can just display the ones you mentioned in the bean info class. The bean info class is also the place where you mention any icon GIF file that you would like to be associated with the bean in the toolbox window.

If you do not provide a bean info class, the tool will make its best guess about what the properties are. Without a bean info class, the property sheet window for ConverterField when you load it in the beanbox will look like Figure 24-1. These are all the properties of ConverterField, its parent JTextField, JTextField ’s parent, and so on, all the way back to java.lang.Object.

Properties of ConverterField without a BeanInfo class.

Figure 24-1. Properties of ConverterField without a BeanInfo class.

In many cases you will want to let every property in the inheritance chain be customizable at design time. To make this example easier to follow, we will code a ConverterField BeanInfo to restrict the properties to just the ones we are going to use directly. We do the same thing for DoItButton properties in the DoItButtonBeanInfo class. We write descriptors explicitly mentioning only the font, label, background color, and visible properties.

This section builds up to a complete bean info class for each of our beans. Bean info classes are always named as “the name of your class” with “BeanInfo” appended. If you call your bean XxxxBean, then the info class will be XxxxBeanBeanInfo. So here, the info classes will be called ConverterFieldBeanInfo and DoItButtonBeanInfo.

Bean info classes have to provide the eight methods promised by the java.beans.BeanInfo interface. The simplest way to do this is to extend the java.beans.SimpleBeanInfo class which is provided for this purpose. It implements all eight methods with no-ops saying “I don’t have any info on that.” You then provide versions of the methods you want to implement. Your methods will override the corresponding ones in SimpleBeanInfo. Following are the methods promised by the BeanInfo interface, and implemented by SimpleBeanInfo. The method names are highlighted in bold type; the return types are to the left of the method names.

How to Write BeanInfo

public interface java.beans.BeanInfo {

    public static final int ICON_MONO_32x32;  // other icon consts omitted

    public java.awt.Image                 getIcon(int);

    public java.beans.BeanDescriptor      getBeanDescriptor();

    public java.beans.EventSetDescriptor[] getEventSetDescriptors();

    public int                            getDefaultEventIndex();

    public java.beans.PropertyDescriptor[] getPropertyDescriptors();

    public int                            getDefaultPropertyIndex();

    public java.beans.MethodDescriptor[]  getMethodDescriptors();

    public java.beans.BeanInfo[]          getAdditionalBeanInfo();

}

public class java.beans.SimpleBeanInfo implements BeanInfo {

    // same 8 methods, returning empty or null info

    ...

}

  ...

//---------------------------------

  ...  then in other files, your code is:

public class ConverterFieldBeanInfo

         extends java.beans.SimpleBeanInfo { ...

      ... getPropertyDescriptors() { ... }

      ... getBeanDescriptor() { ... }

}


public class DoItButtonBeanInfo

                extends java.beans.SimpleBeanInfo { ...

      ... getPropertyDescriptors() { ... }

      ... getBeanDescriptor() { ... }

} 

Descriptor Classes

Beans are all about three things: properties, events, and methods. You will write descriptor objects to hold information about the bean properties, events, and methods for the design tool. You will put these descriptor objects in the bean info class for each bean. The descriptor classes are all part of the java.beans package, and you should review the javadoc-generated HTML pages in your browser. The classes are named PropertyDescriptor, EventSetDescriptor, and MethodDescriptor. There are other descriptors, too: BeanDescriptor and ParameterDescriptor.

Be clear about this point: The BeanInfo classes are solely to help with connecting beans at design time. Never put any of your processing logic into a bean info class, and remember that the BeanInfo class has finished its job after running at design time.

A descriptor sounds like it might be complicated. Luckily, it is not. A more familiar and accurate name would be Description, and that’s exactly what these classes do. Descriptors just gather together some strings and other data describing the properties, methods, events, and more of your bean. The details include:

  • the class that we’re providing information on

  • a string giving the name of the property, method, event, etc.

  • other related information specific to the kind of descriptor it is. For example, event descriptors also keep track of the type of listener. Method descriptors allow you to supplement the information with parameter descriptors and so on.

Let’s say your bean has a property called rate. That property will be described using an object of class java.beans.PropertyDescriptor. You call a constructor, passing the property name (“rate”) and your bean class (ConverterField.class) as arguments. That instantiates the property descriptor object for this property of this bean. You write descriptors for all the interesting attributes of your bean, and declare them in your bean info class for that bean. You don’t have to construct a descriptor object for every single thing that a bean contains—create them only for the things that you want to be visible at design time.

The visual tool will invoke methods in your bean info class at design time to get data structures that correspond to your bean class. That, along with introspection, tells the design tool what to display to the person connecting these beans. The beans introspection API is built on top of reflection mechanisms introduced with JDK 1.1. Reflection provides classes and interfaces for obtaining information about classes, methods, and fields loaded in the JVM. The class java.beans.Introspector uses classes in package java.lang.reflect to learn about the properties, events, and methods supported by a target Java Bean.

No information on reflection or introspection is provided here because it is only useful to programmers writing specialized software like bean development tools, or debuggers, or database drivers. It is enough to know that Java has support for discovering at runtime the kinds of methods, fields, and parameters that objects have, and even for generating parameters of the right type and invoking methods dynamically. This is what the beanbox uses the bean info for.

We’ll write the descriptors for the properties of our class, put them in an array, and make the array be returned as the value of the, e.g., getPropertyDescriptors() method that we will override in our extension of the java.beans.BeanInfo class. I did warn you that this topic can be a little abstract! Here’s the same information in Figure 24-2, and then we’ll look at it in code.

How descriptors are declared in a Bean Info class.

Figure 24-2. How descriptors are declared in a Bean Info class.

Properties

The word “property” comes directly from the Microsoft Windows world. It means some characteristic or default value of the bean that we will be able to set in the visual tool when we customize the bean. Customization is a very important part of component software, and you want to give bean integrators maximum freedom. The properties that our specification calls for are outlined in Table 24-1.

Table 24-1. Properties of the Two Beans

Bean

Properties

ConverterField

  • conversion rate value

  • font size and style

  • whether the display is currently a valid number

DoItButton

  • label on button

  • font size and style

  • background color of the button

  • hide/reveal the button when another component says to

Some properties (like button label) will be visible on the screen, and some (like the rate value) are not. Some things are visible at runtime, but are not customizable at design time, so they are not properties. The color that ConverterField displays on error (red) is an example of a field that we chose not to make customizable. The visual tool typically has a separate window that lists all the properties, and lets you edit their values. This window is known as a “property sheet,” a “property editor,” or a “property sheet editor.”

The bean info class explicitly tells the visual tool what properties should be displayed on the property sheet. Here is the bean info code to do that for the ConverterField.

Describing the Properties in a BeanInfo Class

import java.beans.*;
public class ConverterFieldBeanInfo extends SimpleBeanInfo {
   public PropertyDescriptor[] getPropertyDescriptors() {
     try {
       PropertyDescriptor rate =
          new PropertyDescriptor("rate", ConverterField.class);
       PropertyDescriptor font =
           new PropertyDescriptor("font", ConverterField.class);
       PropertyDescriptor validNum =
           new PropertyDescriptor("validNum", ConverterField.class);
       PropertyDescriptor props[] = {rate, font, validNum};
       return props;
     } catch (IntrospectionException e) {
            throw new Error(e.toString());
     }
   }
} 

Whenever you have to identify the class you are dealing with for the design tool, you use the java.lang.Class object. There is an object of that type for every class, and one way to get it is by invoking the getClass() method on any class or interface. The code above uses the equivalent and more convenient “ .class ” suffix that can be appended to the name of a class or object to get the same information.

Weird Exception Handling

Note the idiomatic exception handling in the BeanInfo classes, borrowed from the examples that Sun supplies. The method will throw an exception at design time if your descriptions are not accurate. You should send the exception back to the invoker. However, none of the methods in BeanInfo is defined to throw exceptions, so you cannot introduce them in the signature of your overriding methods. But you want to get that exception back to the caller! What you do is get the string from the original exception; e.toString() is used here, but e.getMessage() might be better.

Then construct a new Error instance using the String and send that back to the caller. The class java.lang.Error represents a serious runtime problem that a reasonable application should not even try to catch. So a method is not required to declare in its throws clause any subclasses of Error that might be thrown during its execution.

The bean design here for exceptions is certainly a little suspect. PropertyDescriptor is returning us an Exception which we turn into an Error in order to evade the need for declaring the exceptions. It would have been better to define getPropertyDescriptors() as able to throw an IntrospectionException. This appears to be an oversight on the part of Sun’s software engineers.

Back to Descriptors

You can and should provide descriptors for the interesting fields that you have from your parent class. In the DoItButton example, all the property fields come from the parent class. The source code for all the files in the example is on the CD, and listed at the end of the chapter.

After doing the compiling and jar creation mentioned earlier, start up the beanbox program, load your jar, and click your ConverterField bean into the beanbox window (see Figure 24-3). You will see that the property sheet window contains just these three properties, along with GUI controls for setting them. This is the result of the bean info code shown in the previous code example.

Properties of ConverterField with our BeanInfo class.

Figure 24-3. Properties of ConverterField with our BeanInfo class.

Next, click the DoItButton into the beanbox. Highlight the DoItButton bean, and change some of its properties. Give the button label a bold typeface and a pink background, for example.

Bound Properties

So far we have just been dealing with simple properties, but there are some variations on the theme. A simple property is the basic variety of property, which represents a single value. This could be an object such as the font used, or the background color, or the pathname to a datafile, or it could be a primitive value like a desired number of decimal places of precision, or the visibility status of a component.

Some properties are better represented by an array, not a single value. Say our converter field was modified to hold 25 different rates, perhaps representing conversion rates for the dollar against 25 different currencies on a particular day. It would be convenient to make rate be an array of double, rather than a double. This is termed an indexed property, but a better name would be an array property. With this kind of property, you need accessor methods to get/set the whole array, as well as individual indexed elements.

double[] getRate()
double getRate(int indx)
void setRate(double rates[])
void setRate(int indx, int rate); 

Apart from the index, an indexed property works exactly like a simple property.

So far we have just been dealing with simple properties that only affect their own class. But the whole point of component software is to make it easy for components to communicate state information to each other. The easiest way to do this is to use our old friend, the event.

There are two slight twists we can give a property concerning the way it affects other beans. We can “bind” a property to some similar property in another class. And we can “constrain” a property so that you can’t change its value without getting agreement from all the other beans that are tracking it. These two operations (changing a field in one bean to keep it consistent with a field in another bean, and letting some beans veto a proposed change in another bean) are felt to happen frequently enough that they are given special support in the beans component framework.

In other words, a property can be:

  • a bound property—. the property has a number of customers that need to hear the new value when it changes. This is done by sending an event to everyone who has registered as a listener. (A better name would be a “keep in sync” or “make same change” property.) If you explicitly write the listeners, then your code can take an arbitrary action based on the change to the bean property. If you don’t code the listeners, then the design tool will provide them for you, and they will simply make the same change in the property you are bound to.

  • a constrained property—. the property is a “bound” property, but one that also sends an event message before it changes value as well as after. You might like to think of this as an “agree to change” property. The listeners have the ability to refuse the proposed property value change. One place to use this kind of property is when the bean is collecting values, and listeners are in a better position to validate the values for consistency and accuracy. If a listener judges that some proposed new value for a property is invalid, it can prevent the change. An example would be a bean that represents “withdrawal transaction” and another bean that represents “account balance.” The second bean may veto a proposed withdrawal if it exceeds the amount in the account.

    All the properties in our beans are private fields, but we provide public accessors, effectively making them public. Obviously, in order to be useful to other classes, a property must be public.

Practical Example of a Bound Property: validNum

The next part of the specification to implement in our beans is the part that says the button should disappear when the text field contains a string that is not a valid double, and reappear when the user corrects it. All Swing components have a setVisible(boolean b) method that makes them disappear or reappear according to the argument. That method follows the property naming convention, and indeed is already a boolean-valued property of JButton and, hence, of DoItButton.

If you refer back a few paragraphs, you’ll see that we made validNum a boolean property of ConverterField. The validNum property keeps track of whether the String in the text field represents a double length floating point number. A string like “3.1412” does represent such a number, whereas a string like “3 eggs” does not.

So all we have to do is make DoItButton.visible track and move in lockstep with ConverterField.validNum, and we will automatically get our “button is only visible when display holds a number” behavior for free! It turns out that it is quite common for one property to need to track another in a different bean. And that is exactly what bound properties are for. We’ll make sure that visible is mentioned as a property in the DoItButtonBeanInfo class. We’ll also set an attribute saying that this is a bound property in ConverterFieldBeanInfo. You should add this line to the ConverterFieldBeanInfo after the existing line shown:

PropertyDescriptor validNum = ...  // no change to this line
validNum.setBound(true);     // add this line 

That code in the bean info class will give you the menu item in the beanbox, as shown in Figure 24-4.

The effect of myProperty.setBound(true) in the bean info class.

Figure 24-4. The effect of myProperty.setBound(true) in the bean info class.

When you select “Bind property” on the menu, another pop-up appears. This pop-up lets you specify the name of the source property that is being bound. Here you will choose “validNum” (see Figure 24-5).

Selecting “bind property” in the beanbox edit menu.

Figure 24-5. Selecting “bind property” in the beanbox edit menu.

The purpose of a bound property is to communicate changes between two beans. When the bound property changes at runtime, an event is sent to every bean that it was bound to at design time. Here we are using it to keep two boolean properties consistent. But the receiving beans can take whatever action is appropriate for their specification: execute a method, update a file, delete a record, spell check a document—anything.

By providing bean info saying that this property is bound in bean A, we make it show up on the beanbox “edit” menu item when bean A is highlighted. After you have selected the source property that you are binding, the stretchy red line appears. You stretch the red line to the component containing the property you want to bind to. A dialog will then pop up, allowing you to choose the receiving component.

When you choose the receiving component, a fourth dialog panel pops up to allow you to select the receiving property that you are binding to. The source property and the destination property that it is bound to must be assignment compatible (capable of being assigned directly to one another). With reference to Figures 24-6 and 24-7, bean A is the ConverterField, and bean B is the DoItButton.

Choosing bean B that bean A property will be bound to.

Figure 24-6. Choosing bean B that bean A property will be bound to.

Choosing the property in bean B that bean A property is bound to.

Figure 24-7. Choosing the property in bean B that bean A property is bound to.

“Under the hood” bound properties are implemented by the sending bean firing a PropertyChangeEvent to all receiving beans. The visual design tool will automatically generate Java code to listen for the PropertyChangeEvent that signifies a change in the bound property. The programmer must manually write the code (in the bean with the bound property) to fire the event when the property changes.

That is easily done by putting the event firing code in the setter method for this property. A rule for get/set methods is that all changes to properties must be done using these methods, even if a class has the ability to assign to the field directly. Earlier, we saw the code needed in ConverterField to make it fire property changes. This architecture comes together so neatly you really have to admire it. The code is:

public void setValidNum(boolean newVal) {
    boolean oldVal = validNum;
    firePropertyChange( "validNum", oldVal, newVal);
    validNum = newVal;
} 

There are several overloaded versions of firePropertyChange() in class java.beans.PropertyChangeSupport. The signatures are:

void firePropertyChange( PropertyChangeEvent evt );
void firePropertyChange( String propname, Object oldVal, Object newVal);
void firePropertyChange( String propname, int oldVal, int newVal);
void firePropertyChange( String propname, boolean oldVal, boolean newVal); 

According to the documentation, firePropertyChange() does not fire an event if the old and new value are equal and non-null. If you pass in null instead of the old value, as in setValidNum() above, then firePropertyChange can’t suppress changes that aren’t really a change. For each of these invocations, a PropertyChangeEvent will be fired, even though the property is just being set to the same value repeatedly. If you know that users of this bean are able to cope with duplicated change events, you can send null instead of the old value, as in the ConverterField bean.

The class javax.swing.JComponent has several overloaded firePropertyChange methods. It has the four methods mentioned above, plus additional ones for all the other primitive types (long, byte, float, etc.). If your component is a Swing-based bean, you can call firePropertyChange directly. If your component is not a visual bean, you need to declare a PropertyChangeSupport object and invoke the firePropertyChange() methods in that. It seems to be an oversight that java.beans.PropertyChangeSupport does not have all the overriding versions of firePropertyChange that are present in JComponent.

If you are following along online, making changes to the BeanInfo class for our two beans, recompiling and reloading the bean jar file in the beanbox, then connecting them, you should now see results. When you enter some text that is not a number in the ConverterField and tap “enter,” the field turns red and the button disappears! It will reappear when you change the text to something that is a number. We bound a boolean value of validNum in ConverterField to a boolean value of visible in DoItButton. It’s more common to bind identical properties (e.g., make sure two beans have the same font or background color). But as long as the types match, you can bind any properties together.

The beanbox works only with components and never shows code to you, but most of the other IDE tools treat beans as easily introspected classes and let you add code at design time. Recent versions of both JBuilder and CodeWarrior will actually pop up editable source code to hook beans together. That’s a curious approach. It makes it easier for programmers to use non-bean interfaces (bad), and it makes it harder for HTML coders to use beans (also bad). At the other end of the spectrum, IBM’s VisualAge is closer to the beanbox model. IBM takes beans very seriously.

Property Change Support for Non-Visual Beans

The code shown below, with some design time “glue” from the visual tool, lets one bean register as a listener with another. Something like this needs to be in all beans that have bound properties. The code was not added to our example beans above because it is automatically present for all Swing components (it is in javax.swing.JComponent). You must explicitly code these lines for beans that are not subclassed from Swing components.

private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener p) {
        pcs.addPropertyChangeListener(p);
}
public void removePropertyChangeListener(PropertyChangeListener p) {
        pcs.removePropertyChangeListener(p);
} 

This code is an example of the “Delegate” design pattern. It delegates the work (passes it on) to an instance of the java.beans.PropertyChangeSupport class. That instance then handles add and remove listener requests from other beans. You also need to use the design tool to ensure that other beans do add and remove themselves as property change event listeners to the bean with the bound property.

When you mark a property as bound in the bean info class, you are telling the visual design tool that this class will send out a property event change to all beans that have registered as listeners. There is only one property change event class that handles all properties, so you must provide a string that identifies which of the several possible bound properties in this class has changed. Use the property name as the string value. That string will be sent over as part of the property change event, as a parameter of firePropertyChange.

Summary: Telling Another Bean About a Change in One of Your Values

Table 24-2 summarizes how a bean that changes one of its property values will communicate that to some other bean.

Table 24-2. Bean That Generates the Changed Value

Purpose

Code

initial setup

(already present in JComponents)

private PropertyChangeSupport pcs = new
  PropertyChangeSupport(this);
public void
  addPropertyChangeListener(PropertyChange
  Listener p) {
      pcs.addPropertyChangeListener(p);
  }
public void
  removePropertyChangeListener(Property
  ChangeListener p) {
     pcs.removePropertyChangeListener(p);
  } 

send an event with the old and new changed values (needed in all beans with bound properties)

Double oldVal = new Double(prevNum);
  Double newVal = new Double(num);
pcs.firePropertyChange( "num", oldVal,
  newVal); 

Table 24-3 summarizes the code in a bean that needs to hear about a property change from some other bean.

Table 24-3. Bean That Needs to Hear About the Changed Value

Purpose

Code

initial setup

class ...  implements
java.beans.PropertyChangeListener
...
   // this is hooked up at design time by
beanbox
   //
someOtherBean.addPropertyChangeListener(this
); 

This event handler is called each time the value changes. You can either write an explicit property change handler, or let the default one be generated for you. Note: The code opposite illustrates typical code in a property change handler, but is not meant to be part of the beans we are developing.

void propertyChange( PropertyChangeEvent e) {
      String s = e.getPropertyName();
      if (s.equals("validNum")) {
          Boolean B =
  (Boolean)e.getNewValue();
          setVisible( B.booleanValue() );
          Object oldVal = e.getOldValue();
      } else if (s.equals("rate") {
          Object newVal = e.getNewValue();
          double dnew = ((Double) newVal
  ).doubleValue();
          ...
  } 

Fortunately, events in the beans world are exactly the same as events in the Java GUI world, and are generated and handled in exactly the same way. If an object is interested in something happening, it registers itself as a listener for that event with the thing that fires the event. When the thing happens, the event-causing object looks at its group of registered listeners and does a callback to each, saying in effect, “that event you said you were interested in just happened.”

Connecting a Swing Event to a Method in Another Bean

This section describes something very common: connecting an event (not a property) in one bean to a method in another bean. In this case, we want to connect the button press in DoItButton to the validate-and-multiply method in ConverterField. We have already seen how to do that in the beanbox (refer to the example that added a button to the Juggling animator bean).

The usual approach is taken to set things up in the BeanInfo files. As always, you do not have to provide a BeanInfo class. The beanbox will do introspection on the class to guess at what the events are. If you are following along with code as you read this chapter, try this example with and without providing event information in the BeanInfo class. You will see that the bean is simpler and more polished in the beanbox when you code the event set descriptor in the BeanInfo class.

The DoItButtonBeanInfo class will need an EventSetDescriptor describing the button push event, what you want it to be called on the beanbox menu, and the event it generates. The code that will be added to our DoItButtonBeanInfo class looks like this:

EventSet Descriptor Code

public EventSetDescriptor[] getEventSetDescriptors() {
// these are the events that this bean generates and wants to publish
    try {
      EventSetDescriptor push = new
EventSetDescriptor(DoItButton.class,
                                     "ActionEvent",

java.awt.event.ActionListener.class,
                                     "actionPerformed");
      push.setDisplayName("button push");
      EventSetDescriptor[] rv = { push };
      return rv;
    } catch (IntrospectionException e) {
            throw new Error(e.toString());
    }
} 

You may wonder why it is an EventSetDescriptor rather than just an Event-Descriptor. The reason is that in many cases, a given group or set of events are all delivered as different method calls within a single event listener interface. For example, the standard AWT keyboard event java.awt.event.KeyEvent represents any of three events (key pressed, key released, and key typed), all of which are delivered via the java.awt.event.KeyListener interface. Therefore, programmers need to keep in mind that they are usually dealing with a set of events, and will need to implement the correct method to receive each specific event.

The ConverterFieldBeanInfo class will need a MethodDescriptor describing the method that should be made visible. The code looks like this:

import java.lang.reflect.*;
   ...
public MethodDescriptor[] getMethodDescriptors() {
    try {
       Method m1 = ConverterField.class.getDeclaredMethod(
                                 "validateAndMultiply", null);
       MethodDescriptor validateAndMultiply = new MethodDescriptor(m1);
       MethodDescriptor md[] = {validateAndMultiply};
       return md;
    } catch (NoSuchMethodException e) {
            throw new Error(e.toString());
    }
} 

Make these additions to the bean info files, recompile, put into a jar, move the jar to the beanbox/jars directory, and start the beanbox up. Put these two beans on the beanbox and set the rate field to 1.15.

Hidden State

The purpose of the property sheet is to allow bean integrators to set initial values for properties. This is where we will enter our rate constant, by typing it into the field to the right of the text “rate.” You can see that it is currently set to 1.0, which was the initial value in our code (refer to the listing of ConverterField.java).

We will create a “tip calculator” component. The standard tip in the western world is 15%, so to calculate the bill plus 15% our rate should be 1.15. Set this by typing into the rate field on the property sheet. If you try to type something that is not a valid string for a double, the property sheet will not pass it on to the bean.

If you, as a programmer, intend to allow designers to provide a new value for any property, you must provide an additional descriptor in the bean info class. Of course, this is almost always the case. You need to provide a descriptor for the bean as a whole and set an attribute that says that it has “hidden state,” meaning you expect property changes at design time. The code you need to add to our two bean info files looks like this (the .class object will have a different name in the DoItButton bean, of course).

 public BeanDescriptor getBeanDescriptor() {
      BeanDescriptor bd = new BeanDescriptor(ConverterField.class);
      bd.setValue("hidden-state", Boolean.TRUE);
      return bd;
} 

The customizations are stored with the jar file that is written out of the design tool. If you fail to provide this descriptor, then the design time customizations will be lost after you leave the beanbox. It is not clear why hidden state is not assumed as the default behavior. This “hidden state” attribute was a last-minute addition right before the final release of beans, so we can only conjecture that it was for an important but obscure reason.

The Completed, Integrated Rate Calculator

Now the moment of truth. Connect the button event to the converter field validate-and-multiply routine, just as we did with the juggler bean button to the gif animator. Put a good value in the convertor field, say 60, and press the button. Check that the converter field now reads “69.0” and we’re done! We’ve created two beans and planted them together! (See Figure 24-8.)

The completed beans in the beanbox.

Figure 24-8. The completed beans in the beanbox.

At this point with a real set of beans, you would write them out to a jar file ready for deployment with a client application or a servlet. The rest of the chapter fills in the gaps with a few more bean-related topics.

Customizing More Complicated Properties

The beanbox’s property sheet editor knows how to accept and validate several types of properties: most primitive types, and types commonly seen in Swing components like Color and Font. But builder tools cannot know how to display and edit arbitrary classes. The class java.beans.PropertyEditorManager is used here. You have to write some code that implements the PropertyEditor interface and then register it with the PropertyEditorManager. At design time, when the visual tool finds a property which it doesn’t know how to edit, it will ask the PropertyEditorManager to locate a suitable editing class. If it can find a suitable editor, it will use it. Otherwise, the visual tool will issue a warning message.

Writing a PropertyEditor is nowhere near as hard as writing a general purpose editor. You only have to deal with getting and setting a few values that will ultimately be expressed in primitive types. There’s a PropertyEditorSupport class that you can start from. You can choose whether to give your editor a GUI in a pop-up window invoked from the property sheet, or restrict yourself to textfields. The bean info class will have an entry in the property descriptor that references the property editor.

There’s a level of sophistication beyond writing your own property editor. For a really involved bean, you might write a customizer. This is a class that displays a GUI allowing you to set any and all properties of bean in one place. It’s your own custom-written alternative to editing via the property sheet. The bean info class will have an entry in its bean descriptor that mentions the customizer, so the design tool knows to invoke it. The property sheet editor is adequate for your first few beans.

Constrained Properties

Constrained properties in a bean are one step further than bound properties. The two example beans we have been developing do not use constrained properties at all. You can skip this section entirely if you just want to work through the code of the two example beans.

A bean with a constrained property will send out a PropertyChangeEvent indicating that it wishes to make this change from old value to new value. Listeners can veto the change by throwing an exception which is rethrown back into the first bean. The “glue” code that non-visual beans need is shown below; Swing components already have this and you should not add it.

private VetoableChangeSupport vcs = new VetoableChangeSupport(this);
public void addVetoableChangeListener(VetoableChangeListener v) {
        vcs.addVetoableChangeListener(v);
}
public void removeVetoableChangeListener(VetoableChangeListener v) {
        vcs.removeVetoableChangeListener(v);
} 

The code in the bean with the constrained property will also need to fire the event, like this:

try {
    vcs.fireVetoableChange(propertyName, oldValue, newValue);
    // proposed change may now be made
    setXxxx( newValue );
    pcs.firePropertyChange(propertyName, oldValue, newValue);
}
catch (java.beans.PropertyVetoException pve) {
    // some bean vetoed the change
} 

If you look in the class java.beans.VetoableChangeSupport, you’ll see that the fireVetoableChange() method actually compares the old and new values, and will not fire the event if they are the same. You should be aware of this when you write your bean code. You’ll also want to add a line like this to the Bean Info class for the bean with the constrained property:

validNum.setConstrained(true); 

The property name used here is “validNum,” and you actually want to use whatever name you gave your constrained property. Adding this line to the bean info class makes an entry for “vetoableChange” show up in the “Edit | events” menu in the beanbox. You need to make sure you connect this entry at design time to the vetoableChange() method of all the beans that can veto the change. The beans that are receiving vetoable change events will have vetoable change listeners like this:

public void vetoableChange(PropertyChangeEvent ev)
             throws PropertyVetoException {
    Integer I = (Integer)(ev.getNewValue());
    int newVal = I.intValue();
    if (newVal < 0)
        throw new java.beans.PropertyVetoException(
            "proposed value is negative", ev );
} 

If a listener does veto the proposed change, then the fireVetoableChange() method automatically fires a new event reverting everyone to the old value and then rethrows the PropertyVetoException so your code gets a crack at it. The code example above shows how to read the new value in the Listener. The old value associated with the proposed change can be read in a similar way. The code example shows how an int value is passed. The property may be any type, of course. If it is a primitive type, the appropriate wrapper must be used (here, Integer) to turn it into an object. Your code needs to make the association between the property name string, and the type of values that property stores. You can get and use the property name from the event object with these lines of code:

String propName = ev.getPropertyName();
if (propName.equals("visible")) { ... 

All constrained properties should also be bound properties (so that the bean has a way to inform listeners when a value does successfully change). That means that the beans that listen to constrained properties must have the standard code for handling a property change event, namely something like this:

public void propertyChange(PropertyChangeEvent ev) {
  // check the property name
    String propName = ev.getPropertyName();
    if (propName.equals("visible")) {
          Boolean B = (Boolean)ev.getNewValue();
          setVisible( B.booleanValue() );
    }
} 

If you are binding one property to another of the same type, you can let the code be generated for you at design time. If the property change handler needs to do anything more sophisticated, you need to write the code explicitly.

Notice the symmetry here. The bean with the constrained property will fire two property change events to make a change—one from fireVetoableChange() and then (if no one objected) one from firePropertyChange(). The beans that are listening to the constrained property will handle the first event in vetoableChange, and the second in propertyChange. In all cases, the event that is sent and handled is a PropertyChangeEvent. Don’t let that confuse you.

Calling a Method in Another Bean—Don’t Do It!

Writing a bean that directly invokes a method in another bean is a mistake. Tempting as it is to try to do that, beans programming requires you to give up the style of programming where objects know about other objects and their methods. You cannot directly call a method of another bean, pass in arbitrary parameters, and get back an arbitrary return value. Think about it: you (usually) need to know the name of a method to invoke it, and you need to have an instance of a class to invoke instance methods on. But you do not want to build those kinds of dependencies into your beans.

You can get exactly the same effect by using the same tools we have already seen. The bean that needs to make a call should instead send an event to the other bean. The event should contain an object representing any arguments that need to be passed across. The handler for the event should unwrap the arguments and then make the call itself on its own method. Any return value can be sent back to the would-be invoker the same way, by sending it as an event.

The event that you send across either way can be a property change event (in which case you’ll probably want to make whatever provokes the need for the call be a property), or you can define your own event type by extending java.util.EventObject. If you create your own event type you’ll also need to support add and remove listener methods for it, and to fire the custom event. You must follow the standard event naming conventions here, or the visual design tool probably won’t recognize what you are trying to do. In the following lines of code we show the event-naming conventions using the fruit name “plum” where the event name should appear.

// visible to both beans
public class PlumEvent extends java.util.EventObject { ... }
public interface PlumListener extends java.util.EventListener {
    public void plumHappened(PlumEvent evt);
}

// in the bean sending the event, add 3 methods
    public synchronized void addPlumListener(PlumListener l) { ...
    public synchronize void removePlumListener(PlumListener l) { ...
    public void firePlumEvent(PlumEvent evt) {  ...

// in the bean receiving the event
public class SomeBean implements PlumListener {
    ...
       SendingBean.addPlumListener(this);
    ...
    public void plumHappened(PlumEvent evt) {
       this.myMethod( ... );
       ...
    }
    public void myMethod ( ... 

The design tool will take care of planting code to actually call addPlumListener() when you connect the beans.

Hasn’t this merely swapped a dependency on knowing a method name for a dependency on knowing an event name? Absolutely! That is the point. Anyone can send an event to anyone who has registered an interest in listening to that event. But you cannot call a method unless you have an instance of the object. In the first case, the bean that does the work has to register with the event generator. In the second case, the responsibility would be the other way around, and the identities are not known at compile time. Before leaving the topic, let us just mention that you can get instances of communicating beans. Any bean that has registered as a listener for one of your events has given you a reference to itself. Any bean that has sent you an event has given you a reference to itself, if you call the event.getSource() method. But you want to avoid these back doors in your beans, as they reduce flexibility and create unwanted interbean dependencies.

As an aside, the names we give events are not defined by any naming pattern. Hence, we see a variety of event names with different forms like keyTyped, actionPerformed, textValueChanged, propertyChange (not changed), and so on.

Exercises

  1. Hide the floating point inaccuracies in the rate calculator beans by rounding results to two decimal places. This exercise lets you make a small change and then practice rebuilding the beans from first principles.

  2. Modify the ConverterField so that it takes its background color from the DoItButton, i.e., make the DoItButton background color a bound property that tells ConverterField when it has changed. Make each button press cause the button to change to a new color.

  3. Make the button background color property change to a new primary color after each press. Bind the color to the ConverterField as in the previous exercise, but allow the ConverterField to veto a proposed color change if it would conflict with its “red means bad data entry” convention.

  4. Modify the two beans in this chapter so that users can type “s,” “c,” “t” on the DoItButton, and that character will replace the button label. It will also change the operation done by the ConverterField so that it provides the sine, cosine, or tangent (respectively) of the number on the display. The button should generate an event when the label changes, and the text field should listen for that event.

  5. It’s unrealistic to have a currency converter that doesn’t allow the rate to be updated at runtime. Find and program a way to allow the rate to be updated at runtime. Hint: support a special case of text that can be typed in the ConverterField. This will allow you to input a new rate and a new label for the button.

  6. Write a pocket calculator as a series of components. Don’t use the usual horrible GUI design of mimicking the appearance of a physical pocket calculator with one button per number. Instead, acquire your numbers in text fields, use sliders to specify the number of decimal places in the display, and use a radiobutton list to specify the operations. To do this easily, you need properties like “I am the left operand,” “I am the right operand,” “I am the result field.” You could also make three subclasses of your NumberField class for this purpose, and have three different beans.

Some Light Relief—Java’s Duke Mascot

Some Light Relief—Java’s Duke Mascot

In spite of appearances, Duke is not really the tooth fairy. Duke was created in 1992 by artist Joe Palrang. Joe was working for Sun’s Green project which preceeded Java when he was tasked with designing a figure to represent the user’s actions in the GUI. The Green project was an early set-top box for TVs. It sat in the user’s hands like a remote control and had a tiny display screen. The figure had to be small, which is why Duke is such a simple triangular shape. The hands, nose, and hat are simple visual cues to Duke’s position and direction. The team wanted a visual agent that could move, turn, and look expressive. Duke was the result.

Before he was called “Duke,” the figure had several other names including “tooth,” “fang,” and “the agent.” Joe didn’t like any of them, so he rechristened his creation “Duke” in vague honor of John Wayne. You can surely see the likeness to the cinematic gunslinger with the whiskey nose. The Green project came to an end without turning into a product, but the team members liked Duke and he became the mascot of the Java project that followed. Sun was pretty big on mascots at that time. You might remember “Network,” the dog who graced Sun’s advertising material and was brought on stage at a JavaOne conference. Network even toured Sun’s Menlo Park campus once, and all us dog lovers got to pet him. He was big, woolly, and patient. He sniffed my hand to check if I had anything edible up my sleeve. I checked his neck to see if he was carrying a flask of brandy. Both of us were slightly disappointed. Network’s kennel was situated outside Sun’s corporate headquarters in Palo Alto for several years.

No one was quite sure what role Duke should play in Java, so he has gone in and out of the spotlight over the past few years. He appeared in early demonstration software, then disappeared for a while, and resurfaced in some Java tutorials. He now limits himself to appearances on Sun’s Java website (java.sun.com), book covers, and live appearances at JavaOne (by a short person in a life-sized stuffed suit). Duke didn’t make any live appearances for several months in the late 1990s when the costume mysteriously disappeared from its storage closet in the Javasoft office building in Cupertino. Despite heart-rending email from the manager who was supposed to look after the costume, it never resurfaced, nor was a ransom note ever found. They made up a second costume and now Duke walks again. Since there’s no obvious connection with Java, the live appearances confuse a lot of people who haven’t heard the early history! They wonder if Duke is a penguin, perhaps suggestive of Linux. But no, Duke is just Duke.

Complete Code Listings

DoItButton.java

 import javax.swing.*;
public class DoItButton extends JButton {
    public DoItButton() {
        super("press for service");
    }
} 

DoItButtonBeanInfo.java

import java.beans.*;
import java.lang.reflect.*;
public class DoItButtonBeanInfo extends SimpleBeanInfo {
    public PropertyDescriptor[] getPropertyDescriptors() {
        try {
            PropertyDescriptor label =
             new PropertyDescriptor("label",
                               javax.swing.AbstractButton.class);
            PropertyDescriptor font =
             new PropertyDescriptor("font", DoItButton.class);
            PropertyDescriptor visible =
             new PropertyDescriptor("visible",
                                          java.awt.Component.class);
            PropertyDescriptor background =
             new PropertyDescriptor("background", DoItButton.class);
            PropertyDescriptor rv[] = {label, font, visible,
                                                        background};
            return rv;
        } catch (IntrospectionException e) {
            throw new Error(e.toString());
        }
    }
    public EventSetDescriptor[] getEventSetDescriptors() {
        // these are the events that this bean generates
        try {
            EventSetDescriptor push =
            new EventSetDescriptor(DoItButton.class,
                                   "ActionEvent",

java.awt.event.ActionListener.class,
                                   "actionPerformed");
            push.setDisplayName("button push");
            EventSetDescriptor[] rv = { push }; 
            return rv;
        } catch (IntrospectionException e) {
            throw new Error(e.toString());
        }
    }
   public BeanDescriptor getBeanDescriptor() {
      BeanDescriptor bd = new BeanDescriptor(DoItButton.class);
      bd.setValue("hidden-state", Boolean.TRUE);
      return bd;
    }
   public java.awt.Image getIcon(int iconKind) {
     if (iconKind == BeanInfo.ICON_MONO_16x16 ||
         iconKind == BeanInfo.ICON_COLOR_16x16 ) {
         java.awt.Image img = loadImage("JellyBeanIconColor16.gif");
         return img;
     }
     if (iconKind == BeanInfo.ICON_MONO_32x32 ||
         iconKind == BeanInfo.ICON_COLOR_32x32 ) {
         java.awt.Image img = loadImage("JellyBeanIconColor32.gif");
         return img;
     }
     return null;
   }
} 

ConverterField.java

 /* A bean that allows the user to type a number in a text
 * field and get it multiplied by a constant on demand.
 * Peter van der Linden, May 2001, Silicon Valley.
 */
import javax.swing.*;
import java.awt.Color;
import java.awt.event.*;
public class ConverterField extends javax.swing.JTextField
           implements KeyListener, java.io.Serializable {
  // holds the rate that we will multiply with the TextField
  // the rate will be set at design time, and doesn't change
  private double rate = 1.0;
  public double getRate() {  return rate; }
  public void setRate(double d) {
      rate = d;
      this.setText("rate set to " + Double.toString(d));
  }
  private boolean validNum = true;
  public boolean isValidNum() {  return validNum; }
  public void setValidNum(boolean newVal) {
      boolean oldVal = validNum;
      firePropertyChange( "validNum", oldVal, newVal);
      validNum = newVal;
  }
  public ConverterField() {
      super("0",16);
      setBackground(Color.white);
      addKeyListener(this);
  }

  public void keyPressed(KeyEvent e) {}
  public void keyReleased(KeyEvent e) {}
  public void keyTyped(KeyEvent e) {
      // ConverterField generates these events, and
      // validates the number when user hits "enter".
      if (e.getKeyChar() == KeyEvent.VK_ENTER) {
           validateNumber();
      }
  }
  private double n;
  public void validateNumber() {
      // sets a double from the text field, and
      // sets the textfield color to red if invalid.
      String s = this.getText();
      try {
          n = Double.parseDouble(s);
            // success -- n holds the number 
          setBackground(Color.white);
          setValidNum(true);
      } catch (NumberFormatException nfe) {
            // failed to parse TextField as number
            // set background to red!
          setValidNum(false);
          requestFocus();
          setBackground(Color.red);
          n = Double.NaN;
      }
  }
  public void validateAndMultiply() {
      // we do this when another bean tells us to
      validateNumber();
      if ( !(isValidNum()))  return;
      n = n * rate;
      setText( Double.toString(n) );
  }
} 

ConverterFieldBeanInfo.java

import java.beans.*;
import java.lang.reflect.*;

/**
 * BeanInfo for the ConverterField.
 */
public class ConverterFieldBeanInfo extends SimpleBeanInfo {
    public PropertyDescriptor[] getPropertyDescriptors() {
        try {
            PropertyDescriptor rate =
                 new PropertyDescriptor("rate",
                                              ConverterField.class);
            PropertyDescriptor font =
                 new PropertyDescriptor("font",
                                              ConverterField.class);
            font.setBound(true);
            PropertyDescriptor validNum =
                 new PropertyDescriptor("validNum",
                                              ConverterField.class);
            validNum.setBound(true);
            PropertyDescriptor rv[] = {rate, font, validNum};
            return rv;
        } catch (IntrospectionException e) {
            throw new Error(e.toString());
        }
    }
    public MethodDescriptor[] getMethodDescriptors() {
        try {
            Method m1 = ConverterField.class.getDeclaredMethod(
                                 "validateAndMultiply", null);
            MethodDescriptor validateAndMultiply = new
                                               MethodDescriptor(m1);
            MethodDescriptor md[] = {validateAndMultiply};
            return md;
        } catch (NoSuchMethodException e) {
            throw new Error(e.toString());
        }
    }
    public BeanDescriptor getBeanDescriptor() {
      BeanDescriptor bd = new BeanDescriptor(ConverterField.class);
      bd.setValue("hidden-state", Boolean.TRU E);
      return bd;
    }
    public java.awt.Image getIcon(int iconKind) {
        if (iconKind == BeanInfo.ICON_MONO_16x16 ||
           iconKind == BeanInfo.ICON_COLOR_16x16 ) {
           java.awt.Image img = l 
oadImage("JellyBeanIconColor16.gif");
           return img;
        }
        if (iconKind == BeanInfo.ICON_MONO_32x32 ||
           iconKind == BeanInfo.ICON_COLOR_32x32 ) {
           java.awt.Image img =
loadImage("JellyBeanIconColor32.gif");
           return img;
        }
        return null;
    }
} 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.134.103.74