1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.swing;
import javax.swing.SwingUtilities;
import javax.swing.table.TableModel;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.gui.TableFormat;
/**
* A {@link DefaultEventTableModel} that silently wraps it's source list in a
* SwingThreadProxyEventList to ensure that events that arrive at the TableModel do so on
* the EDT. 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}.
* <p>
* The EventTableModel class is <strong>not thread-safe</strong>. 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)}.
*
* @see <a href="http://publicobject.com/glazedlists/tutorial/">Glazed Lists Tutorial</a>
* @see SwingUtilities#invokeAndWait(Runnable)
* @see <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=112">Bug 112</a>
* @see <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=146">Bug 146</a>
* @see <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=177">Bug 177</a>
*
* @deprecated Use {@link DefaultEventTableModel} instead. This class will be removed in the GL
* 2.0 release. The wrapping of the source list with an EDT safe list has been
* determined to be undesirable (it is better for the user to provide their own EDT
* safe list).
*
* @author <a href="mailto:jesse@swank.ca">Jesse Wilson</a>
*/
public class EventTableModel<E> extends DefaultEventTableModel<E> {
/** the proxy moves events to the Swing Event Dispatch thread */
protected TransformedList<E,E> swingThreadSource;
/**
* Creates a new table model that extracts column data from the given <code>source</code>
* using the the given <code>tableFormat</code>.
*
* @param source the EventList that provides the row objects
* @param tableFormat the object responsible for extracting column data from the row objects
*
* @deprecated Use {@link DefaultEventTableModel} and
* {@link GlazedListsSwing#swingThreadProxyList(EventList)} instead
*/
public EventTableModel(EventList<E> source, TableFormat<? super E> tableFormat) {
super(source, tableFormat);
// lock the source list for reading since we want to prevent writes
// from occurring until we fully initialize this EventTableModel
// KD 20090306 - Not crazy about these locks, but it was in the original
// EventTableModel implementation, so we need to leave it. DefaultTableModel
// should be considered to be non-Thread Safe, so source lists should be locked
// before constructing a DefaultEventTableModel if there are any potential
// race conditions
source.getReadWriteLock().readLock().lock();
try {
final TransformedList<E,E> decorated = createSwingThreadProxyList(source);
if (decorated != null && decorated != source){
//we need to switch the configuration of the DefaultEventTableModel so it
// uses the swingThreadProxyList instead
this.source.removeListEventListener(this);
this.source = swingThreadSource = decorated;
this.source.addListEventListener(this);
}
} finally {
source.getReadWriteLock().readLock().unlock();
}
}
/**
* Creates a new table that renders the specified list with an automatically
* generated {@link TableFormat}. It uses JavaBeans and reflection to create
* a {@link TableFormat} as specified.
*
* <p>Note that the classes which will be obfuscated may not work with
* reflection. In this case, implement a {@link TableFormat} manually.
*
* @param source the EventList that provides the row objects
* @param propertyNames an array of property names in the JavaBeans format.
* For example, if your list contains Objects with the methods getFirstName(),
* setFirstName(String), getAge(), setAge(Integer), then this array should
* contain the two strings "firstName" and "age". This format is specified
* by the JavaBeans {@link java.beans.PropertyDescriptor}.
* @param columnLabels the corresponding column names for the listed property
* names. For example, if your columns are "firstName" and "age", then
* your labels might be "First Name" and "Age".
* @param writable an array of booleans specifying which of the columns in
* your table are writable.
*
* @deprecated Use {@link GlazedListsSwing#createEventTableModel(EventList, String[], String[], boolean[])}
* and {@link GlazedListsSwing#swingThreadProxyList(EventList)} instead
*/
public EventTableModel(EventList<E> source, String[] propertyNames, String[] columnLabels, boolean[] writable) {
this(source, GlazedLists.tableFormat(propertyNames, columnLabels, writable));
}
/**
* This method exists as a hook for subclasses that may have custom
* threading needs within their EventTableModels. By default, this method
* will wrap the given <code>source</code> in a SwingThreadProxyList if it
* is not already a SwingThreadProxyList. Subclasses may replace this logic
* and return either a custom ThreadProxyEventList of their choosing, or
* return <code>null</code> or the <code>source</code> unchanged in order
* to indicate that <strong>NO</strong> ThreadProxyEventList is desired.
* In these cases it is expected that some external mechanism will ensure
* that threading is handled correctly.
*
* @param source the EventList that provides the row objects
* @return the source wrapped in some sort of ThreadProxyEventList if
* Thread-proxying is desired, or either <code>null</code> or the
* <code>source</code> unchanged to indicate that <strong>NO</strong>
* Thread-proxying is desired
*/
protected TransformedList<E,E> createSwingThreadProxyList(EventList<E> source) {
return GlazedListsSwing.isSwingThreadProxyList(source) ? null : GlazedListsSwing.swingThreadProxyList(source);
}
/**
* Releases the resources consumed by this {@link EventTableModel} so that it
* may eventually be garbage collected.
*
* <p>An {@link EventTableModel} will be garbage collected without a call to
* {@link #dispose()}, but not before its source {@link EventList} is garbage
* collected. By calling {@link #dispose()}, you allow the {@link EventTableModel}
* to be garbage collected before its source {@link EventList}. This is
* necessary for situations where an {@link EventTableModel} is short-lived but
* its source {@link EventList} is long-lived.
*
* <p><strong><font color="#FF0000">Warning:</font></strong> It is an error
* to call any method on an {@link EventTableModel} after it has been disposed.
* As such, this {@link EventTableModel} should be detached from its
* corresponding Component <strong>before</strong> it is disposed.
*/
@Override
public void dispose() {
// if we created the swingThreadSource then we must also dispose it
if (swingThreadSource != null)
swingThreadSource.dispose();
swingThreadSource = null;
super.dispose();
}
}
|