[ Team LiB ] Previous Section Next Section

11.15 The ShowBean Program

We began this chapter using the ShowBean program to experiment with Swing components, and with the custom components developed as examples. As promised, the implementation is here in Example 11-30. This is another example of a Swing application that uses JFrame, JMenuBar, and so on. It uses the LookAndFeelPrefs utility class from Example 11-29, demonstrates the JTabbedPane component, and shows the use of tooltips in Swing.

ShowBean relies on the Bean class of Example 15-10. This class provides the JavaBeans magic for listing, querying, setting, and invoking bean properties and commands. You'll have to skip ahead to Chapter 15 if you want to see how that piece works.

ShowBean also has a feature that was not mentioned before: in addition to creating beans based on their classname, it can also read beans that have been serialized to a file using the java.io serialization mechanism or the (new in Java 1.4) JavaBeans persistence mechanism. Use the -ser or -xml options on the command line to create a bean from a file. Additionally, the File menu contains entries that allow you to save a bean (and its currently set properties) to a file using either serialization or the JavaBeans persistence mechanism.

Example 11-30. ShowBean.java
package je3.gui;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.beans.*;
import java.lang.reflect.*;
import java.util.List;  // explicit import to disambiguate from java.awt.List
import java.util.*;
import java.io.*;
import je3.beans.Bean;

/**
 * This class is a program that uses reflection and JavaBeans introspection to
 * create a set of named components, set named properties on those components,
 * and display them.  It allows the user to view the components using any
 * installed look-and-feel.  It is intended as a simple way to experiment with
 * AWT and Swing components, and to view a number of the other examples
 * developed in this chapter.  It also demonstrates frames, menus, and the
 * JTabbedPane component.
 **/
public class ShowBean extends JFrame {
    // The main program
    public static void main(String[  ] args) {
        // Set the look-and-feel for the application.  
        // LookAndFeelPrefs is defined elsewhere in this package.
        LookAndFeelPrefs.setPreferredLookAndFeel(ShowBean.class);

        // Process the command line to get the components to display
        List beans = getBeansFromArgs(args);

        JFrame frame = new ShowBean(beans);  // Create frame

        // Handle window close requests by exiting the VM
        frame.addWindowListener(new WindowAdapter( ) { // Anonymous inner class
                public void windowClosing(WindowEvent e) { System.exit(0); }
            });

        frame.setVisible(true);    // Make the frame visible on the screen

        // The main( ) method exits now, but the Java VM keeps running because
        // all AWT programs automatically start an event-handling thread.
    }

    List beans;
    JMenu propertyMenu, commandMenu;
    JTabbedPane pane;
    JFileChooser fileChooser = new JFileChooser( );

