Previous | Next | Trail Map | To 1.1 -- And Beyond! | Migrating to 1.1


How to Convert Code that Uses the AWT

Why should you update a program to the 1.1 AWT API? Here are five possible reasons:

Converting a 1.0 program to the 1.1 AWT API usually requires two steps:

  1. Replace deprecated methods with their 1.1 equivalents.
  2. Convert the program's event-handling code to use the new AWT event system.
The following two sections describe each step in detail, and then give an example.

Step 1: Replace Deprecated Methods with Their 1.1 Equivalents

Because so many methods were deprecated JavaSoft provides a script to help you convert 1.0 programs to the 1.1 versions. This script, called updateAWT, uses on a UNIX utility called sed to change the names of many deprecated 1.0 AWT methods into their 1.1 equivalents. If you're developing on a PC, you might be able to run the script using a product such as MKS Toolkit, which provides PC versions of UNIX tools. For instructions, see Platform-specific Details: Using the updateAWT Script.

You'll probably have to make some changes by hand, whether or not you use the updateAWT script. As described in How to Convert Your Program, you can get a list of the deprecated methods in the program by compiling its files with the -deprecation flag.

Once you know which deprecated methods the program uses, look up each method in the section Alternatives to Deprecated Methods in the AWT. Then replace each deprecated method with its 1.1 alternative. Most of the changes you need to make are straightforward, except those to event-handling code, which are described in the next section.

Step 2: Convert Event-Handling Code

In 1.0, the Component handleEvent method (along with the methods it called, such as action) was the center of event handling. Only Component objects could handle events, and the component that handled an event had to be either the component in which the event occurred or a component above it in the component containment hierarchy.

In 1.1, event handling is no longer restricted to objects in the component containment hierarchy, and the handleEvent method is no longer the center of event handling. Instead, objects of any type can register as event listeners. Event listeners receive notification only about the types of events they've registered their interest in. Never again will you have to create a Component subclass just to handle events.

When upgrading to the 1.1 release, the simplest way to convert event-handling code is to leave it in the same class, making that class a listener for that type of event. That's the scheme that this section illustrates.

Another possibility is to centralize event-handling code in one or more non-Component listeners. This approach lets you separate the GUI of your program from implementation details. It requires that you modify your existing code so that the listeners can get whatever state information they require from the components. This approach can be worth your while if you're trying to keep your program's architecture clean.

We recommend that you do not mix the 1.0 event model with the 1.1 event model in the same program. The results would be unpredictable and might be difficult to debug.

Making a Component a Listener

The process of making a Component a listener can be straightforward, once you figure out which events a program handles and which components generate the events. If you're using a Java compiler from JDK 1.1.1 or later, then compiling with the -deprecation flag generates a list that includes all old-style event handling methods. (Before 1.1.1, the compiler didn't generate a complete list, so you had to search for "Event" in a source file.) While you're looking at the code, you should note whether any classes exist solely for the purpose of handling events; you might be able to eliminate such classes.

Here are the steps to follow when converting a 1.0 component into a listener:

  1. Figure out which components generate each event type. The Event-Conversion Table can help you know what to look for. For example, if you're converting event code that's in an action method, the table tells you to look for Button, List, MenuItem, TextField, Checkbox, and Choice objects.

  2. By looking up each event type in the Event-Conversion Table, note which listener interfaces the listener should implement, and which methods within each interface should contain event-handling code. For example, if you're trying to handle an action event generated by a Button, the table tells you to implement the ActionListener interface, putting the event-handling code in the actionPerformed method

  3. Change the class declaration so that the class imports the necessary types and implements the appropriate listener interfaces:
    import java.awt.event.*;
    ...
    public class MyClass extends SomeComponent
                         implements ActionListener {
    
    Alternative: Instead of implementing an interface, you can declare an inner class that extends an event adapter class. Inner classes are useful when you need to implement only one method of an interface, but the interface contains many other methods.

  4. Create empty implementations of all the methods in the listener interfaces your class must implement. Copy the event-handling code into the appropriate methods. For example, ActionListener has just one method, actionPerformed. So as a shortcut way of creating the new method and copying the event-handling code to it, you can simply change the signature of an action method from this:
    public boolean action(Event event, Object arg) {
    
    to this:
    public void actionPerformed(ActionEvent event) {
    
    Alternative: If you use an adapter subclass to handle the events, you don't need to create empty implementations of methods.

  5. Modify the event-handling code in these ways:
    • Delete all return statements.
    • Change references to event.target to be event.getSource().
    • Delete any code that unnecessarily tests for which component the event came from. Now that events are forwarded only if the generating component has a listener, you don't have to worry about receiving events from an unwanted component.
    • Perform any other modifications required to make the program compile cleanly and execute correctly.

  6. Determine where the components that generate the events are created. Just after the code that creates each one, register this as the appropriate type of listener. For example:
    newComponentObject.addActionListener(this);
    
    Alternative: If you use an inner class to handle the events, register an instance of that inner class instead.

  7. Compile and test your program. If your program is ignoring some events, make sure you added the correct event-listener object as a listener to the correct event-generating object.

Example: Converting DialogWindow

This section shows how to convert a program called DialogWindow from the 1.0 API to the 1.1 API. DialogWindow is an application, but it can also run as an applet, with the help of a class named AppletButton. The DialogWindow program is featured in the section How to Use Dialogs.

You can find the 1.0 source code for DialogWindow (DialogWindow.java) in the online version of this tutorial.

Step 1: Replace Deprecated Methods with Their 1.1 Equivalents

  1. Move the 1.0 source and bytecode files to a safe place, keeping a copy of the source file that you can modify. For example, on a UNIX system:
    % mkdir 1.0example
    % mv DialogWindow.class 1.0example
    % cp DialogWindow.java 1.0example
    
  2. Perform as much automatic conversion as possible. For example:
    % updateAWT DialogWindow.java > tmp.java
    % diff DialogWindow.java tmp.java
    33c33
    <         dialog.show();
    ---
    >         dialog.setVisible(true);
    38c38
    <         textArea.appendText(text + "\n");
    ---
    >         textArea.append(text + "\n");
    47c47
    <         window.show();
    ---
    >         window.setVisible(true);
    87c87
    <         hide();
    ---
    >         setVisible(false);
    % mv tmp.java DialogWindow.java
    
  3. Compile DialogWindow, making sure to use the 1.1 compiler. For example:
    % which javac
    /usr/local/java/jdk1.1.1/solaris/bin/javac
    % javac DialogWindow.java
    Note: DialogWindow.java uses a deprecated API.  Recompile with
    "-deprecation" for details.
    1 warning
    %
    
    % javac -deprecation DialogWindow.java
    DialogWindow.java:18: Note: The method boolean handleEvent(java.awt.Event) in
    class java.awt.Component has been deprecated, and class DialogWindow overrides
    it.
        public boolean handleEvent(Event event) {
                       ^
    DialogWindow.java:26: Note: The method boolean handleEvent(java.awt.Event) in
    class java.awt.Component has been deprecated.
            return super.handleEvent(event);
                                    ^
    DialogWindow.java:29: Note: The method boolean action(java.awt.Event,
    java.lang.Object) in class java.awt.Component has been deprecated, and class
    DialogWindow overrides it.
        public boolean action(Event event, Object arg) {
                       ^
    DialogWindow.java:81: Note: The method boolean action(java.awt.Event,
    java.lang.Object) in class java.awt.Component has been deprecated, and class
    SimpleDialog overrides it.
        public boolean action(Event event, Object arg) {
                       ^
    Note: DialogWindow.java uses a deprecated API.  Please consult the
    documentation for a better alternative.
    5 warnings
    %
    
    As you can see, the example compiles successfully. However, a few calls or overrides of deprecated methods remain. The Event arguments to these methods are a tell-tale sign that the methods contain event-handling code.

    Step 2: Convert Event-Handling Code

    To convert DialogWindow, follow the sequence described previously in Making a Component a Listener.

    1. The first and hardest task is determining which components generate events that must be handled. Compiling with the -deprecation flag tells us that DialogWindow has three 1.0-style event-handling methods:
      • DialogWindow.handleEvent
      • DialogWindow.action
      • SimpleDialog.action

      Studying DialogWindow's code tells us these things about its event handling architecture:

      • DialogWindow handles window destroy events for itself.
      • DialogWindow handles action events for the components it contains. Upon closer inspection, we can see that it contains only one component that can generate action events: a Button.
      • SimpleDialog handles action events for the components it contains. Upon closer inspection, we can see that it contains three components that can generate action events: two Buttons (Cancel and Set) and a TextField.
      • Both DialogWindow and SimpleDialog contain non-event-handling code, so you can't eliminate them by moving their event-handling code elsewhere.

    2. Now that we know which objects generate which events, it's easy to determine which interfaces the event handlers should implement. Consulting the Event-Conversion Table tells us that WINDOW_DESTROY corresponds to the windowClosing method of WindowListener, and that action events generated by buttons and text fields are handled by the actionPerformed method of ActionListener.

    3. Now all that's left is to implement the event-handling methods and register the event listener. The following example gives the highlights of the event-related code converted DialogWindow program. Significant changes are in bold.
      import java.awt.event.*;
      
      public class DialogWindow extends Frame 
      			  implements WindowListener,
      				     ActionListener {
          ...
          public DialogWindow() {
      	...
              Button button = new Button("Click to bring up dialog");
      	button.addActionListener(this);
      	...
      	addWindowListener(this);
          }
      
          public void windowClosed(WindowEvent event) {
          }
      
          public void windowDeiconified(WindowEvent event) {
          }
      
          public void windowIconified(WindowEvent event) {
          }
      
          public void windowActivated(WindowEvent event) {
          }
      
          public void windowDeactivated(WindowEvent event) {
          }
      
          public void windowOpened(WindowEvent event) {
          }
      
          public void windowClosing(WindowEvent event) {
              if (inAnApplet) {
                  dispose();
              } else {
                  System.exit(0);
              }
          }
      
          public void actionPerformed(ActionEvent event) {
              if (dialog == null) {
                  dialog = new SimpleDialog(this, "A Simple Dialog");
              }
              dialog.setVisible();
          }
          ...
      }
      
      class SimpleDialog extends Dialog implements ActionListener {
          ...
          SimpleDialog(Frame dw, String title) {
      	...
              field = new TextField(40);
      	field.addActionListener(this);
      	...
              Button b = new Button("Cancel");
      	b.addActionListener(this);
              setButton = new Button("Set");
      	setButton.addActionListener(this);
      	...
          }
      
          public void actionPerformed(ActionEvent event) {
      	Object source = event.getSource();
              if ( (source == setButton)
                 | (source == field)) {
                  parent.setText(field.getText());
              }
              field.selectAll();
              setVisible(false);
          }
      }
      
      Instead of implementing the WindowListener interface, DialogWindow could simply contain an inner class that extends WindowAdapter. This change makes the empty method bodies unnecessary.

      The following are the highlights of a DialogWindow class that uses an inner class to handle window events. Significant changes from the 1.1 window listener version are in bold. (To see the whole program, go to DialogWindow.java.)

      public class DialogWindow extends Frame 
      			  implements ActionListener {
          ...
          public DialogWindow() {
      	...
              Button button = new Button("Click to bring up dialog");
      	button.addActionListener(this);
      	...
      	addWindowListener(new WindowAdapter() {
                  public void windowClosing(WindowEvent event) {
                      if (inAnApplet) {
                          dispose();
                      } else {
                          System.exit(0);
                      }
      	    }
      	});
          }
      
          ...//No empty windowXXX method implementations!...
      
          ...
      }
      
    By using the DialogWindow program, both as an applet and as an application, we can see that it handles all events properly.


Previous | Next | Trail Map | To 1.1 -- And Beyond! | Migrating to 1.1