6

After upgrading to Lion, and Java 7, I am running into issues with JTables. When I use arrow keys to move the selection around, its calling setValueAt() with empty strings as the edit value.

To test this, I created a simple JFrame with a table in it, and set the following class as its model.

public class SpyModel extends AbstractTableModel {
    public int getColumnCount() { return 5; }
    public int getRowCount() { return 5; }
    public Object getValueAt(int rowIndex, int columnIndex) { return ""; }
    public boolean isCellEditable(int rowIndex, int columnIndex) { return true; }

    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        System.out.println(aValue == null ? "null" : "\"" + aValue + "\"");
    }
}

When I run it under Java 6, and then use the arrow keys to move through it. it works fine. e.g.

$ java -version
java version "1.6.0_33"
Java(TM) SE Runtime Environment (build 1.6.0_33-b03-424-11M3720)
Java HotSpot(TM) 64-Bit Server VM (build 20.8-b03-424, mixed mode)
$ java -jar JavaApplication5.jar 

However, when I run it under Java 7 (on Lion), and move the selection with the arrow keys, it ends up calling setValueAt() with empty strings.

e.g.

$ java -version
java version "1.7.0_05"
Java(TM) SE Runtime Environment (build 1.7.0_05-b06)
Java HotSpot(TM) 64-Bit Server VM (build 23.1-b03, mixed mode)
$ java -jar JavaApplication5.jar 
""
""
""
""
""
$

I've searched for bugs, but I haven't come up with anything. Is this a known problem?

BillRobertson42
  • 12,602
  • 4
  • 40
  • 57
  • 1
    I've submitted this as a bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7185868 – BillRobertson42 Jul 21 '12 at 05:59
  • The bug is not fixed in the latest JDK 7 (release 9). The bug report is no longer visible, and Apple's latest JDK 6 push makes Java 7 the default *and* removes the java preferences app. Fun times. – BillRobertson42 Oct 28 '12 at 16:27

3 Answers3

1

Seems like there is more then one bug when playing with that table example.

I used the following SSCCE which works as expected under JDK1.6

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

public class TableBugDemo {
  public static void main( String[] args ) {
    JFrame frame = new JFrame( "TestFrame" );
    final JTable table = new JTable( new SpyModel() );
    table.getSelectionModel().addListSelectionListener( new ListSelectionListener() {
      @Override
      public void valueChanged( ListSelectionEvent e ) {
        Thread.dumpStack();
        System.out.println(table.getSelectedRow());
      }
    } );
    frame.add( table );
    frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
    frame.pack();
    frame.setVisible( true );
  }

  public static class SpyModel extends DefaultTableModel{
    public SpyModel() {
      super( new String[][]{
          new String[]{ "row1-1", "row1-2", "row1-3"},
          new String[]{ "row2-1", "row2-2", "row2-3"},
          new String[]{ "row3-1", "row3-2", "row3-3"},
          new String[]{ "row4-1", "row4-2", "row4-3"},
      }, new String[]{"col1", "col2", "col3"});
    }

    @Override
    public void setValueAt( Object aValue, int row, int column ) {
      System.out.println( "TableBugDemo$SpyModel.setValueAt" );
      Thread.dumpStack();
      super.setValueAt( aValue, row, column );
    }

    @Override
    public boolean isCellEditable( int row, int column ) {
      return false;
    }
  }
}

Under JDK1.7 however:

  • I see the setValueAt being called when making the table editable. However, not with an empty string but with the actual values which are contained in my TableModel. This means that nothing is changed to my data. Only annoying thing is that during the navigation my table is constantly updating. Workaround is of course to adjust the setValueAt method with a quick exit path when the value is not updated at all, e.g adding

    if ( ( aValue != null && aValue.equals( getValueAt( row, column ) ) ) ||
         ( aValue == null && getValueAt( row, column ) == null ) ){
      return;
    }
    
  • Navigating with the up and down arrows make the selection jump 2 rows at a time. Stacktraces reveal the change in selection originates from the BasicTableUI#Actions class (which makes sense as that is the action which is placed in the action map). The weird thing is that for one key-press this action is triggered twice. That already explains why the selection jumps 2 rows at a time. Further debugging revealed that for hitting my arrow key once I received two distinct KEY_PRESSED events. As far as I can tell, those event are placed like that on the EventQueue and have nothing to do with the JTable. Just to make sure, I created a small SSCCE which does not include a JTable:

     import javax.swing.JFrame;
     import javax.swing.WindowConstants;
     import java.awt.AWTEvent;
     import java.awt.EventQueue;
     import java.awt.Toolkit;
     import java.awt.event.AWTEventListener;
     import java.awt.event.KeyEvent;
    
     public class KeyEventBugDemo {
       public static void main( String[] args ) {
         EventQueue.invokeLater(new Runnable() {
           @Override
           public void run() {
             JFrame testframe = new JFrame( "testframe" );
             testframe.setSize( 300,300 );
             testframe.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
             testframe.setVisible( true );
           }
         } );
         Toolkit.getDefaultToolkit().addAWTEventListener( new AWTEventListener() {
           @Override
           public void eventDispatched( AWTEvent event ) {
             if (event instanceof KeyEvent ){
               KeyEvent keyevent = ( KeyEvent ) event;
               System.out.println( "keyevent.getKeyCode() = " + keyevent.getKeyCode() );
               System.out.println( "ID = " + System.identityHashCode( keyevent ) );
               System.out.println( "keyevent = " + keyevent );
             }
           }
         }, AWTEvent.KEY_EVENT_MASK );
       }
     }
    

