Widening and also NPE immediately if someone tries to add a null observer.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@2482 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
samskivert
2008-11-18 20:45:31 +00:00
parent 23f37550cd
commit 30d558785a
+58 -70
View File
@@ -29,15 +29,14 @@ import com.samskivert.util.StringUtil;
import static com.samskivert.Log.log; import static com.samskivert.Log.log;
/** /**
* Provides a simplified mechanism for maintaining a list of observers (or * Provides a simplified mechanism for maintaining a list of observers (or listeners or whatever
* listeners or whatever you like to call them) and notifying those * you like to call them) and notifying those observers when desired. Notification takes place
* observers when desired. Notification takes place through a {@link * through a {@link ObserverOp} which allows the list maintainer to call an arbitrary method on its
* ObserverOp} which allows the list maintainer to call an arbitrary * observers.
* method on its observers.
* *
* <p> A couple of different usage patterns will help to illuminate. The * <p> A couple of different usage patterns will help to illuminate. The first, where a new notify
* first, where a new notify operation is created every time the observers * operation is created every time the observers are notified. This wins on brevity but loses on
* are notified. This wins on brevity but loses on performance. * performance.
* *
* <pre> * <pre>
* ... * ...
@@ -53,8 +52,8 @@ import static com.samskivert.Log.log;
* ... * ...
* </pre> * </pre>
* *
* The second where a singleton instance of the operation class is * The second where a singleton instance of the operation class is maintained and is configured
* maintained and is configured each time notification is desired. * each time notification is desired.
* *
* <pre> * <pre>
* ... * ...
@@ -86,38 +85,35 @@ import static com.samskivert.Log.log;
* *
* Bear in mind that this latter case is not thread safe. * Bear in mind that this latter case is not thread safe.
* *
* <p> Other usage patterns are most certainly conceivable, and hopefully * <p> Other usage patterns are most certainly conceivable, and hopefully these two will give you a
* these two will give you a useful starting point for determining what is * useful starting point for determining what is the most appropriate usage for your needs.
* the most appropriate usage for your needs.
*/ */
public class ObserverList<T> extends ArrayList<T> public class ObserverList<T> extends ArrayList<T>
{ {
/** /**
* Instances of this interface are used to apply methods to all * Instances of this interface are used to apply methods to all observers in a list.
* observers in a list.
*/ */
public static interface ObserverOp<T> public static interface ObserverOp<T>
{ {
/** /**
* Called once for each observer in the list. * Called once for each observer in the list.
* *
* @return true if the observer should remain in the list, false * @return true if the observer should remain in the list, false if it should be removed in
* if it should be removed in response to this application. * response to this application.
*/ */
public boolean apply (T observer); public boolean apply (T observer);
} }
/** A notification ordering policy indicating that the observers /** A notification ordering policy indicating that the observers should be notified in the
* should be notified in the order they were added and that the * order they were added and that the notification should be done on a snapshot of the
* notification should be done on a snapshot of the array. */ * array. */
public static final int SAFE_IN_ORDER_NOTIFY = 1; public static final int SAFE_IN_ORDER_NOTIFY = 1;
/** A notification ordering policy wherein the observers are notified /** A notification ordering policy wherein the observers are notified last to first so that
* last to first so that they can be removed during the notification * they can be removed during the notification process and new observers added will not
* process and new observers added will not inadvertently be notified * inadvertently be notified as well, but no copy of the observer list need be made. This will
* 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
* not work if observers are added or removed from arbitrary positions * notification call. */
* in the list during a notification call. */
public static final int FAST_UNSAFE_NOTIFY = 2; public static final int FAST_UNSAFE_NOTIFY = 2;
/** /**
@@ -148,11 +144,10 @@ public class ObserverList<T> extends ArrayList<T>
} }
/** /**
* Creates an empty observer list with the supplied notification * Creates an empty observer list with the supplied notification policy and that only allows
* policy and that only allows observers to observe the list once. * observers to observe the list once.
* *
* @param notifyPolicy Either {@link #SAFE_IN_ORDER_NOTIFY} or {@link * @param notifyPolicy Either {@link #SAFE_IN_ORDER_NOTIFY} or {@link #FAST_UNSAFE_NOTIFY}.
* #FAST_UNSAFE_NOTIFY}.
*/ */
public ObserverList (int notifyPolicy) public ObserverList (int notifyPolicy)
{ {
@@ -160,21 +155,16 @@ public class ObserverList<T> extends ArrayList<T>
} }
/** /**
* Creates an empty observer list with the supplied notification and * Creates an empty observer list with the supplied notification and duplicate observer policy.
* duplicate observer policy.
* *
* @param notifyPolicy Either {@link #SAFE_IN_ORDER_NOTIFY} or {@link * @param notifyPolicy Either {@link #SAFE_IN_ORDER_NOTIFY} or {@link #FAST_UNSAFE_NOTIFY}.
* #FAST_UNSAFE_NOTIFY}. * @param allowDups whether to allow observers to be added to the list more than once.
* @param allowDups whether to allow observers to be added to the list
* more than once.
*/ */
public ObserverList (int notifyPolicy, boolean allowDups) public ObserverList (int notifyPolicy, boolean allowDups)
{ {
// make sure the policy is valid // make sure the policy is valid
if (notifyPolicy != SAFE_IN_ORDER_NOTIFY && if (notifyPolicy != SAFE_IN_ORDER_NOTIFY && notifyPolicy != FAST_UNSAFE_NOTIFY) {
notifyPolicy != FAST_UNSAFE_NOTIFY) { throw new RuntimeException("Invalid notification policy [policy=" + notifyPolicy + "]");
throw new RuntimeException("Invalid notification policy " +
"[policy=" + notifyPolicy + "]");
} }
_policy = notifyPolicy ; _policy = notifyPolicy ;
@@ -184,6 +174,9 @@ public class ObserverList<T> extends ArrayList<T>
@Override @Override
public void add (int index, T element) public void add (int index, T element)
{ {
if (element == null) {
throw new NullPointerException("Null observers not allowed.");
}
if (!isDuplicate(element)) { if (!isDuplicate(element)) {
super.add(index, element); super.add(index, element);
} }
@@ -192,26 +185,31 @@ public class ObserverList<T> extends ArrayList<T>
@Override @Override
public boolean add (T element) public boolean add (T element)
{ {
if (element == null) {
throw new NullPointerException("Null observers not allowed.");
}
return isDuplicate(element) ? false : super.add(element); return isDuplicate(element) ? false : super.add(element);
} }
@Override @Override
public boolean addAll (Collection<? extends T> c) public boolean addAll (Collection<? extends T> c)
{ {
throw new UnsupportedOperationException(UNSUPPORTED_ADD_MESSAGE); throw new UnsupportedOperationException(
"Observers may only be added via ObserverList.add(Object).");
} }
@Override @Override
public boolean addAll (int index, Collection<? extends T> c) public boolean addAll (int index, Collection<? extends T> c)
{ {
throw new UnsupportedOperationException(UNSUPPORTED_ADD_MESSAGE); throw new UnsupportedOperationException(
"Observers may only be added via ObserverList.add(Object).");
} }
@Override @Override
public int indexOf (Object element) public int indexOf (Object element)
{ {
// indexOf and lastIndexOf are implemented using reference equality // indexOf and lastIndexOf are implemented using reference equality so that contains() also
// so that contains() also uses reference equality. // uses reference equality
for (int ii = 0, nn = size(); ii < nn; ii++) { for (int ii = 0, nn = size(); ii < nn; ii++) {
if (element == get(ii)) { if (element == get(ii)) {
return ii; return ii;
@@ -223,8 +221,8 @@ public class ObserverList<T> extends ArrayList<T>
@Override @Override
public int lastIndexOf (Object element) public int lastIndexOf (Object element)
{ {
// indexOf and lastIndexOf are implemented using reference equality // indexOf and lastIndexOf are implemented using reference equality so that contains() also
// so that contains() also uses reference equality. // uses reference equality
for (int ii = size() - 1; ii >= 0; ii--) { for (int ii = size() - 1; ii >= 0; ii--) {
if (element == get(ii)) { if (element == get(ii)) {
return ii; return ii;
@@ -245,9 +243,8 @@ public class ObserverList<T> extends ArrayList<T>
} }
/** /**
* Applies the supplied observer operation to all observers in the * Applies the supplied observer operation to all observers in the list in a manner conforming
* list in a manner conforming to the notification ordering policy * to the notification ordering policy specified at construct time.
* specified at construct time.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void apply (ObserverOp<T> obop) public void apply (ObserverOp<T> obop)
@@ -257,12 +254,10 @@ public class ObserverList<T> extends ArrayList<T>
return; return;
} }
if (_policy == SAFE_IN_ORDER_NOTIFY) { if (_policy == SAFE_IN_ORDER_NOTIFY) {
// if we have to notify our observers in order, we need to // if we have to notify our observers in order, we need to create a snapshot of the
// create a snapshot of the observer array at the time we // observer array at the time we start the notification to ensure that modifications to
// start the notification to ensure that modifications to the // the array during notification don't hose us
// array during notification don't hose us if (_snap == null || _snap.length < ocount || _snap.length > (ocount << 3)) {
if (_snap == null || _snap.length < ocount ||
_snap.length > (ocount << 3)) {
_snap = (T[])new Object[ocount]; _snap = (T[])new Object[ocount];
} }
toArray(_snap); toArray(_snap);
@@ -284,16 +279,14 @@ public class ObserverList<T> extends ArrayList<T>
} }
/** /**
* Returns true and issues a warning if this list does not allow duplicates * Returns true and issues a warning if this list does not allow duplicates and the supplied
* and the supplied observer is already in the list. Returns false if the * observer is already in the list. Returns false if the supplied observer is not a duplicate.
* supplied observer is not a duplicate.
*/ */
protected boolean isDuplicate (T obs) protected boolean isDuplicate (T obs)
{ {
// make sure we're not violating the list constraints // make sure we're not violating the list constraints
if (!_allowDups && contains(obs)) { if (!_allowDups && contains(obs)) {
log.warning("Observer attempted to observe list it's already " + log.warning("Observer attempted to observe list it's already observing!", "obs", obs);
"observing! [obs=" + obs + "].");
Thread.dumpStack(); Thread.dumpStack();
return true; return true;
} }
@@ -301,16 +294,15 @@ public class ObserverList<T> extends ArrayList<T>
} }
/** /**
* Applies the operation to the observer, catching and logging any * Applies the operation to the observer, catching and logging any exceptions thrown in the
* exceptions thrown in the process. * process.
*/ */
protected static <T> boolean checkedApply (ObserverOp<T> obop, T obs) protected static <T> boolean checkedApply (ObserverOp<T> obop, T obs)
{ {
try { try {
return obop.apply(obs); return obop.apply(obs);
} catch (Throwable thrown) { } catch (Throwable thrown) {
log.warning("ObserverOp choked during notification [op=" + obop + log.warning("ObserverOp choked during notification", "op", obop, "obs", obs, thrown);
", obs=" + StringUtil.safeToString(obs) + "].", thrown);
// if they booched it, definitely don't remove them // if they booched it, definitely don't remove them
return true; return true;
} }
@@ -322,11 +314,7 @@ public class ObserverList<T> extends ArrayList<T>
/** Whether to allow observers to observe more than once simultaneously. */ /** Whether to allow observers to observe more than once simultaneously. */
protected boolean _allowDups; protected boolean _allowDups;
/** Used to avoid creating a new snapshot array every time we notify /** Used to avoid creating a new snapshot array every time we notify our observers if the size
* our observers if the size has not changed. */ * has not changed. */
protected T[] _snap; protected T[] _snap;
/** Message reported for unsupported <code>add()</code> variants. */
protected static final String UNSUPPORTED_ADD_MESSAGE =
"Observers may only be added via ObserverList.add(Object).";
} }