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

JTable doesn't remove active CellEditor when TableModel changes

    XMLWordPrintable

    Details

    • Subcomponent:
    • Understanding:
      Fix Understood
    • CPU:
      generic, x86
    • OS:
      generic, windows_98, windows_nt

      Description

      Name: jk109818 Date: 11/12/2002


      FULL PRODUCT VERSION :
      java version "1.4.0_01"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0_01-b03)
      Java HotSpot(TM) Client VM (build 1.4.0_01-b03, mixed mode)

      AND

      java version "1.4.1_01"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
      Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)

      FULL OPERATING SYSTEM VERSION :
      Windows NT Version 4.0 SP6

      A DESCRIPTION OF THE PROBLEM :
      I use a JTable which has editable cells. While a cell edit
      is underway, a change to the TableModel's data is made, and
      the JTable informed through a fireTableDataChanged() call.
      The cells not being edited are modified by the JTable.
      However, the cell currently being edited retains its editor
      with the old value.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Run the FlipTable program (source below).
      2. Double-click on a savoury flavour to start editing.
      3. Click on the "Swap" button to flip the table data to sweet.
      4. Double-click on a sweet flavour to start editing.
      5. Click on the "Swap" button to flip back to savoury.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      Expected: the cell editor should vanish when the table data
      are flipped, so that all new data are shown. The editing
      should terminate.

      Acutal: the cell editor -- containing an out-of-date value
      from the previous table data -- remains visible and active.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      No error messages.

      REPRODUCIBILITY :
      This bug can be reproduced often.

      ---------- BEGIN SOURCE ----------
      import java.awt.Component;
      import java.awt.Container;
      import java.awt.GridBagConstraints;
      import java.awt.GridBagLayout;
      import java.awt.Insets;
      import java.awt.event.ActionEvent;
      import java.awt.event.ActionListener;
      import java.util.ArrayList;
      import java.util.List;
      import javax.swing.JButton;
      import javax.swing.JFrame;
      import javax.swing.JScrollPane;
      import javax.swing.JTable;
      import javax.swing.table.AbstractTableModel;
      import javax.swing.table.TableModel;

      class FlipTable extends JFrame {
        private JButton myFlipButton;
        private JTable myDisplayTable;
        private StringTableModel myDataModel;
        private String mySavouryValues[];
        private String mySweetValues[];
        private JScrollPane myDisplayPane;
        private ActionListener myFlipActionListener;
        protected class StringTableModel extends AbstractTableModel {
          private List myData;
          private List myHiddenData;
          public void flipData() {
            final List l = getData();
            myData = getHiddenData();
            myHiddenData = l;
            fireTableDataChanged();
          }
          public int getColumnCount() {
            return 1;
          }
          public String getColumnName(final int i) {
            final String result;

            if (0 == i) {
              result = "Flavour";
            } else {
              result = super.getColumnName(i - 1);
            }
            return result;
          }
          protected List getData() {
            if (null == myData) {
              myData = new ArrayList();
            }
            return myData;
          }
          protected List getHiddenData() {
            if (null == myHiddenData) {
              myHiddenData = new ArrayList();
            }
            return myHiddenData;
          }
          public int getRowCount() {
            return getData().size();
          }
          public Object getValueAt(final int row, final int column) {
            return getData().get(row);
          }
          public boolean isCellEditable(final int row, final int column) {
            final boolean result;

            if (0 == column) {
              result = true;
            } else {
              result = super.isCellEditable(row, column);
            }
            return result;
          }
          public void setData(final String data[]) {
            final int count = data.length;
            myData = new ArrayList(count);

            for (int i = 0; count > i; i += 1) {
              myData.add(data[i]);
            }
            fireTableDataChanged();
          }
        }
        public FlipTable() {
          initialize();
        }
        protected void flip() {
          final String me = "reload()";
          final JTable t = getDisplayTable();

          final TableModel m = t.getModel();

          try {
            final StringTableModel n = (StringTableModel) m;

            n.flipData();
          } catch (final ClassCastException x) {
            System.err.println(me + ": wrong type: " + m.getClass().getName());
          }
        }
        protected StringTableModel getDataModel() {
          if (null == myDataModel) {
            myDataModel = new StringTableModel();
            myDataModel.setData(getSweetValues());
            myDataModel.flipData();
            myDataModel.setData(getSavouryValues());
          }
          return myDataModel;
        }
        protected JScrollPane getDisplayPane() {
          if (null == myDisplayPane) {
            final JScrollPane p = myDisplayPane = new JScrollPane();
            p.setViewportView(getDisplayTable());
          }
          return myDisplayPane;
        }
        protected JTable getDisplayTable() {
          final String me = "getDisplayTable()";
          if (null == myDisplayTable) {
            final JTable t = myDisplayTable = new JTable();
            t.setModel(getDataModel());
          }
          return myDisplayTable;
        }
        protected ActionListener getFlipActionListener() {
          if (null == myFlipActionListener) {
            myFlipActionListener = new ActionListener() {
              public final void actionPerformed(final ActionEvent e) {
                flip();
              }
            };
          }
          return myFlipActionListener;
        }
        protected JButton getFlipButton() {
          if (null == myFlipButton) {
            final JButton b = myFlipButton = new JButton("Swap");
            b.addActionListener(getFlipActionListener());
          }
          return myFlipButton;
        }
        protected String getSavouryValues()[] { if (null == mySavouryValues) {
          mySavouryValues = new String[] { "Cheese", "Onion", "Tomato", "Ham" };
          }
          return mySavouryValues;
        }
        protected String getSweetValues()[] { if (null == mySweetValues) {
          mySweetValues =
            new String[] { "Strawberry", "Raspberry", "Blackberry", "Choocolate" };
          }
          return mySweetValues;
        }
        protected void initialize() {
          final Container c = getContentPane();
          final GridBagConstraints k = new GridBagConstraints();
          c.setLayout(new GridBagLayout());

          k.insets = new Insets(4, 4, 4, 4);
          k.gridx = 0;
          k.fill = k.NONE;
          k.weightx = k.weighty = 1.0;
          c.add(getDisplayPane(), k);

          k.fill = k.NONE;
          k.weightx = k.weighty = 0.0;
          c.add(getFlipButton(), k);

          setDefaultCloseOperation(EXIT_ON_CLOSE);
        }
        public static void main(final String args[]) {
          System.out.println();
          final FlipTable t = new FlipTable();
          t.pack();
          t.setVisible(true);
        }
      }
      ---------- END SOURCE ----------

      CUSTOMER WORKAROUND :
      None found.
      (Review ID: 166836)
      ======================================================================
      Contribution from java.net member leouser:
      A DESCRIPTION OF THE FIX :
      BUGID: 4777756 JTable doesn't remove active CellEditor when TableModel changes.
      FILES AFFECTED: javax.swing.JTable
       JDK VERSION
      jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin


      Discusion(embeded in test case as well):
      /**
       * BUGID: 4777756 JTable doesn't remove active CellEditor when TableModel changes.
       * This bug is simple--> when the editor is in place and the cell beneath it
       * changes the edit is not canceled. This surely cannot be what the user wants.
       * When an edit is taking place it starts from a known value. If that basis
       * is changed in mid edit then the new value created by the edit will be most
       * likely incorrectly derived. To fix this I have implemented 8 lines of simple
       * code in the tableChanged method of JTable. This code analyses the event
       * if the table is currently being edited. If their is an intersection between
       * the event and the editor, the editor is removed. The user is then presented
       * with the new value from which they can begin their edit. Too bad the
       * TableCellEditor doesn't extend the TableModelListener, the code may have
       * been more appropiately placed there. It also would have made it simpler
       * for the developer to alter this behavior if there is an oddball case where
       * the user doesn't want the editor to be removed. I have added the test
       * "removeEditorOnCellChange" to see if the user truly wants this behavior.
       * If the client property is False, then we skip the removal. I would wager
       * that 99.9% of the time, removal is what the client wants. This does
       * raise the question: Where do I document this client property.
       *
       * ANTI-RATIONALE:
       * Clients that have worked around this bug will be in for a pleasant surprise,
       * when they discover that their removal code is no longer doing anything. Though
       * of course this is dependant upon whose removal code gets called first.
       *
       * TESTING STRATEGY:
       * Create a JTable with lots of values in it. Start editing and press
       * one of the four buttons and see how the editor reacts. The buttons
       * + an editor in place is the test.
       *
       * ODD SELECTION BEHAVIOR NOTICED:
       * Ive noticed that selection is extended when a row is inserted at the
       * selected row spot. This
       * probably is not the behavior wanted, will investigate for a bug report and
       * maybe fix.
       *
       * FILES AFFECTED: javax.swing.JTable
       *
       * JDK VERSION
       * jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
       *
       * test ran succesfully on a SUSE 7.3 Linux distribution
       *
       * Brian Harry
       * ###@###.###
       * Jan 23, 2006
       *
       */

      UNIFIED DIFF:
      --- /home/nstuff/java6/jdk1.6.0/javax/swing/JTable.java Thu Dec 15 02:17:37 2005
      +++ /home/javarefs/javax/swing/JTable.java Mon Jan 23 12:39:14 2006
      @@ -4321,6 +4321,22 @@
                   return;
               }
       
      + if(isEditing()){
      + Object obj = getClientProperty("removeEditorOnCellChange");
      + boolean doremove = true;
      + if(obj != null)
      + doremove = (Boolean)obj;
      + if(doremove){
      + int erow = getEditingRow();
      + if(erow >= e.getFirstRow() && erow <= e.getLastRow()){
      + int ecol = e.getColumn();
      + if(getEditingColumn() == ecol || e.ALL_COLUMNS == ecol)
      + removeEditor();
      + }
      + }
      + }
      +
      +
               if (sortManager != null) {
                   sortedTableChanged(null, e);
                   return;




      JUnit TESTCASE :
      import javax.swing.*;
      import javax.swing.table.*;
      import java.awt.*;
      import java.awt.event.*;
      import static java.lang.System.*;
      import java.util.*;


      /**
       * BUGID: 4777756 JTable doesn't remove active CellEditor when TableModel changes.
       * This bug is simple--> when the editor is in place and the cell beneath it
       * changes the edit is not canceled. This surely cannot be what the user wants.
       * When an edit is taking place it starts from a known value. If that basis
       * is changed in mid edit then the new value created by the edit will be most
       * likely incorrectly derived. To fix this I have implemented 8 lines of simple
       * code in the tableChanged method of JTable. This code analyses the event
       * if the table is currently being edited. If their is an intersection between
       * the event and the editor, the editor is removed. The user is then presented
       * with the new value from which they can begin their edit. Too bad the
       * TableCellEditor doesn't extend the TableModelListener, the code may have
       * been more appropiately placed there. It also would have made it simpler
       * for the developer to alter this behavior if there is an oddball case where
       * the user doesn't want the editor to be removed. I have added the test
       * "removeEditorOnCellChange" to see if the user truly wants this behavior.
       * If the client property is False, then we skip the removal. I would wager
       * that 99.9% of the time, removal is what the client wants. This does
       * raise the question: Where do I document this client property.
       *
       * ANTI-RATIONALE:
       * Clients that have worked around this bug will be in for a pleasant surprise,
       * when they discover that their removal code is no longer doing anything. Though
       * of course this is dependant upon whose removal code gets called first.
       *
       * TESTING STRATEGY:
       * Create a JTable with lots of values in it. Start editing and press
       * one of the four buttons and see how the editor reacts. The buttons
       * + an editor in place is the test.
       *
       * ODD SELECTION BEHAVIOR NOTICED:
       * Ive noticed that selection is extended when a row is inserted at the
       * selected row spot. This
       * probably is not the behavior wanted, will investigate for a bug report and
       * maybe fix.
       *
       * FILES AFFECTED: javax.swing.JTable
       *
       * JDK VERSION
       * jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
       *
       * test ran succesfully on a SUSE 7.3 Linux distribution
       *
       * Brian Harry
       * ###@###.###
       * Jan 23, 2006
       *
       */
      public class TestTable4777756{

          Random ran = new Random();


          public void showGUI(){

              JFrame jf = new JFrame();
      final DefaultTableModel tm = createModel(2000,10);
      final JTable jt = new JTable(tm);
      JScrollPane jsp = new JScrollPane(jt);
      JPanel bpanel = new JPanel();
      jf.add(bpanel, BorderLayout.SOUTH);
      Action rrow = new AbstractAction("Remove Row"){
      public void actionPerformed(ActionEvent ae){
      int row = jt.getSelectedRow();
      if(row != -1) tm.removeRow(row);

                      }

      };
              JButton jb = new JButton(rrow);
      bpanel.add(jb);
      Action irow = new AbstractAction("Insert Row"){
      public void actionPerformed(ActionEvent ae){
      int row = jt.getSelectedRow();
                          if(row == -1) return;
      Object[] ndata = new Object[10];
      for(int i = 0; i < 10; i++)
                              ndata[i] = ran.nextInt();
      tm.insertRow(row, ndata);

                      }
      };
              JButton jb2 = new JButton(irow);
      bpanel.add(jb2);
      jf.add(jsp);
      Action incrow = new AbstractAction("Increment Row"){
      public void actionPerformed(ActionEvent ae){
      int row = jt.getSelectedRow();
                          if(row == -1) return;
      for(int i = 0; i < 10; i++){
                              int value = (Integer)tm.getValueAt(row,i);
                              value++;
                              tm.setValueAt(value, row, i);

                          }

                      }
      };
              JButton jb3 = new JButton(incrow);
      bpanel.add(jb3);
      Action incarow = new AbstractAction("Increment Row Above"){
      public void actionPerformed(ActionEvent ae){
      int row = jt.getSelectedRow();
                          row--;
                          if(row <= -1) return;
      for(int i = 0; i < 10; i++){
                              int value = (Integer)tm.getValueAt(row,i);
                              value++;
                              tm.setValueAt(value, row, i);

                          }

                      }
      };
              JButton jb4 = new JButton(incarow);
      bpanel.add(jb4);
      jf.add(jsp);

      jf.pack();
      jf.setVisible(true);


          }

          public DefaultTableModel createModel(int rows, int columns){
      Object[][] values = new Object[rows][columns];
      Object[] names = new Object[columns];
      for(int i = 0; i < rows; i++){
      for(int i2 = 0; i2 < columns; i2++)
      values[i][i2] = i2;
      }
      for(int i = 0; i < columns; i++)
      names[i] = String.valueOf(i);
      return new DefaultTableModel(values, names);
          }

          public void testTable(){
      Runnable run = new Runnable(){
      public void run(){
                          showGUI();
                      }


      };
      SwingUtilities.invokeLater(run);
          }


          public static void main(String ... args){
              new TestTable4777756().testTable();

          }

      }


      FIX FOR BUG NUMBER:
      4777756

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              peterz Peter Zhelezniakov
              Reporter:
              jkimsunw Jeffrey Kim (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

                Dates

                Created:
                Updated:
                Imported:
                Indexed: