JavaSwingJTable and TableModel: TableModelListener


TableModelListener

An example to distinguish between the different kind of events send by a TableModel, which are all send to the single method of the listener interface,tableChanged(). To properly recognize "tableDataChanged" events, you actually have to rely on undocumented values (Integer.MAX_VALUE) used in a TableModelEvent constructor (That means a "tableDataChanged" event can be constructed without knowledge of the internals, but only understood by the listeners if you know them). Still, it is very improbable that this will ever change (And if you always use the code below, you will only have to fix it in one place).

The following classification is according to the event's "type".

TableModelEvent.UPDATE

firstRow == TableModelEvent.HEADER_ROW (== -1)

(lastRow and column are unused)

Everything has changed.

firstRow == 0 && lastRow == Integer.MAX_VALUE

(column is unused)

The column structure has remained the same, but the number of the rows and the rows' contents may (and probably will) have completely changed.

column == TableModelEvent.ALL_COLUMNS (== -1)

The complete rows in the interval [firstRow, lastRow] have changed.

column != TableModelEvent.ALL_COLUMNS

The data in the column column in the row interval [firstRow, lastRow] has changed.

TableModelEvent.INSERT

(column is unused)

The (new) row interval [firstRow, lastRow] has been added.

TableModelEvent.DELETE

(column is unused)

The (old) row interval [firstRow, lastRow] has been removed.

Note: TableModelEvent javadoc talks (since ages) of the possibility that this event may be (somehow) used to signify "before removal" instead of "after removal". This would be an incompatible change if the same event type would be used for both. Instead one could use a new event type and hope that listeners explicitly test for the types (which they probably don't, so it isn't possible).

import javax.swing.table.TableModel;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;

public abstract class AbstractTableModelListener
    implements TableModelListener
{
    public final void tableChanged(TableModelEvent e)
    {
        TableModel source = (TableModel)e.getSource();
        int first = e.getFirstRow(), last = e.getLastRow();

        if (first == TableModelEvent.HEADER_ROW)
        {
            structureChanged(source);
            return;
        }
        if (first == 0 && last == Integer.MAX_VALUE)
        {
            dataChanged(source);
            return;
        }

        switch (e.getType())
        {
            case TableModelEvent.DELETE:
                rowsRemoved(source, first, last); break;
            case TableModelEvent.UPDATE:
                if (e.getColumn() == TableModelEvent.ALL_COLUMNS)
                    rowsChanged(source, first, last);
                else
                    cellsChanged(source, first, last, e.getColumn());
                break;
            case TableModelEvent.INSERT: rowsAdded(source, first, last);break;
            default: unknownEvent(e);
         }
    }

    protected void unknownEvent(TableModelEvent e)
    {
    }

    protected void cellsChanged(TableModel source, int first, int last, int column)
    {
        rowsChanged(source, first, last);
    }

    protected abstract void structureChanged(TableModel source);
    protected abstract void dataChanged(TableModel source);
    protected abstract void rowsRemoved(TableModel source, int first, int last);
    protected abstract void rowsChanged(TableModel source, int first, int last);
    protected abstract void rowsAdded(TableModel source, int first, int last);
}

(C) 2001-2009 Christian Kaufhold (swing@chka.de)