2

编辑3:这个不重复一般的“我的代码中有indexoutofboundsexception”问题,因为正如答案中明确指出的那样,这是一个广泛使用的第三方库中的一个错误jide-common,经开发人员确认,下面有一个链接。此错误最近已修复(在发布此问题后),并且该库的许多旧版本仍然受到影响。因此,此信息可能对偶然发现 jide-common 相同问题的其他开发人员有用。

我在 Java 中遇到了 GUI 编程的问题。我不确定问题的具体来源(仍然无法弄清楚) - 这可能是我在某处的错误,jide 组件中的一些错误代码,甚至是 awt/swing 焦点或事件处理有问题。

以下代码取决于

  • com.jidesoft:jide-grids:3.5.1
  • com.jidesoft:jide-components:3.5.1
  • com.jidesoft:jide-common:3.5.1

我在 Stackoverflow 上找到了这个提示,但经过一些调试后,对我来说似乎对 CellEditor 和其他组件的每次修改都是在 EDT 中完成的。

要重现该错误,您应该运行此示例并打开包含 CellEditor 的选项卡(它必须是最后一行,否则不会发生索引溢出),然后在其中输入一些“不正确”的值并且不从编辑器中移除焦点,单击关闭前一个选项卡上的“x”按钮。之后发生以下事件链:1) 选项卡关闭/删除,2) CellEditor 验证程序显示模式对话框,该对话框反过来触发选项卡窗格的重绘 3) 由于缺少(关闭)选项卡,抛出 ArrayIndexOutOfBounds 异常。

package com.example;

import com.jidesoft.document.DocumentComponent;
import com.jidesoft.document.DocumentPane;
import com.jidesoft.grid.JideTable;
import com.jidesoft.grid.TableModelWrapperUtils;
import com.jidesoft.grid.TextFieldCellEditor;
import com.jidesoft.swing.JideTabbedPane;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.math.BigDecimal;
import java.util.Random;

/**
 * Dependends on:
 * com.jidesoft:jide-grids:3.5.1
 * com.jidesoft:jide-components:3.5.1
 * com.jidesoft:jide-common:3.5.1
 */
public class ModalPopupFailure {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setTitle("test");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        JComponent panel = new ModalPopupFailure().buildPanel();
        frame.setContentPane(panel);
        frame.setBounds(450, 300, 700, 500);
        frame.setVisible(true);
    }

    /**
     * @return table editor panel
     */
    private JPanel tableTab() {
        JPanel result = new JPanel(new BorderLayout());

        DefaultTableModel model = new DefaultTableModel(1, 1);
        model.setValueAt(new BigDecimal(0L), 0, 0);
        JideTable table = new JideTable(model);
        table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

        final int actualCol0 = TableModelWrapperUtils.getActualColumnAt(table.getModel(), 0);
        CustomCellEditor bdce = new CustomCellEditor(Integer.class);
        table.getColumnModel().getColumn(actualCol0).setCellEditor(bdce);

        result.add(table, BorderLayout.CENTER);
        return result;
    }

    /**
     * @return Main panel with tabs
     */
    private JComponent buildPanel() {
        JPanel mainPanel = new JPanel(new BorderLayout());

        final DocumentPane documentPane = new DocumentPane();

        DocumentComponent dc1 = new DocumentComponent(new JLabel("tab1 label"), "aaa");
        documentPane.openDocument(dc1);

        DocumentComponent dc2 = new DocumentComponent(tableTab(), "TABLETAB");
        documentPane.openDocument(dc2);
        documentPane.setActiveDocument("TABLETAB");

        documentPane.setTabbedPaneCustomizer(new DocumentPane.TabbedPaneCustomizer() {
            @Override
            public void customize(final JideTabbedPane tabbedPane) {
                tabbedPane.setShowCloseButton(true);
                tabbedPane.setUseDefaultShowCloseButtonOnTab(false);
                tabbedPane.setShowCloseButtonOnTab(true);
            }
        });

        documentPane.setTabPlacement(SwingConstants.TOP);

        mainPanel.add(documentPane, BorderLayout.CENTER);
        mainPanel.add(new JButton(new AbstractAction("New tab") {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        Integer randInt = new Random().nextInt();
                        DocumentComponent newdc = new DocumentComponent(
                                new JLabel("Generated tab label " + randInt),
                                Integer.toString(randInt));

                        documentPane.openDocument(newdc);
                    }
                }),
                BorderLayout.NORTH);
        JLabel decription = new JLabel("<html>Steps to reproduce:<br />" +
                "TABLETAB should be the last tab<br />" +
                "The \"correct\" value for cell editor is 777.<br />" +
                "Enter any \"incorrect\" number, then close any tab standing before TABLETAB.<br />" +
                "An exception caused by window repaint should be raised by now.<br />" +
                "If dialog would not be modal, there would be no window repaint triggered.</html>");
        mainPanel.add(decription, BorderLayout.SOUTH);

        return mainPanel;
    }

    private class CustomCellEditor extends TextFieldCellEditor {

        boolean firstTime = true;

        public CustomCellEditor(Class<?> aClass) {
            super(aClass);
            _textField.setInputVerifier(new InputVerifier() {
                @Override
                public boolean verify(JComponent input) {
                    boolean valid = "777".equals(_textField.getText());
                    if (valid) return true;

                    final JDialog dialog = new JDialog();
                    dialog.setTitle("Exception expected");
                    dialog.setContentPane(new JLabel("<html>If one of previous tabs was closed, an exception should be raised by now.</html>"));
                    dialog.setLocationRelativeTo(null);

                    dialog.setModal(true); // Switching to false seems to fix the problem

                    dialog.setSize(new Dimension(300, 100));
                    dialog.setVisible(true);

                    return true;
                }
            });
        }

        @Override
        public boolean stopCellEditing() {
            System.out.println("Stopping cell edit " + new Random().nextInt() + "in edt: " + SwingUtilities.isEventDispatchThread());
            if (firstTime) {
                firstTime = false;
                return false;
            }

            return _textField.getInputVerifier().shouldYieldFocus(_textField) && super.stopCellEditing();
        }
    }
}

