java.lang.IndexOutOfBoundsException: индекс: 1, размер: 1 в Jidesoft DocumentPane
Edit3: этот не дублирует общие вопросы "у меня есть indexoutofboundsexception в моем коде", потому что, как ясно указано в ответе, это была ошибка в широко используемой сторонней библиотеке jide-common
, подтверждено разработчиками, есть ссылка ниже. Эта ошибка была недавно исправлена (после публикации этого вопроса), и многие старые версии библиотеки по-прежнему затрагиваются. Так что эта информация может быть полезна для других разработчиков, которые сталкиваются с той же проблемой с jide-common.
Я столкнулся с проблемой программирования GUI на Java. Я не уверен в конкретном источнике проблемы (все еще не могу понять это) - это может быть или моя ошибка где-то, какой-то неисправный код в jide-компонентах или даже что-то не так с фокусировкой awt / swing или обработкой событий.
Следующий код зависит от
- com.jidesoft: Jide-сетка:3.5.1
- com.jidesoft: Jide-компонента:3.5.1
- com.jidesoft: Jide-общее:3.5.1
Я обнаружил этот намек на Stackru, но после некоторой отладки мне кажется, что каждая модификация CellEditor и других компонентов выполняется в EDT.
Чтобы воспроизвести ошибку, вы должны запустить этот пример и открыть вкладку, содержащую CellEditor (он должен быть последним в строке, иначе переполнение индекса не произойдет), затем введите туда какое-то "неправильное" значение и, не удаляя фокус из редактора, нажмите "Закрыть". Кнопка "х" на одной из предыдущих вкладок. После этого происходит следующая цепочка событий: 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() является неправильным, согласно документации:
Метод проверки существует только для определения того, является ли ввод действительным, он никогда не должен вызывать диалоговое окно или вызывать какие-либо другие побочные эффекты. Вызов метода shouldYieldFocus проверяет и, если значения являются недопустимыми, устанавливает для них минимальное или максимальное значение. Метод shouldYieldFocus может вызывать побочные эффекты...
Поэтому я переместил весь не проверяющий код код в shouldYieldFocus(). Это не решило проблему, но дальнейшая задержка dialog.show() с SwingUtilities.invokeLater(), кажется, работает нормально, никаких ошибок... пока.
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 с захваченным видео
1 ответ
ОК, это была ошибка в JideTabbedPaneUI. Я протестировал пропатченный jide-oss на своем примере, и больше нет исключений (однако при закрытии вкладки теперь есть некоторые некритические глюки интерфейса). Источник проблемы найден.
РЕДАКТИРОВАТЬ: они исправили эту ошибку позже в 3.6.17:
поддержка JIDE "Ср Фев 01, 2017 0:10 Просто чтобы вы знали, эта ошибка была исправлена в 3.6.17, которая была только что выпущена.