All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.odell.glazedlists.swing.DefaultEventTableModel Maven / Gradle / Ivy

/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.swing;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.gui.AdvancedTableFormat;
import ca.odell.glazedlists.gui.TableFormat;
import ca.odell.glazedlists.gui.WritableTableFormat;

import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;

/**
 * A {@link TableModel} that holds an {@link EventList}. Each element of the list
 * corresponds to a row in the {@link TableModel}. The columns of the table are
 * specified using a {@link TableFormat}.
 *
 * 

The EventTableModel class is not thread-safe. Unless otherwise * noted, all methods are only safe to be called from the event dispatch thread. * To do this programmatically, use {@link SwingUtilities#invokeAndWait(Runnable)} and * wrap the source list (or some part of the source list's pipeline) using * GlazedListsSwing#swingThreadProxyList(EventList).

* * @see Glazed Lists Tutorial * * @see GlazedListsSwing#swingThreadProxyList(EventList) * @see SwingUtilities#invokeAndWait(Runnable) * @see Bug 112 * @see Bug 146 * @see Bug 177 * * @author Jesse Wilson */ public class DefaultEventTableModel extends AbstractTableModel implements AdvancedTableModel, ListEventListener { /** the source of data for this TableModel. */ protected EventList source; /** indicator to dispose source list */ private boolean disposeSource; /** specifies how column data is extracted from each row object */ private TableFormat tableFormat; /** encapsulates the strategy how to convert {@link ListEvent}s to {@link TableModelEvent}s */ private TableModelEventAdapter eventAdapter = GlazedListsSwing.defaultEventAdapterFactory().create(this); /** * Creates a new table model that extracts column data from the given * source using the the given tableFormat. * * @param source the EventList that provides the row objects * @param tableFormat the object responsible for extracting column data * from the row objects */ public DefaultEventTableModel(EventList source, TableFormat tableFormat) { this(source, false, tableFormat); } /** * Creates a new table model that extracts column data from the given * source using the the given tableFormat. * * @param source the EventList that provides the row objects * @param disposeSource true if the source list should be disposed when disposing * this model, false otherwise * @param tableFormat the object responsible for extracting column data * from the row objects */ public DefaultEventTableModel(EventList source, boolean disposeSource, TableFormat tableFormat) { this.source = source; this.disposeSource = disposeSource; this.tableFormat = tableFormat; source.addListEventListener(this); } /** * {@inheritDoc} */ @Override public TableFormat getTableFormat() { return tableFormat; } /** * {@inheritDoc} */ @Override public void setTableFormat(TableFormat tableFormat) { this.tableFormat = tableFormat; eventAdapter.fireTableStructureChanged(); } /** * {@inheritDoc} */ @Override public E getElementAt(int index) { source.getReadWriteLock().readLock().lock(); try { return source.get(index); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * For implementing the ListEventListener interface. This sends changes * to the table which repaints the table cells. Because this class is * backed by {@link GlazedListsSwing#swingThreadProxyList}, all natural * calls to this method are guaranteed to occur on the Swing EDT. */ @Override public void listChanged(ListEvent listChanges) { handleListChange(listChanges); } /** * Default implementation for converting a {@link ListEvent} to * TableModelEvents. There will be one TableModelEvent per ListEvent block. * Subclasses may choose to implement a different conversion. * * @param listChanges ListEvent to translate */ protected void handleListChange(ListEvent listChanges) { eventAdapter.listChanged(listChanges); } // /** // * @return reusable TableModelEvent for broadcasting changes // */ // protected final MutableTableModelEvent getMutableTableModelEvent() { // return tableModelEvent; // } /** * Fetch the name for the specified column. */ @Override public String getColumnName(int column) { return tableFormat.getColumnName(column); } /** * The number of rows equals the number of entries in the source event list. */ @Override public int getRowCount() { source.getReadWriteLock().readLock().lock(); try { return source.size(); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * Get the column count as specified by the table format. */ @Override public int getColumnCount() { return tableFormat.getColumnCount(); } /** * Gets the class of elements in the specified column. This behaviour can be * customized by implementing the {@link AdvancedTableFormat} interface. */ @Override public Class getColumnClass(int columnIndex) { // See if the TableFormat is specifies a column class if(tableFormat instanceof AdvancedTableFormat) { return ((AdvancedTableFormat)tableFormat).getColumnClass(columnIndex); // If not, use the default... } else { return super.getColumnClass(columnIndex); } } /** * Retrieves the value at the specified location of the table. */ @Override public Object getValueAt(int row, int column) { source.getReadWriteLock().readLock().lock(); try { return tableFormat.getColumnValue(source.get(row), column); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * Delegates the question of whether the cell is editable or not to the * backing TableFormat if it is a {@link WritableTableFormat}. Otherwise, * the column is assumed to be uneditable. */ @Override public boolean isCellEditable(int row, int column) { if (!(tableFormat instanceof WritableTableFormat)) { return false; } source.getReadWriteLock().readLock().lock(); try { final E toEdit = source.get(row); return ((WritableTableFormat) tableFormat).isEditable(toEdit, column); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * Attempts to update the object for the given row with the * editedValue. This requires the backing TableFormat * be a {@link WritableTableFormat}. {@link WritableTableFormat#setColumnValue} * is expected to contain the logic for updating the object at the given * row with the editedValue which was in the * given column. */ @Override public void setValueAt(Object editedValue, int row, int column) { // ensure this is a writable table if (!(tableFormat instanceof WritableTableFormat)) { throw new UnsupportedOperationException("Unexpected setValueAt() on read-only table"); } source.getReadWriteLock().writeLock().lock(); try { // get the object being edited from the source list final E baseObject = source.get(row); // tell the table format to set the value based final WritableTableFormat writableTableFormat = (WritableTableFormat) tableFormat; final E updatedObject = writableTableFormat.setColumnValue(baseObject, editedValue, column); // if the edit was discarded we have nothing to do if (updatedObject != null) { // check if updating the baseObject has caused it to be removed from this // TableModel (FilterList) or moved to another location (SortedList) final boolean baseObjectHasNotMoved = row < getRowCount() && source.get(row) == baseObject; // if the row is still present in its original location, update it to induce a // TableModelEvent that will redraw that row in the table if (baseObjectHasNotMoved) { source.set(row, updatedObject); } } } finally { source.getReadWriteLock().writeLock().unlock(); } } /** * {@inheritDoc} */ @Override public void dispose() { source.removeListEventListener(this); if (disposeSource) { source.dispose(); } // this encourages exceptions to be thrown if this model is incorrectly accessed again source = null; } /** * Gets the {@link TableModelEventAdapter} used to convert {@link ListEvent}s to {@link TableModelEvent}s. * * @return the {@link TableModelEventAdapter} */ public final TableModelEventAdapter getEventAdapter() { return eventAdapter; } /** * Sets the {@link TableModelEventAdapter} used to convert {@link ListEvent}s to {@link TableModelEvent}s. * * @param eventAdapter the new {@link TableModelEventAdapter} not null */ public final void setEventAdapter(TableModelEventAdapter eventAdapter) { this.eventAdapter = eventAdapter; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy