HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

How often do you find yourself copying and pasting old code when developing GUIs that are similar to others you've developed? Many times I find that if I do that more than once or twice on some code, it's worthwhile to make a component out of it for the benefits of reuse and highly maintainable code.

The initial overhead for creating a component is high, but it will pay off if you design it well, making it useful for other occasions. The better you design it, the more likely you'll use it in the future and the more benefits you'll get.

ListPanel and ListTabPanel are two reusable components I developed. In this article, I describe how you can use them to simplify and speed up your GUI development. I also explain how they're implemented and hope you can learn some useful skills from them.

ListPanel
ListPanel is a GUI component that allows a user to filter, sort, display, and search a list of any object in different ways. Figure 1 shows a list of Auction items that can be filtered by their status (open, closed). The list can sort, display, and search by the Auction item number or name. Using this component the developer writes only a few lines of code and some inner classes to create the list GUI, which normally involves complicated code. It's easy to understand and the code is very maintainable.

Figure 1
Figure 1

Please note that for simplicity, this component assumes that the way you display and search data is the same way you sort data. You can modify this component to make it more sophisticated and to handle the sort, display, and search differently.

Creating an Instance of ListPanel
The following shows how easy it is to create the GUI in Figure 1 using the ListPanel.

The ListPanel contains a list of Auction objects (see Listing 1 for the Auction class). (Listings 17 can be downloaded from below.)

ListPanel listPane = new ListPanel();
listPane.setData(createTestData ());
listPane.addFilterSpec(new StatusFilter("Open") );
listPane.addFilterSpec(new StatusFilter("Closed") );
listPane.addSortSpec(new IDSort ());
listPane.addSortSpec(new NameSort ());
listPane.refreshGUI();
This code involves only five steps:
  1. Create the ListPanel with the constructor.
  2. Set the data (in this case, a list of all Auction objects) by setData.
  3. For each FilterSpec, call addFilterSpec.
  4. For each SortSpec, call addSortSpec.
  5. Call refreshGUI once you've set up all the filter and sort conditions.
Creating a FilterSpec
To create a FilterSpec, extend the abstract class FilterSpec (see Listing 2) and provide implementation for the following abstract methods:

  • String getName(): Defines how the filter name is displayed in the JComboBox

  • boolean evaluate(Object o): Defines the filter condition

    To create the filter based on the Auction status use the following:

    class StatusFilter extends FilterSpec
    {
    private String status;
    public StatusFilter (String s)
    {
    status = s;
    }
    public boolean evaluate(Object o)
    {
    return (((Auction)
    o).status.equals(status));
    }
    public String getName()
    {
    return status;
    }
    }
    Creating a SortSpec
    To create a SortSpec, extend the abstract class SortSpec (see Listing 2) and provide implementation for the following abstract methods:

  • String getName(): Defines how the sort name is displayed in the JComboBox

  • getSortString(Object o): Defines how to sort, display, and search by

    The code for creating the sort by ItemNo, Name of the Auction is as follows.

    class IDSort extends SortSpec
    {
    public String
    getSortString(Object o)
    {
    return ((Auction) o).itemNo;
    }
    public String getName()
    {
    return ("Item No");
    }
    }

    class NameSort extends SortSpec
    {
    publicString
    getSortString(Object o)
    {
    return ((Auction) o).name;
    }
    public String getName()
    {
    return ("Name");
    }
    }
    Listing 1 provides the code to generate the AuctionList.

    Another Example
    The ListPanel is a flexible component. Figure 2 shows another list created on a totally different object, User. This list has three filters (active, inactive, all) and two sorts (ID, LastnameFirstname). This entire GUI is created in one routine, createListPane() shown in Listing 3.

    Figure 2
    Figure 2

    In Listing 3, you can see that because the FilterSpec and SortSpec are all small classes, I've made them all inner classes. In situations where I have a lot of little classes used in one place, inner classes are useful to avoid proliferation of classes.

    ListTabPanel
    ListTabPanel contains a ListPanel and a TabPanel. It provides a structure that you can add tabs to to perform different actions on a particular selected object from the list. The implementation uses the Mediator pattern, where the ListTabPanel is a mediator that takes care of the communication between the ListPanel and the TabPanel.

    Using the same Auction class I discussed earlier and the AuctionList, I create the AuctionPanel shown in Figure 3.

    Figure 3
    Figure 3

    Creating the AuctionPanel involves three simple steps:

    1. Create the Auction ListPanel defined in the previous section.
    2. Create a TabPanel that contains three different tabs.
    3. Call the constructor, passing it the ListPanel and the TabPanel.
    ListPanel l = AuctionList.createListPanel();
    TabPanel t = createTabPanel();
    ListTabPanel p = new ListTabPanel (l, t);
    The details for creating the TabPanel in createTabPanel follows:

    TabPanel t = new TabPanel();
    t.addTab(new AuctionTab());
    t.addTab(new MinimalTab("Seller"));
    t.addTab(new
    MinimalTab("Buyer"));
    Here I create the TabPanel, and then add as many TabObjects as needed by calling addTab. Each TabObject defines how the object is displayed and handled in a particular tab. Let's look at how the first tab is implemented in AuctionTab.

    public class AuctionTab extends TabObject
    {
    public AuctionTab()
    {
    // ... Create the GUI here
    }

    public void setData(Object o)
    {
    // ... populate the data to the GUI
    }
    }
    The AuctionTab extends TabObject, which is a JPanel. The developer usually does three things to implement this tab:

    1.   Creates the GUI in the constructor

    2.   Implements the method setData to populate the GUI with an object

    3.   Manipulates the object in its own way and updates it as appropriate (e.g., save to database, etc.); when manipulated, an object should call the appropriate methods notifyAdd(), notifyDelete, or notifyUpdate() to let its listener know of the change

    Listing 4 provides the code for AuctionPanel.

    Implementation of the ListPanel
    The ListPanel component provides a GUI that can filter, sort, display, and search any kind of object. The component has no idea what kind of objects are in the list and how it should filter or sort; it's up to the developer to define this information by calling:

    • setData (List data)
    • addFilterSpec (FilterSpec f)
    • addSortSpec (SortSpec s)
    With this information from the developer, the component then handles the rest, which includes:
    • Creating JComboBox to allow the user to choose the methods to sort and filter
    • Filtering the data according to the filter the user selected
    • Sorting the data according to the sort the user selected
    • Displaying the data according to the sort the user selected
    • Implementing the search according to the sort the user selected
    Jakarta Common Collection
    To implement the filter and search mechanism, I need some methods where I can:
    • Filter from a list based on some condition and return a filtered list
    • Search from a list based on some condition and return the first object that matches
    While the Collections class in the java.util package does provide a search method, binarySearch(List l, Object o, Comparator c), there's no mechanism to filter the data in the list. Furthermore, this search method requires that I sort the list before passing the list in. In addition, if I want the filter condition to be a union or intersection of two conditions, the Collections class doesn't have set theory methods that I can apply to the collections.

    The Jakarta Commons Collections (http://jakarta.apache.org/commons/index.html) is a set of reusable components related to collections. It strives to provide some features that were left unfilled by Sun's implementations in the Collection class.

    Using Jakarta Commons Collections you can search and filter a collection of data based on a Predicate using the method search and find in the CollectionsUtil class. Predicate is an abstract class with a method evaluate(Object o) that performs some Predicate that returns true or false based on the input object. In addition, the CollectionUtils class provides set theory methods like intersection and union if you want to operate the filter on more than one condition.

    Implementation Details
    The ListPanel has three pieces of information after the developer creates an instance of it: dataList (a list of all objects), filterList (a list of all filters), and sortList (a list of all sorts).

    Using filterList and sortList, ListPanel creates combo boxes that contain lists of all available filters and sorts. When the user selects a sort or a filter, the currentFilter or currentSort object is updated, and the method refresh is called. In refresh, the data is first filtered as follows:

    // Filter the data according to currentFilter
    filteredData = CollectionUtils.select(data, currentFilter);
    displayModel.clear();
    Iterator i = filteredData.iterator();
    while (i.hasNext())
    displayModel.addElement(i.next());
    I get the filteredData by calling the select (List l, Predicate p) method in the CollectionUtils class of Apache Common Collections. You should now see why FilterSpec implements Predicate, because now all I need to do is to pass currentFilter (which is a FilterSpec) to the select (List l, Predicate p) method.

    After the data is filtered, it's sorted as follows:

    // Sort the data according to currentSort
    displayModel.sort(currentSort);
    displayList.setModel (displayModel);

    // Display the data according to currentSort
    displayList.setCellRenderer(currentSort);
    I created a class SortableListModel (see Listing 5) that can sort data by calling sort (Comparable c). The displayModel you saw earlier is a SortableListModel, so I sort this list by simply calling displayModel.sort (currentSort). You should now see why SortSpec implements Comparator, because with that, all you need is to pass currentSort to this sort method.

    Next, I display the data in the JList by setting the cellRenderer. Again you can see that SortSpec extends DefaultListCellRenderer, which means you can just pass currentSort to the setCellRenderer(ListCellRenderer) method.

    Last, when the user types something in the text field and hits the search button, the method search does the following:

    currentSort.setSearch(searchTF.getText());
    Object o = CollectionUtils.find
    (filteredData,currentSort);
    displayList.setSelectedValue(o, true);
    Here I first set the text to be searched, then use the CollectionUtils.find (List list, Predicate predicate) method to find the first object that satisfies the Predicate from the list. Again, you see that SortSpec implements Predicate, which is why you can just pass currentSort to the CollectionsUtils.find method. Once the object is found, select it on the JList.

    Listing 5 provides the code for the ListPanel.

    Implementation of ListTabPanel
    To understand how the ListTabPanel is implemented, you first need to understand the Mediator pattern. Mediator is a pattern that promotes loose coupling between classes. It accomplishes this by being the only class with detailed knowledge of the methods of other classes. Classes inform the Mediator when changes occur, and the Mediator then passes them on to any other classes that need to be informed.

    The ListTabPanel (which contains the ListPanel and the TabPanel) is the Mediator here. The ListPanel and TabPanel don't know about each other. They notify only the ListTabPanel of changes in themselves, or get notified by the ListTabPanel when changes come in. Clearly defining the tasks of each component will make coding easy and clear. Let's now look at the tasks of each component:

    Tasks of the ListPanel

  • Provide a list that can filter, sort, display, and search in different ways.

  • When a user selects an item, it should notify its listener (in this case the Mediator).

  • Provide update, delete, and add methods so another object (in this case the Mediator) can adjust its list data.

    Tasks of the TabPanel

  • Provide a setData method for each tab so that another object (in this case the Mediator) can set data and populate the tab accordingly.

  • When a tab calls update, delete, or add on an object, it should notify its listener (in this case the Mediator).

    Tasks of the ListTabPanel

  • When an item is selected from the ListPanel, this component (as a Mediator) gets notified and calls setData on the TabPanel.

  • When the TabPanel updates some object, this component (as a Mediator) gets notified and calls update, add, or delete on the ListPanel.

    Using PropertyListener
    In my implementation, the communication between the ListPanel and TabPanel and their Mediator is done by PropertyListener. The ListTabPanel is a PropertyChangeListener. When you construct a ListTabPanel with a ListPanel and a TabPanel, the ListTabPanel will add itself as a PropertyChangeListener to the ListPanel and TabPanel.

    tabPane.addPropertyChangeListener(this);
    listPane.addPropertyChangeListener(this);
    By doing so, when the TabPanel or ListPanel wants to notify the ListTabPanel of anything, it'll call firePropertyChange to pass the property change and the object that was changed to the ListTabPanel.

    The ListTabPanel received these change events in its propertyChange method, and it will do the appropriate thing to notify other components if necessary.

    public void propertyChange
    (PropertyChangeEvent evt)
    {
    // Get notify when ListPanel
    select some object
    if (evt.getPropertyName().equals
    (LIST_SELECTED))
    // Ask TabPanel to set the
    object to its screen
    tabPane.getSelected().setData
    (evt.getNewValue());

    // Get notify when TabPanel update
    some object
    if (evt.getPropertyName().equals
    (TAB_OBJECT_UPDATE))
    // Ask ListPanel to update its list
    listPane.update(evt.getOldValue(),
    evt.getOldValue());

    // .... Other events
    }
    The full source of TabPanel, TabObject, can be found in Listing 6, and the full source of ListTabPanel can be found in Listing 7.

    Conclusion
    The ListPanel and ListTabPanel are two useful GUI components that are highly reusable and can promote rapid GUI development. The ListPanel takes care of the common features like sort, filter, search, and display on a list. The ListTabPanel takes care of the interaction between a list of objects and the detail panels on a specific object in that list. These two components are created so that they're applicable on any kind of object and provide a clean interface for developers. Using them, developers can concentrate on coding the details specific to their object.

    More important, this article not only described how to use these components, but also their implementation. It's useful to understand the whole design process for making a component reusable and loosely coupled, resulting in clear and maintainable code.

    Author Bio
    Teresa Lau has been an independent Java consultant for over four years, with an emphasis on financial applications. She received her MS in computer science from the University of Waterloo, and her BS in engineering from the University of California, Berkeley. [email protected]

    Source Code for this Article (~ 7.83 KB ~zip format )

    All Rights Reserved
    Copyright ©  2004 SYS-CON Media, Inc.
      E-mail: [email protected]

    Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.