Hi, We continue (but don't conclude) the JTree memory leak story...
This commit implements a temporary fix for the memory leak, as well as a patch from Roman that starts fixing the root problem (however our javax.swing.tree.DefaultTreeModel isn't firing events properly yet). Cheers, Francis 2006-11-06 Roman Kennke <[EMAIL PROTECTED]> * javax/swing/JTree.java (TreeModelHandler.treeNodesRemoved): Implemented. (TreeModelHandler.treeStructureChanged): Implemented. (nodeStates): Made package private. 2006-11-06 Francis Kung <[EMAIL PROTECTED]> * javax/swing/JTree.java (clearSelectionPathStates): New private method to clean up nodeStates. (removeSelectionPath): Call clearSelectionPathStates(). (removeSelectionPaths): Call clearSelectionPathStates(). (removeSelectionRow): Call clearSelectionPathStates(). (setSelectionPath): Call clearSelectionPathStates(). (setSelectionPaths): Call clearSelectionPathStates(). (setSelectionRow): Call clearSelectionPathStates().
Index: javax/swing/JTree.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/JTree.java,v retrieving revision 1.77 diff -u -r1.77 JTree.java --- javax/swing/JTree.java 18 Oct 2006 18:46:25 -0000 1.77 +++ javax/swing/JTree.java 6 Nov 2006 16:34:25 -0000 @@ -1230,8 +1230,32 @@ */ public void treeNodesRemoved(TreeModelEvent ev) { - // TODO: The API docs suggest that this method should do something - // but I cannot really see what has to be done here ... + if (ev != null) + { + TreePath parent = ev.getTreePath(); + Object[] children = ev.getChildren(); + TreeSelectionModel sm = getSelectionModel(); + if (children != null) + { + TreePath path; + Vector toRemove = new Vector(); + // Collect items that we must remove. + for (int i = children.length - 1; i >= 0; i--) + { + path = parent.pathByAddingChild(children[i]); + if (nodeStates.containsKey(path)) + toRemove.add(path); + // Clear selection while we are at it. + if (sm != null) + removeDescendantSelectedPaths(path, true); + } + if (toRemove.size() > 0) + removeDescendantToggledPaths(toRemove.elements()); + TreeModel model = getModel(); + if (model == null || model.isLeaf(parent.getLastPathComponent())) + nodeStates.remove(parent); + } + } } /** @@ -1243,9 +1267,38 @@ */ public void treeStructureChanged(TreeModelEvent ev) { - // Set state of new path. - TreePath path = ev.getTreePath(); - setExpandedState(path, isExpanded(path)); + if (ev != null) + { + TreePath parent = ev.getTreePath(); + if (parent != null) + { + if (parent.getPathCount() == 1) + { + // We have a new root, clear everything. + clearToggledPaths(); + Object root = treeModel.getRoot(); + if (root != null && treeModel.isLeaf(root)) + nodeStates.put(parent, Boolean.TRUE); + } + else if (nodeStates.containsKey(parent)) + { + Vector toRemove = new Vector(); + boolean expanded = isExpanded(parent); + toRemove.add(parent); + removeDescendantToggledPaths(toRemove.elements()); + if (expanded) + { + TreeModel model = getModel(); + if (model != null + || model.isLeaf(parent.getLastPathComponent())) + collapsePath(parent); + else + nodeStates.put(parent, Boolean.TRUE); + } + } + removeDescendantSelectedPaths(parent, false); + } + } } } @@ -1399,8 +1452,10 @@ * This contains the state of all nodes in the tree. Al/ entries map the * TreePath of a note to to its state. Valid states are EXPANDED and * COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED. + * + * This is package private to avoid accessor methods. */ - private Hashtable nodeStates = new Hashtable(); + Hashtable nodeStates = new Hashtable(); protected transient TreeCellEditor cellEditor; @@ -2201,20 +2256,35 @@ public void setSelectionPath(TreePath path) { + clearSelectionPathStates(); selectionModel.setSelectionPath(path); } public void setSelectionPaths(TreePath[] paths) { + clearSelectionPathStates(); selectionModel.setSelectionPaths(paths); } + + /** + * This method, and all calls to it, should be removed once the + * DefaultTreeModel fires events properly. Maintenance of the nodeStates + * table should really be done in the TreeModelHandler. + */ + private void clearSelectionPathStates() + { + TreePath[] oldPaths = selectionModel.getSelectionPaths(); + if (oldPaths != null) + for (int i = 0; i < oldPaths.length; i++) + nodeStates.remove(oldPaths[i]); + } public void setSelectionRow(int row) { TreePath path = getPathForRow(row); if (path != null) - selectionModel.setSelectionPath(path); + setSelectionPath(path); } public void setSelectionRows(int[] rows) @@ -2289,11 +2359,13 @@ public void removeSelectionPath(TreePath path) { + clearSelectionPathStates(); selectionModel.removeSelectionPath(path); } public void removeSelectionPaths(TreePath[] paths) { + clearSelectionPathStates(); selectionModel.removeSelectionPaths(paths); } @@ -2302,7 +2374,7 @@ TreePath path = getPathForRow(row); if (path != null) - selectionModel.removeSelectionPath(path); + removeSelectionPath(path); } public void removeSelectionRows(int[] rows)