/** * **************************************************************************** * Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com). * <p> * This file is part of the PickaPack library. * <p> * PickaPack is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * <p> * PickaPack is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * <p> * You should have received a copy of the GNU General Public License * along with PickaPack. If not, see <http://www.gnu.org/licenses/>. * **************************************************************************** */ package archimulator.util.event; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Consumer; /** * Blocking event dispatcher. * * @param <BlockingEventT> the type of the event * @author Min Cai */ public class BlockingEventDispatcher<BlockingEventT extends BlockingEvent> { /** * Map of event listeners. */ protected final Map<Class<? extends BlockingEventT>, List<BiConsumer<?, ? extends BlockingEventT>>> listeners; /** * Map of any event listeners. */ protected final List<BiConsumer<Object, BlockingEventT>> anyListeners; /** * Create a blocking event dispatcher. */ public BlockingEventDispatcher() { this.listeners = new LinkedHashMap<>(); this.anyListeners = new ArrayList<>(); } /** * Dispatch the specified event. * * @param <BlockingEventK> the type of the event * @param event the event */ public synchronized <BlockingEventK extends BlockingEventT> void dispatch(BlockingEventK event) { this.dispatch(null, event); } /** * Dispatch the specified event from the specified sender. * * @param <BlockingEventK> the type of the event * @param sender the sender * @param event the event */ @SuppressWarnings({"unchecked"}) public synchronized <BlockingEventK extends BlockingEventT> void dispatch(Object sender, BlockingEventK event) { Class<? extends BlockingEventT> eventClass = (Class<? extends BlockingEventT>) event.getClass(); if (this.listeners.containsKey(eventClass)) { for (BiConsumer<?, ? extends BlockingEventT> listener : this.listeners.get(eventClass)) { ((BiConsumer<Object, BlockingEventK>) listener).accept(sender, event); } } for (BiConsumer<Object, BlockingEventT> anyListener : this.anyListeners) { anyListener.accept(sender, event); } } /** * Add a listener for the specified event class. * * @param <BlockingEventK> the type of the event * @param eventClass the event class * @param listener the listener that is to be added */ public synchronized <BlockingEventK extends BlockingEventT> void addListener(Class<BlockingEventK> eventClass, final Consumer<BlockingEventK> listener) { this.addListener(eventClass, new ProxyAction2<>(listener)); } /** * Add a listener for the specified event class. * * @param <BlockingEventK> the type of the event * @param eventClass the event class * @param listener the listener that is to be added */ public synchronized <BlockingEventK extends BlockingEventT> void addListener(Class<BlockingEventK> eventClass, BiConsumer<?, BlockingEventK> listener) { if (!this.listeners.containsKey(eventClass)) { this.listeners.put(eventClass, new ArrayList<>()); } if (!this.listeners.get(eventClass).contains(listener)) { this.listeners.get(eventClass).add(listener); } } /** * Add an any listener. * * @param listener the listener that is to be added */ public synchronized void addAnyListener(BiConsumer<Object, BlockingEventT> listener) { if (!this.anyListeners.contains(listener)) { this.anyListeners.add(listener); } } /** * Remove the specified listener for the specified event class. * * @param <BlockingEventK> the type of the event * @param eventClass the event class * @param listener the listener that is to be removed */ public synchronized <BlockingEventK extends BlockingEventT> void removeListener(Class<BlockingEventK> eventClass, Consumer<BlockingEventK> listener) { if (this.listeners.containsKey(eventClass)) { List<BiConsumer<?, ? extends BlockingEventT>> listenersInTheEventClass = this.listeners.get(eventClass); for (BiConsumer<?, ? extends BlockingEventT> listenerFound : listenersInTheEventClass) { if (listenerFound instanceof ProxyAction2) { if (((ProxyAction2) listenerFound).listener == listener) { this.listeners.get(eventClass).remove(listenerFound); break; } } } } } /** * Remove the specified any listener. * * @param listener the listener that is to be removed */ public synchronized void removeAnyListener(BiConsumer<Object, BlockingEventT> listener) { if (this.anyListeners.contains(listener)) { this.anyListeners.remove(listener); } } /** * Remove the specified listener for the specified event class. * * @param <BlockingEventK> the type of the event * @param eventClass the event class * @param listener the listener that is to be removed */ public synchronized <BlockingEventK extends BlockingEventT> void removeListener(Class<BlockingEventK> eventClass, BiConsumer<?, BlockingEventK> listener) { if (this.listeners.containsKey(eventClass)) { this.listeners.get(eventClass).remove(listener); } } /** * Clear all the listeners. */ public synchronized void clearListeners() { this.listeners.clear(); } /** * Get a value indicating whether the listeners is empty or not. * * @return a value indicating whether the listeners is empty or not */ public boolean isEmpty() { return this.listeners.isEmpty(); } /** * Proxy action. * * @param <BlockingEventK> the type of the event */ protected static class ProxyAction2<BlockingEventK> implements BiConsumer<Object, BlockingEventK> { private Consumer<BlockingEventK> listener; /** * Create a proxy action. * * @param listener the listener */ public ProxyAction2(Consumer<BlockingEventK> listener) { this.listener = listener; } /** * Apply. * * @param param1 the first parameter * @param param2 the second parameter */ @Override public void accept(Object param1, BlockingEventK param2) { this.listener.accept(param2); } } }