I l@ve RuBoard |
![]() ![]() |
17.2 Tree ModelsLooking at Figure 17-4 you can get an overview of where all the tree pieces come from. As with many of the Swing components you've seen already, the models supporting the data for trees play a crucial role in making the component run. Two interfaces are particularly important: TreeModel, which describes how to work with tree data, and TreeSelectionModel, which describes how to select nodes. 17.2.1 The TreeModel InterfaceTo get started, you need a tree model. The TreeModel interface is the starting point for your model. You don't have to start from scratch; there is a default implementation (DefaultTreeModel) that you can subclass or just look at for ideas. (We'll look at this class later in the chapter.) 17.2.1.1 PropertyThe TreeModel has one root property, listed in Table 17-1. This read-only property designates the root of a tree: by definition, the node that has no parent. All other nodes in your tree are descendants of this node.
17.2.1.2 EventsThe tree model uses the TreeModelEvent class defined in the javax.swing.event package. A TreeModelEvent indicates that the tree changed: one or more nodes were added, modified, or deleted. You will find a more detailed discussion in Section 17.6, later in this chapter.
17.2.1.3 Miscellaneous methodsSeveral miscellaneous methods are defined in this model for querying the tree and its structure. Although the actual data structures are defined by the classes that implement the model, the model works as if every node maintains an array of its children, which in turn can be identified by an index into the array.
17.2.2 The DefaultTreeModel ClassThe DefaultTreeModel class puts together a basic tree model using TreeNode objects. (We'll look at TreeNode in the next section.) This DefaultTreeModel is the basis for the first example we built. It implements the TreeModel interface and adds a few new methods. If you're in a mood to create your own tree, this is a great place to start. Each node's data is really just an Object reference, pointing to just about anything you could want to represent. (Base types, of course, must be wrapped up in objects.) You can usually get away with building on a DefaultTreeModel, unless you need to impose a specific structure, such as limiting the number of children a node can have. Normally, the children of a node are just kept in a vector, so no limits exist. One other note about tree models: if you plan to allow multiple threads to access your model, you need to find a synchronization point. A conservative approach to thread safety dictates that you lock on the root node of the tree. Less conservative approaches require a bit of searching to make sure that you are not locking on a descendant of a node that is already locked. (If you did, it's possible the other thread would delete one of your ancestors, and thus delete you.) 17.2.2.1 PropertiesThe DefaultTreeModel class contains the properties listed in Table 17-2. It provides a root property (inherited from TreeModel) and a boolean property (asksAllowsChildren) to determine whether the tree model asks a node if it can accept children before inserting them. This property allows you to write nodes that can reject the addition of a child at runtime.
17.2.2.2 EventsThe DefaultTreeModel fires a TreeModelEvent whenever the tree is changed. It implements the standard methods for registering listeners, plus a number of convenience methods for firing different versions of the TreeModelEvent.
17.2.2.3 Constructors
17.2.2.4 Miscellaneous methodsThe DefaultTreeModel contains several methods to help you query and update the contents of your tree. These methods should help you add and remove nodes, as well as update the tree whenever you make other changes (such as changing user data).
17.2.3 Working with Tree ModelsThe TestTree example earlier in the chapter shows the DefaultTreeModel in action. However, if you want to build a more tailored tree, you can extend DefaultTreeModel and control things like the addition and removal of nodes. (Of course, if you want to build a tree model out of something other than TreeNode objects, you need to build your own class and implement the TreeModel interface. You could even have the model generate the tree dynamically.) As an example, we build a model that sorts incoming children using DefaultMutableTreeNode for the nodes. This type of model works for mailbox managers and other places where the user can create folders and subfolders any old time. (Trees that support Drag and Drop insertions would definitely benefit.) Figure 17-5 shows an example tree that uses the SortTreeModel to load a hierarchy of files and folders. This particular example supplies a case-insensitive comparator so that the filenames don't arrange themselves in that annoying uppercase-before-lowercase thing. Figure 17-5. A case-insensitive sorting tree![]() We extend DefaultTreeModel and provide a new method for adding children to the model that does not require an index. We then calculate the proper (comparator-determined) index and insert the node using the super.insertNodeInto( ) call. Here is the SortTreeModel class: // SortTreeModel.java // This class is similar to the DefaultTreeModel, but it keeps // a node's children in alphabetical order. import javax.swing.tree.*; import java.util.Comparator; public class SortTreeModel extends DefaultTreeModel { private Comparator comparator; public SortTreeModel(TreeNode node, Comparator c) { super(node); comparator = c; } public SortTreeModel(TreeNode node, boolean asksAllowsChildren, Comparator c) { super(node, asksAllowsChildren); comparator = c; } public void insertNodeInto(MutableTreeNode child, MutableTreeNode parent) { int index = findIndexFor(child, parent); super.insertNodeInto(child, parent, index); } public void insertNodeInto(MutableTreeNode child, MutableTreeNode par, int i) { // The index is useless in this model, so just ignore it. insertNodeInto(child, par); } // Perform a recursive binary search on the children to find the right // insertion point for the next node. private int findIndexFor(MutableTreeNode child, MutableTreeNode parent) { int cc = parent.getChildCount( ); if (cc == 0) { return 0; } if (cc == 1) { return comparator.compare(child, parent.getChildAt(0)) <= 0 ? 0 : 1; } return findIndexFor(child, parent, 0, cc - 1); // First and last index } private int findIndexFor(MutableTreeNode child, MutableTreeNode parent, int i1, int i2) { if (i1 == i2) { return comparator.compare(child, parent.getChildAt(i1)) <= 0 ? i1 : i1 + 1; } int half = (i1 + i2) / 2; if (comparator.compare(child, parent.getChildAt(half)) <= 0) { return findIndexFor(child, parent, i1, half); } return findIndexFor(child, parent, half + 1, i2); } } If you want to see a model built from scratch, check out the algebraic expression tree model (ExpressionTreeModel.java) from our online examples. It stores expressions, as a computer language parser might view and evaluate such things. The ExprTree1 class starts the demonstration. ![]() |
I l@ve RuBoard |
![]() ![]() |