Доброе время суток. Возникла следующая проблема. Имеется приложение, использующее JTree, в котором отображается некая иерархия. При удалении узлов дерева (у модели дерева вызывается removeNodeFromParent) вылетает следующая ошибка:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
java.lang.NullPointerException
at javax.swing.plaf.basic.BasicTreeUI.completeEditing(BasicTreeUI.java:1855)
at javax.swing.plaf.basic.BasicTreeUI.completeEditing(BasicTreeUI.java:1820)
at javax.swing.plaf.basic.BasicTreeUI$TreeSelectionHandler.valueChanged(BasicTreeUI.java:2418)
at javax.swing.tree.DefaultTreeSelectionModel.fireValueChanged(DefaultTreeSelectionModel.java:612)
at javax.swing.tree.DefaultTreeSelectionModel.notifyPathChange(DefaultTreeSelectionModel.java:1006)
at javax.swing.tree.DefaultTreeSelectionModel.removeSelectionPaths(DefaultTreeSelectionModel.java:498)
at javax.swing.JTree.removeDescendantSelectedPaths(JTree.java:2879)
at javax.swing.JTree.removeDescendantSelectedPaths(JTree.java:2929)
at javax.swing.JTree$TreeModelHandler.treeNodesRemoved(JTree.java:3012)
at javax.swing.tree.DefaultTreeModel.fireTreeNodesRemoved(DefaultTreeModel.java:492)
at javax.swing.tree.DefaultTreeModel.nodesWereRemoved(DefaultTreeModel.java:303)
at javax.swing.tree.DefaultTreeModel.removeNodeFromParent(DefaultTreeModel.java:239)
(строки стека, заканчивая вызовом removeNodeFromParent, опущены, поскольку не содержат никакой информации к размышлению).
У этой ошибки есть две интересных особенности:
1) где-то в 1 из 50 случаев ее не происходит;
2) она происходит, если на дереве висит свой собственный (т.е. не default'ный) TreeCellEditor; в противном случае - все нормально.
Исследование стека отладчиком показало, что этот NullPointerException происходит из-за того, что методе getNodeForPath класса VariableHeightLayoutCache (extends AbstractLayoutCache) помеченное моим комментарием (на кириллице ) условие возращает true, и метод возвращает null:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
private TreeStateNode getNodeForPath(TreePath path, boolean onlyIfVisible, boolean shouldCreate) {
if (path != null ) {
TreeStateNode node;
node = getMapping(path);
if (node != null ) {
if (onlyIfVisible && !node.isVisible())
return null ;
return node;
}
// Check all the parent paths, until a match is found.
Stack paths;
if (tempStacks.size() == 0 ) {
paths = new Stack();
}
else {
paths = (Stack)tempStacks.pop();
}
try {
paths.push(path);
path = path.getParentPath();
node = null ;
while (path != null ) {
node = getMapping(path);
if (node != null ) {
// Found a match, create entries for all paths in
// paths.
while (node != null && paths.size() > 0 ) {
path = (TreePath)paths.pop();
node.getLoadedChildren(shouldCreate);
int childIndex = treeModel.
getIndexOfChild(node.getUserObject(),
path.getLastPathComponent());
// Это условие возвращает true
if (childIndex == - 1 ||
childIndex >= node.getChildCount() ||
(onlyIfVisible && !node.isVisible())) {
node = null ;
}
else
node = (TreeStateNode)node.getChildAt
(childIndex);
}
return node;
}
paths.push(path);
path = path.getParentPath();
}
}
finally {
paths.removeAllElements();
tempStacks.push(paths);
}
// If we get here it means they share a different root!
// We could throw an exception...
}
return null ;
}
К сожалению, при отладке этого кода я не смог просмотреть значения локальных переменных, что, возможно, дало бы больше информации о причинах ошибки - отладчик пишет, что Local variables debug info unavailable.
Буду премного благодарен за информацию о местонахождении граблей.
P.S. jdk, увы, 1.3.1_06.