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
|
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package ca.odell.glazedlists;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventPublisher;
import ca.odell.glazedlists.util.concurrent.ReadWriteLock;
/**
* An {@link EventList} which delegates all List methods to a given source
* {@link EventList} that may be replaced at runtime using
* {@link #setSource(EventList)}.
*
* <p>Note that the source {@link EventList} must use the same
* {@link ListEventPublisher} and {@link ReadWriteLock}, particularly if this
* {@link EventList} is to be used by multiple threads concurrently. To
* construct an {@link EventList} that shares the {@link ListEventPublisher}
* and {@link ReadWriteLock} with this {@link PluggableList}, use
* {@link #createSourceList()}.
*
* <p><strong><font color="#FF0000">Warning:</font></strong> This class is
* thread ready but not thread safe. See {@link EventList} for an example
* of thread safe code.
*
* <p><table border="1" width="100%" cellpadding="3" cellspacing="0">
* <tr class="TableHeadingColor"><td colspan=2><font size="+2"><b>EventList Overview</b></font></td></tr>
* <tr><td class="TableSubHeadingColor"><b>Writable:</b></td><td>yes</td></tr>
* <tr><td class="TableSubHeadingColor"><b>Concurrency:</b></td><td><strong>only {@link #setSource(EventList)}</strong></td></tr>
* <tr><td class="TableSubHeadingColor"><b>Performance:</b></td><td>delegates to source EventList</td></tr>
* <tr><td class="TableSubHeadingColor"><b>Memory:</b></td><td>N/A</td></tr>
* <tr><td class="TableSubHeadingColor"><b>Unit Tests:</b></td><td>N/A</td></tr>
* <tr><td class="TableSubHeadingColor"><b>Issues:</b></td><td></td></tr>
* </table>
*
* @author James Lemieux
*/
public class PluggableList<E> extends TransformedList<E, E> {
/**
* Constructs a PluggableList which uses the given <code>publisher</code>
* and <code>lock</code>. The PluggableList will default to use a
* {@link BasicEventList} that also uses the same <code>publisher</code>
* and <code>lock</code>.
*
* @param publisher the {@link ListEventPublisher} to use within the {@link PluggableList}
* @param lock the {@link ReadWriteLock} to use within the {@link PluggableList}
*/
public PluggableList(ListEventPublisher publisher, ReadWriteLock lock) {
this(new BasicEventList<E>(publisher, lock));
}
/**
* Constructs a PluggableList which delegates all List methods to the given
* <code>source</code>. At some future time, the source EventList may be
* replaced using {@link #setSource(EventList)} and this PluggableList will
* produce a {@link ListEvent} describing the change in data.
*
* @param source the source of data to this PluggableList
*/
public PluggableList(EventList<E> source) {
super(source);
source.addListEventListener(this);
}
/**
* Creates a new {@link EventList} that shares its
* {@link ca.odell.glazedlists.util.concurrent.ReadWriteLock} and
* {@link ca.odell.glazedlists.event.ListEventPublisher} with this
* {@link PluggableList}. This is necessary when this {@link PluggableList}
* will be used by multiple threads.
*
* <p>Note that the created {@link EventList} must be explicitly set as the
* source of this {@link PluggableList} using {@link #setSource(EventList)}.
*
* @return a new EventList appropriate for use as the
* {@link #setSource(EventList) source} of this PluggableList
*/
public EventList<E> createSourceList() {
return new BasicEventList<E>(getPublisher(), getReadWriteLock());
}
/**
* Sets the source EventList to which this PluggableList will delegate all
* calls. This method is the entire reason that PluggableList exists. It
* allows the data source of the remaining pipeline to be altered.
* <p>
* To ensure correct behaviour when this {@link PluggableList} is used by
* multiple threads, the given <code>source</code> <strong>must</strong>
* share the same {@link ca.odell.glazedlists.util.concurrent.ReadWriteLock} and
* {@link ca.odell.glazedlists.event.ListEventPublisher} with this PluggableList.
*
* @param source the new source of data for this PluggableList, and all
* downstream EventLists
* @throws IllegalStateException if this PluggableList is already disposed
* @throws IllegalArgumentException if any of the following are true
* <ul>
* <li>the given source is null</li>
* <li>the given source has a different ListEventPublisher than this PluggableList</li>
* <li>the given source has a different ReadWriteLock than this PluggableList</li>
* </ul>
*/
public void setSource(EventList<E> source) {
// lock the pipeline while the source list is swapped
getReadWriteLock().writeLock().lock();
try {
if (this.source == null)
throw new IllegalStateException("setSource may not be called on a disposed PluggableList");
if (source == null)
throw new IllegalArgumentException("source may not be null");
if (!getReadWriteLock().equals(source.getReadWriteLock()))
throw new IllegalArgumentException("source list must share lock with PluggableList");
if (!getPublisher().equals(source.getPublisher()))
throw new IllegalArgumentException("source list must share publisher with PluggableList");
if (this.source == source)
return;
updates.beginEvent();
// add deletions to the ListEvent for all the elements in the old source
for (int i = 0, n = size(); i < n; i++)
updates.elementDeleted(0, get(i));
this.source.removeListEventListener(this);
this.source = source;
this.source.addListEventListener(this);
// add insertions to the ListEvent for all the elements in the new source
for (int i = 0, n = size(); i < n; i++)
updates.elementInserted(i, get(i));
// broadcast the ListEvent that describes the data change
updates.commitEvent();
} finally {
getReadWriteLock().writeLock().unlock();
}
}
/** @inheritDoc */
@Override
protected boolean isWritable() {
return true;
}
/** @inheritDoc */
@Override
public void listChanged(ListEvent<E> listChanges) {
updates.forwardEvent(listChanges);
}
/** @inheritDoc */
@Override
public void dispose() {
if (source != null)
source.removeListEventListener(this);
source = null;
}
}
|