EventListenerList.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.configuration2.event;

  18. import java.util.Collections;
  19. import java.util.HashMap;
  20. import java.util.Iterator;
  21. import java.util.LinkedList;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.NoSuchElementException;
  25. import java.util.Set;
  26. import java.util.concurrent.CopyOnWriteArrayList;

  27. /**
  28.  * <p>
  29.  * A class for managing event listeners for an event source.
  30.  * </p>
  31.  * <p>
  32.  * This class allows registering an arbitrary number of event listeners for specific event types. Event types are
  33.  * specified using the {@link EventType} class. Due to the type parameters in method signatures, it is guaranteed that
  34.  * registered listeners are compatible with the event types they are interested in.
  35.  * </p>
  36.  * <p>
  37.  * There are also methods for firing events. Here all registered listeners are determined - based on the event type
  38.  * specified at registration time - which should receive the event to be fired. So basically, the event type at listener
  39.  * registration serves as a filter criterion. Because of the hierarchical nature of event types it can be determined in
  40.  * a fine-grained way which events are propagated to which listeners. It is also possible to register a listener
  41.  * multiple times for different event types.
  42.  * </p>
  43.  * <p>
  44.  * Implementation note: This class is thread-safe.
  45.  * </p>
  46.  *
  47.  * @since 2.0
  48.  */
  49. public class EventListenerList {
  50.     /**
  51.      * A special {@code Iterator} implementation used by the {@code getEventListenerIterator()} method. This iterator
  52.      * returns only listeners compatible with a specified event type. It has a convenience method for invoking the current
  53.      * listener in the iteration with an event.
  54.      *
  55.      * @param <T> the event type
  56.      */
  57.     public static final class EventListenerIterator<T extends Event> implements Iterator<EventListener<? super T>> {
  58.         /** The underlying iterator. */
  59.         private final Iterator<EventListenerRegistrationData<?>> underlyingIterator;

  60.         /** The base event type. */
  61.         private final EventType<T> baseEventType;

  62.         /** The set with accepted event types. */
  63.         private final Set<EventType<?>> acceptedTypes;

  64.         /** The next element in the iteration. */
  65.         private EventListener<? super T> nextElement;

  66.         private EventListenerIterator(final Iterator<EventListenerRegistrationData<?>> it, final EventType<T> base) {
  67.             underlyingIterator = it;
  68.             baseEventType = base;
  69.             acceptedTypes = EventType.fetchSuperEventTypes(base);
  70.             initNextElement();
  71.         }

  72.         /**
  73.          * Extracts the listener from the given data object and performs a cast to the target type. This is safe because it has
  74.          * been checked before that the type is compatible.
  75.          *
  76.          * @param regData the data object
  77.          * @return the extracted listener
  78.          */
  79.         @SuppressWarnings("unchecked")
  80.         private EventListener<? super T> castListener(final EventListenerRegistrationData<?> regData) {
  81.             @SuppressWarnings("rawtypes")
  82.             final EventListener listener = regData.getListener();
  83.             return listener;
  84.         }

  85.         @Override
  86.         public boolean hasNext() {
  87.             return nextElement != null;
  88.         }

  89.         /**
  90.          * Determines the next element in the iteration.
  91.          */
  92.         private void initNextElement() {
  93.             nextElement = null;
  94.             while (underlyingIterator.hasNext() && nextElement == null) {
  95.                 final EventListenerRegistrationData<?> regData = underlyingIterator.next();
  96.                 if (acceptedTypes.contains(regData.getEventType())) {
  97.                     nextElement = castListener(regData);
  98.                 }
  99.             }
  100.         }

  101.         /**
  102.          * Obtains the next event listener in this iteration and invokes it with the given event object.
  103.          *
  104.          * @param event the event object
  105.          * @throws NoSuchElementException if iteration is at its end
  106.          */
  107.         public void invokeNext(final Event event) {
  108.             validateEvent(event);
  109.             invokeNextListenerUnchecked(event);
  110.         }

  111.         /**
  112.          * Invokes the next event listener in the iteration without doing a validity check on the event. This method is called
  113.          * internally to avoid duplicate event checks.
  114.          *
  115.          * @param event the event object
  116.          */
  117.         private void invokeNextListenerUnchecked(final Event event) {
  118.             callListener(next(), event);
  119.         }

  120.         @Override
  121.         public EventListener<? super T> next() {
  122.             if (nextElement == null) {
  123.                 throw new NoSuchElementException("No more event listeners!");
  124.             }

  125.             final EventListener<? super T> result = nextElement;
  126.             initNextElement();
  127.             return result;
  128.         }

  129.         /**
  130.          * {@inheritDoc} This implementation always throws an exception. Removing elements is not supported.
  131.          */
  132.         @Override
  133.         public void remove() {
  134.             throw new UnsupportedOperationException("Removing elements is not supported!");
  135.         }

  136.         /**
  137.          * Checks whether the specified event can be passed to an event listener in this iteration. This check is done via the
  138.          * hierarchy of event types.
  139.          *
  140.          * @param event the event object
  141.          * @throws IllegalArgumentException if the event is invalid
  142.          */
  143.         private void validateEvent(final Event event) {
  144.             if (event == null || !EventType.fetchSuperEventTypes(event.getEventType()).contains(baseEventType)) {
  145.                 throw new IllegalArgumentException("Event incompatible with listener iteration: " + event);
  146.             }
  147.         }
  148.     }

  149.     /**
  150.      * Helper method for calling an event listener with an event. We have to operate on raw types to make this code compile.
  151.      * However, this is safe because of the way the listeners have been registered and associated with event types - so it
  152.      * is ensured that the event is compatible with the listener.
  153.      *
  154.      * @param listener the event listener to be called
  155.      * @param event the event to be fired
  156.      */
  157.     @SuppressWarnings("unchecked")
  158.     private static void callListener(final EventListener<?> listener, final Event event) {
  159.         @SuppressWarnings("rawtypes")
  160.         final EventListener rowListener = listener;
  161.         rowListener.onEvent(event);
  162.     }

  163.     /** A list with the listeners added to this object. */
  164.     private final List<EventListenerRegistrationData<?>> listeners;

  165.     /**
  166.      * Creates a new instance of {@code EventListenerList}.
  167.      */
  168.     public EventListenerList() {
  169.         listeners = new CopyOnWriteArrayList<>();
  170.     }

  171.     /**
  172.      * Adds all event listener registrations stored in the specified {@code EventListenerList} to this list.
  173.      *
  174.      * @param c the list to be copied (must not be <strong>null</strong>)
  175.      * @throws IllegalArgumentException if the list to be copied is <strong>null</strong>
  176.      */
  177.     public void addAll(final EventListenerList c) {
  178.         if (c == null) {
  179.             throw new IllegalArgumentException("List to be copied must not be null!");
  180.         }
  181.         c.getRegistrations().forEach(this::addEventListener);
  182.     }

  183.     /**
  184.      * Adds the specified listener registration data object to the internal list of event listeners. This is an alternative
  185.      * registration method; the event type and the listener are passed as a single data object.
  186.      *
  187.      * @param regData the registration data object (must not be <strong>null</strong>)
  188.      * @param <T> the type of events processed by this listener
  189.      * @throws IllegalArgumentException if the registration data object is <strong>null</strong>
  190.      */
  191.     public <T extends Event> void addEventListener(final EventListenerRegistrationData<T> regData) {
  192.         if (regData == null) {
  193.             throw new IllegalArgumentException("EventListenerRegistrationData must not be null!");
  194.         }
  195.         listeners.add(regData);
  196.     }

  197.     /**
  198.      * Adds an event listener for the specified event type. This listener is notified about events of this type and all its
  199.      * sub types.
  200.      *
  201.      * @param type the event type (must not be <strong>null</strong>)
  202.      * @param listener the listener to be registered (must not be <strong>null</strong>)
  203.      * @param <T> the type of events processed by this listener
  204.      * @throws IllegalArgumentException if a required parameter is <strong>null</strong>
  205.      */
  206.     public <T extends Event> void addEventListener(final EventType<T> type, final EventListener<? super T> listener) {
  207.         listeners.add(new EventListenerRegistrationData<>(type, listener));
  208.     }

  209.     /**
  210.      * Removes all event listeners registered at this object.
  211.      */
  212.     public void clear() {
  213.         listeners.clear();
  214.     }

  215.     /**
  216.      * Fires an event to all registered listeners matching the event type.
  217.      *
  218.      * @param event the event to be fired (must not be <strong>null</strong>)
  219.      * @throws IllegalArgumentException if the event is <strong>null</strong>
  220.      */
  221.     public void fire(final Event event) {
  222.         if (event == null) {
  223.             throw new IllegalArgumentException("Event to be fired must not be null!");
  224.         }

  225.         for (final EventListenerIterator<? extends Event> iterator = getEventListenerIterator(event.getEventType()); iterator.hasNext();) {
  226.             iterator.invokeNextListenerUnchecked(event);
  227.         }
  228.     }

  229.     /**
  230.      * Gets a specialized iterator for obtaining all event listeners stored in this list which are compatible with the
  231.      * specified event type.
  232.      *
  233.      * @param eventType the event type object
  234.      * @param <T> the event type
  235.      * @return an {@code Iterator} with the selected event listeners
  236.      */
  237.     public <T extends Event> EventListenerIterator<T> getEventListenerIterator(final EventType<T> eventType) {
  238.         return new EventListenerIterator<>(listeners.iterator(), eventType);
  239.     }

  240.     /**
  241.      * Gets an {@code Iterable} allowing access to all event listeners stored in this list which are compatible with the
  242.      * specified event type.
  243.      *
  244.      * @param eventType the event type object
  245.      * @param <T> the event type
  246.      * @return an {@code Iterable} with the selected event listeners
  247.      */
  248.     public <T extends Event> Iterable<EventListener<? super T>> getEventListeners(final EventType<T> eventType) {
  249.         return () -> getEventListenerIterator(eventType);
  250.     }

  251.     /**
  252.      * Gets an (unmodifiable) list with registration information about all event listeners registered at this object.
  253.      *
  254.      * @return a list with event listener registration information
  255.      */
  256.     public List<EventListenerRegistrationData<?>> getRegistrations() {
  257.         return Collections.unmodifiableList(listeners);
  258.     }

  259.     /**
  260.      * Gets a list with {@code EventListenerRegistrationData} objects for all event listener registrations of the
  261.      * specified event type or an event type having this type as super type (directly or indirectly). Note that this is the
  262.      * opposite direction than querying event types for firing events: in this case event listener registrations are
  263.      * searched which are super event types from a given type. This method in contrast returns event listener registrations
  264.      * for listeners that extend a given super type.
  265.      *
  266.      * @param eventType the event type object
  267.      * @param <T> the event type
  268.      * @return a list with the matching event listener registration objects
  269.      */
  270.     public <T extends Event> List<EventListenerRegistrationData<? extends T>> getRegistrationsForSuperType(final EventType<T> eventType) {
  271.         final Map<EventType<?>, Set<EventType<?>>> superTypes = new HashMap<>();
  272.         final List<EventListenerRegistrationData<? extends T>> results = new LinkedList<>();

  273.         listeners.forEach(reg -> {
  274.             final Set<EventType<?>> base = superTypes.computeIfAbsent(reg.getEventType(), EventType::fetchSuperEventTypes);
  275.             if (base.contains(eventType)) {
  276.                 @SuppressWarnings("unchecked")
  277.                 final
  278.                 // This is safe because we just did a check
  279.                 EventListenerRegistrationData<? extends T> result = (EventListenerRegistrationData<? extends T>) reg;
  280.                 results.add(result);
  281.             }
  282.         });

  283.         return results;
  284.     }

  285.     /**
  286.      * Removes the event listener registration defined by the passed in data object. This is an alternative method for
  287.      * removing a listener which expects the event type and the listener in a single data object.
  288.      *
  289.      * @param regData the registration data object
  290.      * @param <T> the type of events processed by this listener
  291.      * @return a flag whether a listener registration was removed
  292.      * @see #removeEventListener(EventType, EventListener)
  293.      */
  294.     public <T extends Event> boolean removeEventListener(final EventListenerRegistrationData<T> regData) {
  295.         return listeners.remove(regData);
  296.     }

  297.     /**
  298.      * Removes the event listener registration for the given event type and listener. An event listener instance may be
  299.      * registered multiple times for different event types. Therefore, when removing a listener the event type of the
  300.      * registration in question has to be specified. The return value indicates whether a registration was removed. A value
  301.      * of <strong>false</strong> means that no such combination of event type and listener was found.
  302.      *
  303.      * @param eventType the event type
  304.      * @param listener the event listener to be removed
  305.      * @param <T> the type of events processed by this listener
  306.      * @return a flag whether a listener registration was removed
  307.      */
  308.     public <T extends Event> boolean removeEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
  309.         return !(listener == null || eventType == null) && removeEventListener(new EventListenerRegistrationData<>(eventType, listener));
  310.     }
  311. }