Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (C) 2011-2018 Rinde R.S. van Lon
*
* 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 com.github.rinde.rinsim.event;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Arrays.asList;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
/**
* Basic event dispatcher for easily dispatching {@link Event}s to
* {@link Listener}s. It provides methods for dispatching events and removing
* and adding of listeners.
* @author Rinde van Lon
*/
public final class EventDispatcher implements EventAPI {
/**
* A map of event types to registered {@link Listener}s.
*/
final SetMultimap, Listener> listeners;
/**
* The set of event types that this event dispatcher supports.
*/
final ImmutableSet> supportedTypes;
/**
* The 'public' api of this dispatcher. Public in this context means API that
* is intended for users of the dispatcher, that is, classes that want
* to be notified of events.
*/
final PublicEventAPI publicAPI;
private final AtomicInteger dispatching;
private final SetMultimap, Listener> toRemove;
private final SetMultimap, Listener> toAdd;
/**
* Creates a new {@link EventDispatcher} instance which is capable of
* dispatching any {@link Event} with a type attribute that is
* one of eventTypes.
* @param supportedEventTypes The types of events this EventDispatcher
* supports.
*/
public EventDispatcher(Set> supportedEventTypes) {
checkArgument(!supportedEventTypes.isEmpty(),
"At least one event type must be supported.");
listeners = Multimaps.synchronizedSetMultimap(
LinkedHashMultimap., Listener>create());
supportedTypes = ImmutableSet.copyOf(supportedEventTypes);
publicAPI = new PublicEventAPI(this);
dispatching = new AtomicInteger(0);
toRemove = LinkedHashMultimap.create();
toAdd = LinkedHashMultimap.create();
}
/**
* Creates a new {@link EventDispatcher} instance which is capable of
* dispatching any {@link Event} with a type attribute that is
* one of eventTypes.
* @param supportedEventTypes The types of events this EventDispatcher
* supports.
*/
public EventDispatcher(Enum>... supportedEventTypes) {
this(new HashSet<>(asList(supportedEventTypes)));
}
/**
* Dispatch an event. Notifies all listeners that are listening for this type
* of event.
* @param e The event to be dispatched, only events with a supported type can
* be dispatched.
*/
public void dispatchEvent(Event e) {
synchronized (listeners) {
dispatching.incrementAndGet();
checkCanDispatchEventType(e.getEventType());
for (final Listener l : listeners.get(e.getEventType())) {
l.handleEvent(e);
}
dispatching.decrementAndGet();
}
update();
}
void update() {
if (dispatching.get() == 0) {
if (!toRemove.isEmpty()) {
for (final Entry, Listener> entry : toRemove.entries()) {
removeListener(entry.getValue(), entry.getKey());
}
toRemove.clear();
}
if (!toAdd.isEmpty()) {
for (final Entry, Listener> entry : toAdd.entries()) {
add(entry.getValue(),
ImmutableSet.>of(entry.getKey()), false);
}
toAdd.clear();
}
}
}
void checkCanDispatchEventType(Enum> eventType) {
checkArgument(
supportedTypes.contains(eventType),
"Cannot dispatch an event of type %s since it was not registered at "
+ "this dispatcher.",
eventType);
}
public void safeDispatchEvent(Event e) {
dispatching.incrementAndGet();
final Set targetListeners;
synchronized (listeners) {
checkCanDispatchEventType(e.getEventType());
targetListeners = ImmutableSet.copyOf(listeners.get(e.getEventType()));
}
for (final Listener l : targetListeners) {
l.handleEvent(e);
}
dispatching.decrementAndGet();
update();
}
/**
* {@inheritDoc}
*/
@Override
public void addListener(Listener listener, Enum>... eventTypes) {
add(listener, ImmutableSet.copyOf(eventTypes),
eventTypes.length == 0);
}
/**
* {@inheritDoc}
*/
@Override
public void addListener(Listener listener,
Iterable extends Enum>> eventTypes) {
add(listener, ImmutableSet.>copyOf(eventTypes), false);
}
/**
* Adds the specified listener. From now on, the specified listener will be
* notified of events with one of the eventTypes. If
* eventTypes is empty, the listener will be notified of no
* events. If all is true the value for
* eventTypes is ignored and the listener is registered for
* all events. Otherwise, if all is false the
* listener is only registered for the event types in eventTypes.
* @param listener The listener to register.
* @param eventTypes The event types to listen to.
* @param all Indicates whether eventTypes is used or if the
* listener is registered to all event types.
*/
void add(Listener listener, ImmutableSet> eventTypes, boolean all) {
synchronized (listeners) {
final Set> theTypes =
all ? supportedTypes : ImmutableSet.copyOf(eventTypes);
for (final Enum> eventType : theTypes) {
checkArgument(supportedTypes.contains(eventType),
"A listener for type %s is not allowed.", eventType);
if (dispatching.get() == 0) {
listeners.put(eventType, listener);
} else {
toAdd.put(eventType, listener);
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void removeListener(Listener listener, Enum>... eventTypes) {
removeListener(listener, ImmutableSet.copyOf(eventTypes));
}
/**
* {@inheritDoc}
*/
@Override
public void removeListener(Listener listener,
Iterable extends Enum>> eventTypes) {
synchronized (listeners) {
if (Iterables.isEmpty(eventTypes)) {
// remove all store keys in intermediate set to avoid concurrent
// modifications
final Set> keys = new HashSet<>(listeners.keySet());
for (final Enum> eventType : keys) {
if (listeners.containsEntry(eventType, listener)) {
removeListener(listener, eventType);
}
}
} else {
for (final Enum> eventType : eventTypes) {
checkNotNull(eventType, "event type to remove can not be null");
checkArgument(
containsListener(listener, eventType),
"The listener %s for the type %s cannot be removed because it "
+ "does not exist.",
listener, eventType);
if (dispatching.get() == 0) {
listeners.remove(eventType, listener);
} else {
toRemove.put(eventType, listener);
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsListener(Listener listener, Enum> eventType) {
return listeners.containsEntry(eventType, listener);
}
/**
* Checks if the dispatcher has a listener for the specific event type.
* @param eventType The event type.
* @return true if there is a listener for the specific type,
* false otherwise.
*/
public boolean hasListenerFor(Enum> eventType) {
return listeners.containsKey(eventType);
}
/**
* This method returns the public {@link EventAPI} instance associated to this
* {@link EventDispatcher}. This instance can be made publicly available to
* classes outside the scope of the events. Through this instance listeners
* can be added and removed to this {@link EventDispatcher}.
* @return A wrapper for {@link EventDispatcher}, only shows the methods which
* should be allowed to be called outside of the dispatcher's parent.
*/
public EventAPI getPublicEventAPI() {
return publicAPI;
}
static class PublicEventAPI implements EventAPI {
private final EventDispatcher ref;
PublicEventAPI(EventDispatcher ed) {
ref = ed;
}
@Override
public void addListener(Listener l, Enum>... eventTypes) {
ref.addListener(l, eventTypes);
}
@Override
public void addListener(Listener listener,
Iterable extends Enum>> eventTypes) {
ref.addListener(listener, eventTypes);
}
@Override
public void removeListener(Listener l, Enum>... eventTypes) {
ref.removeListener(l, eventTypes);
}
@Override
public void removeListener(Listener listener,
Iterable extends Enum>> eventTypes) {
ref.removeListener(listener, eventTypes);
}
@Override
public boolean containsListener(Listener l, Enum> eventType) {
return ref.containsListener(l, eventType);
}
}
}