import java.awt.BorderLayout;
import java.util.Comparator;

import javax.swing.DefaultRowSorter;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableModel;

public class DefaultRowSorterBug
{

  /**
   * @param args ignored
   */
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      /**
       * NOTE: The run() method here contains only boilerplate code to bring up a visible dialog.
       */
      @Override
      public void run() {
        final JDialog dialog = new JDialog();
        JComponent content = createContent();
        dialog.getContentPane().add(content, BorderLayout.CENTER);
        dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        dialog.pack();
        dialog.setLocationRelativeTo(null);
        dialog.setVisible(true);
      }

      /**
       * Creates the JTable and demonstrates the issue.
       * @return the JTable to display.
       */
      protected JComponent createContent() {
        // (1) Create a TableModel that derives from AbstractTableModel
        DefaultTableModel model = new DefaultTableModel(new String[][] { { "One", "Orange" }, { "Two", "Green" } }, new String[] { "First", "Second" });

        // (2) Create a JTable with that model
        JTable table = new JTable(model);

        // (3) Enable AutoCreateRowSorter (or set any RowSorter that derives from DefaultRowSorter)
        table.setAutoCreateRowSorter(true);

        // (4) Set at least one Comparator to any column
        ((DefaultRowSorter<?, ?>) table.getRowSorter()).setComparator(0, Comparator.naturalOrder());

        // (5) Add a TableModelListener that resets the RowFilter on TableStructureChanged events
        model.addTableModelListener(e -> {
          if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
            DefaultRowSorter<?, ?> rowSorter = (DefaultRowSorter<?, ?>) table.getRowSorter();
            // The following line will create an java.lang.ArrayIndexOutOfBoundsException in DefaultRowSorter.getComparator
            // The method getComparator already calls checkColumn to check the parameter value, but that check is done using the current value of the TableModel
            // which is already updated to be three. But the Arrays of comparators wan't updated yet.
            // There is no issue if the column count is reduced, but the issue occurs when the column count is increased.
            rowSorter.setRowFilter(null);
          }
        });

        // (6) At a later point in time -> increase the column count
        SwingUtilities.invokeLater(() -> model.setColumnCount(3));

        // NOTE: This is a very simple example. A read-world example may have a custom TableModel that adjusts its own structure due to some external events.

        return table;
      }
    });
  }
}