ClassTree.java Source code

Java tutorial

Introduction

Here is the source code for ClassTree.java

Source

/*
   This program is a part of the companion code for Core Java 8th ed.
   (http://horstmann.com/corejava)
    
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
    
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
    
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Enumeration;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

/**
 * This program demonstrates cell rendering and listening to tree selection events.
 * @version 1.03 2007-08-01
 * @author Cay Horstmann
 */
public class ClassTree {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new ClassTreeFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}

/**
 * This frame displays the class tree, a text field and add button to add more classes into the
 * tree.
 */
class ClassTreeFrame extends JFrame {
    public ClassTreeFrame() {
        setTitle("ClassTree");
        setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

        // the root of the class tree is Object
        root = new DefaultMutableTreeNode(java.lang.Object.class);
        model = new DefaultTreeModel(root);
        tree = new JTree(model);

        // add this class to populate the tree with some data
        addClass(getClass());

        // set up node icons
        ClassNameTreeCellRenderer renderer = new ClassNameTreeCellRenderer();
        renderer.setClosedIcon(new ImageIcon("red-ball.gif"));
        renderer.setOpenIcon(new ImageIcon("yellow-ball.gif"));
        renderer.setLeafIcon(new ImageIcon("blue-ball.gif"));
        tree.setCellRenderer(renderer);

        // set up selection mode
        tree.addTreeSelectionListener(new TreeSelectionListener() {
            public void valueChanged(TreeSelectionEvent event) {
                // the user selected a different node--update description
                TreePath path = tree.getSelectionPath();
                if (path == null)
                    return;
                DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) path.getLastPathComponent();
                Class<?> c = (Class<?>) selectedNode.getUserObject();
                String description = getFieldDescription(c);
                textArea.setText(description);
            }
        });
        int mode = TreeSelectionModel.SINGLE_TREE_SELECTION;
        tree.getSelectionModel().setSelectionMode(mode);

        // this text area holds the class description
        textArea = new JTextArea();

        // add tree and text area
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(1, 2));
        panel.add(new JScrollPane(tree));
        panel.add(new JScrollPane(textArea));

        add(panel, BorderLayout.CENTER);

        addTextField();
    }

    /**
     * Add the text field and "Add" button to add a new class.
     */
    public void addTextField() {
        JPanel panel = new JPanel();

        ActionListener addListener = new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                // add the class whose name is in the text field
                try {
                    String text = textField.getText();
                    addClass(Class.forName(text)); // clear text field to indicate success
                    textField.setText("");
                } catch (ClassNotFoundException e) {
                    JOptionPane.showMessageDialog(null, "Class not found");
                }
            }
        };

        // new class names are typed into this text field
        textField = new JTextField(20);
        textField.addActionListener(addListener);
        panel.add(textField);

        JButton addButton = new JButton("Add");
        addButton.addActionListener(addListener);
        panel.add(addButton);

        add(panel, BorderLayout.SOUTH);
    }

    /**
     * Finds an object in the tree.
     * @param obj the object to find
     * @return the node containing the object or null if the object is not present in the tree
     */
    @SuppressWarnings("unchecked")
    public DefaultMutableTreeNode findUserObject(Object obj) {
        // find the node containing a user object
        Enumeration<TreeNode> e = (Enumeration<TreeNode>) root.breadthFirstEnumeration();
        while (e.hasMoreElements()) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
            if (node.getUserObject().equals(obj))
                return node;
        }
        return null;
    }

    /**
     * Adds a new class and any parent classes that aren't yet part of the tree
     * @param c the class to add
     * @return the newly added node.
     */
    public DefaultMutableTreeNode addClass(Class<?> c) {
        // add a new class to the tree

        // skip non-class types
        if (c.isInterface() || c.isPrimitive())
            return null;

        // if the class is already in the tree, return its node
        DefaultMutableTreeNode node = findUserObject(c);
        if (node != null)
            return node;

        // class isn't present--first add class parent recursively

        Class<?> s = c.getSuperclass();

        DefaultMutableTreeNode parent;
        if (s == null)
            parent = root;
        else
            parent = addClass(s);

        // add the class as a child to the parent
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(c);
        model.insertNodeInto(newNode, parent, parent.getChildCount());

        // make node visible
        TreePath path = new TreePath(model.getPathToRoot(newNode));
        tree.makeVisible(path);

        return newNode;
    }

    /**
     * Returns a description of the fields of a class.
     * @param the class to be described
     * @return a string containing all field types and names
     */
    public static String getFieldDescription(Class<?> c) {
        // use reflection to find types and names of fields
        StringBuilder r = new StringBuilder();
        Field[] fields = c.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field f = fields[i];
            if ((f.getModifiers() & Modifier.STATIC) != 0)
                r.append("static ");
            r.append(f.getType().getName());
            r.append(" ");
            r.append(f.getName());
            r.append("\n");
        }
        return r.toString();
    }

    private DefaultMutableTreeNode root;
    private DefaultTreeModel model;
    private JTree tree;
    private JTextField textField;
    private JTextArea textArea;
    private static final int DEFAULT_WIDTH = 400;
    private static final int DEFAULT_HEIGHT = 300;
}

/**
 * This class renders a class name either in plain or italic. Abstract classes are italic.
 */
class ClassNameTreeCellRenderer extends DefaultTreeCellRenderer {
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
            boolean leaf, int row, boolean hasFocus) {
        super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
        // get the user object
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
        Class<?> c = (Class<?>) node.getUserObject();

        // the first time, derive italic font from plain font
        if (plainFont == null) {
            plainFont = getFont();
            // the tree cell renderer is sometimes called with a label that has a null font
            if (plainFont != null)
                italicFont = plainFont.deriveFont(Font.ITALIC);
        }

        // set font to italic if the class is abstract, plain otherwise
        if ((c.getModifiers() & Modifier.ABSTRACT) == 0)
            setFont(plainFont);
        else
            setFont(italicFont);
        return this;
    }

    private Font plainFont = null;
    private Font italicFont = null;
}