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

时间:2014-04-15 17:24:33

标签: java swing jtree treenode

Java-应用程序需要根据用户输入添加和删除TreeNodes。实际上一切正常,但删除节点有时会导致ArrayIndexOutOfBoundsException。我所做的是首先确定是否应该显示节点。虽然第一种情况不会改变任何东西,但第二种情况将从其父节点中删除节点。

removeNodeFromParent(treeNode);

不幸的是,有时候下面的例外情况会被打印到控制台,例如:1 >= 1这样的表达可能会有所不同,例如: 177 >= 177177 >= 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)

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

(我知道DefaultUncaughtExceptionHandler,为了隐藏这样的堆栈跟踪,但我想知道根本原因)


[编辑]

用户输入是一种过滤模式,根据该过滤模式,需要完全删除某些第二级根节点。单个剩余的第二级根节点的子节点正在被过滤。

  1. 用户插入过滤器模式
  2. 自行应用过滤器: private synchronized static void reloadTree(final JTree tree,final VAFilteredTreeModel treeModel)
  3. 删除不需要的第二级根节点 public void removeApplicationElementNodes(List<String> appElemNodeTypesToKeepAlive)
  4. private synchronized static void reloadTree(最终的JTree树,             最终MVAFilteredTreeModel 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);
                }
            }
        }
    }
    

    为了过滤节点,我覆盖了getChild

    的方法getChildCountDefaultTreeModel
    @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;
    }
    

    我有什么想法可以避免上面的RuntimeExceptions?

1 个答案:

答案 0 :(得分:1)

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

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

在自己的线程中执行,在EDT中。解决方案是将此方法调用移动到 SwingWorker 实例中,以便节点的渲染由EDT完成。