From 4dc2bbcfbc445ccdb26e51fe55c1aff9d3612a0c Mon Sep 17 00:00:00 2001 From: cyklon73 Date: Mon, 4 Dec 2023 23:42:10 +0100 Subject: [PATCH] initial commit --- .gitignore | 38 ++++++++++ pom.xml | 25 +++++++ .../java/de/cyklon/jevent/Cancellable.java | 23 ++++++ .../de/cyklon/jevent/CancellableEvent.java | 25 +++++++ src/main/java/de/cyklon/jevent/Event.java | 57 +++++++++++++++ .../java/de/cyklon/jevent/EventException.java | 59 +++++++++++++++ .../java/de/cyklon/jevent/EventHandler.java | 43 +++++++++++ .../java/de/cyklon/jevent/EventManager.java | 35 +++++++++ src/main/java/de/cyklon/jevent/Handler.java | 73 +++++++++++++++++++ src/main/java/de/cyklon/jevent/JEvent.java | 51 +++++++++++++ .../java/de/cyklon/jevent/package-info.java | 5 ++ 11 files changed, 434 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/de/cyklon/jevent/Cancellable.java create mode 100644 src/main/java/de/cyklon/jevent/CancellableEvent.java create mode 100644 src/main/java/de/cyklon/jevent/Event.java create mode 100644 src/main/java/de/cyklon/jevent/EventException.java create mode 100644 src/main/java/de/cyklon/jevent/EventHandler.java create mode 100644 src/main/java/de/cyklon/jevent/EventManager.java create mode 100644 src/main/java/de/cyklon/jevent/Handler.java create mode 100644 src/main/java/de/cyklon/jevent/JEvent.java create mode 100644 src/main/java/de/cyklon/jevent/package-info.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f51e8dc --- /dev/null +++ b/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + de.cyklon + JEvent + 1.0 + + + 17 + 17 + UTF-8 + + + + + org.jetbrains + annotations + 22.0.0 + + + + \ No newline at end of file diff --git a/src/main/java/de/cyklon/jevent/Cancellable.java b/src/main/java/de/cyklon/jevent/Cancellable.java new file mode 100644 index 0000000..a5fe28e --- /dev/null +++ b/src/main/java/de/cyklon/jevent/Cancellable.java @@ -0,0 +1,23 @@ +package de.cyklon.jevent; + +/** + * This interface can be implemented to make an event cancelable + *

+ * If the event is marked as canceld, all listeners that would be executed afterwards will not be executed unless ignoreCancelled is set to true in the @{@link EventHandler} annotation. + * @author Cyklon73 + */ +public interface Cancellable { + + /** + * returns true if the event was marked as canceled + * @return true if event was canceled + */ + boolean isCancelled(); + + /** + * sets the canceled status of the event to the specified value + * @param cancelled the new status + */ + void setCancelled(boolean cancelled); + +} diff --git a/src/main/java/de/cyklon/jevent/CancellableEvent.java b/src/main/java/de/cyklon/jevent/CancellableEvent.java new file mode 100644 index 0000000..ecc8342 --- /dev/null +++ b/src/main/java/de/cyklon/jevent/CancellableEvent.java @@ -0,0 +1,25 @@ +package de.cyklon.jevent; + +import org.jetbrains.annotations.Nullable; + +public abstract class CancellableEvent extends Event implements Cancellable { + + private boolean cancelled = false; + + protected CancellableEvent() { + } + + protected CancellableEvent(@Nullable String name) { + super(name); + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } +} diff --git a/src/main/java/de/cyklon/jevent/Event.java b/src/main/java/de/cyklon/jevent/Event.java new file mode 100644 index 0000000..4b52ec2 --- /dev/null +++ b/src/main/java/de/cyklon/jevent/Event.java @@ -0,0 +1,57 @@ +package de.cyklon.jevent; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * the event base class. + * Extend this class to create your own event + * @author Cyklon73 + */ +public abstract class Event { + + /** + * the event name + */ + private final String name; + + + /** + * constructs the event, with the class name as the event name + */ + protected Event() { + this(null); + } + + /** + * constructs the event with a specific name + * @param name the event name. If it is null, it is set to the class name + */ + protected Event(@Nullable String name) { + this.name = name==null ? this.getClass().getSimpleName() : name; + } + + /** + * call all listeners for this event + * @see EventManager#callEvent(Event) + * @return true if the event was canceled + */ + public boolean callEvent() { + JEvent.MANAGER.callEvent(this); + if (this instanceof Cancellable) return ((Cancellable)this).isCancelled(); + return false; + } + + /** + * @return the name of the Event + */ + @NotNull + public String getEventName() { + return name; + } + + @Override + public String toString() { + return Event.class + "(" + getEventName() + ")"; + } +} diff --git a/src/main/java/de/cyklon/jevent/EventException.java b/src/main/java/de/cyklon/jevent/EventException.java new file mode 100644 index 0000000..a971e2b --- /dev/null +++ b/src/main/java/de/cyklon/jevent/EventException.java @@ -0,0 +1,59 @@ +package de.cyklon.jevent; + + +/** + * is called when an error occurs in the event system + * @author Cyklon73 + */ +public class EventException extends RuntimeException { + + private final Throwable cause; + + /** + * Constructs a new EventException based on the given Exception + * + * @param throwable Exception that triggered this Exception + */ + public EventException(Throwable throwable) { + cause = throwable; + } + + /** + * Constructs a new EventException + */ + public EventException() { + cause = null; + } + + /** + * Constructs a new EventException with the given message + * + * @param cause The exception that caused this + * @param message The message + */ + public EventException(Throwable cause, String message) { + super(message); + this.cause = cause; + } + + /** + * Constructs a new EventException with the given message + * + * @param message The message + */ + public EventException(String message) { + super(message); + cause = null; + } + + /** + * If applicable, returns the Exception that triggered this Exception + * + * @return Inner exception, or null if one does not exist + */ + @Override + public Throwable getCause() { + return cause; + } + +} diff --git a/src/main/java/de/cyklon/jevent/EventHandler.java b/src/main/java/de/cyklon/jevent/EventHandler.java new file mode 100644 index 0000000..63e5aa0 --- /dev/null +++ b/src/main/java/de/cyklon/jevent/EventHandler.java @@ -0,0 +1,43 @@ +package de.cyklon.jevent; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An annotation to mark methods as being event handler methods + * @author Cyklon73 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface EventHandler { + + byte LOWEST = -2; + byte LOW = -1; + byte NORMAL = 0; + byte HIGH = 1; + byte HIGHEST = 2; + byte MONITOR = 3; + + + /** + * Define the priority of the event. + *

+ * Lowest priority to the Highest priority executed. + * @return the priority + */ + byte priority() default NORMAL; + + /** + * Define if the handler ignores a cancelled event. + *

+ * If ignoreCancelled is true and the event is cancelled, the method is + * not called. Otherwise, the method is always called. + * + * @return whether cancelled events should be ignored + */ + boolean ignoreCancelled() default false; + + +} diff --git a/src/main/java/de/cyklon/jevent/EventManager.java b/src/main/java/de/cyklon/jevent/EventManager.java new file mode 100644 index 0000000..c635750 --- /dev/null +++ b/src/main/java/de/cyklon/jevent/EventManager.java @@ -0,0 +1,35 @@ +package de.cyklon.jevent; + +import org.jetbrains.annotations.NotNull; + +/** + * The EventManager to manage and execute events + * @author Cyklon73 + */ +public sealed interface EventManager permits JEvent { + + /** + * registers all methods annotated with @{@link EventHandler} + * @param obj the object from which events are to be registered + */ + void registerListener(@NotNull Object obj); + + /** + * removes all listeners that have the type of the specified class + * @param clazz The type of listener Object to be removed + */ + void unregisterListener(@NotNull Class clazz); + + /** + * removes all listeners + */ + void unregisterAll(); + + + /** + * calls the passed event and executes all registered listeners, as well as all listeners that were registered for a superclass of the event + * @param event the event to be executed + */ + void callEvent(@NotNull Event event); + +} diff --git a/src/main/java/de/cyklon/jevent/Handler.java b/src/main/java/de/cyklon/jevent/Handler.java new file mode 100644 index 0000000..4f58bcf --- /dev/null +++ b/src/main/java/de/cyklon/jevent/Handler.java @@ -0,0 +1,73 @@ +package de.cyklon.jevent; + +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +/** + * The handler object represents a single event listener method + * @author Cyklon73 + */ +class Handler implements Comparable { + + private final Object listener; + private final Method handler; + private Class eventType = null; + private final byte priority; + private final boolean ignoreCancelled; + + @SuppressWarnings("unchecked") + private Handler(Object listener, Method handler, byte priority, boolean ignoreCancelled) { + this.listener = listener; + this.handler = handler; + this.handler.setAccessible(true); + for (Class parameterType : handler.getParameterTypes()) { + if (Event.class.isAssignableFrom(parameterType)) { + this.eventType = (Class) parameterType; + break; + } + } + if (eventType==null) throw new EventException("the method must have an event as a parameter!"); + this.priority = priority; + this.ignoreCancelled = ignoreCancelled; + } + + public Object getListener() { + return listener; + } + + public boolean isSuitableHandler(Class event) { + return eventType.isAssignableFrom(event); + } + + public void invoke(Event event) { + if (event instanceof Cancellable && ((Cancellable)event).isCancelled() && !ignoreCancelled) return; + if (isSuitableHandler(event.getClass())) { + try { + handler.invoke(listener, event); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new EventException(e); + } + } + } + + @Override + public int compareTo(@NotNull Handler o) { + return Byte.compare(o.priority, this.priority); + } + + public static Collection getHandlers(Object listener) { + Method[] methods = listener.getClass().getDeclaredMethods(); + List handlers = new LinkedList<>(); + for (Method handler : methods) { + EventHandler annotation = handler.getAnnotation(EventHandler.class); + if (annotation==null) continue; + handlers.add(new Handler(listener, handler, annotation.priority(), annotation.ignoreCancelled())); + } + return handlers; + } +} diff --git a/src/main/java/de/cyklon/jevent/JEvent.java b/src/main/java/de/cyklon/jevent/JEvent.java new file mode 100644 index 0000000..96df2da --- /dev/null +++ b/src/main/java/de/cyklon/jevent/JEvent.java @@ -0,0 +1,51 @@ +package de.cyklon.jevent; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * JEvent provides a powerful and lightweight event system based on the syntax of the Spigot event system + * @author Cyklon73 + */ +public final class JEvent implements EventManager { + + static final EventManager MANAGER = new JEvent(); + + /** + * @return The EventManager instance + */ + public static EventManager getManager() { + return MANAGER; + } + + private final Collection HANDLER_LIST = new ArrayList<>(); + + private JEvent() {} + + private Collection getHandlers(Class event) { + return HANDLER_LIST.stream() + .filter(h -> h.isSuitableHandler(event)) + .sorted() + .toList(); + } + + + public void registerListener(@NotNull Object obj) { + HANDLER_LIST.addAll(Handler.getHandlers(obj)); + } + + public void unregisterListener(@NotNull Class clazz) { + HANDLER_LIST.removeIf(h -> h.getListener().getClass().isInstance(clazz)); + } + + public void unregisterAll() { + HANDLER_LIST.clear(); + } + + public void callEvent(@NotNull Event event) { + getHandlers(event.getClass()).forEach(h -> h.invoke(event)); + } + +} diff --git a/src/main/java/de/cyklon/jevent/package-info.java b/src/main/java/de/cyklon/jevent/package-info.java new file mode 100644 index 0000000..15dc31f --- /dev/null +++ b/src/main/java/de/cyklon/jevent/package-info.java @@ -0,0 +1,5 @@ +/** + * JEvent provides a powerful and lightweight event system based on the syntax of the Spigot event system + * @author Cyklon73 + */ +package de.cyklon.jevent; \ No newline at end of file