PageRenderTime 212ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/EventBus.java

https://gitlab.com/Skull3x/WorldEdit
Java | 229 lines | 89 code | 27 blank | 113 comment | 8 complexity | d1bb742b15d8b1576a31f5f46c0acc01 MD5 | raw file
  1. /*
  2. * WorldEdit, a Minecraft world manipulation toolkit
  3. * Copyright (C) sk89q <http://www.sk89q.com>
  4. * Copyright (C) WorldEdit team and contributors
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU Lesser General Public License as published by the
  8. * Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  14. * for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.sk89q.worldedit.util.eventbus;
  20. import com.google.common.base.Supplier;
  21. import com.google.common.collect.Multimap;
  22. import com.google.common.collect.Multimaps;
  23. import com.google.common.collect.SetMultimap;
  24. import com.google.common.eventbus.DeadEvent;
  25. import com.sk89q.worldedit.internal.annotation.RequiresNewerGuava;
  26. import java.lang.reflect.InvocationTargetException;
  27. import java.util.*;
  28. import java.util.logging.Level;
  29. import java.util.logging.Logger;
  30. import static com.google.common.base.Preconditions.checkNotNull;
  31. /**
  32. * Dispatches events to listeners, and provides ways for listeners to register
  33. * themselves.
  34. *
  35. * <p>This class is based on Guava's {@link EventBus} but priority is supported
  36. * and events are dispatched at the time of call, rather than being queued up.
  37. * This does allow dispatching during an in-progress dispatch.</p>
  38. *
  39. * <p>This implementation utilizes naive synchronization on all getter and
  40. * setter methods. Dispatch does not occur when a lock has been acquired,
  41. * however.</p>
  42. */
  43. public class EventBus {
  44. private final Logger logger = Logger.getLogger(EventBus.class.getCanonicalName());
  45. private final SetMultimap<Class<?>, EventHandler> handlersByType =
  46. Multimaps.newSetMultimap(new HashMap<Class<?>, Collection<EventHandler>>(),
  47. new Supplier<Set<EventHandler>>() {
  48. @Override
  49. public Set<EventHandler> get() {
  50. return newHandlerSet();
  51. }
  52. });
  53. /**
  54. * Strategy for finding handler methods in registered objects. Currently,
  55. * only the {@link AnnotatedSubscriberFinder} is supported, but this is
  56. * encapsulated for future expansion.
  57. */
  58. private final SubscriberFindingStrategy finder = new AnnotatedSubscriberFinder();
  59. @RequiresNewerGuava
  60. private HierarchyCache flattenHierarchyCache = new HierarchyCache();
  61. /**
  62. * Registers the given handler for the given class to receive events.
  63. *
  64. * @param clazz the event class to register
  65. * @param handler the handler to register
  66. */
  67. public synchronized void subscribe(Class<?> clazz, EventHandler handler) {
  68. checkNotNull(clazz);
  69. checkNotNull(handler);
  70. handlersByType.put(clazz, handler);
  71. }
  72. /**
  73. * Registers the given handler for the given class to receive events.
  74. *
  75. * @param handlers a map of handlers
  76. */
  77. public synchronized void subscribeAll(Multimap<Class<?>, EventHandler> handlers) {
  78. checkNotNull(handlers);
  79. handlersByType.putAll(handlers);
  80. }
  81. /**
  82. * Unregisters the given handler for the given class.
  83. *
  84. * @param clazz the class
  85. * @param handler the handler
  86. */
  87. public synchronized void unsubscribe(Class<?> clazz, EventHandler handler) {
  88. checkNotNull(clazz);
  89. checkNotNull(handler);
  90. handlersByType.remove(clazz, handler);
  91. }
  92. /**
  93. * Unregisters the given handlers.
  94. *
  95. * @param handlers a map of handlers
  96. */
  97. public synchronized void unsubscribeAll(Multimap<Class<?>, EventHandler> handlers) {
  98. checkNotNull(handlers);
  99. for (Map.Entry<Class<?>, Collection<EventHandler>> entry : handlers.asMap().entrySet()) {
  100. Set<EventHandler> currentHandlers = getHandlersForEventType(entry.getKey());
  101. Collection<EventHandler> eventMethodsInListener = entry.getValue();
  102. if (currentHandlers != null &&!currentHandlers.containsAll(entry.getValue())) {
  103. currentHandlers.removeAll(eventMethodsInListener);
  104. }
  105. }
  106. }
  107. /**
  108. * Registers all handler methods on {@code object} to receive events.
  109. * Handler methods are selected and classified using this EventBus's
  110. * {@link SubscriberFindingStrategy}; the default strategy is the
  111. * {@link AnnotatedSubscriberFinder}.
  112. *
  113. * @param object object whose handler methods should be registered.
  114. */
  115. public void register(Object object) {
  116. subscribeAll(finder.findAllSubscribers(object));
  117. }
  118. /**
  119. * Unregisters all handler methods on a registered {@code object}.
  120. *
  121. * @param object object whose handler methods should be unregistered.
  122. * @throws IllegalArgumentException if the object was not previously registered.
  123. */
  124. public void unregister(Object object) {
  125. unsubscribeAll(finder.findAllSubscribers(object));
  126. }
  127. /**
  128. * Posts an event to all registered handlers. This method will return
  129. * successfully after the event has been posted to all handlers, and
  130. * regardless of any exceptions thrown by handlers.
  131. *
  132. * <p>If no handlers have been subscribed for {@code event}'s class, and
  133. * {@code event} is not already a {@link DeadEvent}, it will be wrapped in a
  134. * DeadEvent and reposted.
  135. *
  136. * @param event event to post.
  137. */
  138. public void post(Object event) {
  139. List<EventHandler> dispatching = new ArrayList<EventHandler>();
  140. synchronized (this) {
  141. Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());
  142. for (Class<?> eventType : dispatchTypes) {
  143. Set<EventHandler> wrappers = getHandlersForEventType(eventType);
  144. if (wrappers != null && !wrappers.isEmpty()) {
  145. dispatching.addAll(wrappers);
  146. }
  147. }
  148. }
  149. Collections.sort(dispatching);
  150. for (EventHandler handler : dispatching) {
  151. dispatch(event, handler);
  152. }
  153. }
  154. /**
  155. * Dispatches {@code event} to the handler in {@code handler}. This method
  156. * is an appropriate override point for subclasses that wish to make
  157. * event delivery asynchronous.
  158. *
  159. * @param event event to dispatch.
  160. * @param handler handler that will call the handler.
  161. */
  162. protected void dispatch(Object event, EventHandler handler) {
  163. try {
  164. handler.handleEvent(event);
  165. } catch (InvocationTargetException e) {
  166. logger.log(Level.SEVERE,
  167. "Could not dispatch event: " + event + " to handler " + handler, e);
  168. }
  169. }
  170. /**
  171. * Retrieves a mutable set of the currently registered handlers for
  172. * {@code type}. If no handlers are currently registered for {@code type},
  173. * this method may either return {@code null} or an empty set.
  174. *
  175. * @param type type of handlers to retrieve.
  176. * @return currently registered handlers, or {@code null}.
  177. */
  178. synchronized Set<EventHandler> getHandlersForEventType(Class<?> type) {
  179. return handlersByType.get(type);
  180. }
  181. /**
  182. * Creates a new Set for insertion into the handler map. This is provided
  183. * as an override point for subclasses. The returned set should support
  184. * concurrent access.
  185. *
  186. * @return a new, mutable set for handlers.
  187. */
  188. protected synchronized Set<EventHandler> newHandlerSet() {
  189. return new HashSet<EventHandler>();
  190. }
  191. /**
  192. * Flattens a class's type hierarchy into a set of Class objects. The set
  193. * will include all superclasses (transitively), and all interfaces
  194. * implemented by these superclasses.
  195. *
  196. * @param concreteClass class whose type hierarchy will be retrieved.
  197. * @return {@code clazz}'s complete type hierarchy, flattened and uniqued.
  198. */
  199. Set<Class<?>> flattenHierarchy(Class<?> concreteClass) {
  200. return flattenHierarchyCache.get(concreteClass);
  201. }
  202. }