ShowComponent.java Source code

Java tutorial

Introduction

Here is the source code for ShowComponent.java

Source

/*
 * Copyright (c) 2000 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 2nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book (recommended),
 * visit http://www.davidflanagan.com/javaexamples2.
 */

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Vector;

import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
 * 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 ShowComponent {
    // The main program
    public static void main(String[] args) {
        // Process the command line to get the components to display
        Vector components = getComponentsFromArgs(args);

        // Create a frame (a window) to display them in
        JFrame frame = new JFrame("ShowComponent");

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

        // Set up a menu system that allows the user to select the
        // look-and-feel of the component from a list of installed PLAFs
        JMenuBar menubar = new JMenuBar(); // Create a menubar
        frame.setJMenuBar(menubar); // Tell the frame to display it
        JMenu plafmenu = createPlafMenu(frame); // Create a menu
        menubar.add(plafmenu); // Add the menu to the menubar

        // Create a JTabbedPane to display each of the components
        JTabbedPane pane = new JTabbedPane();

        // Now add each component as a tab of the tabbed pane
        // Use the unqualified component classname as the tab text
        for (int i = 0; i < components.size(); i++) {
            Component c = (Component) components.elementAt(i);
            String classname = c.getClass().getName();
            String tabname = classname.substring(classname.lastIndexOf('.') + 1);
            pane.addTab(tabname, c);
        }

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

        // Set the frame size and pop it up
        frame.pack(); // Make frame as big as its kids need
        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.
    }

    /**
     * This static method queries the system to find out what Pluggable
     * Look-and-Feel (PLAF) implementations are available. Then it creates a
     * JMenu component that lists each of the implementations by name and allows
     * the user to select one of them using JRadioButtonMenuItem components.
     * When the user selects one, the selected menu item traverses the component
     * hierarchy and tells all components to use the new PLAF.
     */
    public static JMenu createPlafMenu(final JFrame frame) {
        // Create the menu
        JMenu plafmenu = new JMenu("Look and Feel");

        // Create an object used for radio button mutual exclusion
        ButtonGroup radiogroup = new ButtonGroup();

        // Look up the available look and feels
        UIManager.LookAndFeelInfo[] plafs = UIManager.getInstalledLookAndFeels();

        // Loop through the plafs, and add a menu item for each one
        for (int i = 0; i < plafs.length; i++) {
            String plafName = plafs[i].getName();
            final String plafClassName = plafs[i].getClassName();

            // Create the menu item
            JMenuItem item = plafmenu.add(new JRadioButtonMenuItem(plafName));

            // Tell the menu item what to do when it is selected
            item.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    try {
                        // Set the new look and feel
                        UIManager.setLookAndFeel(plafClassName);
                        // Tell each component to change its look-and-feel
                        SwingUtilities.updateComponentTreeUI(frame);
                        // Tell the frame to resize itself to the its
                        // children's new desired sizes
                        frame.pack();
                    } catch (Exception ex) {
                        System.err.println(ex);
                    }
                }

            });

            // Only allow one menu item to be selected at once
            radiogroup.add(item);
        }
        return plafmenu;
    }

    /**
     * This method loops through the command line arguments looking for class
     * names of components to create and property settings for those components
     * in the form name=value. This method demonstrates reflection and JavaBeans
     * introspection as they can be applied to dynamically created GUIs
     */
    public static Vector getComponentsFromArgs(String[] args) {
        Vector components = new Vector(); // List of components to return
        Component component = null; // The current component
        PropertyDescriptor[] properties = null; // Properties of the component
        Object[] methodArgs = new Object[1]; // We'll use this below

        nextarg: // This is a labeled loop
        for (int i = 0; i < args.length; i++) { // Loop through all arguments
            // If the argument does not contain an equal sign, then it is
            // a component class name. Otherwise it is a property setting
            int equalsPos = args[i].indexOf('=');
            if (equalsPos == -1) { // Its the name of a component
                try {
                    // Load the named component class
                    Class componentClass = Class.forName(args[i]);
                    // Instantiate it to create the component instance
                    component = (Component) componentClass.newInstance();
                    // Use JavaBeans to introspect the component
                    // And get the list of properties it supports
                    BeanInfo componentBeanInfo = Introspector.getBeanInfo(componentClass);
                    properties = componentBeanInfo.getPropertyDescriptors();
                } catch (Exception e) {
                    // If any step failed, print an error and exit
                    System.out.println("Can't load, instantiate, " + "or introspect: " + args[i]);
                    System.exit(1);
                }

                // If we succeeded, store the component in the vector
                components.addElement(component);
            } else { // The arg is a name=value property specification
                String name = args[i].substring(0, equalsPos); // property name
                String value = args[i].substring(equalsPos + 1); // property
                // value

                // If we don't have a component to set this property on, skip!
                if (component == null)
                    continue nextarg;

                // Now look through the properties descriptors for this
                // component to find one with the same name.
                for (int p = 0; p < properties.length; p++) {
                    if (properties[p].getName().equals(name)) {
                        // Okay, we found a property of the right name.
                        // Now get its type, and the setter method
                        Class type = properties[p].getPropertyType();
                        Method setter = properties[p].getWriteMethod();

                        // Check if property is read-only!
                        if (setter == null) {
                            System.err.println("Property " + name + " is read-only");
                            continue nextarg; // continue with next argument
                        }

                        // Try to convert the property value to the right type
                        // We support a small set of common property types here
                        // Store the converted value in an Object[] so it can
                        // be easily passed when we invoke the property setter
                        try {
                            if (type == String.class) { // no conversion needed
                                methodArgs[0] = value;
                            } else if (type == int.class) { // String to int
                                methodArgs[0] = Integer.valueOf(value);
                            } else if (type == boolean.class) { // to boolean
                                methodArgs[0] = Boolean.valueOf(value);
                            } else if (type == Color.class) { // to Color
                                methodArgs[0] = Color.decode(value);
                            } else if (type == Font.class) { // String to Font
                                methodArgs[0] = Font.decode(value);
                            } else {
                                // If we can't convert, ignore the property
                                System.err
                                        .println("Property " + name + " is of unsupported type " + type.getName());
                                continue nextarg;
                            }
                        } catch (Exception e) {
                            // If conversion failed, continue with the next arg
                            System.err.println("Can't convert  '" + value + "' to type " + type.getName()
                                    + " for property " + name);
                            continue nextarg;
                        }

                        // Finally, use reflection to invoke the property
                        // setter method of the component we created, and pass
                        // in the converted property value.
                        try {
                            setter.invoke(component, methodArgs);
                        } catch (Exception e) {
                            System.err.println("Can't set property: " + name);
                        }

                        // Now go on to next command-line arg
                        continue nextarg;
                    }
                }

                // If we get here, we didn't find the named property
                System.err.println("Warning: No such property: " + name);
            }
        }

        return components;
    }
}