Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8154534

JTable throws IndexOutOfBoundsException on cell value updation using RowSorter.

XMLWordPrintable

      FULL PRODUCT VERSION :
      java version "9-ea"
      Java(TM) SE Runtime Environment (build 9-ea+111)
      Java HotSpot(TM) Client VM (build 9-ea+111, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Windows 8.1 Pro 64bit

      A DESCRIPTION OF THE PROBLEM :
      JTable that TableRowSorter is set throws java.lang.IndexOutOfBoundsException when I input the cell value.

      REGRESSION. Last worked in version 8u77

      ADDITIONAL REGRESSION INFORMATION:
      I tested JDK version 6 to 8, this problem never occurs.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. copy source code for test case and make java source file 'Jdk9eaJTableBugFrame2.java'.
      2. compile 'Jdk9eaJTableBugFrame2.java'.
      3. execute java with main method of Jdk9eaJTableBugFrame2.
      4. push the 'addRow' button that calls DefaultTableModel#addRow() for several times.
      5. click column header 'A' of JTableHeader to sort by column A.
      6. select a cell on JTable and input text with CellEditor.
        -> JTable throws a java.lang.IndexOutOfBoundsException.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      no Exception must be thrown.
      ACTUAL -
      JTable throws a java.lang.IndexOutOfBoundsException.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Invalid range
      at javax.swing.DefaultRowSorter.checkAgainstModel(java.desktop@9-ea/DefaultRowSorter.java:923)
      at javax.swing.DefaultRowSorter.rowsUpdated(java.desktop@9-ea/DefaultRowSorter.java:896)
      at javax.swing.DefaultRowSorter.rowsUpdated(java.desktop@9-ea/DefaultRowSorter.java:917)
      at javax.swing.JTable.notifySorter(java.desktop@9-ea/JTable.java:4273)
      at javax.swing.JTable.sortedTableChanged(java.desktop@9-ea/JTable.java:4127)
      at javax.swing.JTable.tableChanged(java.desktop@9-ea/JTable.java:4408)
      at javax.swing.table.AbstractTableModel.fireTableChanged(java.desktop@9-ea/AbstractTableModel.java:297)
      at javax.swing.table.AbstractTableModel.fireTableCellUpdated(java.desktop@9-ea/AbstractTableModel.java:276)
      at javax.swing.table.DefaultTableModel.setValueAt(java.desktop@9-ea/DefaultTableModel.java:679)
      at javax.swing.JTable.setValueAt(java.desktop@9-ea/JTable.java:2749)
      at javax.swing.JTable.editingStopped(java.desktop@9-ea/JTable.java:4737)
      at javax.swing.AbstractCellEditor.fireEditingStopped(java.desktop@9-ea/AbstractCellEditor.java:147)
      at javax.swing.DefaultCellEditor$EditorDelegate.stopCellEditing(java.desktop@9-ea/DefaultCellEditor.java:370)
      at javax.swing.DefaultCellEditor.stopCellEditing(java.desktop@9-ea/DefaultCellEditor.java:234)
      at javax.swing.JTable$GenericEditor.stopCellEditing(java.desktop@9-ea/JTable.java:5492)
      at javax.swing.plaf.basic.BasicTableUI$Actions.actionPerformed(java.desktop@9-ea/BasicTableUI.java:520)
      at javax.swing.SwingUtilities.notifyAction(java.desktop@9-ea/SwingUtilities.java:1768)
      at javax.swing.JComponent.processKeyBinding(java.desktop@9-ea/JComponent.java:2882)
      at javax.swing.JTable.processKeyBinding(java.desktop@9-ea/JTable.java:5273)
      at javax.swing.JComponent.processKeyBindings(java.desktop@9-ea/JComponent.java:2943)
      at javax.swing.JComponent.processKeyEvent(java.desktop@9-ea/JComponent.java:2845)
      at java.awt.Component.processEvent(java.desktop@9-ea/Component.java:6379)
      at java.awt.Container.processEvent(java.desktop@9-ea/Container.java:2259)
      at java.awt.Component.dispatchEventImpl(java.desktop@9-ea/Component.java:4986)
      at java.awt.Container.dispatchEventImpl(java.desktop@9-ea/Container.java:2317)
      at java.awt.Component.dispatchEvent(java.desktop@9-ea/Component.java:4818)
      at java.awt.KeyboardFocusManager.redispatchEvent(java.desktop@9-ea/KeyboardFocusManager.java:1948)
      at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(java.desktop@9-ea/DefaultKeyboardFocusManager.java:806)
      at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(java.desktop@9-ea/DefaultKeyboardFocusManager.java:1075)
      at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(java.desktop@9-ea/DefaultKeyboardFocusManager.java:945)
      at java.awt.DefaultKeyboardFocusManager.dispatchEvent(java.desktop@9-ea/DefaultKeyboardFocusManager.java:771)
      at java.awt.Component.dispatchEventImpl(java.desktop@9-ea/Component.java:4867)
      at java.awt.Container.dispatchEventImpl(java.desktop@9-ea/Container.java:2317)
      at java.awt.Window.dispatchEventImpl(java.desktop@9-ea/Window.java:2755)
      at java.awt.Component.dispatchEvent(java.desktop@9-ea/Component.java:4818)
      at java.awt.EventQueue.dispatchEventImpl(java.desktop@9-ea/EventQueue.java:761)
      at java.awt.EventQueue.access$500(java.desktop@9-ea/EventQueue.java:97)
      at java.awt.EventQueue$3.run(java.desktop@9-ea/EventQueue.java:712)
      at java.awt.EventQueue$3.run(java.desktop@9-ea/EventQueue.java:706)
      at java.security.AccessController.doPrivileged(java.base@9-ea/Native Method)
      at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(java.base@9-ea/ProtectionDomain.java:77)
      at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(java.base@9-ea/ProtectionDomain.java:87)
      at java.awt.EventQueue$4.run(java.desktop@9-ea/EventQueue.java:734)
      at java.awt.EventQueue$4.run(java.desktop@9-ea/EventQueue.java:732)
      at java.security.AccessController.doPrivileged(java.base@9-ea/Native Method)
      at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(java.base@9-ea/ProtectionDomain.java:77)
      at java.awt.EventQueue.dispatchEvent(java.desktop@9-ea/EventQueue.java:731)
      at java.awt.EventDispatchThread.pumpOneEventForFilters(java.desktop@9-ea/EventDispatchThread.java:192)
      at java.awt.EventDispatchThread.pumpEventsForFilter(java.desktop@9-ea/EventDispatchThread.java:117)
      at java.awt.EventDispatchThread.pumpEventsForHierarchy(java.desktop@9-ea/EventDispatchThread.java:106)
      at java.awt.EventDispatchThread.pumpEvents(java.desktop@9-ea/EventDispatchThread.java:102)
      at java.awt.EventDispatchThread.pumpEvents(java.desktop@9-ea/EventDispatchThread.java:94)
      at java.awt.EventDispatchThread.run(java.desktop@9-ea/EventDispatchThread.java:83)


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import javax.swing.JFrame;
      import javax.swing.JPanel;
      import javax.swing.JScrollPane;
      import javax.swing.JTable;
      import javax.swing.RowFilter;
      import javax.swing.SwingUtilities;
      import javax.swing.table.DefaultTableModel;

      import java.awt.BorderLayout;
      import javax.swing.JButton;
      import java.awt.event.ActionListener;
      import java.awt.event.ActionEvent;
      import java.util.Vector;
      import javax.swing.table.TableRowSorter;

      public class Jdk9eaJTableBugFrame2 extends JFrame {
      private JPanel panel;
      private JButton btnAddRow;
      private JScrollPane scrollPane;
      private JTable table;
      private DefaultTableModel defaultTableModel;
      private TableRowSorter<DefaultTableModel> tableRowSorter;
      private RowFilter<DefaultTableModel,Integer> rowFilter;

      public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
      public void run() {
      new Jdk9eaJTableBugFrame2().setVisible(true);
      }
      });
      }

      public Jdk9eaJTableBugFrame2() {
      initialize();
      }

      private void initialize() {
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      getContentPane().add(getPanel(), BorderLayout.NORTH);
      getContentPane().add(getScrollPane(), BorderLayout.CENTER);
      setSize(600,500);
      }
      private JPanel getPanel() {
      if (panel == null) {
      panel = new JPanel();
      panel.add(getBtnAddRow());
      }
      return panel;
      }
      private JButton getBtnAddRow() {
      if (btnAddRow == null) {
      btnAddRow = new JButton("addRow");
      btnAddRow.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
      getDefaultTableModel().addRow(new Vector<Object>());
      }
      });
      }
      return btnAddRow;
      }
      private JScrollPane getScrollPane() {
      if (scrollPane == null) {
      scrollPane = new JScrollPane();
      scrollPane.setViewportView(getTable());
      }
      return scrollPane;
      }
      private JTable getTable() {
      if (table == null) {
      table = new JTable();
      // table = new WorkroundTable();
      table.setModel(getDefaultTableModel());
      table.setRowSorter(getTableRowSorter());
      }
      return table;
      }
      private DefaultTableModel getDefaultTableModel() {
      if (defaultTableModel == null) {
      defaultTableModel = new DefaultTableModel();
      defaultTableModel.setColumnCount(5);
      }
      return defaultTableModel;
      }
      private TableRowSorter<DefaultTableModel> getTableRowSorter() {
      if (tableRowSorter == null) {
      tableRowSorter = new TableRowSorter<DefaultTableModel>(getDefaultTableModel());
      }
      return tableRowSorter;
      }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      I checked difference of JTable.java source code, and I found the related change in the method JTable#tableChanged(TableModelEvent).

      on JDK 8, tableChanged() always calls sortedTableChanged(RowSorterEvent,TableModelEvent) when RowSorter is set.
      ------------------------------------------------------------------------------------
      JTable.java Line 4394-4397
      ------------------------------------------------------------------------------------
              if (sortManager != null) {
                  sortedTableChanged(null, e);
                  return;
              }
      ------------------------------------------------------------------------------------

      but on JDK 9, tableChanged() does not calls sortedTableChanged() when RowSorter's sortKey is empty.
      ------------------------------------------------------------------------------------
      JTable.java Line 4403-4411
      ------------------------------------------------------------------------------------
              if (sortManager != null) {
                  List<? extends RowSorter.SortKey> sortKeys =
                          sortManager.sorter.getSortKeys();
                  if (sortKeys.size() != 0 &&
                          sortKeys.get(0).getSortOrder() != SortOrder.UNSORTED) {
                      sortedTableChanged(null, e);
                      return;
                  }
              }
      ------------------------------------------------------------------------------------

      so I write a sub-class of JTable and override tableChanged() method to call sortedTableChanged() method with reflection and it works fine.

      However, I think that this is never strictly correct way.
      I think that the souce of JDK 8 is correct.
      If JTable has RowSorter, sortedTableChanged() must be always called.

      the sub-class is bellow:
      ------------------------------------------------------------------------------------
      import java.util.List;

      import javax.swing.JTable;
      import javax.swing.RowSorter;
      import javax.swing.SortOrder;
      import javax.swing.event.RowSorterEvent;
      import javax.swing.event.TableModelEvent;
      import javax.swing.table.TableModel;

      public class WorkroundTable extends JTable {

      public void tableChanged(TableModelEvent e) {
      boolean needsInvokeSortedTableChanged = false;

      if("9-ea".equals(System.getProperty("java.version")) &&
      e!=null && e.getFirstRow()!=TableModelEvent.HEADER_ROW) {
      RowSorter<? extends TableModel> sorter = getRowSorter();
      if(sorter!=null) {
      List<? extends RowSorter.SortKey> sortKeys = sorter.getSortKeys();
      if(sortKeys.size()==0 ||
      sortKeys.get(0).getSortOrder()==SortOrder.UNSORTED) {
      needsInvokeSortedTableChanged = true;
      }
      }
      }

      super.tableChanged(e);

      if(needsInvokeSortedTableChanged) {
      try {
      java.lang.reflect.Method method =
      JTable.class.getDeclaredMethod("sortedTableChanged",
      RowSorterEvent.class,
      TableModelEvent.class);
      method.setAccessible(true);
      method.invoke(this, null, e);
      }
      catch(Throwable ex) {
      ex.printStackTrace();
      }
      }
      }
      }
      ------------------------------------------------------------------------------------


            rchamyal Rajeev Chamyal (Inactive)
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: