JTree 'DefaultTreeModel.removeNodeFromParent()'会导致一个'java.lang.ArrayIndexOutOfBoundsException'

时间:2022-03-29 12:33:23

A Java- Application needs to add and remove TreeNodes based on the users input. Actually everything works fine, except that removing nodes will sometimes cause an ArrayIndexOutOfBoundsException. What I'm doing is to first determine whether a node should be displayed or not. While the first case doesn't change anything, the second one will remove the node from its parent.

Java应用程序需要根据用户输入添加和删除TreeNodes。实际上,除了删除节点有时会导致ArrayIndexOutOfBoundsException之外,一切都正常。我要做的是首先确定一个节点是否应该显示。当第一个case没有改变任何东西时,第二个case将从其父节点中移除节点。

removeNodeFromParent(treeNode);

removeNodeFromParent(treeNode);

Unfortunately, somtimes the following excetpion will be printed to console, wherby the expression like: 1 >= 1 may be different, e.g. 177 >= 177 or 177 >= 98

不幸的是,下面的部分将被打印到控制台,这样的表达式:1 >= 1可能是不同的,例如177 >= 177或177 >= 98。

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 1 >= 1
    at java.util.Vector.elementAt(Unknown Source)
    at javax.swing.tree.DefaultMutableTreeNode.getChildAt(Unknown Source)
    at javax.swing.tree.VariableHeightLayoutCache$VisibleTreeStateNodeEnumeration.nextElement(Unknown Source)
    at javax.swing.tree.VariableHeightLayoutCache$VisibleTreeStateNodeEnumeration.nextElement(Unknown Source)
    at javax.swing.plaf.basic.BasicTreeUI.paint(Unknown Source)
    at javax.swing.plaf.ComponentUI.update(Unknown Source)
    at javax.swing.JComponent.paintComponent(Unknown Source)
    at javax.swing.JComponent.paint(Unknown Source)
    at javax.swing.JComponent.paintChildren(Unknown Source)
    at javax.swing.JComponent.paint(Unknown Source)
    at javax.swing.JComponent.paintChildren(Unknown Source)
    at javax.swing.JComponent.paint(Unknown Source)
    at javax.swing.JViewport.paint(Unknown Source)
    at javax.swing.JComponent.paintChildren(Unknown Source)
    at javax.swing.JComponent.paint(Unknown Source)
    at javax.swing.JComponent.paintToOffscreen(Unknown Source)
    at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
    at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
    at javax.swing.RepaintManager.paint(Unknown Source)
    at javax.swing.JComponent._paintImmediately(Unknown Source)
    at javax.swing.JComponent.paintImmediately(Unknown Source)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.prePaintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.access$700(Unknown Source)
    at javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$000(Unknown Source)
    at java.awt.EventQueue$1.run(Unknown Source)
    at java.awt.EventQueue$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

Any ideas how I can avoid this behavior?

有什么办法可以避免这种行为吗?

