You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2014/07/11 22:15:08 UTC
svn commit: r1609789 - in /commons/proper/configuration/trunk/src:
main/java/org/apache/commons/configuration/event/EventListenerList.java
test/java/org/apache/commons/configuration/event/TestEventListenerList.java
Author: oheger
Date: Fri Jul 11 20:15:08 2014
New Revision: 1609789
URL: http://svn.apache.org/r1609789
Log:
Added methods to EventListenerList for querying registered event listeners.
This is mainly useful in unit tests, but also supports advanced iteration over
event listeners.
Modified:
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/event/EventListenerList.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/event/TestEventListenerList.java
Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/event/EventListenerList.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/event/EventListenerList.java?rev=1609789&r1=1609788&r2=1609789&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/event/EventListenerList.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/event/EventListenerList.java Fri Jul 11 20:15:08 2014
@@ -17,7 +17,9 @@
package org.apache.commons.configuration.event;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -147,34 +149,65 @@ public class EventListenerList
throw new IllegalArgumentException(
"Event to be fired must not be null!");
}
- Set<EventType<?>> matchingTypes =
- fetchSuperEventTypes(event.getEventType());
- for (EventListenerRegistrationData<?> listenerData : listeners)
+ for (EventListenerIterator<? extends Event> iterator =
+ getEventListenerIterator(event.getEventType()); iterator
+ .hasNext();)
{
- if (matchingTypes.contains(listenerData.getEventType()))
+ iterator.invokeNextListenerUnchecked(event);
+ }
+ }
+
+ /**
+ * Returns an {@code Iterable} allowing access to all event listeners stored
+ * in this list which are compatible with the specified event type.
+ *
+ * @param eventType the event type object
+ * @param <T> the event type
+ * @return an {@code Iterable} with the selected event listeners
+ */
+ public <T extends Event> Iterable<EventListener<? super T>> getEventListeners(
+ final EventType<T> eventType)
+ {
+ return new Iterable<EventListener<? super T>>()
+ {
+ @Override
+ public Iterator<EventListener<? super T>> iterator()
{
- callListener(listenerData, event);
+ return getEventListenerIterator(eventType);
}
- }
+ };
+ }
+
+ /**
+ * Returns a specialized iterator for obtaining all event listeners stored
+ * in this list which are compatible with the specified event type.
+ *
+ * @param eventType the event type object
+ * @param <T> the event type
+ * @return an {@code Iterator} with the selected event listeners
+ */
+ public <T extends Event> EventListenerIterator<T> getEventListenerIterator(
+ EventType<T> eventType)
+ {
+ return new EventListenerIterator<T>(listeners.iterator(), eventType);
}
/**
- * Helper method for calling an event listener from a listener registration
- * data. We have to operate on raw types to make this code compile. However,
- * this is safe because of the way the listeners have been registered and
- * associated with event types - so it is ensured that the event is
- * compatible with the listener.
+ * Helper method for calling an event listener with an event. We have to
+ * operate on raw types to make this code compile. However, this is safe
+ * because of the way the listeners have been registered and associated with
+ * event types - so it is ensured that the event is compatible with the
+ * listener.
*
- * @param listenerData the event listener data
+ * @param listener the event listener to be called
* @param event the event to be fired
*/
@SuppressWarnings("unchecked, rawtypes")
- private static void callListener(
- EventListenerRegistrationData<?> listenerData, Event event)
+ private static void callListener(EventListener<?> listener, Event event)
{
- EventListener listener = listenerData.getListener();
- listener.onEvent(event);
+ EventListener rowListener = listener;
+ rowListener.onEvent(event);
}
/**
@@ -196,4 +229,145 @@ public class EventListenerList
}
return types;
}
+
+ /**
+ * A special {@code Iterator} implementation used by the
+ * {@code getEventListenerIterator()} method. This iterator returns only
+ * listeners compatible with a specified event type. It has a convenience
+ * method for invoking the current listener in the iteration with an event.
+ *
+ * @param <T> the event type
+ */
+ public static class EventListenerIterator<T extends Event> implements
+ Iterator<EventListener<? super T>>
+ {
+ /** The underlying iterator. */
+ private final Iterator<EventListenerRegistrationData<?>> underlyingIterator;
+
+ /** The base event type. */
+ private final EventType<T> baseEventType;
+
+ /** The set with accepted event types. */
+ private final Set<EventType<?>> acceptedTypes;
+
+ /** The next element in the iteration. */
+ private EventListener<? super T> nextElement;
+
+ private EventListenerIterator(
+ Iterator<EventListenerRegistrationData<?>> it, EventType<T> base)
+ {
+ underlyingIterator = it;
+ baseEventType = base;
+ acceptedTypes = fetchSuperEventTypes(base);
+ initNextElement();
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return nextElement != null;
+ }
+
+ @Override
+ public EventListener<? super T> next()
+ {
+ if (nextElement == null)
+ {
+ throw new NoSuchElementException("No more event listeners!");
+ }
+
+ EventListener<? super T> result = nextElement;
+ initNextElement();
+ return result;
+ }
+
+ /**
+ * Obtains the next event listener in this iteration and invokes it with
+ * the given event object.
+ *
+ * @param event the event object
+ * @throws NoSuchElementException if iteration is at its end
+ */
+ public void invokeNext(Event event)
+ {
+ validateEvent(event);
+ invokeNextListenerUnchecked(event);
+ }
+
+ /**
+ * {@inheritDoc} This implementation always throws an exception.
+ * Removing elements is not supported.
+ */
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException(
+ "Removing elements is not supported!");
+ }
+
+ /**
+ * Determines the next element in the iteration.
+ */
+ private void initNextElement()
+ {
+ nextElement = null;
+ while (underlyingIterator.hasNext() && nextElement == null)
+ {
+ EventListenerRegistrationData<?> regData =
+ underlyingIterator.next();
+ if (acceptedTypes.contains(regData.getEventType()))
+ {
+ nextElement = castListener(regData);
+ }
+ }
+ }
+
+ /**
+ * Checks whether the specified event can be passed to an event listener
+ * in this iteration. This check is done via the hierarchy of event
+ * types.
+ *
+ * @param event the event object
+ * @throws IllegalArgumentException if the event is invalid
+ */
+ private void validateEvent(Event event)
+ {
+ if (event == null
+ || !fetchSuperEventTypes(event.getEventType()).contains(
+ baseEventType))
+ {
+ throw new IllegalArgumentException(
+ "Event incompatible with listener iteration: " + event);
+ }
+ }
+
+ /**
+ * Invokes the next event listener in the iteration without doing a
+ * validity check on the event. This method is called internally to
+ * avoid duplicate event checks.
+ *
+ * @param event the event object
+ */
+ private void invokeNextListenerUnchecked(Event event)
+ {
+ EventListener<? super T> listener = next();
+ callListener(listener, event);
+ }
+
+ /**
+ * Extracts the listener from the given data object and performs a cast
+ * to the target type. This is safe because it has been checked before
+ * that the type is compatible.
+ *
+ * @param regData the data object
+ * @return the extracted listener
+ */
+ @SuppressWarnings("unchecked, rawtypes")
+ private EventListener<? super T> castListener(
+ EventListenerRegistrationData<?> regData)
+ {
+ EventListener listener = regData.getListener();
+ return listener;
+ }
+ }
}
Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/event/TestEventListenerList.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/event/TestEventListenerList.java?rev=1609789&r1=1609788&r2=1609789&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/event/TestEventListenerList.java (original)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/event/TestEventListenerList.java Fri Jul 11 20:15:08 2014
@@ -22,6 +22,12 @@ import static org.junit.Assert.assertNot
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NoSuchElementException;
+
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -272,6 +278,156 @@ public class TestEventListenerList
}
/**
+ * Helper method for collecting the event listeners in the given iterable.
+ *
+ * @param iterable the iterable
+ * @return a list with the content of the iterable
+ */
+ private static <T extends Event> List<EventListener<? super T>> fetchListeners(
+ Iterable<EventListener<? super T>> iterable)
+ {
+ List<EventListener<? super T>> listeners =
+ new LinkedList<EventListener<? super T>>();
+ for (EventListener<? super T> listener : iterable)
+ {
+ listeners.add(listener);
+ }
+ return listeners;
+ }
+
+ /**
+ * Helper method for checking whether a specific set of event listeners is
+ * returned by getEventListeners().
+ *
+ * @param eventType the event type
+ * @param expListeners the expected listeners
+ */
+ private void checkEventListenersForType(
+ EventType<? extends Event> eventType,
+ EventListener<?>... expListeners)
+ {
+ List<?> listeners = fetchListeners(list.getEventListeners(eventType));
+ assertEquals("Wrong number of listeners", expListeners.length,
+ listeners.size());
+ assertTrue("Wrong event listeners: " + listeners,
+ listeners.containsAll(Arrays.asList(expListeners)));
+ }
+
+ /**
+ * Tests whether event listeners for a null type can be queried.
+ */
+ @Test
+ public void testGetEventListenersNull()
+ {
+ assertTrue("Got listeners",
+ fetchListeners(list.getEventListeners(null)).isEmpty());
+ }
+
+ /**
+ * Tests that an empty result is correctly handled by getEventListeners().
+ */
+ @Test
+ public void testGetEventListenersNoMatch()
+ {
+ list.addEventListener(typeSub1, new ListenerTestImpl());
+ checkEventListenersForType(typeSub2);
+ }
+
+ /**
+ * Tests whether only matching event listeners are returned by
+ * getEventListeners().
+ */
+ @Test
+ public void testGetEventListenersMatchingType()
+ {
+ ListenerTestImpl listener1 = new ListenerTestImpl();
+ ListenerTestImpl listener2 = new ListenerTestImpl();
+ list.addEventListener(typeSub1, listener1);
+ list.addEventListener(typeSub2, listener2);
+ checkEventListenersForType(typeSub1, listener1);
+ }
+
+ /**
+ * Tests whether the base type is taken into account when querying for event
+ * listeners.
+ */
+ @Test
+ public void testGetEventListenersBaseType()
+ {
+ ListenerTestImpl listener1 = new ListenerTestImpl();
+ ListenerTestImpl listener2 = new ListenerTestImpl();
+ list.addEventListener(typeBase, listener1);
+ list.addEventListener(typeBase, listener2);
+ checkEventListenersForType(typeSub1, listener1, listener2);
+ }
+
+ /**
+ * Tests that the iterator returned by getEventListeners() throws an
+ * exception if the iteration goes beyond the last element.
+ */
+ @Test(expected = NoSuchElementException.class)
+ public void testGetEventListenersIteratorNextNoElement()
+ {
+ ListenerTestImpl listener1 = new ListenerTestImpl();
+ ListenerTestImpl listener2 = new ListenerTestImpl();
+ list.addEventListener(typeBase, listener1);
+ list.addEventListener(typeBase, listener2);
+ Iterator<EventListener<? super EventBase>> iterator =
+ list.getEventListeners(typeBase).iterator();
+ for (int i = 0; i < 3; i++)
+ {
+ iterator.next();
+ }
+ }
+
+ /**
+ * Tests that the iterator returned by getEventListeners() does not support
+ * remove() operations.
+ */
+ @Test(expected = UnsupportedOperationException.class)
+ public void testGetEventListenersIteratorRemove()
+ {
+ list.addEventListener(typeBase, new ListenerTestImpl());
+ Iterator<EventListener<? super EventBase>> iterator =
+ list.getEventListeners(typeBase).iterator();
+ assertTrue("Wrong result", iterator.hasNext());
+ iterator.remove();
+ }
+
+ /**
+ * Tests whether the event listener iterator validates the passed in event
+ * object.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testEventListenerIteratorWrongEvent()
+ {
+ EventListener<EventSub2> listener = new EventListener<EventSub2>()
+ {
+ @Override
+ public void onEvent(EventSub2 event)
+ {
+ }
+ };
+ list.addEventListener(typeSub2, listener);
+ EventListenerList.EventListenerIterator<EventSub2> iterator =
+ list.getEventListenerIterator(typeSub2);
+ assertTrue("No elements", iterator.hasNext());
+ iterator.invokeNext(new EventBase(this, typeBase, "Test"));
+ }
+
+ /**
+ * Tests that a null event is handled by the iterator.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testEventListenerIteratorNullEvent()
+ {
+ list.addEventListener(typeBase, new ListenerTestImpl());
+ EventListenerList.EventListenerIterator<EventBase> iterator =
+ list.getEventListenerIterator(typeBase);
+ iterator.invokeNext(null);
+ }
+
+ /**
* Test event class. For testing purposes, a small hierarchy of test event
* class is created. This way it can be checked whether event types are
* correctly evaluated and take the event hierarchy into account.