例外:

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
    at java.util.ArrayList.rangeCheck(ArrayList.java:635)
    at java.util.ArrayList.get(ArrayList.java:411)
    at javax.swing.JTabbedPane.getComponentAt(JTabbedPane.java:1224)
    at com.jidesoft.plaf.vsnet.VsnetJideTabbedPaneUI.paintContentBorder(Unknown Source)
    at com.jidesoft.plaf.basic.BasicJideTabbedPaneUI.paintContentBorder(Unknown Source)
    at com.jidesoft.plaf.basic.BasicJideTabbedPaneUI.paint(Unknown Source)
    at javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
    at javax.swing.JComponent.paintComponent(JComponent.java:779)
    at javax.swing.JComponent.paint(JComponent.java:1055)
    at javax.swing.JComponent.paintChildren(JComponent.java:888)
    at javax.swing.JComponent.paint(JComponent.java:1064)
    at javax.swing.JComponent.paintChildren(JComponent.java:888)
    at javax.swing.JComponent.paint(JComponent.java:1064)
    at javax.swing.JComponent.paintToOffscreen(JComponent.java:5232)
    at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:295)
    at javax.swing.RepaintManager.paint(RepaintManager.java:1249)
    at javax.swing.JComponent._paintImmediately(JComponent.java:5180)
    at javax.swing.JComponent.paintImmediately(JComponent.java:4991)
    at javax.swing.RepaintManager$3.run(RepaintManager.java:808)
    at javax.swing.RepaintManager$3.run(RepaintManager.java:796)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:769)
    at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:718)
    at javax.swing.RepaintManager.access$1100(RepaintManager.java:62)
    at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1677)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:312)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:745)
    at java.awt.EventQueue.access$300(EventQueue.java:103)
    at java.awt.EventQueue$3.run(EventQueue.java:706)
    at java.awt.EventQueue$3.run(EventQueue.java:704)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:715)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:154)
    at java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:182)
    at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:221)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:219)
    at java.awt.Dialog.show(Dialog.java:1082)
    at java.awt.Component.show(Component.java:1655)
    at java.awt.Component.setVisible(Component.java:1607)
    at java.awt.Window.setVisible(Window.java:1014)
    at java.awt.Dialog.setVisible(Dialog.java:1005)
    at com.example.ModalPopupFailure$CustomCellEditor$1.verify(ModalPopupFailure.java:124)
    at javax.swing.InputVerifier.shouldYieldFocus(InputVerifier.java:132)
    at javax.swing.JComponent$1.acceptRequestFocus(JComponent.java:3589)
    at java.awt.Component.isRequestFocusAccepted(Component.java:7739)
    at java.awt.Component.requestFocusHelper(Component.java:7621)
    at java.awt.Component.requestFocusInWindow(Component.java:7544)
    at java.awt.Component.transferFocus(Component.java:7842)
    at java.awt.Component.hide(Component.java:1688)
    at javax.swing.JComponent.hide(JComponent.java:5585)
    at java.awt.Component.show(Component.java:1657)
    at java.awt.Component.setVisible(Component.java:1607)
    at javax.swing.JComponent.setVisible(JComponent.java:2641)
    at com.jidesoft.plaf.basic.BasicJideTabbedPaneUI.setVisibleComponent(Unknown Source)
    at com.jidesoft.plaf.basic.BasicJideTabbedPaneUI$PropertyChangeHandler.propertyChange(Unknown Source)
    at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
    at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:327)
    at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263)
    at java.awt.Component.firePropertyChange(Component.java:8393)
    at javax.swing.JComponent.putClientProperty(JComponent.java:4103)
    at javax.swing.JTabbedPane.removeTabAt(JTabbedPane.java:971)
    at com.jidesoft.swing.JideTabbedPane.removeTabAt(Unknown Source)
    at com.jidesoft.document.TdiGroup.removeDocument(Unknown Source)
    at com.jidesoft.document.DocumentPane.a(Unknown Source)
    at com.jidesoft.document.DocumentPane.a(Unknown Source)
    at com.jidesoft.document.DocumentPane.a(Unknown Source)
    at com.jidesoft.document.DocumentPane.a(Unknown Source)
    at com.jidesoft.document.DocumentPane.closeDocument(Unknown Source)
    at com.jidesoft.document.DocumentPane.closeSingleDocument(Unknown Source)
    at com.jidesoft.document.DocumentPane$34.actionPerformed(Unknown Source)
    at com.jidesoft.plaf.basic.BasicJideTabbedPaneUI$CloseTabAction.actionPerformed(Unknown Source)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2018)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2341)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:289)
    at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:289)
    at java.awt.Component.processMouseEvent(Component.java:6516)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
    at java.awt.Component.processEvent(Component.java:6281)
    at java.awt.Container.processEvent(Container.java:2229)
    at java.awt.Component.dispatchEventImpl(Component.java:4872)
    at java.awt.Container.dispatchEventImpl(Container.java:2287)
    at java.awt.Component.dispatchEvent(Component.java:4698)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4492)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)
    at java.awt.Container.dispatchEventImpl(Container.java:2273)
    at java.awt.Window.dispatchEventImpl(Window.java:2719)
    at java.awt.Component.dispatchEvent(Component.java:4698)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:747)
    at java.awt.EventQueue.access$300(EventQueue.java:103)
    at java.awt.EventQueue$3.run(EventQueue.java:706)
    at java.awt.EventQueue$3.run(EventQueue.java:704)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
    at java.awt.EventQueue$4.run(EventQueue.java:720)
    at java.awt.EventQueue$4.run(EventQueue.java:718)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:717)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)

有一个“扩展”版本的代码,添加了一些调试信息。我仍然无法弄清楚问题的根源,更不用说修复了。提前致谢。

编辑:好的,我现在发现的一件事是,根据文档显示从 verify() 方法中的对话框是错误的

verify 方法的存在只是为了确定输入是否有效,它永远不会打开对话框或导致任何其他副作用。shouldYieldFocus 方法调用验证,如果值无效,则将其设置为最小值或最大值。允许 shouldYieldFocus 方法产生副作用...

因此,我将所有非实际验证代码移至 shouldYieldFocus()。这并没有解决问题,但使用 SwingUtilities.invokeLater() 进一步延迟 dialog.show() 似乎工作正常,没有陷阱......但是。

public CustomCellEditor(Class<?> aClass) {
    super(aClass);
    _textField.setInputVerifier(new InputVerifier() {

        @Override
        public boolean shouldYieldFocus(JComponent input) {
            boolean inputOK = verify(input);
            if (!inputOK) {
                final ru.esc.erp.core.components.Dialog dialog = new ru.esc.erp.core.components.Dialog();
                dialog.setTitle("Exception may be raised");
                dialog.setContentPane(new JLabel("<html>Modal dialog opened with yet another delay.</html>"));
                dialog.setLocationRelativeTo(null);

                dialog.setModal(true); // false "fixes" the problem

                dialog.setSize(new Dimension(300, 100));
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        dialog.setVisible(true);
                    }
                });
            }
            return inputOK;
        }

        @Override
        public boolean verify(JComponent input) {
            return "777".equals(_textField.getText());
        }
    });
}

Edit2: 带有捕获视频的 gif

4

1 回答 1

1

好的,这是JideTabbedPaneUI 中的一个错误。我已经用我的示例测试了已修补的 jide-oss,并且不再有例外(但是,现在在关闭选项卡时会出现一些非关键的 UI 故障)。问题的根源找到了。

编辑:他们在 3.6.17 后期修复了这个错误:

by JIDE Support » 2017 年 2 月 1 日,星期三 12:10 am 你知道,这个错误已在刚刚发布的 3.6.17 中修复。

于 2016-10-11T07:59:18.757 回答