// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package org.eclipse.che.ide.util; import java.util.ArrayList; import java.util.List; /** * Lightweight manager for listeners that's designed to reduce boilerplate in * classes that have listeners. * <p/> * The client stores a final member for this class, and exposes a {@code * getListenerRegistrar()} with return type {@link ListenerRegistrar}. To * dispatch, the client calls {@link #dispatch(Dispatcher)} with a custom * dispatcher. (If the dispatches are frequent, consider keeping a single * dispatcher instance whose state you set prior to passing it to the dispatch * method.) * * @param <L> * the type of the listener */ public class ListenerManager<L> implements ListenerRegistrar<L> { /** * Dispatches to a listener. * * @param <L> * the type of the listener */ public interface Dispatcher<L> { void dispatch(L listener); } /** * Listener that is notified when clients are added or removed from this * manager. */ public interface RegistrationListener<L> { void onListenerAdded(L listener); void onListenerRemoved(L listener); } public static <L> ListenerManager<L> create() { return new ListenerManager<L>(null); } public static <L> ListenerManager<L> create(RegistrationListener<L> registrationListener) { return new ListenerManager<L>(registrationListener); } private boolean isDispatching; private final List<L> listeners; /** Listeners that were added during a dispatch */ private final List<L> queuedListenerAdditions; /** Listeners that were removed during a dispatch */ private final List<L> queuedListenerRemovals; private final RegistrationListener<L> registrationListener; private ListenerManager(RegistrationListener<L> registrationListener) { this.listeners = new ArrayList<>(); this.queuedListenerAdditions = new ArrayList<>(); this.queuedListenerRemovals = new ArrayList<>(); this.registrationListener = registrationListener; } /** Adds a new listener to this event. */ @Override public Remover add(final L listener) { if (!isDispatching) { addListenerImpl(listener); } else { if (!queuedListenerRemovals.remove(listener)) { queuedListenerAdditions.add(listener); } } return new Remover() { @Override public void remove() { ListenerManager.this.remove(listener); } }; } /** Dispatches this event to all listeners. */ public void dispatch(final Dispatcher<L> dispatcher) { isDispatching = true; try { for (int i = 0, n = listeners.size(); i < n; i++) { dispatcher.dispatch(listeners.get(i)); } } finally { isDispatching = false; addQueuedListeners(); removeQueuedListeners(); } } /** * Removes a listener from this manager. * <p/> * It is strongly preferred that you use the {@link ListenerRegistrar.Remover} * returned by {@link #add(Object)} instead of calling this method directly. */ @Override public void remove(L listener) { if (!isDispatching) { removeListenerImpl(listener); } else { if (!queuedListenerAdditions.remove(listener)) { queuedListenerRemovals.add(listener); } } } /** * Returns the number of listeners registered on this manager. This does not * include those listeners that are queued to be added and it does include * those listeners that are queued to be removed. */ public int getCount() { return listeners.size(); } /** Returns true if the listener manager is currently dispatching to listeners. */ public boolean isDispatching() { return isDispatching; } private void addQueuedListeners() { for (int i = 0, n = queuedListenerAdditions.size(); i < n; i++) { addListenerImpl(queuedListenerAdditions.get(i)); } queuedListenerAdditions.clear(); } private void removeQueuedListeners() { for (int i = 0, n = queuedListenerRemovals.size(); i < n; i++) { removeListenerImpl(queuedListenerRemovals.get(i)); } queuedListenerRemovals.clear(); } private void addListenerImpl(final L listener) { if (!listeners.contains(listener)) { listeners.add(listener); if (registrationListener != null) { registrationListener.onListenerAdded(listener); } } } private void removeListenerImpl(final L listener) { if (listeners.remove(listener) && registrationListener != null) { registrationListener.onListenerRemoved(listener); } } }