I am trying to create a simple Input Verifier for a JTable. I ended up with overriding the method: editingStopped(). The problem is that the event does not include informations
hmm, there might be a simpler solution to this. Please try this, it worked for me. The key is to remember last selected item, and then perform validation on the current item. If input is wrong, you can roll back to the last selected item, and notify user about that. Roll back is performed using EventQueue.invokeLater(...), therefore avoiding recursive call to the listeners.
private final DefaultTableModel dtm = new DefaultTableModel();
private final JTable table = new JTable(dtm);
private final Object[] lastItem;
private final AtomicInteger lastIndex = new AtomicInteger(-1);
private final ItemValidator validator = new ItemValidator();
public YourConstructor() {
lastItem = new Object[table.getColumnCount()];
//store last value of selected table item in an array.
table.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent evt){
lastIndex.set(table.getSelectedRow());
int row = lastIndex.get();
for(int i=0;i<lastItem.length;i++){
lastItem[i] = table.getValueAt(row, i);
}
}
});
//for input validation, and database update.
dtm.addTableModelListener(new TableModelListener(){
@Override
public void tableChanged(TableModelEvent e) {
switch(e.getType()){
case TableModelEvent.INSERT:
System.out.println("insert");
break;
case TableModelEvent.UPDATE:
validateUpdate();
break;
case TableModelEvent.DELETE:
System.out.println("delete");
break;
default:
break;
}
}
});
}
public void validateUpdate(){
String item;
for(int i=0;i<lastItem.length;i++)
{
item = (String)table.getValueAt(lastIndex.get(), i);
if(i>1 && i<lastItem.length)//column range to be checked
{
if(!validator.hasNumericText(item))
{
final int col = i;
final Object lastObject = lastItem[i];
final int row = lastIndex.get();
//the most important part, to avoid StackOverflow
//by using EventQueue, you avoid looping around
//the TableModelListener.
EventQueue.invokeLater(new Runnable(){
public void run(){
table.setValueAt(lastObject, row, col);
}
});
System.out.println("Error at " + i);
break;
}
}
}
}
First: input validation on JTable editing is not well supported. A couple of comments
After all those (incomplete, unfortunately ;-) no-nos, a little hope: best bet is to implement a custom CellEditor which does the validation in stopCellCellEditing: if the new value isn't valid, return false and optionally provide a visual error feedback. Have a look at JTable.GenericEditor to get an idea of how that might be done
What worked for me (tip 'o the hat to kleopatra):
private class CellEditor extends DefaultCellEditor {
InputVerifier verifier = null;
public CellEditor(InputVerifier verifier) {
super(new JTextField());
this.verifier = verifier;
}
@Override
public boolean stopCellEditing() {
return verifier.verify(editorComponent) && super.stopCellEditing();
}
}
// ...
private class PortVerifier extends InputVerifier {
@Override
public boolean verify(JComponent input) {
boolean verified = false;
String text = ((JTextField) input).getText();
try {
int port = Integer.valueOf(text);
if ((0 < port) && (port <= 65535)) {
input.setBackground(Color.WHITE);
verified = true;
} else {
input.setBackground(Color.RED);
}
} catch (NumberFormatException e) {
input.setBackground(Color.RED);
}
return verified;
}
}
// ...
table.getColumn("Port").setCellEditor(new CellEditor(new PortVerifier()));