Find out how the Swing GUI component set utilizes the Command pattern for easy support of undo/redo Not too long ago, Sun’s Java division introduced the JFC, a comprehensive set of UI components and foundation services that give Java developers more flexibility to determine the look and feel of their applications. The UI component library, called Swing, is a whole lot more than a stripped-down set of lightweight UI components. Swing takes Java applications a step forward by allowing easy implementation of application services like undo — the topic of our discussion today. Historically, application frameworks (for example, MacApp framework) have based the design of the undo/redo mechanism around the Command pattern. And that’s what we’re going to do as well. We’ll discuss the Command pattern and describe how it supports the design of undo/redo systems. We’ll then examine Java’s support for the pattern and see how Swing’s undo package adds the missing functionality, providing you with a complete undo/redo mechanism.Requirements from an undo/redo mechanismUndo allows users to correct their mistakes and also to try out different aspects of the application without risk of repercussions. At minimum, an undo/redo mechanism should provide users with the ability to:Unexecute (undo) the last action they just performedRe-execute (redo) the last undone actionUndo and redo several recent actions (preferable, but optional)In order to design such a mechanism, we must treat the user’s operations as individual atomic actions (self-contained actions that know how to undo/redo their effect on the application state) that should be stored for undo or redo later on. We can fulfill these requirements by using design patterns, specifically the Command pattern. If you are already familiar with the design patterns (specifically the Command pattern and how Java supports it), you can skip the next three sections and move right to “The undo/redo mechanism in Swing.”Design patternsIn a nutshell, design patterns encourage design reuse by providing established, successful design solutions for particular situations (also known as contexts). You may be wondering why I’m discussing design patterns in an article devoted to undo/redo mechanisms. It’s really quite simple. In addition to presenting you with the technical implementation issues, I think it’s important that you understand the design essentials of an undo/redo systems.According to Design Patterns: Elements of Reusable Object-Oriented Software by the now infamous Gang of Four (see Resources for more information), the essential parts of any pattern are:Intent — The design goal that this pattern addressesApplicability — In what situation the pattern can be appliedStructure — The design solution to the design problemConsequences — The trade-off of the solutionA number of different patterns exist, but we’re concerned only with the one that addresses undo/redo capabilities: the Command pattern. The Command pattern Once again, according to Design Patterns, the purpose of the Command pattern is to:Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.Let’s see how this works. At the heart of the pattern is the Command interface, which defines the execute() method.001 public interface Command { 002 003 public void execute(); 004 005 } All user actions (such as insert text, cut, paste; any operation that the user can perform) are encapsulated as command objects. A command object is a class that implements the function of performing the user’s requested operation. For example, in a text editor when the user types in some text, we do not add this text directly to the document within the UI code; instead we create an InsertTextCommand object that we then apply to the text document.All command objects must implement the Command interface. This interface declares an execute() method that should invoke the actual command operation on the target object. The following snippet shows a cut command for a text editor application. 001 public class CutCommand implements Command{ 002 003 private TextArea target_; 004 005 public CutCommand(TextArea area) { 006 target_ = area; 007 } 008 009 public execute() { 010 011 // target_.cut(); 012 int startPos = target_.getSelectionStart(); 013 int endPos = target_.getSelectionEnd (); 014 String text = target_.getText (); 015 target_.setText (text.substring (0, startPos) + text.substring (endPos)); 016 017 } 018 019 } When a CutCommand object is created, it’s initialized with its target — in this case, a text area. To build a text editor with cut capabilities, we attach this CutCommand to the editor’s Cut menu item with the text area as its target. When the Cut menu item is selected, the execute() method of this command object is automatically invoked and the cut operation is performed as desired.From a design perspective, the important thing about the Command pattern is that it treats each user operation as a first-class object. If we add unexecute() and reexecute() methods to these objects (that is, to the Command interface), we can then support unexecution (undo) or re-execution (redo) of operations, giving us a basic undo/redo mechanism.Of course, adding undo and redo capabilities to this mechanism requires “smarter” commands. Not only should a command be able to invoke an operation on its target, but it should also be able to undo or redo that operation when prompted. For example, the cut command would store the selected string and location before the cut, allowing it to return the selection to the text area if its undo() method is invoked. Encapsulating each operation as a separate first-level object means that we can also easily support multilevel undo/redo operations: We store each command operation that the user performs in a history list. When the user selects undo, we perform the undo operation of the current item in the list and then step backwards to the previous item. To redo an operation, we execute the redo operation of the next item in the list and step forward. If the user performs a new command after some undo operations, we clear the front of the list to disable further redos.However, because the Command pattern is attached to the menu item only at initialization, we must provide the means to duplicate commands and store them inside the history list. We can accomplish this using two different approaches:Use the Prototype pattern — Define the clone () method for each command. After the command is executed, it is cloned and stored inside the history list.Separate the effect of the command from the actual execution — When the command is executed, it creates a new “effect” object, which stores the effect of the command. This object is then stored in the history list.The Swing designers chose the latter approach. Java support for the Command pattern — event listeners The closest support that Java provides for the Command pattern is the AWT delegation event model. Within this framework, command objects implement AWT listener interfaces instead of the Command interface. To associate a command object with an AWT component, we simply register it as an event listener. The component knows that the listener interface is there but it doesn’t care how you implement it; that is, it doesn’t care what the actual command operation is.For example, to support the cut operation we would provide an ActionListener that implements the actionPerformed() method (in place of execute()) to perform the cut operation, and would then register this with the Cut (MenuItem addActionListener()) method. Unfortunately, this system does not support the easy undo/redo facilities of the Command pattern. To support undo, each operation must result in a separate command object being instantiated. These objects can then maintain local state information about the effects of the operation they performed. Within the AWT model, however, just a single command object is attached to each operation and is repeatedly invoked for the user’s actions.Enter Swing.The undo/redo mechanism in SwingIn Swing, the effect of a user action is stored separately from the listener implementation. Each effect is stored in an object that implements the UndoableEdit interface. As before, just a single command object (event listener) is instantiated and registered with an AWT component; however, each time this command object is invoked, a new UndoableEdit object is created to describe the effect of the operation. These effect objects take the place of the local state information in traditional command objects. The following is a Unified Modeling Language (UML) class diagram of Swing’s undo mechanism. UML is an industry-standard language for specifying software systems; in this case, it is a useful standard for describing the classes of the Swing undo mechanism.Here’s how it works.As I mentioned a moment ago, the effect of a user action is stored in an UndoableEdit object. For convenience, Swing includes an AbstractUndoableEdit class that provides default implementations of the various methods of this interface. So instead of directly implementing UndoableEdit, you can simply subclass AbstractUndoableEdit and only implement those methods that you require. For every type of edit you want to support (an edit being the effect of a command that the user can invoke, such as inserting or cutting text), you must provide a subclass of AbstractUndoableEdit that encapsulates information about the effect of the command and provides facilities for undoing this effect. In essence, the concept of a command object has been replaced by a listener class that implements the command operation and an edit class that encapsulates information about the effect of each execution of this command.Sometimes, an edit will actually consist of a sequence of other, simpler edits (for example, a global-replace edit will be a sequence of individual replace edits). This type of edit can be captured by the CompoundEdit class. This class implements UndoableEdit and overrides the undo() method to invoke the undo() on each of its children in reverse order.The next aspect of the Swing undo mechanism is the undo listener. Undo listeners are objects that implement the UndoableEditListener interface and are notified with UndoableEditEvent objects each time an undoable edit occurs. A special kind of listener is the UndoManager. When this listener is notified about an UndoableEditEvent, it extracts the edit from the event and stores it a queue. More about the UndoManager in a moment. An application that supports undoable edits must provide the addUndoableEditListener() and removeUndoableEditListener() methods that allow UndoManagers to be registered.Following the JavaBeans event model, Swing provides you with an UndoableEditSupport class to easily manage your listeners. You register your listeners with addUndoableListener() and deregister with removeUndoableListener(). To notify registered listeners of an edit, simply invoke the postEdit() method. This method automatically creates an UndoableEditEvent and passes it to each listener’s undoableEditHappened() method. Additionally, this class provides simple support for performing compounds edits, allowing a sequence of simple edit operations to be automatically combined into a single CompoundEdit.Finally, Swing provides convenient support for adding a multilevel undo function to your application by supplying the UndoManager class, which implements UndoableEditListener and acts as a history list. The UndoManager class saves you from having to manually store all the edits performed by the user; it automatically stores these edits and provides pointers to the current undo and redo edits. Each time an undoable edit occurs (for example, a new element is added to a list) the UndoManager is notified, and the edit is added to its internal queue. You can set the queue limits by calling setLimit() on this manager. The following figure illustrates what happens inside the undo queue.That’s pretty much it. Now we can apply this foundation to a real example.Hands on exampleWe’re going to build a simple program to showcase Swing’s undo/redo mechanism. Our applet, shown below, allows the user to add and remove elements to a JList component. When the user adds or removes an element, the applet stores the effect of that operation — in either an AddEdit or RemoveEdit object — for undo later.Of course, to view and manipulate the applet, you must have Swing installed on your system. See Installing Swing for step-by-step instructions on the installation process. CODEBASE = “.” CODE = “undoapplet.LunchApplet.class” NAME = “TestApplet” WIDTH = “96” HEIGHT = “96” HSPACE = 0 VSPACE = 0> You need a Java-enabled browser to view this applet.Note: You must use a JDK 1.1-compliant browser to access this applet. Options include Netscape Communicator 4.0x with the AWT 1.1 support patch, Netscape Communicator 4.05 pre-beta release for Windows 95/NT, and Appletviewer 1.1. (See Resources for links to upgrade Communicator 4.0x and the latest version of Communicator).Enough talk, let’s code.The following classes comprise the example applet:LunchApplet — Starting point for the undo appletUndoPanel — The main program classAddEdit — Captures the effect of adding elements to the listRemoveEdit — Captures the effect of removing elements from the listAddAction — Inner class of UndoPanel; the add commandRemoveAction — Inner class of UndoPanel; the remove commandWe’ll begin with AddEdit.001 class AddEdit extends AbstractUndoableEdit { 002 003 private Object element_; 004 005 private int index_; 006 007 private DefaultListModel model_; 008 009 public AddEdit(DefaultListModel model, Object element, int index) { 010 model_=model; 011 012 element_ = element; 013 014 index_=index; 015 } 016 017 public void undo() throws CannotUndoException { 018 019 model_.removeElementAt(index_); 020 021 } 022 023 public void redo() throws CannotRedoException { 024 model_.insertElementAt(element_,index_); 025 } 026 027 public boolean canUndo() { return true; } 028 029 public boolean canRedo() { return true; } 030 031 public String getPresentationName() { return "Add"; } 032 034 } In the constructor (009-015) we store all the information needed to unexecute/re-execute an add action, including:The element that was added to the listThe element indexThe list model itself (the receiver): The DefaultListModel class is a simple Vector-like interface to accessing the contents of a Swing JList componentThe undo() method (017-021) removes the element from the list, while redo() (023-025) inserts it back in. The getPresentationName() method, returns the name to be used for the undo and redo menu items. Note that if you inherit from AbstractUndoableEdit, Swing will handle the getUndoPresentationName() and getRedoPresentationName() return values by adding either “undo” or “redo” to the value returned from the getPresentationName() method.Now let’s examine the add operation itself. The following segment defines the action object attached to the Add button. Action is a new Swing interface that makes the UI the central point of control. That is, an action can be added directly to a toolbar (resulting in a new button), or to a menu (resulting in a new menu item). When the action changes one of its properties (for example, becomes enabled or disabled), the UI elements are notified and change their state accordingly. For example, when CutAction becomes disabled, both the Cut toolbar button and the Cut menu item will be disabled as well. In any case, the Action interface encapsulates the ActionListener interface for handling ActionEvent events, and a description of the action itself.When added to a container that supports Action, like JToolBar or JMenu, the Action item is queried to determine details of the component to be produced and is then automatically registered for UI events. The container registers the new component as a PropertyListener of the action.AbstractAction is a concrete implementation of this interface that provides default implementations of all the new methods. In this case, however, we use only the listener aspect of Action.001 private class AddAction extends AbstractAction { 002 003 public void actionPerformed(ActionEvent evt) { 005 // always add to the end of the JList 006 int NumOfElements = elementModel_.getSize(); 007 // however, give the element its ID number 008 Object element = new String("Foo " + _lastElementID); 009 010 // record the effect 011 UndoableEdit edit = new AddEdit(elementModel_, 012 element, NumOfElements); 013 // perform the operation 014 elementModel_.addElement(element); 015 016 // notify the listeners 017 undoSupport_.postEdit(edit); 017 018 // increment the ID 019 _lastElementID ++ ; 020 021 } 022 023 } The AddAction class:Creates a new element (008)Creates a new AddEdit object and passes it the ListModel (the receiver of the action), the index of the new element, and the element itself (011)Performs the actual add operation (014)Notifies the undo listeners by calling postEdit on the undoSupport objects (017)Note that the AddAction class is a private inner class of our undo applet. This approach guarantees direct access to private members of the applet (for example, undoSupport_). Generally, I prefer to define actions as inner classes of the object that handles them, both to prevent bloated interfaces and to avoid breaking encapsulation (by exposing the object’s internal structure — for instance, where an external add action might want access to the ListModel).UI consistency Of course, for a simple undo system, you will likely want to register the UndoManager as a sole listener to UndoableEvents, providing an UndoAction that shows only a generic Undo label and invoking undo() on the manager.However, for a more sophisticated UI, you will want to provide the user with the last undoable operation. For instance, instead of just showing Undo you’ll want to show Undo Cut.The following code snippet shows how to provide an UndoAdaptor class that updates the state of the undo components (in this case, Undo and Redo buttons) according to the new state of the undo history list.001 private class UndoAdaptor implements UndoableEditListener { 002 003 public void undoableEditHappened (UndoableEditEvent evt) { 004 005 UndoableEdit edit = evt.getEdit(); 006 007 undoManager_.addEdit(edit); 008 009 refreshUndoRedo(); 010 } 011 } The UndoAdaptor is registered in the UndoableEditSupport during the application setup. Each time an undo event occurs, the adaptor:Extracts the edit from the event (005)Adds it to the UndoManager (007)Refreshes the undo-related GUI state (009)An alternative approach would be to implement a subclass of UndoManager that overrides the addEdit() method to automatically refresh our user interface.Here’s the refreshUndoRedo() method.001 public void refreshUndoRedo() { 002 003 //refresh undo 004 005 undoBtn_.setText(undoManager_.getUndoPresentationName()); 006 undoBtn_.setEnabled(undoManager_.canUndo()); 007 008 // refresh redo 009 010 redoBtn_.setText(undoManager_.getRedoPresentationName()); 011 redoBtn_.setEnabled(undoManager_.canRedo()); 012 013 } This method refreshes both the undo and the redo UI. The method retrieves the current edit information from the undoManager.The Undo action When the user presses the Undo button, the undo action is invoked, as shown next.001 private class UndoAction extends AbstractAction { 002 003 public void actionPerformed(ActionEvent evt ) { 004 005 undoManager_.undo(); 006 007 refreshUndoRedo(); 008 } 009 } All the complexity of managing the undo history list is handled by the UndoManager class. The undo operation simply invokes undo() on the manager and refreshes the applet GUI. We use the same code for the redo action, so I won’t detail it here.Wiring the parts together In the application constructor, we set up the system.001 public UndoPanel () { 002 // construct the actions 003 ActionListener undoAction = new undoAction(); 004 ActionListener redoAction = new redoAction(); 005 006 // register the listener 007 undoBtn_ = new JButton("undo"); 008 undoBtn_.addActionListener(undoAction); 009 redoBtn_ = new JButton("redo"); 010 redoBtn_.addActionListener(redoAction); 011 012 013 // initialize the undo.redo system 014 undoManager_= new UndoManager(); 015 undoSupport_ = new UndoableEditSupport(); 016 undoSupport_.addUndoableEditListener(new UndoAdapter()); 017 018 } ConclusionThe Command pattern of encapsulating user actions in an application as individual first-class objects is extremely useful. It allows us to localize the implementation of undo/redo facilities to individual classes that themselves perform and undo the changes. This approach greatly eases maintenance; when we change a command operation, the undo/redo code is nearby and is completely independent of the undo/redo user interface code.Supporting undo/redo in your apps will provide your users with a sense of confidence as they learn how to manipulate the program. This article has shown you how easy it is to use Swing to implement such a feature. While it may be a small step for you as a developer, it is a huge step toward developing more complete and friendly applications.Tomer Meshorer is a framework architect at Comverse Network Systems in Tel Aviv, Israel. He develops Java-based object-oriented frameworks for visual programming IDEs. Tomer is a certified JDK 1.1 programmer and is devoted to design patterns, frameworks and, of course, Java. Java