/*
* This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT).
*
* Copyright (c) JCThePants (www.jcwhatever.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.jcwhatever.nucleus.internal.managed.scripting.api;
import com.jcwhatever.nucleus.Nucleus;
import com.jcwhatever.nucleus.collections.observer.subscriber.SubscriberLinkedList;
import com.jcwhatever.nucleus.mixins.IDisposable;
import com.jcwhatever.nucleus.utils.PreCon;
import com.jcwhatever.nucleus.utils.observer.ISubscriber;
import com.jcwhatever.nucleus.utils.observer.event.EventSubscriberPriority;
import com.jcwhatever.nucleus.utils.observer.script.IScriptEventSubscriber;
import com.jcwhatever.nucleus.utils.observer.script.ScriptEventSubscriber;
import com.jcwhatever.nucleus.utils.text.TextUtils;
import org.bukkit.event.Event;
import org.bukkit.event.EventException;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.plugin.EventExecutor;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
/**
* Provide scripts with an event registration API
*/
public class SAPI_Events implements IDisposable {
private final Plugin _plugin;
private final List<RegisteredBukkitEvent> _registeredBukkit = new ArrayList<>(10);
private final Deque<ISubscriber> _subscribers = new SubscriberLinkedList<>();
private boolean _isDisposed;
public SAPI_Events(Plugin plugin) {
_plugin = plugin;
}
@Override
public boolean isDisposed() {
return _isDisposed;
}
@Override
public void dispose() {
// unregister Bukkit event handlers
for (RegisteredBukkitEvent registered : _registeredBukkit) {
registered._handlerList.unregister(registered._registeredListener);
}
_registeredBukkit.clear();
// unregister nucleus event handlers
while (!_subscribers.isEmpty()) {
ISubscriber subscriber = _subscribers.remove();
subscriber.dispose();
}
_isDisposed = true;
}
/**
* Registers an event handler.
*
* @param eventName The event class name.
* @param priority The event priority as a string. Can specify "invokeForCancelled".
* ie. "NORMAL:invokeForCancelled". By default, handlers are not invoked if
* event is cancelled by another handler.
* @param handler The event handler.
*
* @return True if successfully registered.
*/
public boolean on(String eventName, String priority, final IScriptEventSubscriber handler) {
PreCon.notNullOrEmpty(eventName);
PreCon.notNullOrEmpty(priority);
PreCon.notNull(handler);
String[] priorityComp = TextUtils.PATTERN_COLON.split(priority);
boolean ignoreCancelled = true;
if (priorityComp.length == 2) {
if (priorityComp[1].equalsIgnoreCase("invokeForCancelled")) {
ignoreCancelled = false;
}
}
Class<?> eventClass;
try {
eventClass = Class.forName(eventName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
}
// check for and register bukkit events
if (Event.class.isAssignableFrom(eventClass)) {
Class<? extends Event> bukkitEventClass = eventClass.asSubclass(Event.class);
return registerBukkitEvent(bukkitEventClass, priorityComp[0], ignoreCancelled, handler);
}
else {
return registerNucleusEvent(eventClass, priorityComp[0], ignoreCancelled, handler);
}
}
/*
* Register NucleusFramework event.
*/
private boolean registerNucleusEvent(Class<?> event, String priority, boolean ignoreCancelled,
final IScriptEventSubscriber<?> handler) {
EventSubscriberPriority eventPriority = EventSubscriberPriority.NORMAL;
try {
eventPriority = EventSubscriberPriority.valueOf(priority.toUpperCase());
} catch (Exception e) {
e.printStackTrace();
}
ScriptEventSubscriber subscriber = new ScriptEventSubscriber<>(handler);
subscriber.setPriority(eventPriority);
subscriber.setInvokedForCancelled(!ignoreCancelled);
//noinspection unchecked
Nucleus.getEventManager().register(_plugin, event, subscriber);
_subscribers.add(subscriber);
return true;
}
/*
* Register Bukkit event
*/
private boolean registerBukkitEvent(Class<? extends Event> event, String priority,
boolean ignoreCancelled,
final IScriptEventSubscriber handler) {
EventPriority eventPriority = EventPriority.NORMAL;
try {
eventPriority = EventPriority.valueOf(priority.toUpperCase());
} catch (Exception e) {
e.printStackTrace();
}
Object result;
try {
result = event.getMethod("getHandlerList").invoke(null);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
return false;
}
if (!(result instanceof HandlerList))
return false;
final HandlerList handlerList = (HandlerList)result;
final Listener dummyListener = new Listener() {};
final EventExecutor eventExecutor = new EventExecutor() {
boolean isDisposed = false;
@Override
public void execute(Listener listener, Event event) throws EventException {
try {
//noinspection unchecked
handler.onEvent(event, new IDisposable() {
@Override
public boolean isDisposed() {
return isDisposed;
}
@Override
public void dispose() {
isDisposed = true;
handlerList.unregister(dummyListener);
}
});
} catch (Throwable e) {
e.printStackTrace();
}
}
};
RegisteredListener registeredListener = new RegisteredListener(dummyListener,
eventExecutor, eventPriority, _plugin, ignoreCancelled);
handlerList.register(registeredListener);
_registeredBukkit.add(new RegisteredBukkitEvent(handlerList, registeredListener));
return true;
}
private static class RegisteredBukkitEvent {
HandlerList _handlerList;
RegisteredListener _registeredListener;
RegisteredBukkitEvent(HandlerList handlerList, RegisteredListener listener) {
_handlerList = handlerList;
_registeredListener = listener;
}
}
}