[ Team LiB ] Previous Section Next Section

11.9 Displaying Tables

Now that we've seen how to assemble a prototypical Swing GUI, we can move on and start studying some more advanced Swing programming topics. We'll start with examples of some of the more powerful, and therefore complicated, components. The JTable class displays tabular data. It is particularly easy to use if your data happens to be organized into arrays of arrays. If this is not the case, however, you must implement the javax.swing.table.TableModel interface to serve as a translator between your data and the JTable component.

Example 11-19, which is a listing of PropertyTable.java, does exactly this. PropertyTable is a subclass of JTable that uses a custom TableModel implementation to display a table of the properties defined by a specified JavaBeans class (it uses the java.beans package, which we'll see more of in Chapter 15). The example includes a main( ) method so you can run it as a standalone application. Figure 11-16 shows the PropertyTable class in action. When studying this example, pay particular attention to the TableModel implementation: the TableModel is the key to working with the JTable component. Also note the PropertyTable constructor method that uses the TableColumnModel to modify the default appearance of the columns in the table.

Figure 11-16. The PropertyTable application
figs/Jex3_1116.gif
Example 11-19. PropertyTable.java
package je3.gui;
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;   // TableModel and other JTable-related classes
import java.beans.*;          // For JavaBean introspection
import java.util.*;           // For array sorting 

/**
 * This class is a JTable subclass that displays a table of the JavaBeans
 * properties of any specified class.
 **/
public class PropertyTable extends JTable {
    /** This main method allows the class to be demonstrated  standalone */
    public static void main(String[  ] args) {
        // Specify the name of the class as a command-line argument
        Class beanClass = null;
        try {
            // Use reflection to get the Class from the classname
            beanClass = Class.forName(args[0]);
        }
        catch (Exception e) {  // Report errors
            System.out.println("Can't find specified class: "+e.getMessage( ));
            System.out.println("Usage: java PropertyTable <bean class name>");
            System.exit(0);
        }
        
        // Create a table to display the properties of the specified class
        JTable table = new PropertyTable(beanClass);
        
        // Then put the table in a scrolling window, put the scrolling 
        // window into a frame, and pop it all up on to the screen
        JScrollPane scrollpane = new JScrollPane(table);
        JFrame frame = new JFrame("Properties of JavaBean: " + args[0]);
        frame.getContentPane( ).add(scrollpane);
        frame.setSize(500, 400);
        frame.setVisible(true);
    }

    /**
     * This constructor method specifies what data the table will display
     * (the table model) and uses the TableColumnModel to customize the
     * way that the table displays it.  The hard work is done by the
     * TableModel implementation below.
     **/
    public PropertyTable(Class beanClass) {
        // Set the data model for this table
        try {
            setModel(new JavaBeanPropertyTableModel(beanClass));
        }
        catch (IntrospectionException e) {
            System.err.println("WARNING: can't introspect: " + beanClass);
        }
        
        // Tweak the appearance of the table by manipulating its column model
        TableColumnModel colmodel = getColumnModel( );
        
        // Set column widths
        colmodel.getColumn(0).setPreferredWidth(125);
        colmodel.getColumn(1).setPreferredWidth(200);
        colmodel.getColumn(2).setPreferredWidth(75);
        colmodel.getColumn(3).setPreferredWidth(50);
        
        // Right justify the text in the first column
        TableColumn namecol = colmodel.getColumn(0);
        DefaultTableCellRenderer renderer = new DefaultTableCellRenderer( );
        renderer.setHorizontalAlignment(SwingConstants.RIGHT);
        namecol.setCellRenderer(renderer);
    }

    /**
     * This class implements TableModel and represents JavaBeans property data
     * in a way that the JTable component can display.  If you've got some
     * type of tabular data to display, implement a TableModel class to
     * describe that data, and the JTable component will be able to display it.
     **/
    static class JavaBeanPropertyTableModel extends AbstractTableModel {
        PropertyDescriptor[  ] properties;  // The properties to display

        /**
         * The constructor: use the JavaBeans introspector mechanism to get 
         * information about all the properties of a bean.  Once we've got
         * this information, the other methods will interpret it for JTable.
         **/
        public JavaBeanPropertyTableModel(Class beanClass)
            throws java.beans.IntrospectionException
        {
            // Use the introspector class to get "bean info" about the class.
            BeanInfo beaninfo = Introspector.getBeanInfo(beanClass);
            // Get the property descriptors from that BeanInfo class
            properties = beaninfo.getPropertyDescriptors( );
            // Now do a case-insensitive sort by property name
            // The anonymous Comparator implementation specifies how to 
            // sort PropertyDescriptor objects by name
            Arrays.sort(properties, new Comparator( ) {
                    public int compare(Object p, Object q) {
                        PropertyDescriptor a = (PropertyDescriptor) p;
                        PropertyDescriptor b = (PropertyDescriptor) q;
                        return a.getName( ).compareToIgnoreCase(b.getName( ));
                    }
                    public boolean equals(Object o) { return o == this; }
                });
        }

        // These are the names of the columns represented by this TableModel
        static final String[  ] columnNames = new String[  ] {
            "Name", "Type", "Access", "Bound"
        };

        // These are the types of the columns represented by this TableModel
        static final Class[  ] columnTypes = new Class[  ] {
            String.class, Class.class, String.class, Boolean.class
        };

        // These simple methods return basic information about the table
        public int getColumnCount( ) { return columnNames.length; }
        public int getRowCount( ) { return properties.length; }
        public String getColumnName(int column) { return columnNames[column]; }
        public Class getColumnClass(int column) { return columnTypes[column]; }

        /**
         * This method returns the value that appears at the specified row and
         * column of the table
         **/
        public Object getValueAt(int row, int column) {
            PropertyDescriptor prop = properties[row];
            switch(column) {
            case 0: return prop.getName( );
            case 1: return prop.getPropertyType( );
            case 2: return getAccessType(prop);
            case 3: return new Boolean(prop.isBound( ));
            default: return null;
            }
        }

        // A helper method called from getValueAt( ) above
        String getAccessType(PropertyDescriptor prop) {
            java.lang.reflect.Method reader = prop.getReadMethod( );
            java.lang.reflect.Method writer = prop.getWriteMethod( );
            if ((reader != null) && (writer != null)) return "Read/Write";
            else if (reader != null) return "Read-Only";
            else if (writer != null) return "Write-Only";
            else return "No Access";  // should never happen
        }
    }
}
    [ Team LiB ] Previous Section Next Section