ca.odell.glazedlists.PluggableList Maven / Gradle / Ivy
/* 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)}.
*
* 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()}.
*
*
Warning: This class is
* thread ready but not thread safe. See {@link EventList} for an example
* of thread safe code.
*
*
* EventList Overview
* Writable: yes
* Concurrency: only {@link #setSource(EventList)}
* Performance: delegates to source EventList
* Memory: N/A
* Unit Tests: N/A
* Issues:
*
*
* @author James Lemieux
*/
public class PluggableList extends TransformedList {
/**
* Constructs a PluggableList which uses the given publisher
* and lock
. The PluggableList will default to use a
* {@link BasicEventList} that also uses the same publisher
* and lock
.
*
* @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(publisher, lock));
}
/**
* Constructs a PluggableList which delegates all List methods to the given
* source
. 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 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.
*
* 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 createSourceList() {
return new BasicEventList(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.
*
* To ensure correct behaviour when this {@link PluggableList} is used by
* multiple threads, the given source
must
* 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
*
* - the given source is null
* - the given source has a different ListEventPublisher than this PluggableList
* - the given source has a different ReadWriteLock than this PluggableList
*
*/
public void setSource(EventList 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 listChanges) {
updates.forwardEvent(listChanges);
}
/** @inheritDoc */
@Override
public void dispose() {
if (source != null)
source.removeListEventListener(this);
source = null;
}
}