class ElementsDataTableModel<T extends Element> extends AbstractTableModel {
    private T[] elements;
    private ElementDataColumn<T>[] columns;

    public ElementsDataTableModel(T[] elements, ElementDataColumn<T>[] cols) {
        this.elements = elements;
        this.columns = cols;
    }

    @Override
    public int getRowCount() {
        return elements.length;
    }

    @Override
    public int getColumnCount() {
        return columns.length;
    }

    @Override
    public String getColumnName(int columnIndex) {
        return columns[columnIndex].getColumnName();
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return columns[columnIndex].getColumnClass();
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return columns[columnIndex].isEditable();
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        try {
            return columns[columnIndex].getValueFor(elements[rowIndex]);
        } catch (Exception e) {
            /**
             * We need to do this because the JTable might repaint itself 
             * while datalab still has not detected that the column has been deleted 
             * (it does so by polling on graph and table observers).
             * I can't find a better solution...
             */
            return null;
        }
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        columns[columnIndex].setValueFor(elements[rowIndex], aValue);
    }

    public T getElementAtRow(int row) {
        return elements[row];
    }

    public ElementDataColumn<T>[] getColumns() {
        return columns;
    }

    public T[] getElements() {
        return elements;
    }

    public void configure(T[] elements, ElementDataColumn<T>[] columns) {<FILL_FUNCTION_BODY>}

    /**
     * Column at index or null if it's a fake column.
     *
     * @return
     */
    public Column getColumnAtIndex(int i) {
        if (i >= 0 && i < columns.length) {
            return columns[i].getColumn();
        } else {
            return null;
        }
    }
}

        Set<ElementDataColumn> oldColumns = new HashSet<ElementDataColumn>(Arrays.asList(this.columns));
        Set<ElementDataColumn> newColumns = new HashSet<ElementDataColumn>(Arrays.asList(columns));

        boolean columnsChanged = !oldColumns.equals(newColumns);
        this.columns = columns;
        this.elements = elements;

        if (columnsChanged) {
            fireTableStructureChanged();//Only firing this event if columns change is useful because JXTable will not reset columns width if there is no change
        } else {
            fireTableDataChanged();
        }
    
<methods>public void addTableModelListener(javax.swing.event.TableModelListener) ,public int findColumn(java.lang.String) ,public void fireTableCellUpdated(int, int) ,public void fireTableChanged(javax.swing.event.TableModelEvent) ,public void fireTableDataChanged() ,public void fireTableRowsDeleted(int, int) ,public void fireTableRowsInserted(int, int) ,public void fireTableRowsUpdated(int, int) ,public void fireTableStructureChanged() ,public Class<?> getColumnClass(int) ,public java.lang.String getColumnName(int) ,public T[] getListeners(Class<T>) ,public javax.swing.event.TableModelListener[] getTableModelListeners() ,public boolean isCellEditable(int, int) ,public void removeTableModelListener(javax.swing.event.TableModelListener) ,public void setValueAt(java.lang.Object, int, int) <variables>protected javax.swing.event.EventListenerList listenerList