Giving focus to the frame and then hitting the DOWN_ARROW results in the following output (stripped the output of toString to keep it readable)

keyevent.getKeyCode() = 40
ID = 960135925
keyevent = java.awt.event.KeyEvent[KEY_PRESSED,keyCode=40,...
keyevent.getKeyCode() = 40
ID = 1192754471
keyevent = java.awt.event.KeyEvent[KEY_PRESSED,keyCode=40,...
keyevent.getKeyCode() = 40
ID = 2012032999
keyevent = java.awt.event.KeyEvent[KEY_RELEASED,keyCode=40,...

Here you can clearly see that you get two KEY_PRESSED events which confuse the JTable. This does not occur when using a regular character key

keyevent.getKeyCode() = 65
ID = 1023134153
keyevent = java.awt.event.KeyEvent[KEY_PRESSED,keyCode=65,keyText=A,
ID = 914147942
keyevent = java.awt.event.KeyEvent[KEY_TYPED,keyCode=0,keyText=Unknown 
keyevent.getKeyCode() = 65
ID = 986450556
keyevent = java.awt.event.KeyEvent[KEY_RELEASED,keyCode=65,keyText=A,keyChar='a',

Looking at the javadoc in the KeyEvent class:

KEY_TYPED (is only generated if a valid Unicode character could be generated.)

it makes sense I won't get the KEY_TYPED event when hitting the arrow, but that it fires the KEY_PRESSED twice is in my opinion a bug (will log a bug report later on for this). A workaround might be to intercept such an event and not pass it through the chain but that sounds like an ugly hack to me.

EDIT

Another weird thing. If you add the following line to the snippet

table.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).
  put( KeyStroke.getKeyStroke( 'a' ), "selectNextRow" );

you can use the a to jump to the next line (same action as which is triggered by default by using the DOWN_ARROW). Due to the correct sequence of events when pressing a, it seems the setValueAt method is also not called. This makes me think that the two KEY_PRESSED events starts the editing somehow ...

Robin
  • 36,233
  • 5
  • 47
  • 99
  • Very interesting that you see it called with the original value and the double key event. I just reran my application (not example), and confirmed the original behaviour. `setValueAt()` is called with an empty string, and I did not observe the double key event. Running your example, I get the replace with same string behavior, but not the double key event. I put the table in a scroll pane and called setCellSelectionEnabled(true) on the table to make it truer to the application. – BillRobertson42 Nov 16 '12 at 00:06
  • I have this with `build 1.7.0_04-ea-b11` (the JDK that you get by default) – Robin Nov 16 '12 at 07:25
  • @Bill I added a small edit. Looks like the `setValueAt` call is related directly to the two `KEY_PRESSED` events on my machine – Robin Nov 16 '12 at 07:39
  • I can't check now, but last night I think I noticed (with cell selection enabled) that the spurious calls to `setValueAt` only occurs when moving up/down and not left/right. *edit* BTW, I'm on the latest download now. u09 IIRC. – BillRobertson42 Nov 16 '12 at 14:42
0

A workaround for this is to use:

putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);

But in this case you will not be able to start editing in a cell by direct typing. You will have to use the mouse to start editing. I have also issued a bug to Oracle http://bugs.sun.com/view_bug.do?bug_id=9006933 but it is also not available... seems they are having a problem with the system.

Tiberiu
  • 508
  • 7
  • 17
0

Jut an update I still see this problem with 1.7.0_40 as I move around my JTable cells with cursor keys it causes it to blank out every field in goes into.

Adding:

table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE)

stops that issue happening, but then I have to press 'Enter' key to start editing.

Instead at the the start of your Jtables model you check for empty strings and return will workaround the problem

i.e.

 if(Platform.isOSX())
        {
            if(value.equals(""))
            {
                return;
            }
        }

but this does mean that if the user actually wants to empty the field that the change will be rejected. In my own application that isnt such a problem because I have a separate deleteField() action that they use.

The issue can be found in the OpenJdk bug tracker here

https://bugs.openjdk.java.net/browse/JDK-8025126

and currently isn't due for fixing until Jdk 9

Paul Taylor
  • 13,411
  • 42
  • 184
  • 351