All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
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.
io.github.llewvallis.commandbuilder.ReflectionCommandCallback Maven / Gradle / Ivy
package io.github.llewvallis.commandbuilder;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
/**
* A {@link CommandCallback} which delegates to a method annotated with {@link ExecuteCommand} on a provided instance.
*/
@RequiredArgsConstructor
public class ReflectionCommandCallback implements CommandCallback {
private final Object instance;
@Override
public void onSuccess(List argumentValues, List variadicArgumentValues, CommandContext context) {
List argumentValuesWithContext = new ArrayList<>();
argumentValuesWithContext.add(context);
argumentValuesWithContext.addAll(argumentValues);
Method onSuccessMethod = ArgumentInference.getMethodByAnnotation(ExecuteCommand.class, instance.getClass(), instance.getClass());
onSuccessMethod.setAccessible(true);
runCallback(onSuccessMethod, argumentValuesWithContext, variadicArgumentValues, context);
}
@Override
public void onFailure(CommandParseException cause, CommandContext context) {
TextComponent errorMessage = new TextComponent("Incorrect command: " + cause.getMessage());
errorMessage.setColor(ChatColor.RED);
TextComponent usageMessage = new TextComponent("Usage: " + context.getUsageMessage());
usageMessage.setItalic(true);
usageMessage.setColor(ChatColor.RED);
context.getSender().spigot().sendMessage(errorMessage);
context.getSender().spigot().sendMessage(usageMessage);
}
@SneakyThrows({ IllegalAccessException.class })
private void runCallback(Method callbackMethod, List argumentValues,
List variadicArgumentValues, CommandContext context) {
if (callbackMethod.isAnnotationPresent(PlayerOnlyCommand.class) && !(context.getSender() instanceof Player)) {
TextComponent message = new TextComponent("Only players can use this command");
message.setColor(ChatColor.RED);
context.getSender().spigot().sendMessage(message);
return;
}
checkCallback(callbackMethod, argumentValues, variadicArgumentValues);
try {
runCallbackUnchecked(callbackMethod, argumentValues, variadicArgumentValues);
} catch (InvocationTargetException e) {
Bukkit.getLogger().log(Level.SEVERE, "Unhandled exception in command callback for " + context.getCommand(), e.getCause());
throw new ReflectionCommandCallbackException("unhandled exception in callback method ", e.getCause());
}
}
private void runCallbackUnchecked(Method callbackMethod, List argumentValues,
List variadicArgumentValues)
throws InvocationTargetException, IllegalAccessException {
boolean variadic = variadicArgumentValues != null;
int argumentCount = argumentValues.size() + (variadic ? 1 : 0) ;
Object[] arguments = new Object[argumentCount];
argumentValues.toArray(arguments);
if (variadic) {
Class>[] parameterTypes = callbackMethod.getParameterTypes();
Class> variadicType = parameterTypes[parameterTypes.length - 1];
Class> elementType = variadicType.getComponentType();
Object[] untypedVariadicArguments = variadicArgumentValues.toArray();
Object typedVariadicArguments = primitiveRespectingArrayCopy(untypedVariadicArguments, elementType);
arguments[arguments.length - 1] = typedVariadicArguments;
}
callbackMethod.invoke(instance, arguments);
}
private Object primitiveRespectingArrayCopy(Object[] untypedVariadicArguments, Class> elementType) {
Object array = Array.newInstance(elementType, untypedVariadicArguments.length);
for (int i = 0; i < untypedVariadicArguments.length; i++) {
Object argument = untypedVariadicArguments[i];
Array.set(array, i, argument);
}
return array;
}
private void checkCallback(Method callbackMethod, List argumentValues,
List variadicArgumentValues) {
Class>[] argumentTypes = callbackMethod.getParameterTypes();
boolean variadic = callbackMethod.isVarArgs();
Class>[] nonVariadicArgumentTypes;
Class> variadicArgumentType;
if (variadic) {
if (argumentTypes.length < 1) {
throw new ReflectionCommandCallbackException("missing variadic argument in callback method");
}
nonVariadicArgumentTypes = new Class>[argumentTypes.length - 1];
System.arraycopy(argumentTypes, 0, nonVariadicArgumentTypes, 0, argumentTypes.length - 1);
variadicArgumentType = argumentTypes[argumentTypes.length - 1].getComponentType();
} else {
nonVariadicArgumentTypes = new Class>[argumentTypes.length];
System.arraycopy(argumentTypes, 0, nonVariadicArgumentTypes, 0, argumentTypes.length);
variadicArgumentType = null;
}
Class> returnType = callbackMethod.getReturnType();
checkArguments(nonVariadicArgumentTypes, argumentValues);
checkVariadicConsistency(variadicArgumentType, variadicArgumentValues);
checkReturnType(returnType);
}
private static void checkArguments(Class>[] argumentTypes, List argumentValues) {
if (argumentTypes.length != argumentValues.size()) {
throw new ReflectionCommandCallbackException("method expected the wrong amount of arguments");
}
for (int i = 0; i < argumentTypes.length; i++) {
Object value = argumentValues.get(i);
Class> expectedType = argumentTypes[i];
if (value == null) {
if (expectedType.isPrimitive()) {
throw new ReflectionCommandCallbackException("primitive type " + expectedType + " cannot be "
+ "optional, try using the boxed type instead");
}
} else {
Class> valueType = value.getClass();
if (!expectedType.isAssignableFrom(valueType) && !ReflectionUtil.boxedType(expectedType).isAssignableFrom(valueType)) {
throw new ReflectionCommandCallbackException(valueType + " is not assignable to " + expectedType);
}
}
}
}
private void checkVariadicConsistency(Class> variadicArgumentType, List variadicArgumentValues) {
if (variadicArgumentValues == null && variadicArgumentType != null) {
throw new ReflectionCommandCallbackException("had a variadic argument for a non-variadic command");
} else if (variadicArgumentValues != null && variadicArgumentType == null) {
throw new ReflectionCommandCallbackException("missing variadic argument for variadic command");
}
if (variadicArgumentType != null) {
for (Object value : variadicArgumentValues) {
Class> valueType = value.getClass();
if (!variadicArgumentType.isAssignableFrom(valueType) && !ReflectionUtil.boxedType(variadicArgumentType).isAssignableFrom(valueType)) {
throw new ReflectionCommandCallbackException(value.getClass() + " is not assignable to " +
variadicArgumentType);
}
}
}
}
private static void checkReturnType(Class> returnType) {
if (returnType != Void.TYPE) {
throw new ReflectionCommandCallbackException("return type must be void");
}
}
}