Hi all,
This is a problem that I have been grappling with for some time. BUT - I think I have cracked it and in a reasonably urbane manner. I post my solution here in the hope that it may of help to others.
The problem:
I have a JTree populated with items from a database (this could be any other type of volatile data source). I browse my tree, expanding and collapsing nodes as I go. I get to a point where I want to refresh the tree so that it resembles the most up to date state of the backing data. BUT - after I refresh it, I want it to expand all those nodes that are currently expanded. The default behaviour is for all nodes to be collapsed. Here is what I do:
I trap each expand and collapse event. For each expand event, I populate a list with the user object present in the node, thus:
private void processTreeExpansion(TreeExpansionEvent e){
if (supressExpansionEvent == false) { // Not interested in this event if we are currently restoring the tree
TreePath p = (TreePath) e.getPath(); // Get the tree path
Object[] Objs = p.getPath(); // Get all the objects withiin that path
if (Objs.length == 0) { return;} // This should never happen
DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) Objs[Objs.length - 1]; // Derive a DMTN
YObject myObject = (YObject) dmtn .getUserObject(); // This will always be a YObject Class object
expandedTreeObjects.add(myObject); // Place the object in my list
}
}
I do the opposite for each collapse event.
When I refresh the JTree I:
removeAllChildren() from the Root Node
Re-populate the tree nodes with the fresh data
call reload(0 on the Tree Model
and then call these methods:
private void restoreTree() {
// Some ommited stuff
// Process tree nodes from root node
restoreTreeNode(tree, new TreePath(rootNode), null); // Not surprisingly, rootNode is my rootNode
}
private void restoreTreeNode(JTree tree, TreePath parent, DefaultMutableTreeNode treeNode) {
// Traverse down through the children
TreeNode node = (TreeNode) parent.getLastPathComponent(); // Get the last TreeNode component for this path
if (node.getChildCount() >= 0) { // If the node have children?
// Create a child numerator over the node
Enumeration en = node.children();
while (en.hasMoreElements()) { // While we have children
DefaultMutableTreeNode dmTreeNode = (DefaultMutableTreeNode)en.nextElement(); // Derive the node
TreePath path = parent.pathByAddingChild(dmTreeNode); // Derive the path
restoreTreeNode(tree, path, dmTreeNode); // Recursive call with new path
} // End While we have more children
} // End If the node have children?
// Nodes need to be expanded from last branch node up
if (treeNode != null) { // If true, this is the root node - ignore it
YObject myUserObject = (YObject) treeNode.getUserObject(); // Get the user object from the node
// Note - all the objects I place in tree nodes
// belong to the same class - YObject
if (expandedTreeObjects.contains(myUserObject)) { // Is this present on the previously expanded list?
tree.expandPath(parent); // et viola
}
} // End If - root node
}
Notes:
1. If any gurus out there spot a problem with this, please expand (pardon the pun) on it.
2. If the backing data for the tree can change, then using the row count to expand nodes is not very good, because they will have changed. In any case, tree.getRowCount() only returns the number of rows currently being displayed.
3. I had considered storing paths instead of user objects to be the basis of my treeRestore; but when I call tableModel.reload(), I think I get a completely new set of paths, and so the old ones are useless.
4. It just so happens that I populate this tree with objects all of the same class (YObject). But it would not be so difficult to extend it to cater for several different classes of object.
I post this here in the sincere hope that others may benefit from the lessons I have learned. This must be an extremely common requirement of a JTree, and yet it was not apparent to me how it may be easily achieved (can one call the above easy?). If anyone can improve on this, I welcome their thoughts.
Best regards to all,
Steve