    // Most initialization code is in the constructor instead of main( )
    public ShowBean(final List beans) {
        super("ShowBean");
        
        this.beans = beans;  // Save the list of Bean objects

        // Create a menubar
        JMenuBar menubar = new JMenuBar( );    
        this.setJMenuBar(menubar);            // Tell the frame to display it

        // Create and populate a File menu
        JMenu filemenu = new JMenu("File");
        filemenu.setMnemonic('F');
        JMenuItem save = new JMenuItem("Save as...");
        save.setMnemonic('S');
        JMenuItem serialize = new JMenuItem("Serialize as...");
        JMenuItem quit = new JMenuItem("Quit");
        quit.setMnemonic('Q');
        menubar.add(filemenu);
        filemenu.add(save);
        filemenu.add(serialize);
        filemenu.add(new JSeparator( ));
        filemenu.add(quit);

        // Here are event handlers for the Save As and Quit menu items
        save.addActionListener(new ActionListener( ) {
                public void actionPerformed(ActionEvent e) {
                    saveCurrentPane( );
                }
            });
        serialize.addActionListener(new ActionListener( ) {
                public void actionPerformed(ActionEvent e) {
                    serializeCurrentPane( );
                }
            });
        quit.addActionListener(new ActionListener( ) {
                public void actionPerformed(ActionEvent e) {
                    System.exit(0);
                }
            });

        // Set up a menu that allows the user to select the look-and-feel of
        // the component from a list of installed look-and-feels.
        // Remember the selected LAF in a persistent Preferences node.
        JMenu plafmenu =
          LookAndFeelPrefs.createLookAndFeelMenu(ShowBean.class,
              new ActionListener( ) {
                  public void actionPerformed(ActionEvent event) {
                      // When the user selects a new LAF, tell each
                      // component to change its look-and-feel
                      SwingUtilities.updateComponentTreeUI(ShowBean.this);
                      // Then repack the frame to its new preferred size.
                      ShowBean.this.pack( );
                  }
              });
        plafmenu.setMnemonic('L');
        menubar.add(plafmenu);                // Add the menu to the menubar

        // Create the Properties and Commands menus, but don't populate them.
        // JMenuItems are added each time a new tab is selected.
        propertyMenu = new JMenu("Properties");
        propertyMenu.setMnemonic('P');
        menubar.add(propertyMenu);
        commandMenu = new JMenu("Commands");
        commandMenu.setMnemonic('C');
        menubar.add(commandMenu);
        
        // Some components have many properties, so make the property menu
        // three columns wide so that we can see all of the entries.
        propertyMenu.getPopupMenu( ).setLayout(new GridLayout(0,3));

        // Create a JTabbedPane to display each of the components
        pane = new JTabbedPane( );
        pane.addChangeListener(new ChangeListener( ) {
                public void stateChanged(ChangeEvent e) {
                    populateMenus(pane.getSelectedIndex( ));
                }
            });

        // Now add each component as a tab of the tabbed pane
        for(int i = 0; i < beans.size( ); i++) {
            Bean b = (Bean) beans.get(i);
            Object o = b.getBean( );
            Component component;
            if (o instanceof Component) component = (Component) o;
            else component = new JLabel("This bean has no Component");
            Image image = b.getIcon( );
            Icon icon = null;
            if (image != null) icon = new ImageIcon(image);
            pane.addTab(b.getDisplayName( ),         // text for the tab
                        icon,                       // icon for the tab
                        component,                  // contents of the tab
                        b.getShortDescription( ));   // tooltip for the tab
        }

        // Add the tabbed pane to this frame. Note the call to getContentPane( )
        // This is required for JFrame, but not for most Swing components.
        this.getContentPane( ).add(pane);

        this.pack( );   // Make frame as big as its kids need 
    }

    /**
     * This static method loops through the command-line arguments looking for
     * the names of files that contain XML encoded or serialized components,
     * or for class names of components to instantiate or for name=value
     * property settings for those components. It relies on the Bean class
     * developed elsewhere.
     **/
    static List getBeansFromArgs(String[  ] args) {
        List beans = new ArrayList( );      // List of beans to return
        Bean bean = null;                  // The current bean
        boolean expert = false;

        for(int i = 0; i < args.length; i++) {  // Loop through all arguments
            if (args[i].charAt(0) == '-') { // Does it begin with a dash?
                try { 
                    if (args[i].equals("-expert")) {
                        expert = true;
                    }
                    else if (args[i].equals("-xml")) { // read from xml
                        if (++i >= args.length) continue;
                        InputStream in = new FileInputStream(args[i]);
                        bean = Bean.fromPersistentStream(in, expert);
                        beans.add(bean);
                        in.close( );
                    }
                    else if (args[i].equals("-ser")) {  // deserialize a file
                        if (++i >= args.length) continue;
                        ObjectInputStream in =
                           new ObjectInputStream(new FileInputStream(args[i]));
                        bean = Bean.fromSerializedStream(in, expert);
                        beans.add(bean);
                        in.close( );
                    }
                    else {
                        System.err.println("Unknown option: " + args[i]);
                        continue;
                    }
                }
                catch(Exception e) {  // In case anything goes wrong
                    System.err.println(e);
                    System.exit(1);
                    continue;
                }
            }
            else if (args[i].indexOf('=') == -1){ // It's a component name
                // If the argument does not contain an equal sign, then it is
                // a component class name.
                try {
                    bean = Bean.forClassName(args[i], expert);
                    beans.add(bean);
                }
                catch(Exception e) {  
                    // If any step failed, print an error and exit
                    System.out.println("Can't load, instantiate, " +
                                       "or introspect: " + args[i]);
                    System.out.println(e);
                    System.exit(1);
                }
            }
            else { // The arg is a name=value property specification 
                // Break into name and value parts
                int pos = args[i].indexOf('=');
                String name = args[i].substring(0, pos); // property name
                String value = args[i].substring(pos+1); // property value
                    
                // If we don't have a component to set this property on, skip!
                if (bean == null) {
                    System.err.println("Property " + name + 
                                       " specified before any bean.");
                    continue;
                }

                // Now try to set the property
                try { bean.setPropertyValue(name, value); }
                catch(Exception e) {
                    // Failure to set a property is not a fatal error;
                    // Just display the message and continue
                    System.out.println(e);
                }
            }
        }
        
        return beans;
    }

