package org.limewire.ui.swing.filter;
import java.util.Comparator;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.UniqueList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.util.concurrent.Lock;
/**
* A factory that converts a source list of values into a UniqueList of unique
* values. UniqueListFactory deals with a potential issue with UniqueList when
* it is preceded in the list chain by an ObservableList. The issue is
* difficult to reproduce, but we occasionally get an NPE when UniqueList
* handles a listChanged event. To prevent this, UniqueListFactory decouples
* list event processing between the source list and the unique list by
* creating a separate EventList chain, and installing its own ListEventListener
* to forward list change events. As a result, please note:
* <ul>
* <li>The unique list uses a separate ListEventPublisher from the one used
* by the source list.</li>
* <li>Applications should not attempt to directly modify the unique list
* since updates will not be forwarded back to the source list.</li>
* </ul>
*
* <p>UniqueListFactory is not thread-safe. It is intended for use in
* single-threaded environments like the Swing UI thread.
*/
public class UniqueListFactory<E> {
/** Source list of values. */
private final EventList<E> sourceList;
/** List of values. */
private final EventList<E> mirrorList;
/** List of unique values. */
private final UniqueList<E> uniqueList;
/** Listener to handle changes to source list. */
private final ListEventListener<E> sourceListener;
/** Component name. */
private String name;
/**
* Constructs a UniqueListFactory for the specified source list and
* comparator.
*/
public UniqueListFactory(EventList<E> sourceList, Comparator<E> comparator) {
// Create lists.
this.sourceList = sourceList;
mirrorList = new BasicEventList<E>();
uniqueList = new UniqueList<E>(mirrorList, comparator);
sourceListener = new SourceEventListener();
// Copy values to mirror list, and add listener to source list.
Lock lock = sourceList.getReadWriteLock().readLock();
lock.lock();
try {
mirrorList.addAll(sourceList);
sourceList.addListEventListener(sourceListener);
} finally {
lock.unlock();
}
}
/**
* Disposes of the factory by removing the listener on the source list, and
* disposing of the unique list. The unique list can no longer be used
* after this method is called.
*/
public void dispose() {
sourceList.removeListEventListener(sourceListener);
uniqueList.dispose();
}
/**
* Returns the name of the list factory.
*/
public String getName() {
return name;
}
/**
* Sets the name of the list factory.
*/
public void setName(String name) {
this.name = name;
}
/**
* Returns a list of unique property values.
*/
public UniqueList<E> getUniqueList() {
return uniqueList;
}
/**
* Listener that handles changes to the source list.
*/
private class SourceEventListener implements ListEventListener<E> {
/**
* Handles list change event to update mirror list.
*/
@Override
public void listChanged(ListEvent<E> listChanges) {
EventList<E> sourceList = listChanges.getSourceList();
while (listChanges.next()) {
int index = listChanges.getIndex();
int type = listChanges.getType();
try {
switch (type) {
case ListEvent.INSERT:
mirrorList.add(index, sourceList.get(index));
break;
case ListEvent.DELETE:
mirrorList.remove(index);
break;
case ListEvent.UPDATE:
mirrorList.set(index, sourceList.get(index));
break;
}
} catch (Throwable th) {
// Throw wrapper exception with message.
throw new RuntimeException(createExceptionMessage(type, index), th);
}
}
}
/**
* Returns a detailed message using the specified ListEvent type and
* index.
*/
private String createExceptionMessage(int type, int index) {
StringBuilder buf = new StringBuilder();
buf.append(getName()).append(" list, unable to ");
switch (type) {
case ListEvent.INSERT:
buf.append("insert");
break;
case ListEvent.DELETE:
buf.append("delete");
break;
case ListEvent.UPDATE:
buf.append("update");
break;
}
buf.append(" at index ").append(index);
return buf.toString();
}
}
}