diff --git a/src/main/java/com/samskivert/util/ObserverList.java b/src/main/java/com/samskivert/util/ObserverList.java
index f43711a9..fb35b237 100644
--- a/src/main/java/com/samskivert/util/ObserverList.java
+++ b/src/main/java/com/samskivert/util/ObserverList.java
@@ -23,6 +23,9 @@ package com.samskivert.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
import static com.samskivert.Log.log;
@@ -86,7 +89,7 @@ import static com.samskivert.Log.log;
*
Other usage patterns are most certainly conceivable, and hopefully these two will give you a
* useful starting point for determining what is the most appropriate usage for your needs.
*/
-public class ObserverList extends ArrayList
+public abstract class ObserverList
{
/**
* Instances of this interface are used to apply methods to all observers in a list.
@@ -102,17 +105,26 @@ public class ObserverList extends ArrayList
public boolean apply (T observer);
}
- /** A notification ordering policy indicating that the observers should be notified in the
- * order they were added and that the notification should be done on a snapshot of the
- * array. */
- public static final int SAFE_IN_ORDER_NOTIFY = 1;
+ /** Notification policies. */
+ public enum Policy {
+ /** A notification ordering policy indicating that the observers should be notified in the
+ * order they were added and that the notification should be done on a snapshot of the
+ * array. */
+ SAFE_IN_ORDER,
- /** A notification ordering policy wherein the observers are notified last to first so that
- * they can be removed during the notification process and new observers added will not
- * inadvertently be notified as well, but no copy of the observer list need be made. This will
- * not work if observers are added or removed from arbitrary positions in the list during a
- * notification call. */
- public static final int FAST_UNSAFE_NOTIFY = 2;
+ /** A notification ordering policy wherein the observers are notified last to first so that
+ * they can be removed during the notification process and new observers added will not
+ * inadvertently be notified as well, but no copy of the observer list need be made. This
+ * will not work if observers are added or removed from arbitrary positions in the list
+ * during a notification call. */
+ FAST_UNSAFE;
+ };
+
+ /** @deprecated Use {@link Policy.SAFE_IN_ORDER}. */
+ @Deprecated public static final Policy SAFE_IN_ORDER_NOTIFY = Policy.SAFE_IN_ORDER;
+
+ /** @deprecated Use {@link Policy.FAST_UNSAFE}. */
+ @Deprecated public static final Policy FAST_UNSAFE_NOTIFY = Policy.FAST_UNSAFE;
/**
* A convenience method for creating an observer list that avoids duplicating the type
@@ -120,7 +132,7 @@ public class ObserverList extends ArrayList
*/
public static ObserverList newSafeInOrder ()
{
- return new ObserverList(SAFE_IN_ORDER_NOTIFY);
+ return newList(Policy.SAFE_IN_ORDER, false);
}
/**
@@ -129,166 +141,146 @@ public class ObserverList extends ArrayList
*/
public static ObserverList newFastUnsafe ()
{
- return new ObserverList(FAST_UNSAFE_NOTIFY);
+ return newList(Policy.FAST_UNSAFE, false);
}
/**
* A convenience method for creating an observer list that avoids duplicating the type
* parameter on the right hand side.
*/
- public static ObserverList newList (int notifyPolicy, boolean allowDups)
+ public static ObserverList newList (Policy notifyPolicy, boolean allowDups)
{
- return new ObserverList(notifyPolicy, allowDups);
+ return new Impl(notifyPolicy, allowDups);
}
/**
- * Creates an empty observer list with the supplied notification policy and that only allows
- * observers to observe the list once.
- *
- * @param notifyPolicy Either {@link #SAFE_IN_ORDER_NOTIFY} or {@link #FAST_UNSAFE_NOTIFY}.
+ * Adds an observer at the specified index.
+ * @return true if the observer was added, false if it was already in the list.
*/
- public ObserverList (int notifyPolicy)
- {
- this(notifyPolicy, false);
- }
+ public abstract boolean add (int index, T element);
/**
- * Creates an empty observer list with the supplied notification and duplicate observer policy.
- *
- * @param notifyPolicy Either {@link #SAFE_IN_ORDER_NOTIFY} or {@link #FAST_UNSAFE_NOTIFY}.
- * @param allowDups whether to allow observers to be added to the list more than once.
+ * Adds an observer to the end of the list.
+ * @return true if the observer was added, false if it was already in the list.
*/
- public ObserverList (int notifyPolicy, boolean allowDups)
- {
- // make sure the policy is valid
- if (notifyPolicy != SAFE_IN_ORDER_NOTIFY && notifyPolicy != FAST_UNSAFE_NOTIFY) {
- throw new RuntimeException("Invalid notification policy [policy=" + notifyPolicy + "]");
- }
+ public abstract boolean add (T element);
- _policy = notifyPolicy ;
- _allowDups = allowDups;
- }
-
- @Override
- public void add (int index, T element)
- {
- if (element == null) {
- throw new NullPointerException("Null observers not allowed.");
- }
- if (!isDuplicate(element)) {
- super.add(index, element);
- }
- }
-
- @Override
- public boolean add (T element)
- {
- if (element == null) {
- throw new NullPointerException("Null observers not allowed.");
- }
- return isDuplicate(element) ? false : super.add(element);
- }
-
- @Override
- public boolean addAll (Collection extends T> c)
- {
- throw new UnsupportedOperationException(
- "Observers may only be added via ObserverList.add(Object).");
- }
-
- @Override
- public boolean addAll (int index, Collection extends T> c)
- {
- throw new UnsupportedOperationException(
- "Observers may only be added via ObserverList.add(Object).");
- }
-
- @Override
- public int indexOf (Object element)
- {
- // indexOf and lastIndexOf are implemented using reference equality so that contains() also
- // uses reference equality
- for (int ii = 0, nn = size(); ii < nn; ii++) {
- if (element == get(ii)) {
- return ii;
- }
- }
- return -1;
- }
-
- @Override
- public int lastIndexOf (Object element)
- {
- // indexOf and lastIndexOf are implemented using reference equality so that contains() also
- // uses reference equality
- for (int ii = size() - 1; ii >= 0; ii--) {
- if (element == get(ii)) {
- return ii;
- }
- }
- return -1;
- }
-
- @Override
- public boolean remove (Object element)
- {
- int dex = indexOf(element);
- if (dex == -1) {
- return false; // not found
- }
- remove(dex);
- return true;
- }
+ /**
+ * Removes the specified observer from the list.
+ * @return true if the observer was located and removed, false if it does not exist.
+ */
+ public abstract boolean remove (T element);
/**
* Applies the supplied observer operation to all observers in the list in a manner conforming
* to the notification ordering policy specified at construct time.
*/
- @SuppressWarnings("unchecked")
- public void apply (ObserverOp obop)
- {
- int ocount = size();
- if (ocount == 0) {
- return;
- }
- if (_policy == SAFE_IN_ORDER_NOTIFY) {
- // if we have to notify our observers in order, we need to create a snapshot of the
- // observer array at the time we start the notification to ensure that modifications to
- // the array during notification don't hose us
- if (_snap == null || _snap.length < ocount || _snap.length > (ocount << 3)) {
- _snap = (T[])new Object[ocount];
- }
- toArray(_snap);
- for (int ii = 0; ii < ocount; ii++) {
- if (!checkedApply(obop, _snap[ii])) {
- remove(_snap[ii]);
- }
- }
- // clear out the snapshot so its contents can be gc'd
- Arrays.fill(_snap, null);
-
- } else if (_policy == FAST_UNSAFE_NOTIFY) {
- for (int ii = ocount-1; ii >= 0; ii--) {
- if (!checkedApply(obop, get(ii))) {
- remove(ii);
- }
- }
- }
- }
+ public abstract void apply (ObserverOp obop);
/**
- * Returns true and issues a warning if this list does not allow duplicates and the supplied
- * observer is already in the list. Returns false if the supplied observer is not a duplicate.
+ * Returns the number of observers in this list.
*/
- protected boolean isDuplicate (T obs)
- {
- // make sure we're not violating the list constraints
- if (!_allowDups && contains(obs)) {
- log.warning("Observer attempted to observe list it's already observing!", "obs", obs,
- new Exception());
+ public abstract int size ();
+
+ /**
+ * Clears all observers from the list.
+ */
+ public abstract void clear ();
+
+ protected static class Impl extends ObserverList {
+ protected Impl (Policy notifyPolicy, boolean allowDups) {
+ _policy = notifyPolicy;
+ _allowDups = allowDups;
+ _list = (_policy == Policy.SAFE_IN_ORDER) ?
+ new CopyOnWriteArrayList() : new ArrayList();
+ }
+
+ @Override public boolean add (int index, T element) {
+ if (element == null) throw new NullPointerException("Null observers not allowed.");
+ if (isDuplicate(element)) return false;
+ _list.add(index, element);
return true;
}
- return false;
+
+ @Override public boolean add (T element) {
+ if (element == null) throw new NullPointerException("Null observers not allowed.");
+ if (isDuplicate(element)) return false;
+ _list.add(element);
+ return true;
+ }
+
+ @Override public boolean remove (T element) {
+ int idx = indexOf(element);
+ if (idx < 0) return false;
+ _list.remove(idx);
+ return true;
+ }
+
+ @Override public void apply (ObserverOp obop) {
+ switch (_policy) {
+ case SAFE_IN_ORDER:
+ // our copy on write array list will prevent us from getting hosed if modifications
+ // take place during iteration
+ Iterator iter = _list.iterator();
+ for (int ii = 0; iter.hasNext(); ii++) {
+ T elem = iter.next();
+ if (!checkedApply(obop, elem)) {
+ // can't remove via COWArrayList iterator, and to be totally safe (because
+ // we don't know if any additions or removals have taken place while we
+ // were iterating), we have to scan the whole list to locate this element
+ // so that we can remove it
+ remove(elem);
+ }
+ }
+ break;
+
+ case FAST_UNSAFE:
+ for (int ii = _list.size()-1; ii >= 0; ii--) {
+ if (!checkedApply(obop, _list.get(ii))) {
+ _list.remove(ii);
+ }
+ }
+ break;
+ }
+ }
+
+ @Override public int size () {
+ return _list.size();
+ }
+
+ @Override public void clear () {
+ _list.clear();
+ }
+
+ /** Used to determine whether an element is in the list. */
+ protected int indexOf (T element) {
+ for (int ii = 0, ll = _list.size(); ii < ll; ii++) {
+ if (_list.get(ii) == element) return ii;
+ }
+ return -1;
+ }
+
+ /** Returns true and issues a warning if this list does not allow duplicates and the
+ * supplied observer is already in the list. Returns false if the supplied observer is not
+ * a duplicate. */
+ protected boolean isDuplicate (T obs) {
+ // make sure we're not violating the list constraints
+ if (!_allowDups && (indexOf(obs) >= 0)) {
+ log.warning("Observer attempted to observe list it's already observing!", "obs", obs,
+ new Exception());
+ return true;
+ }
+ return false;
+ }
+
+ /** The notification policy. */
+ protected Policy _policy;
+
+ /** Whether to allow observers to observe more than once simultaneously. */
+ protected boolean _allowDups;
+
+ /** Our list of observers. */
+ protected List _list;
}
/**
@@ -305,14 +297,4 @@ public class ObserverList extends ArrayList
return true;
}
}
-
- /** The notification policy. */
- protected int _policy;
-
- /** Whether to allow observers to observe more than once simultaneously. */
- protected boolean _allowDups;
-
- /** Used to avoid creating a new snapshot array every time we notify our observers if the size
- * has not changed. */
- protected T[] _snap;
}
diff --git a/src/main/java/com/samskivert/util/ResultListenerList.java b/src/main/java/com/samskivert/util/ResultListenerList.java
index 78c797d8..a5a38b14 100644
--- a/src/main/java/com/samskivert/util/ResultListenerList.java
+++ b/src/main/java/com/samskivert/util/ResultListenerList.java
@@ -23,23 +23,23 @@ package com.samskivert.util;
/**
* Multiplexes ResultListener responses to multiple ResultListeners.
*/
-public class ResultListenerList extends ObserverList>
+public class ResultListenerList extends ObserverList.Impl>
implements ResultListener
{
/**
- * Create a ResultListenerList with the FAST_UNSAFE_NOTIFY policy.
+ * Create a ResultListenerList with the FAST_UNSAFE notification policy.
*/
public ResultListenerList ()
{
- super(FAST_UNSAFE_NOTIFY);
+ super(Policy.FAST_UNSAFE, false);
}
/**
* Create a ResultListenerList with your own notifyPolicy.
*/
- public ResultListenerList (int notifyPolicy)
+ public ResultListenerList (Policy notifyPolicy)
{
- super(notifyPolicy);
+ super(notifyPolicy, false);
}
/**
diff --git a/src/main/java/com/samskivert/util/WeakObserverList.java b/src/main/java/com/samskivert/util/WeakObserverList.java
index 27acc310..74143a29 100644
--- a/src/main/java/com/samskivert/util/WeakObserverList.java
+++ b/src/main/java/com/samskivert/util/WeakObserverList.java
@@ -30,7 +30,7 @@ import com.samskivert.util.ObserverList.ObserverOp;
* An {@link ObserverList} equivalent that does not prevent added observers from being
* garbage-collected.
*/
-public class WeakObserverList extends AbstractList
+public class WeakObserverList extends ObserverList
{
/**
* A convenience method for creating an observer list that avoids duplicating the type
@@ -38,7 +38,7 @@ public class WeakObserverList extends AbstractList
*/
public static WeakObserverList newSafeInOrder ()
{
- return new WeakObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY);
+ return newList(Policy.SAFE_IN_ORDER, false);
}
/**
@@ -47,111 +47,47 @@ public class WeakObserverList extends AbstractList
*/
public static WeakObserverList newFastUnsafe ()
{
- return new WeakObserverList(ObserverList.FAST_UNSAFE_NOTIFY);
+ return newList(Policy.FAST_UNSAFE, false);
}
/**
* A convenience method for creating an observer list that avoids duplicating the type
* parameter on the right hand side.
*/
- public static WeakObserverList newList (int notifyPolicy, boolean allowDups)
+ public static WeakObserverList newList (Policy notifyPolicy, boolean allowDups)
{
return new WeakObserverList(notifyPolicy, allowDups);
}
- /**
- * Creates an empty observer list with the supplied notification
- * policy and that only allows observers to observe the list once.
- *
- * @param notifyPolicy Either {@link ObserverList#SAFE_IN_ORDER_NOTIFY} or {@link
- * ObserverList#FAST_UNSAFE_NOTIFY}.
- */
- public WeakObserverList (int notifyPolicy)
+ @Override public boolean add (int index, T element)
{
- this(notifyPolicy, false);
+ return _delegate.add(index, new WeakReference(element));
}
- /**
- * Creates an empty observer list with the supplied notification and
- * duplicate observer policy.
- *
- * @param notifyPolicy Either {@link ObserverList#SAFE_IN_ORDER_NOTIFY} or {@link
- * ObserverList#FAST_UNSAFE_NOTIFY}.
- * @param allowDups whether to allow observers to be added to the list
- * more than once.
- */
- public WeakObserverList (int notifyPolicy, boolean allowDups)
+ @Override public boolean add (T element)
{
- _wrappedList = new WrappedList(notifyPolicy, allowDups);
+ return _delegate.add(new WeakReference(element));
}
- @Override // documentation inherited
- public int size ()
+ @Override public boolean remove (T element)
{
- return _wrappedList.size();
+ return _delegate.remove(new WeakReference(element));
}
- @Override // documentation inherited
- public boolean contains (Object element)
- {
- @SuppressWarnings("unchecked") T value = (T)element;
- return _wrappedList.contains(new WeakReference(value));
- }
-
- @Override // documentation inherited
- public boolean remove (Object element)
- {
- @SuppressWarnings("unchecked") T value = (T)element;
- return _wrappedList.remove(new WeakReference(value));
- }
-
- @Override // documentation inherited
- public int indexOf (Object element)
- {
- @SuppressWarnings("unchecked") T value = (T)element;
- return _wrappedList.indexOf(new WeakReference(value));
- }
-
- @Override // documentation inherited
- public int lastIndexOf (Object element)
- {
- @SuppressWarnings("unchecked") T value = (T)element;
- return _wrappedList.lastIndexOf(new WeakReference(value));
- }
-
- @Override // documentation inherited
- public T get (int index)
- {
- return _wrappedList.get(index).get();
- }
-
- @Override // documentation inherited
- public T set (int index, T element)
- {
- return _wrappedList.set(index, new WeakReference(element)).get();
- }
-
- @Override // documentation inherited
- public void add (int index, T element)
- {
- _wrappedList.add(index, new WeakReference(element));
- }
-
- @Override // documentation inherited
- public T remove (int index)
- {
- return _wrappedList.remove(index).get();
- }
-
- /**
- * Applies the supplied observer operation to all observers in the
- * list in a manner conforming to the notification ordering policy
- * specified at construct time.
- */
- public void apply (ObserverOp obop)
+ @Override public void apply (ObserverOp obop)
{
_derefOp.init(obop);
- _wrappedList.apply(_derefOp);
+ _delegate.apply(_derefOp);
+ }
+
+ @Override public int size ()
+ {
+ return _delegate.size();
+ }
+
+ @Override public void clear ()
+ {
+ _delegate.clear();
}
/**
@@ -159,30 +95,31 @@ public class WeakObserverList extends AbstractList
*/
public void prune ()
{
- for (int ii = _wrappedList.size() - 1; ii >= 0; ii--) {
- if (_wrappedList.get(ii).get() == null) {
- _wrappedList.remove(ii);
+ // applying an op prunes collected observers, so just apply a NOOP op
+ apply(new ObserverOp() {
+ public boolean apply (T obs) {
+ return true;
}
- }
+ });
+ }
+
+ protected WeakObserverList (Policy notifyPolicy, boolean allowDups)
+ {
+ _delegate = new WrappedList(notifyPolicy, allowDups);
}
/**
* An operation that resolves a reference and applies a wrapped op.
*/
- protected static class DerefOp
- implements ObserverOp>
+ protected static class DerefOp implements ObserverOp>
{
- /**
- * (Re)initializes this op with a reference to the wrapped op.
- */
- public void init (ObserverOp op)
- {
+ /** (Re)initializes this op with a reference to the wrapped op. */
+ public void init (ObserverOp op) {
_op = op;
}
// documentation inherited from interface ObserverOp
- public boolean apply (WeakReference ref)
- {
+ public boolean apply (WeakReference ref) {
T observer = ref.get();
return observer != null && _op.apply(observer);
}
@@ -194,37 +131,23 @@ public class WeakObserverList extends AbstractList
/**
* ObserverList extension that dereferences elements when searching for a value.
*/
- protected static class WrappedList extends ObserverList>
+ protected static class WrappedList extends ObserverList.Impl>
{
- public WrappedList (int notifyPolicy, boolean allowDups) {
+ public WrappedList (Policy notifyPolicy, boolean allowDups) {
super(notifyPolicy, allowDups);
}
- @Override public int indexOf (Object element) {
- @SuppressWarnings("unchecked") WeakReference ref = (WeakReference)element;
+ @Override protected int indexOf (WeakReference ref) {
T value = ref.get();
- for (int ii = 0, nn = size(); ii < nn; ii++) {
- if (value == get(ii).get()) {
- return ii;
- }
- }
- return -1;
- }
-
- @Override public int lastIndexOf (Object element) {
- @SuppressWarnings("unchecked") WeakReference ref = (WeakReference)element;
- T value = ref.get();
- for (int ii = size() - 1; ii >= 0; ii--) {
- if (value == get(ii).get()) {
- return ii;
- }
+ for (int ii = 0, ll = _list.size(); ii < ll; ii++) {
+ if (_list.get(ii).get() == value) return ii;
}
return -1;
}
}
- /** The wrapped list. */
- protected WrappedList _wrappedList;
+ /** A delegate list that contains weak reference wrapped elements. */
+ protected WrappedList _delegate;
/** The wrapper op. */
protected DerefOp _derefOp = new DerefOp();