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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
|
/* 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.GlazedLists;
import ca.odell.glazedlists.ThresholdList;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.gui.TableFormat;
import ca.odell.glazedlists.impl.swing.LowerThresholdRangeModel;
import ca.odell.glazedlists.impl.swing.SwingThreadProxyEventList;
import ca.odell.glazedlists.impl.swing.UpperThresholdRangeModel;
import javax.swing.BoundedRangeModel;
import javax.swing.SwingUtilities;
/**
* A factory for creating all sorts of objects to be used with Glazed Lists.
*
* @author <a href="mailto:jesse@swank.ca">Jesse Wilson</a>
*/
public final class GlazedListsSwing {
/**
* A dummy constructor to prevent instantiation of this class
*/
private GlazedListsSwing() {
throw new UnsupportedOperationException();
}
// EventLists // // // // // // // // // // // // // // // // // // // // //
/**
* Wraps the source in an {@link EventList} that fires all of its update
* events from the Swing event dispatch thread.
*/
public static <E> TransformedList<E, E> swingThreadProxyList(EventList<E> source) {
return new SwingThreadProxyEventList<E>(source);
}
/**
* Returns true iff <code>list</code> is an {@link EventList} that fires
* all of its update events from the Swing event dispatch thread.
*/
public static boolean isSwingThreadProxyList(EventList list) {
return list instanceof SwingThreadProxyEventList;
}
// ThresholdRangeModels // // // // // // // // // // // // // // // // //
/**
* Creates a model that manipulates the lower bound of the specified
* ThresholdList. The ThresholdList linked to this model type will contain
* a range of Objects between the results of getValue() and getMaximum()
* on the BoundedRangeModel.
*/
public static BoundedRangeModel lowerRangeModel(ThresholdList target) {
return new LowerThresholdRangeModel(target);
}
/**
* Creates a model that manipulates the upper bound of the specified
* ThresholdList. The ThresholdList linked to this model type will contain
* a range of Objects between the results of getMinimum() and getValue()
* on the BoundedRangeModel.
*/
public static BoundedRangeModel upperRangeModel(ThresholdList target) {
return new UpperThresholdRangeModel(target);
}
// TableModel convenience creators
/**
* Creates a new table model that extracts column data from the given
* <code>source</code> using the the given <code>tableFormat</code>.
*
* <p>The returned table model 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)} and
* wrap the source list (or some part of the source list's pipeline) using
* {@link GlazedListsSwing#swingThreadProxyList(EventList)}.</p>
*
* @param source the EventList that provides the row objects
* @param tableFormat the object responsible for extracting column data
* from the row objects
*/
public static <E> AdvancedTableModel<E> eventTableModel(EventList<E> source, TableFormat<? super E> tableFormat) {
return new DefaultEventTableModel<E>(source, tableFormat);
}
/**
* Creates a new table model that extracts column data from the given <code>source</code>
* using the the given <code>tableFormat</code>. While holding a read lock,
* this method wraps the source list using
* {@link GlazedListsSwing#swingThreadProxyList(EventList)}.
* <p>
* The returned table model is <strong>not thread-safe</strong>. Unless otherwise noted, all
* methods are only safe to be called from the event dispatch thread.
* </p>
*
* @param source the EventList that provides the row objects
* @param tableFormat the object responsible for extracting column data from the row objects
*/
public static <E> AdvancedTableModel<E> eventTableModelWithThreadProxyList(EventList<E> source, TableFormat<? super E> tableFormat) {
final EventList<E> proxySource = createSwingThreadProxyList(source);
return new DefaultEventTableModel<E>(proxySource, true, tableFormat);
}
/**
* Creates a new table model 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 classes that will be obfuscated may not work with
* reflection. In this case, implement a {@link TableFormat} manually.</p>
*
* <p>The returned table model 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)} and
* wrap the source list (or some part of the source list's pipeline) using
* {@link GlazedListsSwing#swingThreadProxyList(EventList)}.</p>
*
* @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.
*
*/
public static <E> AdvancedTableModel<E> eventTableModel(EventList<E> source, String[] propertyNames, String[] columnLabels, boolean[] writable) {
return eventTableModel(source, GlazedLists.tableFormat(propertyNames, columnLabels, writable));
}
/**
* Creates a new table model that renders the specified list with an automatically
* generated {@link TableFormat}. It uses JavaBeans and reflection to create
* a {@link TableFormat} as specified. While holding a read lock,
* this method wraps the source list using
* {@link GlazedListsSwing#swingThreadProxyList(EventList)}.
*
* <p>Note that classes that will be obfuscated may not work with
* reflection. In this case, implement a {@link TableFormat} manually.</p>
*
* <p>The returned table model is <strong>not thread-safe</strong>. Unless otherwise
* noted, all methods are only safe to be called from the event dispatch thread.<p>
*
* @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.
*
*/
public static <E> AdvancedTableModel<E> eventTableModelWithThreadProxyList(EventList<E> source, String[] propertyNames, String[] columnLabels, boolean[] writable) {
return eventTableModelWithThreadProxyList(source, GlazedLists.tableFormat(propertyNames, columnLabels, writable));
}
// ListSelectionModel convenience creators
/**
* Creates a new selection model that also presents a list of the selection.
*
* The {@link AdvancedListSelectionModel} listens to this {@link EventList} in order
* to adjust selection when the {@link EventList} is modified. For example,
* when an element is added to the {@link EventList}, this may offset the
* selection of the following elements.
*
* <p>The returned selection model 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)} and
* wrap the source list (or some part of the source list's pipeline) using
* {@link GlazedListsSwing#swingThreadProxyList(EventList)}.</p>
*
* @param source the {@link EventList} whose selection will be managed. This should
* be the same {@link EventList} passed to the constructor of your
* {@link AdvancedTableModel} or {@link EventListModel}.
*/
public static <E> AdvancedListSelectionModel<E> eventSelectionModel(EventList<E> source) {
return new DefaultEventSelectionModel<E>(source);
}
/**
* Creates a new selection model that also presents a list of the selection.
* While holding a read lock, it wraps the source list using
* {@link GlazedListsSwing#swingThreadProxyList(EventList)}. The
* {@link AdvancedListSelectionModel} listens to this {@link EventList} in order to adjust
* selection when the {@link EventList} is modified. For example, when an element is added to
* the {@link EventList}, this may offset the selection of the following elements.
* <p>
* The returned selection model is <strong>not thread-safe</strong>. Unless otherwise noted,
* all methods are only safe to be called from the event dispatch thread.
* </p>
*
* @param source the {@link EventList} whose selection will be managed. This should be the
* same {@link EventList} passed to the constructor of your
* {@link AdvancedTableModel} or {@link EventListModel}.
*/
public static <E> AdvancedListSelectionModel<E> eventSelectionModelWithThreadProxyList(EventList<E> source) {
final EventList<E> proxySource = createSwingThreadProxyList(source);
return new DefaultEventSelectionModel<E>(proxySource, true);
}
// EventListModel convenience creators
/**
* Creates a new list model that contains all objects located in the given
* <code>source</code> and reacts to any changes in the given <code>source</code>.
*
* <p>The returned selection model 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)} and
* wrap the source list (or some part of the source list's pipeline) using
* {@link GlazedListsSwing#swingThreadProxyList(EventList)}.
* </p>
*
* @param source the EventList that provides the elements
*/
public static <E> DefaultEventListModel<E> eventListModel(EventList<E> source) {
return new DefaultEventListModel<E>(source);
}
/**
* Creates a new list model that contains all objects located in the given
* <code>source</code> and reacts to any changes in the given <code>source</code>.
* While holding a read lock, it wraps the source list using
* {@link GlazedListsSwing#swingThreadProxyList(EventList)}.
*
* <p>The returned selection model is <strong>not thread-safe</strong>. Unless otherwise
* noted, all methods are only safe to be called from the event dispatch thread.
* </p>
*
* @param source the EventList that provides the elements
*/
public static <E> DefaultEventListModel<E> eventListModelWithThreadProxyList(EventList<E> source) {
final EventList<E> proxySource = createSwingThreadProxyList(source);
return new DefaultEventListModel<E>(proxySource, true);
}
// EventComboBoxModel convenience creators
/**
* Creates a new combobox model that contains all objects located in the given
* <code>source</code> and reacts to any changes in the given <code>source</code>.
*
* <p>The returned combobox model 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)} and
* wrap the source list (or some part of the source list's pipeline) using
* {@link GlazedListsSwing#swingThreadProxyList(EventList)}.
* </p>
*
* @param source the EventList that provides the elements
*/
public static <E> DefaultEventComboBoxModel<E> eventComboBoxModel(EventList<E> source) {
return new DefaultEventComboBoxModel<E>(source);
}
/**
* Creates a new combobox model that contains all objects located in the given
* <code>source</code> and reacts to any changes in the given <code>source</code>.
* While holding a read lock, it wraps the source list using
* {@link GlazedListsSwing#swingThreadProxyList(EventList)}.
*
* <p>The returned combobox model is <strong>not thread-safe</strong>. Unless otherwise
* noted, all methods are only safe to be called from the event dispatch thread.
* </p>
*
* @param source the EventList that provides the elements
*/
public static <E> DefaultEventComboBoxModel<E> eventComboBoxModelWithThreadProxyList(EventList<E> source) {
final EventList<E> proxySource = createSwingThreadProxyList(source);
return new DefaultEventComboBoxModel<E>(proxySource, true);
}
/** Helper method to create a SwingThreadProxyList with read locks. */
private static <E> EventList<E> createSwingThreadProxyList(EventList<E> source) {
final EventList<E> result;
source.getReadWriteLock().readLock().lock();
try {
result = GlazedListsSwing.swingThreadProxyList(source);
} finally {
source.getReadWriteLock().readLock().unlock();
}
return result;
}
}
|