    /**
     * Ask the user to select a filename, and then save the contents of
     * the current tab to that file using the JavaBeans persistence mechanism.
     */
    public void saveCurrentPane( ) {
        int result = fileChooser.showSaveDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            try {
                File file = fileChooser.getSelectedFile( );
                XMLEncoder encoder=new XMLEncoder(new FileOutputStream(file));
                encoder.writeObject(pane.getSelectedComponent( ));
                encoder.close( );
            }
            catch(IOException e) {
                JOptionPane.showMessageDialog(this, e, e.getClass( ).getName( ),
                                              JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /**
     * Ask the user to choose a filename, and then save the contents of the
     * current pane to that file using traditional object serialization
     */
    public void serializeCurrentPane( ) {
        int result = fileChooser.showSaveDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            try {
                File file = fileChooser.getSelectedFile( );
                ObjectOutputStream out =
                    new ObjectOutputStream(new FileOutputStream(file));
                out.writeObject(pane.getSelectedComponent( ));
                out.close( );
            }
            catch(IOException e) {
                JOptionPane.showMessageDialog(this, e, e.getClass( ).getName( ),
                                              JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /**
     * Create JMenuItem objects in the Properties and Command menus.
     * This method is called whenever a new tab is selected.
     */
    void populateMenus(int index) {
        // First, delete the old menu contents
        propertyMenu.removeAll( );
        commandMenu.removeAll( );

        // The Bean object for this tab
        final Bean bean = (Bean) beans.get(index);

        List props = bean.getPropertyNames( );
        for(int i = 0; i < props.size( ); i++) {
            final String name = (String)props.get(i);
            // Create a menu item for the command
            JMenuItem item = new JMenuItem(name);
            // If the property has a nontrivial description, make it a tooltip
            String tip = bean.getPropertyDescription(name);
            if (tip != null && !tip.equals(name)) item.setToolTipText(tip);

            item.addActionListener(new ActionListener( ) {
                    public void actionPerformed(ActionEvent e) {
                        editProperty(bean, name);
                    }
                });

            propertyMenu.add(item);
        }

        List commands = bean.getCommandNames( );
        for(int i = 0; i < commands.size( ); i++) {
            final String name = (String)commands.get(i);
            // Create a menu item for the command
            JMenuItem item = new JMenuItem(name);
            // If the command has a nontrivial description, make it a tooltip
            String tip = bean.getCommandDescription(name);
            if (tip != null && !name.endsWith(tip)) item.setToolTipText(tip);

            // Invoke the command when the item is selected
            item.addActionListener(new ActionListener( ) {
                    public void actionPerformed(ActionEvent e) {
                        try { bean.invokeCommand(name); }
                        catch(Exception ex) {
                            getToolkit( ).beep( );
                            JOptionPane.showMessageDialog(ShowBean.this,
                                                ex, ex.getClass( ).getName( ),
                                                JOptionPane.ERROR_MESSAGE);
                        }
                    }
                });
            // Add the item to the menu
            commandMenu.add(item);
        }
        if (commands.size( ) == 0)
            commandMenu.add("No Commands Available");
    }

    void editProperty(Bean bean, String name) {
        try {
            if (bean.isReadOnly(name)) {
                String value = bean.getPropertyValue(name);
                JOptionPane.showMessageDialog(this, name + " = " + value,
                                              "Read-only Property Value",
                                              JOptionPane.PLAIN_MESSAGE);
            }
            else {
                Component editor = bean.getPropertyEditor(name);
                JOptionPane.showMessageDialog(this,
                                              new Object[  ] { name, editor},
                                              "Edit Property",
                                              JOptionPane.PLAIN_MESSAGE);
            }
        }
        catch(Exception e) {
            getToolkit( ).beep( );
            JOptionPane.showMessageDialog(this, e, e.getClass( ).getName( ),
                                          JOptionPane.ERROR_MESSAGE);
        }
    }
}
    [ Team LiB ] Previous Section Next Section