How to mark JTable cell input as invalid?

Cuga picture Cuga · Sep 23, 2011 · Viewed 11.4k times · Source

If I take a JTable and specify a column's classtype on it's model as follows:

   DefaultTableModel model = new DefaultTableModel(columnNames, 100) {
       @Override
        public Class<?> getColumnClass(int columnIndex) {
            return Integer.class;
        }};

Then whenever a user tries to enter a double value into the table, Swing automatically rejects the input and sets the cell's outline to red.

I want the same effect to occur when someone enters a 'negative or 0' input to the cell. I've got this:

    @Override
    public void setValueAt(Object val, int rowIndex, int columnIndex) {
       if (val instanceof Number && ((Number) val).doubleValue() > 0) {
              super.setValueAt(val, rowIndex, columnIndex);
            } 
       }
   }

This prevents the cell from accepting any non-positive values, but it doesn't set the color to red and leave the cell as editable.

I tried looking into how JTable's doing the rejection by default, but I can't seem to find it.

How can I make it reject the non-positive input the same way it rejects the non-Integer input?

Answer

trashgod picture trashgod · Sep 24, 2011

The private static class JTable.GenericEditor uses introspection to catch exceptions raised by constructing specific Number subclasses with invalid String values. If you don't need such generic behavior, consider creating PositiveIntegerCellEditor as a subclass of DefaultCellEditor. Your stopCellEditing() method would be correspondingly simpler.

Addendum: Updated to use RIGHT alignment and common error code.

Addendum: See also Using an Editor to Validate User-Entered Text.

enter image description here

    private static class PositiveIntegerCellEditor extends DefaultCellEditor {

    private static final Border red = new LineBorder(Color.red);
    private static final Border black = new LineBorder(Color.black);
    private JTextField textField;

    public PositiveIntegerCellEditor(JTextField textField) {
        super(textField);
        this.textField = textField;
        this.textField.setHorizontalAlignment(JTextField.RIGHT);
    }

    @Override
    public boolean stopCellEditing() {
        try {
            int v = Integer.valueOf(textField.getText());
            if (v < 0) {
                throw new NumberFormatException();
            }
        } catch (NumberFormatException e) {
            textField.setBorder(red);
            return false;
        }
        return super.stopCellEditing();
    }

    @Override
    public Component getTableCellEditorComponent(JTable table,
        Object value, boolean isSelected, int row, int column) {
        textField.setBorder(black);
        return super.getTableCellEditorComponent(
            table, value, isSelected, row, column);
    }
}