This is a draft of a small tutorial I have been attempting to submit to
Sun for inclusion in the official Java tutorial. Unfortunately they are
very busy lately and have not had the time to review my work. My intent
is to bring to light some of the subtleties of the DefaulTreeModel API
that are not covered in the Java tutorial on JTree.
This
tutorial will revolve around a demo application designed to show the
difference between the right and wrong way to interact with
DefaulTreeModel. Right clicking on any node in the tree will bring up a
menu with a range of options.

Adding and Removing nodes in a DefaultTreeModel.
A
DefaulTreeModel collects DefaultMutableTreeNodes together into a tree
structure. At first glance the child modification methods like add()
and remove() from DefaultMutableTreeNode look as if they overlap with
the insertNodeInto() and removeNodeFromParent() from DefaulTreeModel.
When the methods from DefaultTreeModel are used events are fired that
update any JTree this model may be associated with. Simply modifying
the DefaultMutableTreeNode does not. This can be demonstrated by the
following chunk of code.
Example of the wrong way to remove a node.
TreePath currentSelection = tree.getSelectionPath();
if (currentSelection != null) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)
currentSelection.getLastPathComponent();
node.removeFromParent();
}
Calls to methods like add() or remove() from DefaultMutableTreeNode
need to be followed by a call to the DefaultTreeModel to notify the
JTree of model changes. In our example a call to nodeStructureChanged()
on the parent of the node being removed would have allowed the tree to
be updated correctly. For adding or removing a single node from a tree
the simplest solution is to call the insertNodeInto and
removeNodeFromParent methods on DefaultTreeModel.
Example of the correct way to remove a node.
TreePath currentSelection = tree.getSelectionPath();
if (currentSelection != null) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)
currentSelection.getLastPathComponent();
DefaultTreeModel model = ((DefaultTreeModel) tree.getModel());
model.removeNodeFromParent(node);
}
There may be situations where multiple additions or removals happen in large blocks. In these cases the model may not need to be notified of a change until the bulk operation is completed. This is exemplified by the following code which adds multiple children to a node.
...
/**
*
* Adds 2 children, "even" and "odd". Even contains all the even numbers from zero to 49.
* Odd contains all the odd numbers from zero to 49.
*/
TreePath currentSelection = tree.getSelectionPath();
if (currentSelection != null) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)
currentSelection.getLastPathComponent();
DefaultTreeModel model = ((DefaultTreeModel) tree.getModel());
DefaultMutableTreeNode odd = new DefaultMutableTreeNode("odd");
node.add(odd);
DefaultMutableTreeNode even = new DefaultMutableTreeNode("even");
node.add(even);
for (int i = 0; i < 50; i++) {
if (i % 2 == 0) {
even.add(new DefaultMutableTreeNode(i));
} else {
odd.add(new DefaultMutableTreeNode(i));
}
}
// The above changes may not seem to take effect until nodeStructureChanged is called
model.nodeStructureChanged(node);
}
...
This example creates 52 new nodes. If they were inserted with
insertNodeInto() then 52 TreeNodesInserted events would have been
fired. This way only one fireTreeStructureChanged event is fired after
all the nodes are modified. Depending upon the particular needs of your
application this may save some event processing.
Modifying user objects in a DefaultMutableTreeNodes.
Just
as a DefaultTreeModel needs to be notified when children are added or
removed for a node, so too does it need to be notified when the user
object returned by getUserObject() is modified. Failure to do so could
lead to rendering issues in the JTree. This is an example of what a
such a situation might look like.

To avoid this make sure to call nodeChanged() on the model after any change that might effect the rendering of that node. Here is an example of code that will update correctly.
...
TreePath currentSelection = tree.getSelectionPath();
if (currentSelection != null) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)
currentSelection.getLastPathComponent();
node.setUserObject("THIS IS A VERY LOOOOOOOOOOOOOOOOOOOOONG STRING");
DefaultTreeModel model = ((DefaultTreeModel) tree.getModel());
model.nodeChanged(node);
}
...
When updated correctly the tree will display the entire string.
Conclusion
As a general rule any time the data associated
with your DefaulTreeModel changes it is important to update the model.
Proper use of the DefaulTreeModel will limit the number of times you
must explicitly do this.
I've made the source code for the example available for download [1].
As
I said this is a draft of a tutorial. I'm looking for feedback on what
can be improved and added while staying within the scope of the
DefaulTreeModel/DefaultMutableTreeNode relationship.
| Attachment | Size |
|---|---|
| TreeModelDemo1.png [2] | 19.2 KB |
| TreeModelDemo2.png [3] | 6.68 KB |
| TreeModelDemo3.png [4] | 7.74 KB |
Links:
[1] http://docs.google.com/Doc?id=dfdmcs8w_16ftth9nfs
[2] http://java.dzone.com/sites/all/files/TreeModelDemo1.png
[3] http://java.dzone.com/sites/all/files/TreeModelDemo2.png
[4] http://java.dzone.com/sites/all/files/TreeModelDemo3.png