| author | Steffen Pingel | 2012-02-23 18:01:34 (EST) |
|---|---|---|
| committer | Steffen Pingel | 2012-02-23 18:20:26 (EST) |
| commit | a847c34384fe171d9dcadcbc34b50e58579a8889 (patch) (side-by-side diff) | |
| tree | 581cf4ebfa62ca1e891505e198dae5d27a398cc5 | |
| parent | bbe877f0a30365cb7662ef4bbbd8265befd96619 (diff) | |
| download | org.eclipse.mylyn.commons-a847c34384fe171d9dcadcbc34b50e58579a8889.zip org.eclipse.mylyn.commons-a847c34384fe171d9dcadcbc34b50e58579a8889.tar.gz org.eclipse.mylyn.commons-a847c34384fe171d9dcadcbc34b50e58579a8889.tar.bz2 | |
NEW - bug 372432: [api] provide a generic list for managing listeners
https://bugs.eclipse.org/bugs/show_bug.cgi?id=372432
Change-Id: I6671363039aae57826a1537ab5d6ef6479c1f959
3 files changed, 218 insertions, 0 deletions
diff --git a/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/CommonListenerList.java b/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/CommonListenerList.java new file mode 100644 index 0000000..2138160 --- a/dev/null +++ b/org.eclipse.mylyn.commons.core/src/org/eclipse/mylyn/commons/core/CommonListenerList.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2012 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.core; + +import java.util.Iterator; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; + +/** + * A list like class for managing listeners. It is safe to call this class from different threads concurrently. + * + * @since 3.7 + */ +public class CommonListenerList<T> implements Iterable<T> { + + /** + * Subclasses should extend to not + */ + public static abstract class Notifier<T> { + + /** + * Fires an event to <code>listener</code>. + * + * @param listener + * the listener to be notified + * @throws Exception + * indicates a unrecoverable problem with <code>listener</code> + */ + public abstract void run(T listener) throws Exception; + + } + + private final CopyOnWriteArrayList<T> listeners; + + private final String pluginId; + + /** + * Constructs an empty list. + * + * @param pluginId + * the ID of the bundle that is managing this instance + */ + public CommonListenerList(String pluginId) { + Assert.isNotNull(pluginId); + this.pluginId = pluginId; + this.listeners = new CopyOnWriteArrayList<T>(); + } + + /** + * Adds <code>listener</code> to the list of listeners. + */ + public void add(T listener) { + Assert.isNotNull(listener); + listeners.addIfAbsent(listener); + } + + /** + * Iterates over the list of listeners. + */ + public Iterator<T> iterator() { + return listeners.iterator(); + } + + /** + * Invokes <code>runnable</code> for each listener. If {@link Notifier#run(Object)} throws an exception the + * corresponding listener is removed from the list and a message is logged. + */ + public void notify(final Notifier<T> runnable) { + for (final T listener : listeners) { + SafeRunner.run(new ISafeRunnable() { + public void handleException(Throwable e) { + StatusHandler.log(new Status(IStatus.ERROR, pluginId, NLS.bind( + "Unexpected error notifying listener {0}", listener.getClass()), e)); //$NON-NLS-1$ + remove(listener); + } + + public void run() throws Exception { + runnable.run(listener); + } + }); + } + } + + /** + * Removes <code>listener</code> to the list of listeners. + */ + public void remove(T listener) { + listeners.remove(listener); + } + +} diff --git a/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/AllCommonsTests.java b/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/AllCommonsTests.java index 720f47a..1f4210d 100644 --- a/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/AllCommonsTests.java +++ b/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/AllCommonsTests.java @@ -15,6 +15,7 @@ import junit.framework.Test; import junit.framework.TestSuite; import org.eclipse.mylyn.commons.tests.core.AuthenticatedProxyTest; +import org.eclipse.mylyn.commons.tests.core.CommonListenerListTest; import org.eclipse.mylyn.commons.tests.core.CoreUtilTest; import org.eclipse.mylyn.commons.tests.core.ExtensionPointReaderTest; import org.eclipse.mylyn.commons.tests.net.NetUtilTest; @@ -40,6 +41,7 @@ public class AllCommonsTests { suite.addTestSuite(TimeoutInputStreamTest.class); suite.addTestSuite(BrowserUtilTest.class); suite.addTestSuite(ExtensionPointReaderTest.class); + suite.addTestSuite(CommonListenerListTest.class); return suite; } diff --git a/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/core/CommonListenerListTest.java b/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/core/CommonListenerListTest.java new file mode 100644 index 0000000..28704fb --- a/dev/null +++ b/org.eclipse.mylyn.commons.tests/src/org/eclipse/mylyn/commons/tests/core/CommonListenerListTest.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2012 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.tests.core; + +import java.util.Iterator; + +import junit.framework.TestCase; + +import org.eclipse.mylyn.commons.core.CommonListenerList; +import org.eclipse.mylyn.commons.core.CommonListenerList.Notifier; + +/** + * @author Steffen Pingel + */ +public class CommonListenerListTest extends TestCase { + + private class Listener { + + private boolean notified; + + } + + public void testAddRemove() { + final Listener addedListener = new Listener(); + CommonListenerList<Listener> list = new CommonListenerList<Listener>("a"); + + list.add(addedListener); + assertTrue(list.iterator().hasNext()); + assertSame(addedListener, list.iterator().next()); + + list.remove(addedListener); + assertFalse(list.iterator().hasNext()); + } + + public void testAddTwice() { + final Listener addedListener = new Listener(); + CommonListenerList<Listener> list = new CommonListenerList<Listener>("a"); + list.add(addedListener); + list.add(addedListener); + + Iterator<Listener> iterator = list.iterator(); + assertTrue(iterator.hasNext()); + iterator.next(); + assertFalse(iterator.hasNext()); + } + + public void testIterator() { + final Listener listener1 = new Listener(); + final Listener listener2 = new Listener(); + final Listener listener3 = new Listener(); + CommonListenerList<Listener> list = new CommonListenerList<Listener>("a"); + list.add(listener1); + list.add(listener2); + list.add(listener3); + list.add(listener1); + + Iterator<Listener> iterator = list.iterator(); + assertSame(listener1, iterator.next()); + assertSame(listener2, iterator.next()); + assertSame(listener3, iterator.next()); + } + + public void testNotify() { + final Listener addedListener = new Listener(); + CommonListenerList<Listener> list = new CommonListenerList<Listener>("a"); + list.add(addedListener); + + list.notify(new Notifier<Listener>() { + @Override + public void run(Listener listener) throws Exception { + assertSame(listener, addedListener); + addedListener.notified = true; + } + }); + assertTrue(addedListener.notified); + } + + public void testNotifyException() { + final Listener addedListener = new Listener(); + CommonListenerList<Listener> list = new CommonListenerList<Listener>("a"); + list.add(addedListener); + + list.notify(new Notifier<Listener>() { + @Override + public void run(Listener listener) throws Exception { + // should cause listener to get removed + throw new LinkageError(); + } + }); + assertFalse(list.iterator().hasNext()); + + list.notify(new Notifier<Listener>() { + @Override + public void run(Listener listener) throws Exception { + addedListener.notified = true; + } + }); + assertFalse(addedListener.notified); + } + +} |

