JTree 'DefaultTreeModel.removeNodeFromParent()' causes a 'java.lang.ArrayIndexOutOfBoundsException'

StackOverflow https://stackoverflow.com/questions/23090470

  •  04-07-2023
  •  | 
  •  

سؤال

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.

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

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)


[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.

  1. The user insert the filter pattern
  2. Apply the filter itself: private synchronized static void reloadTree(final JTree tree,final VAFilteredTreeModel treeModel)
  3. Remove not needed 2nd level root nodes public void removeApplicationElementNodes(List<String> appElemNodeTypesToKeepAlive)

private synchronized static void reloadTree(final JTree tree, final 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);
            }
        }
    }
}

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

@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?

هل كانت مفيدة؟

المحلول

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

private synchronized static void reloadTree(final JTree tree, final 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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top