-
Bug
-
Resolution: Cannot Reproduce
-
P4
-
None
-
6u10
-
x86
-
windows_xp
FULL PRODUCT VERSION :
java version "1.6.0_10-rc"
Java(TM) SE Runtime Environment (build 1.6.0_10-rc-b28)
Java HotSpot(TM) Client VM (build 11.0-b15, mixed mode, sharing)
and
java version "1.6.0_07"
Java(TM) SE Runtime Environment (build 1.6.0_07-b06)
Java HotSpot(TM) Client VM (build 10.0-b23, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
A DESCRIPTION OF THE PROBLEM :
If you input an empty string in a cell that uses the default NumberEditor (DefaultTableModel) in a table with the "terminateEditOnFocusLost" property set and it looses the focus, it crashes because the empty string can not be parsed by the concrete implementation of the Number.
The problem lies in GenericEditor#stopCellEditing where the super implementation is called when the string is empty. It returns false meaning the edition can not be stopped, but it actually stopped it. (Btw, if the parsing succeeds for a custom type, it's called twice, don't know if this is really intended). Here's the method:
public boolean stopCellEditing() {
String s = (String)super.getCellEditorValue();
// Here we are dealing with the case where a user
// has deleted the string value in a cell, possibly
// after a failed validation. Return null, so that
// they have the option to replace the value with
// null or use escape to restore the original.
// For Strings, return "" for backward compatibility.
if ("".equals(s)) {
if (constructor.getDeclaringClass() == String.class) {
value = s;
}
super.stopCellEditing(); // *** ISSUE if coupled with an instantiation exception ***
}
try {
value = constructor.newInstance(new Object[]{s});
}
catch (Exception e) {
((JComponent)getComponent()).setBorder(new LineBorder(Color.red));
return false;
}
return super.stopCellEditing();
}
A potential solution would involve calling the super implementation only for Strings (which is what the comment it telling anyway), but it seems the hack is done on purpose (and very touchy). Or at the bare minimum to check for the cell editor presence in the block that triggers the NPE in the CellEditorRemover:
if (!getCellEditor().stopCellEditing() /*&& getCellEditor() != null*/) {
getCellEditor().cancelCellEditing();
}
The way the default editor works makes sense, but the fact the "terminateEditOnFocusLost" property does not even work with the simplest case (one of the default editor) is a real problem for Swing newcomers. You do not expect anybody to debug that itself.
It fails with both JDK6 update 10 (under finalization) and update 7 (stable release so far).
Please see http://forums.sun.com/thread.jspa?messageID=2452184 for a more in-depth analysis.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Run the attached code
2. Edit the unique cell of the table (F2 or double-click)
3. Click on the button
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The cell keeps its previous value as the string can not be parsed to a Long value.
ACTUAL -
It crashes.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.NullPointerException
at javax.swing.JTable$CellEditorRemover.propertyChange(JTable.java:5964)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:339)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:347)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:276)
at java.awt.KeyboardFocusManager.firePropertyChange(KeyboardFocusManager.java:1387)
at java.awt.KeyboardFocusManager.setGlobalPermanentFocusOwner(KeyboardFocusManager.java:685)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:488)
at java.awt.Component.dispatchEventImpl(Component.java:4373)
at java.awt.Container.dispatchEventImpl(Container.java:2081)
at java.awt.Component.dispatchEvent(Component.java:4331)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class TerminateEditOnFocusLostTest extends JFrame {
public TerminateEditOnFocusLostTest() {
TableModel model = new DefaultTableModel(1, 1) {
public Class getColumnClass(int columnIndex) {
return Long.class;
}
};
JTable table = new JTable(model);
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
getContentPane().add(table);
getContentPane().add(new JButton("Push me!"), BorderLayout.PAGE_END);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
TerminateEditOnFocusLostTest frame = new TerminateEditOnFocusLostTest();
frame.pack();
frame.setVisible(true);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Use a custom editor (it is not possible to delegate to the default one to specifically work around the issue):
private static class LongNumberEditor extends DefaultCellEditor {
private long value = 0;
public LongNumberEditor() {
super(new JTextField());
((JTextField)getComponent()).setHorizontalAlignment(JTextField.RIGHT);
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
this.value = (value == null) ? 0L : ((Number) value).longValue();
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
@Override
public boolean stopCellEditing() {
String text = (String) delegate.getCellEditorValue();
value = (text.length() == 0) ? 0L : Long.valueOf(text);
return delegate.stopCellEditing();
}
@Override
public Object getCellEditorValue() {
return value;
}
}
java version "1.6.0_10-rc"
Java(TM) SE Runtime Environment (build 1.6.0_10-rc-b28)
Java HotSpot(TM) Client VM (build 11.0-b15, mixed mode, sharing)
and
java version "1.6.0_07"
Java(TM) SE Runtime Environment (build 1.6.0_07-b06)
Java HotSpot(TM) Client VM (build 10.0-b23, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
A DESCRIPTION OF THE PROBLEM :
If you input an empty string in a cell that uses the default NumberEditor (DefaultTableModel) in a table with the "terminateEditOnFocusLost" property set and it looses the focus, it crashes because the empty string can not be parsed by the concrete implementation of the Number.
The problem lies in GenericEditor#stopCellEditing where the super implementation is called when the string is empty. It returns false meaning the edition can not be stopped, but it actually stopped it. (Btw, if the parsing succeeds for a custom type, it's called twice, don't know if this is really intended). Here's the method:
public boolean stopCellEditing() {
String s = (String)super.getCellEditorValue();
// Here we are dealing with the case where a user
// has deleted the string value in a cell, possibly
// after a failed validation. Return null, so that
// they have the option to replace the value with
// null or use escape to restore the original.
// For Strings, return "" for backward compatibility.
if ("".equals(s)) {
if (constructor.getDeclaringClass() == String.class) {
value = s;
}
super.stopCellEditing(); // *** ISSUE if coupled with an instantiation exception ***
}
try {
value = constructor.newInstance(new Object[]{s});
}
catch (Exception e) {
((JComponent)getComponent()).setBorder(new LineBorder(Color.red));
return false;
}
return super.stopCellEditing();
}
A potential solution would involve calling the super implementation only for Strings (which is what the comment it telling anyway), but it seems the hack is done on purpose (and very touchy). Or at the bare minimum to check for the cell editor presence in the block that triggers the NPE in the CellEditorRemover:
if (!getCellEditor().stopCellEditing() /*&& getCellEditor() != null*/) {
getCellEditor().cancelCellEditing();
}
The way the default editor works makes sense, but the fact the "terminateEditOnFocusLost" property does not even work with the simplest case (one of the default editor) is a real problem for Swing newcomers. You do not expect anybody to debug that itself.
It fails with both JDK6 update 10 (under finalization) and update 7 (stable release so far).
Please see http://forums.sun.com/thread.jspa?messageID=2452184 for a more in-depth analysis.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Run the attached code
2. Edit the unique cell of the table (F2 or double-click)
3. Click on the button
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The cell keeps its previous value as the string can not be parsed to a Long value.
ACTUAL -
It crashes.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.NullPointerException
at javax.swing.JTable$CellEditorRemover.propertyChange(JTable.java:5964)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:339)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:347)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:276)
at java.awt.KeyboardFocusManager.firePropertyChange(KeyboardFocusManager.java:1387)
at java.awt.KeyboardFocusManager.setGlobalPermanentFocusOwner(KeyboardFocusManager.java:685)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:488)
at java.awt.Component.dispatchEventImpl(Component.java:4373)
at java.awt.Container.dispatchEventImpl(Container.java:2081)
at java.awt.Component.dispatchEvent(Component.java:4331)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class TerminateEditOnFocusLostTest extends JFrame {
public TerminateEditOnFocusLostTest() {
TableModel model = new DefaultTableModel(1, 1) {
public Class getColumnClass(int columnIndex) {
return Long.class;
}
};
JTable table = new JTable(model);
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
getContentPane().add(table);
getContentPane().add(new JButton("Push me!"), BorderLayout.PAGE_END);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
TerminateEditOnFocusLostTest frame = new TerminateEditOnFocusLostTest();
frame.pack();
frame.setVisible(true);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Use a custom editor (it is not possible to delegate to the default one to specifically work around the issue):
private static class LongNumberEditor extends DefaultCellEditor {
private long value = 0;
public LongNumberEditor() {
super(new JTextField());
((JTextField)getComponent()).setHorizontalAlignment(JTextField.RIGHT);
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
this.value = (value == null) ? 0L : ((Number) value).longValue();
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
@Override
public boolean stopCellEditing() {
String text = (String) delegate.getCellEditorValue();
value = (text.length() == 0) ? 0L : Long.valueOf(text);
return delegate.stopCellEditing();
}
@Override
public Object getCellEditorValue() {
return value;
}
}