Collin Fagan is a Sr. Software Engineer at NAVTEQ working on next generation map building software. He holds a BS in Computer Science from the Rochester Institute of Technology. Collin has worked in the Map, Medical, Produce Warehousing and Telecommunications industries on a wide assortment of projects ranging from customized Linux thin clients to highly concurrent telecommunications control systems. Collin has posted 13 posts at DZone. View Full User Profile

The secret life of a DefaultTreeModel

03.21.2008
| 26303 views |
  • submit to reddit

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.

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.

AttachmentSize
TreeModelDemo1.png19.2 KB
TreeModelDemo2.png6.68 KB
TreeModelDemo3.png7.74 KB
Published at DZone with permission of its author, Collin Fagan.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Jeanette Winzenburg replied on Wed, 2008/03/26 - 11:43am

doohhh .. lost my first trial to comment, so here#s a shorter version :-(

 

Collin,

 

always good to see propagation of best-practices :-) Just two minor points:

 

- the insert-many nodes example is a bit off target, IMO: basically it's inserting grandchildren. As tree notification is about children only, there's is no need to care about them (and no need for a structureChanged) - it's good enough to nodify the insertion of the two new children (odd and even)

 

<code>

model.nodesWereInserted(node, new int[] {node.getIndex(odd), node.getIndex(even)});
 

</code> 

 

so it's this one notification vs. two if using the model methods. Not so much of an incentive to go a more error-prone path. Obviously a judgement call :-)

 

 - your change example can be done directly through the model (after all, we know we have DefaultMTNs :-)

 

<code> 

 tree.getModel().valueForPathChanged(currentSelection, "This is a very loooooooooooooooog string");

 </code>

 

 CU

Jeanette 

Collin Fagan replied on Wed, 2008/03/26 - 1:13pm

Jeanette,

Thanks for the feedback. I'll change the bulk child insert to just add a large block of children and not grandchildren. I'll also add in a block for the valueForPathChanged method. Mostly this tutorial grew out of my frustration with a software system that used a node based update system. I can't tell you how many "update causes ... in main tree" bugs I had to deal with as a result.

Thanks again for the feedback.

Collin

Rafael Alvarez replied on Sun, 2008/04/06 - 6:16pm

I wish I had something like this two years ago. Good work! :D

jiji530 (not verified) replied on Mon, 2009/06/29 - 9:42pm

thanks for your post.perhaps you will like abercrombie ed hardy mortgage rates tiffanys ed hardy Is not it?

super pan replied on Sat, 2009/08/01 - 1:39am

I have been thinking about the secret of life, but a secret is still a secret, i cannot figure it out in the end. New Balance

Mike West replied on Tue, 2009/08/18 - 9:11pm

After 3 days of trying to figure out how to make changes update in my jtree, i finally found your article. Thank you so very much.

Carla Brian replied on Wed, 2012/05/30 - 11:18am

I am new to this one. Good thing you provide a step-by-step process. I will study more on this and research about it also. - Instant Tax Solutions Ratings

James Walker replied on Wed, 2012/06/20 - 7:29am

The coolest thing is that the above isn't pseudo code at all.. Nice.. Keep them coming. supermega jet

James Walker replied on Wed, 2012/11/14 - 5:00pm

For adding or removing a single node from a tree the simplest warehouse software solutions is to call the insertNodeInto and removeNodeFromParent methods on DefaultTreeModel

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.