eventMethodsInListener = entry.getValue();
+
+ if (currentHandlers != null &&!currentHandlers.containsAll(entry.getValue())) {
+ currentHandlers.removeAll(eventMethodsInListener);
+ }
+ }
+ }
+
+ /**
+ * Registers all handler methods on {@code object} to receive events.
+ * Handler methods are selected and classified using this EventBus's
+ * {@link SubscriberFindingStrategy}; the default strategy is the
+ * {@link AnnotatedSubscriberFinder}.
+ *
+ * @param object object whose handler methods should be registered.
+ */
+ public void register(Object object) {
+ subscribeAll(finder.findAllSubscribers(object));
+ }
+
+ /**
+ * Unregisters all handler methods on a registered {@code object}.
+ *
+ * @param object object whose handler methods should be unregistered.
+ * @throws IllegalArgumentException if the object was not previously registered.
+ */
+ public void unregister(Object object) {
+ unsubscribeAll(finder.findAllSubscribers(object));
+ }
+
+ /**
+ * Posts an event to all registered handlers. This method will return
+ * successfully after the event has been posted to all handlers, and
+ * regardless of any exceptions thrown by handlers.
+ *
+ * If no handlers have been subscribed for {@code event}'s class, and
+ * {@code event} is not already a {@link DeadEvent}, it will be wrapped in a
+ * DeadEvent and reposted.
+ *
+ * @param event event to post.
+ */
+ public void post(Object event) {
+ List dispatching = new ArrayList();
+
+ synchronized (this) {
+ Set> dispatchTypes = flattenHierarchy(event.getClass());
+
+ for (Class> eventType : dispatchTypes) {
+ Set wrappers = getHandlersForEventType(eventType);
+
+ if (wrappers != null && !wrappers.isEmpty()) {
+ dispatching.addAll(wrappers);
+ }
+ }
+ }
+
+ Collections.sort(dispatching);
+
+ for (EventHandler handler : dispatching) {
+ dispatch(event, handler);
+ }
+ }
+
+ /**
+ * Dispatches {@code event} to the handler in {@code handler}. This method
+ * is an appropriate override point for subclasses that wish to make
+ * event delivery asynchronous.
+ *
+ * @param event event to dispatch.
+ * @param handler handler that will call the handler.
+ */
+ protected void dispatch(Object event, EventHandler handler) {
+ try {
+ handler.handleEvent(event);
+ } catch (InvocationTargetException e) {
+ logger.log(Level.SEVERE,
+ "Could not dispatch event: " + event + " to handler " + handler, e);
+ }
+ }
+
+ /**
+ * Retrieves a mutable set of the currently registered handlers for
+ * {@code type}. If no handlers are currently registered for {@code type},
+ * this method may either return {@code null} or an empty set.
+ *
+ * @param type type of handlers to retrieve.
+ * @return currently registered handlers, or {@code null}.
+ */
+ synchronized Set getHandlersForEventType(Class> type) {
+ return handlersByType.get(type);
+ }
+
+ /**
+ * Creates a new Set for insertion into the handler map. This is provided
+ * as an override point for subclasses. The returned set should support
+ * concurrent access.
+ *
+ * @return a new, mutable set for handlers.
+ */
+ protected synchronized Set newHandlerSet() {
+ return new HashSet();
+ }
+
+ /**
+ * Flattens a class's type hierarchy into a set of Class objects. The set
+ * will include all superclasses (transitively), and all interfaces
+ * implemented by these superclasses.
+ *
+ * @param concreteClass class whose type hierarchy will be retrieved.
+ * @return {@code clazz}'s complete type hierarchy, flattened and uniqued.
+ */
+ Set> flattenHierarchy(Class> concreteClass) {
+ try {
+ return flattenHierarchyCache.get(concreteClass);
+ } catch (ExecutionException e) {
+ throw Throwables.propagate(e.getCause());
+ }
+ }
+
+}
diff --git a/src/main/java/com/sk89q/worldedit/util/eventbus/EventHandler.java b/src/main/java/com/sk89q/worldedit/util/eventbus/EventHandler.java
new file mode 100644
index 000000000..1903d80e3
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/util/eventbus/EventHandler.java
@@ -0,0 +1,105 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program 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.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.util.eventbus;
+
+import java.lang.reflect.InvocationTargetException;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Event handler object for {@link EventBus} that is able to dispatch
+ * an event.
+ *
+ * Original for Guava, licensed under the Apache License, Version 2.0.
+ */
+public abstract class EventHandler implements Comparable {
+
+ public enum Priority {
+ VERY_EARLY,
+ EARLY,
+ NORMAL,
+ LATE,
+ VERY_LATE
+ }
+
+ private final Priority priority;
+
+ /**
+ * Create a new event handler.
+ *
+ * @param priority the priority
+ */
+ protected EventHandler(Priority priority) {
+ checkNotNull(priority);
+ this.priority = priority;
+ }
+
+ /**
+ * Get the priority.
+ *
+ * @return the priority
+ */
+ public Priority getPriority() {
+ return priority;
+ }
+
+ /**
+ * Dispatch the given event.
+ *
+ * Subclasses should override {@link #dispatch(Object)}.
+ *
+ * @param event the event
+ * @throws InvocationTargetException thrown if an exception is thrown during dispatch
+ */
+ public final void handleEvent(Object event) throws InvocationTargetException {
+ try {
+ dispatch(event);
+ } catch (Throwable t) {
+ throw new InvocationTargetException(t);
+ }
+ }
+
+ /**
+ * Dispatch the event.
+ *
+ * @param event the event object
+ * @throws Exception an exception that may be thrown
+ */
+ public abstract void dispatch(Object event) throws Exception;
+
+ @Override
+ public int compareTo(EventHandler o) {
+ return getPriority().ordinal() - o.getPriority().ordinal();
+ }
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ @Override
+ public String toString() {
+ return "EventHandler{" +
+ "priority=" + priority +
+ '}';
+ }
+
+}
diff --git a/src/main/java/com/sk89q/worldedit/util/eventbus/MethodEventHandler.java b/src/main/java/com/sk89q/worldedit/util/eventbus/MethodEventHandler.java
new file mode 100644
index 000000000..40f087b8c
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/util/eventbus/MethodEventHandler.java
@@ -0,0 +1,81 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program 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.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.util.eventbus;
+
+import java.lang.reflect.Method;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Invokes a {@link Method} to dispatch an event.
+ */
+public class MethodEventHandler extends EventHandler {
+
+ private final Object object;
+ private final Method method;
+
+ /**
+ * Create a new event handler.
+ *
+ * @param priority the priority
+ * @param method the method
+ */
+ public MethodEventHandler(Priority priority, Object object, Method method) {
+ super(priority);
+ checkNotNull(method);
+ this.object = object;
+ this.method = method;
+ }
+
+ /**
+ * Get the method.
+ *
+ * @return the method
+ */
+ public Method getMethod() {
+ return method;
+ }
+
+ @Override
+ public void dispatch(Object event) throws Exception {
+ method.invoke(object, event);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MethodEventHandler that = (MethodEventHandler) o;
+
+ if (!method.equals(that.method)) return false;
+ if (object != null ? !object.equals(that.object) : that.object != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = object != null ? object.hashCode() : 0;
+ result = 31 * result + method.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/com/sk89q/worldedit/util/eventbus/Subscribe.java b/src/main/java/com/sk89q/worldedit/util/eventbus/Subscribe.java
new file mode 100644
index 000000000..41b8a4377
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/util/eventbus/Subscribe.java
@@ -0,0 +1,42 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program 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.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.util.eventbus;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+/**
+ * Used to mark methods as event handlers.
+ */
+@Retention(RUNTIME)
+@Target({METHOD})
+public @interface Subscribe {
+
+ /**
+ * The priority as far as order of dispatching is concerned.
+ *
+ * @return the priority
+ */
+ EventHandler.Priority priority() default EventHandler.Priority.NORMAL;
+
+}
diff --git a/src/main/java/com/sk89q/worldedit/util/eventbus/SubscriberFindingStrategy.java b/src/main/java/com/sk89q/worldedit/util/eventbus/SubscriberFindingStrategy.java
new file mode 100644
index 000000000..78e470a6a
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/util/eventbus/SubscriberFindingStrategy.java
@@ -0,0 +1,43 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program 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.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.util.eventbus;
+
+import com.google.common.collect.Multimap;
+
+/**
+ * A method for finding event handler methods in objects, for use by
+ * {@link EventBus}.
+ */
+interface SubscriberFindingStrategy {
+
+ /**
+ * Finds all suitable event handler methods in {@code source}, organizes them
+ * by the type of event they handle, and wraps them in {@link EventHandler}s.
+ *
+ * @param source object whose handlers are desired.
+ * @return EventHandler objects for each handler method, organized by event
+ * type.
+ *
+ * @throws IllegalArgumentException if {@code source} is not appropriate for
+ * this strategy (in ways that this interface does not define).
+ */
+ Multimap, EventHandler> findAllSubscribers(Object source);
+
+}
\ No newline at end of file