(I'm aware about a DefaultUncaughtExceptionHandler, in order to hides such stack traces, but I want to know the root cause)

(我知道有一个DefaultUncaughtExceptionHandler来隐藏这些堆栈跟踪,但是我想知道根本原因)


[EDIT]

(编辑)

The users input is a filter pattern according which some 2nd level root nodes need to be totally removed. The children of the single remaining 2nd level root node, are getting filtered.

用户输入是一个过滤模式,根据这个模式,需要完全删除一些第二层根节点。剩下的第2级根节点的子节点正在被过滤。

  1. The user insert the filter pattern
  2. 用户插入过滤器模式
  3. Apply the filter itself: private synchronized static void reloadTree(final JTree tree,final VAFilteredTreeModel treeModel)
  4. 应用过滤器本身:私有同步静态void reloadTree(最终的JTree树,最终的VAFilteredTreeModel treeModel树)
  5. Remove not needed 2nd level root nodes public void removeApplicationElementNodes(List<String> appElemNodeTypesToKeepAlive)
  6. 删除不需要的二级根节点public void removeapplicationelementnode(列表 appElemNodeTypesToKeepAlive)

private synchronized static void reloadTree(final JTree tree, final MVAFilteredTreeModel treeModel){

私有同步静态void reloadTree(最终的JTree,最终的MVAFilteredTreeModel treeModel treeModel)

        TreePath selectedTP = tree.getSelectionPath();

        MVAFilteredTree filteredTree = (MVAFilteredTree) tree;
        List<String> visibleNodeNames = new Vector<String>(
                filteredTree
                        .getTreeFilter()
                        .getRecentlyAppliedPositiveMVAApplicationElementFilterNodeNames());
        for (String nodeName : visibleNodeNames) {
            TreePath path = treeModel.getPathToNode(nodeName);
            if (path != null) {
                treeModel.reloadTreeNode(nodeName);
                tree.expandPath(path);
                tree.setSelectionPath(path);

                if(selectedTP == null){
                    selectedTP = path;
                }
            }
        }

        /*
         * If the there's still no selected node, then get the path of any
         * available node 2nd level root node
         */
        if (selectedTP == null) {
            MVANode aoTestNode = Plugins.getInstance().getNodeFromTreeNodeContainer();
            if (aoTestNode != null) {
                selectedTP = MVATreeUtil.getTreePath(aoTestNode);
                tree.expandPath(selectedTP);
            }
        }

        AbstractAddon addon = AddonProvider.getInstance().getActiveAddon();
        if (addon.isActive() && addon.expandAllTreeNodes()) {
            MVATreeUtil.expandAllTreeNodes(filteredTree);
        } else {
            MVATreeUtil
                    .expandAllTreeNodesAccordingToTheAppliedFilters(filteredTree);
            tree.scrollPathToVisible(treeModel.getPathToNode(treeModel
                    .getRootNode().getName()));
        }

        TreePath visibleTreePath = MVATreeUtil.getNextVisibleTreePath(
                filteredTree, selectedTP);

        if(visibleTreePath != null){
            tree.setSelectionPath(visibleTreePath);
            tree.scrollPathToVisible(visibleTreePath);
        }
    }


public void removeApplicationElementNodes(
        List<String> appElemNodeTypesToKeepAlive) {
    for (String nodeType : Plugins.getInstance()
            .getPluggedInApplicationElementTreeNodeTypes()) {
        if (!appElemNodeTypesToKeepAlive.contains(nodeType)) {
            MVANode mvaNode = Plugins.getInstance()
                    .getApplicationElementNodes().get(nodeType);
            if (mvaNode!= null && MVATreeUtil.checkNodeExists(getRootNode(),
                    mvaNode.getName())) {
                removeNodeFromParent(mvaNode);
            }
        }
    }
}

In order to filter the nodes, I have overridden the methods getChild and getChildCount of the DefaultTreeModel

为了过滤节点,我重写了DefaultTreeModel的getChild和getChildCount方法

@Override
public Object getChild(Object parent, int index) {
    int count = 0;
    int childCount = super.getChildCount(parent);
    for (int i = 0; i < childCount; i++) {
        /*
         * In case that the child count has changed during iteration, return
         * null
         */
        if (super.getChildCount(parent) != childCount) {
            return null;
        }
        Object child = super.getChild(parent, i);
        if (getTreeFilter().isTreeNodeFiltered(child)) {
            if (count == index) {
                return child;
            }
            count++;
        }
    }
    /*
     * return an pseudo node in case that there isn't any real node at that
     * index. This can happen due to node collapses and node filtering.
     */
    return new DefaultMutableTreeNode();
}

@Override
public int getChildCount(Object parent) {
    int count = 0;
    int childCount = super.getChildCount(parent);
    for (int i = 0; i < childCount; i++) {
        /*
         * in case that the child count changed during iteration, return
         * with -1
         */
        if (super.getChildCount(parent) != childCount) {
            return -1;
        }
        Object node = super.getChild(parent, i);
        if (getTreeFilter().isTreeNodeFiltered(node)) {
            count++;
        }
    }
    return count;
}

Any ideas how I can avoid the above RuntimeExceptions?

有什么办法可以避免上面的运行时异常吗?

1 个解决方案

#1


1  

Recently I resolved this issue. The problem was that the method:

最近我解决了这个问题。问题是方法:

private synchronized static void reloadTree(final JTree tree, final MVAFilteredTreeModel treeModel)

私有同步静态void reloadTree(最终的JTree,最终的MVAFilteredTreeModel treeModel)

was executed within its own thread appart the EDT. The solution was to move this method call into a SwingWorker instance, so that the rendering of nodes is done by the EDT.

在自己的线程中执行并分配EDT。解决方案是将这个方法调用移动到SwingWorker实例中,以便由EDT完成节点的呈现。

#1


1  

Recently I resolved this issue. The problem was that the method:

最近我解决了这个问题。问题是方法:

private synchronized static void reloadTree(final JTree tree, final MVAFilteredTreeModel treeModel)

私有同步静态void reloadTree(最终的JTree,最终的MVAFilteredTreeModel treeModel)

was executed within its own thread appart the EDT. The solution was to move this method call into a SwingWorker instance, so that the rendering of nodes is done by the EDT.

在自己的线程中执行并分配EDT。解决方案是将这个方法调用移动到SwingWorker实例中,以便由EDT完成节点的呈现。