diff options
Diffstat (limited to 'dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java')
-rw-r--r-- | dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java | 830 |
1 files changed, 415 insertions, 415 deletions
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java index 6b1965d..93ac0ee 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java @@ -1,415 +1,415 @@ -package io.dico.dicore.command.registration.reflect; - -import io.dico.dicore.command.*; -import io.dico.dicore.command.annotation.*; -import io.dico.dicore.command.annotation.GroupMatchedCommands.GroupEntry; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.ParameterList; -import io.dico.dicore.command.parameter.type.IParameterTypeSelector; -import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector; -import io.dico.dicore.command.parameter.type.ParameterType; -import io.dico.dicore.command.parameter.type.ParameterTypes; -import io.dico.dicore.command.predef.PredefinedCommand; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.function.Consumer; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -/** - * Takes care of turning a reflection {@link Method} into a command and more. - */ -public class ReflectiveRegistration { - /** - * This object provides names of the parameters. - * Oddly, the AnnotationParanamer extensions require a 'fallback' paranamer to function properly without - * requiring ALL parameters to have that flag. This is weird because it should just use the AdaptiveParanamer on an upper level to - * determine the name of each individual flag. Oddly this isn't how it works, so the fallback works the same way as the AdaptiveParanamer does. - * It's just linked instead of using an array for that part. Then we can use an AdaptiveParanamer for the latest fallback, to get bytecode names - * or, finally, to get the Jvm-provided parameter names. - */ - //private static final Paranamer paranamer = new CachingParanamer(new BytecodeReadingParanamer()); - @SuppressWarnings("StatementWithEmptyBody") - private static String[] lookupParameterNames(Method method, java.lang.reflect.Parameter[] parameters, int start) { - int n = parameters.length; - String[] out = new String[n - start]; - - //String[] bytecode; - //try { - // bytecode = paranamer.lookupParameterNames(method, false); - //} catch (Exception ex) { - // bytecode = new String[0]; - // System.err.println("ReflectiveRegistration.lookupParameterNames failed to read bytecode"); - // //ex.printStackTrace(); - //} - //int bn = bytecode.length; - - for (int i = start; i < n; i++) { - java.lang.reflect.Parameter parameter = parameters[i]; - Flag flag = parameter.getAnnotation(Flag.class); - NamedArg namedArg = parameter.getAnnotation(NamedArg.class); - - boolean isFlag = flag != null; - String name; - if (namedArg != null && !(name = namedArg.value()).isEmpty()) { - } else if (isFlag && !(name = flag.value()).isEmpty()) { - //} else if (i < bn && (name = bytecode[i]) != null && !name.isEmpty()) { - } else { - name = parameter.getName(); - } - - if (isFlag) { - name = '-' + name; - } else { - int idx = 0; - while (name.startsWith("-", idx)) { - idx++; - } - name = name.substring(idx); - } - - out[i - start] = name; - } - - return out; - } - - public static void parseCommandGroup(ICommandAddress address, Class<?> clazz, Object instance) throws CommandParseException { - parseCommandGroup(address, ParameterTypes.getSelector(), clazz, instance); - } - - public static void parseCommandGroup(ICommandAddress address, IParameterTypeSelector selector, Class<?> clazz, Object instance) throws CommandParseException { - boolean requireStatic = instance == null; - if (!requireStatic && !clazz.isInstance(instance)) { - throw new CommandParseException(); - } - - List<Method> methods = new LinkedList<>(Arrays.asList(clazz.getDeclaredMethods())); - - Iterator<Method> it = methods.iterator(); - for (Method method; it.hasNext(); ) { - method = it.next(); - - if (requireStatic && !Modifier.isStatic(method.getModifiers())) { - it.remove(); - continue; - } - - if (method.isAnnotationPresent(CmdParamType.class)) { - it.remove(); - - if (method.getReturnType() != ParameterType.class || method.getParameterCount() != 0) { - throw new CommandParseException("Invalid CmdParamType method: must return ParameterType and take no arguments"); - } - - ParameterType<?, ?> type; - try { - Object inst = Modifier.isStatic(method.getModifiers()) ? null : instance; - type = (ParameterType<?, ?>) method.invoke(inst); - Objects.requireNonNull(type, "ParameterType returned is null"); - } catch (Exception ex) { - throw new CommandParseException("Error occurred whilst getting ParameterType from CmdParamType method '" + method.toGenericString() + "'", ex); - } - - if (selector == ParameterTypes.getSelector()) { - selector = new MapBasedParameterTypeSelector(true); - } - - selector.addType(method.getAnnotation(CmdParamType.class).infolessAlias(), type); - } - } - - GroupMatcherCache groupMatcherCache = new GroupMatcherCache(clazz, address); - for (Method method : methods) { - if (method.isAnnotationPresent(Cmd.class)) { - ICommandAddress parsed = parseCommandMethod(selector, method, instance); - groupMatcherCache.getGroupFor(method).addChild(parsed); - } - } - - } - - private static final class GroupMatcherCache { - private ModifiableCommandAddress groupRootAddress; - private GroupEntry[] matchEntries; - private Pattern[] patterns; - private ModifiableCommandAddress[] addresses; - - GroupMatcherCache(Class<?> clazz, ICommandAddress groupRootAddress) throws CommandParseException { - this.groupRootAddress = (ModifiableCommandAddress) groupRootAddress; - - GroupMatchedCommands groupMatchedCommands = clazz.getAnnotation(GroupMatchedCommands.class); - GroupEntry[] matchEntries = groupMatchedCommands == null ? new GroupEntry[0] : groupMatchedCommands.value(); - - Pattern[] patterns = new Pattern[matchEntries.length]; - for (int i = 0; i < matchEntries.length; i++) { - GroupEntry matchEntry = matchEntries[i]; - if (matchEntry.group().isEmpty() || matchEntry.regex().isEmpty()) { - throw new CommandParseException("Empty group or regex in GroupMatchedCommands entry"); - } - try { - patterns[i] = Pattern.compile(matchEntry.regex()); - } catch (PatternSyntaxException ex) { - throw new CommandParseException(ex); - } - } - - this.matchEntries = matchEntries; - this.patterns = patterns; - this.addresses = new ModifiableCommandAddress[this.matchEntries.length]; - } - - ModifiableCommandAddress getGroupFor(Method method) { - String name = method.getName(); - - GroupEntry[] matchEntries = this.matchEntries; - Pattern[] patterns = this.patterns; - ModifiableCommandAddress[] addresses = this.addresses; - - for (int i = 0; i < matchEntries.length; i++) { - GroupEntry matchEntry = matchEntries[i]; - if (patterns[i].matcher(name).matches()) { - if (addresses[i] == null) { - ChildCommandAddress placeholder = new ChildCommandAddress(); - placeholder.setupAsPlaceholder(matchEntry.group(), matchEntry.groupAliases()); - addresses[i] = placeholder; - groupRootAddress.addChild(placeholder); - generateCommands(placeholder, matchEntry.generatedCommands()); - setDescription(placeholder, matchEntry.description(), matchEntry.shortDescription()); - } - return addresses[i]; - } - } - - return groupRootAddress; - } - - } - - public static ICommandAddress parseCommandMethod(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException { - return new ReflectiveCommand(selector, method, instance).getAddress(); - } - - static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] parameters) throws CommandParseException { - ParameterList list = command.getParameterList(); - - boolean hasReceiverParameter = false; - boolean hasSenderParameter = false; - boolean hasContextParameter = false; - boolean hasContinuationParameter = false; - - int start = 0; - int end = parameters.length; - - Class<?> senderParameterType = null; - - if (parameters.length > start - && command.getInstance() instanceof ICommandInterceptor - && ICommandReceiver.class.isAssignableFrom(parameters[start].getType())) { - hasReceiverParameter = true; - start++; - } - - if (parameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = parameters[start].getType())) { - hasSenderParameter = true; - start++; - } - - if (parameters.length > start && parameters[start].getType() == ExecutionContext.class) { - hasContextParameter = true; - start++; - } - - if (parameters.length > start && parameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) { - hasContinuationParameter = true; - end--; - } - - String[] parameterNames = lookupParameterNames(method, parameters, start); - for (int i = start, n = end; i < n; i++) { - Parameter<?, ?> parameter = parseParameter(selector, method, parameters[i], parameterNames[i - start]); - list.addParameter(parameter); - } - command.setParameterOrder(hasContinuationParameter ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames); - - RequirePermissions cmdPermissions = method.getAnnotation(RequirePermissions.class); - if (cmdPermissions != null) { - for (String permission : cmdPermissions.value()) { - command.addContextFilter(IContextFilter.permission(permission)); - } - - if (cmdPermissions.inherit()) { - command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS); - } - } else { - command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS); - } - - RequireParameters reqPar = method.getAnnotation(RequireParameters.class); - if (reqPar != null) { - list.setRequiredCount(reqPar.value() < 0 ? Integer.MAX_VALUE : reqPar.value()); - } else { - list.setRequiredCount(list.getIndexedParameters().size()); - } - - /* - PreprocessArgs preprocessArgs = method.getAnnotation(PreprocessArgs.class); - if (preprocessArgs != null) { - IArgumentPreProcessor preProcessor = IArgumentPreProcessor.mergeOnTokens(preprocessArgs.tokens(), preprocessArgs.escapeChar()); - list.setArgumentPreProcessor(preProcessor); - }*/ - - Desc desc = method.getAnnotation(Desc.class); - if (desc != null) { - String[] array = desc.value(); - if (array.length == 0) { - command.setDescription(desc.shortVersion()); - } else { - command.setDescription(array); - } - } else { - command.setDescription(); - } - - if (hasSenderParameter && Player.class.isAssignableFrom(senderParameterType)) { - command.addContextFilter(IContextFilter.PLAYER_ONLY); - } else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(senderParameterType)) { - command.addContextFilter(IContextFilter.CONSOLE_ONLY); - } else if (method.isAnnotationPresent(RequirePlayer.class)) { - command.addContextFilter(IContextFilter.PLAYER_ONLY); - } else if (method.isAnnotationPresent(RequireConsole.class)) { - command.addContextFilter(IContextFilter.CONSOLE_ONLY); - } - - list.setRepeatFinalParameter(parameters.length > start && parameters[parameters.length - 1].isVarArgs()); - list.setFinalParameterMayBeFlag(true); - - int flags = 0; - if (hasContinuationParameter) flags |= 1; - flags <<= 1; - if (hasContextParameter) flags |= 1; - flags <<= 1; - if (hasSenderParameter) flags |= 1; - flags <<= 1; - if (hasReceiverParameter) flags |= 1; - return flags; - } - - public static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command) throws CommandParseException { - return parseCommandAttributes(selector, method, command, method.getParameters()); - } - - public static Parameter<?, ?> parseParameter(IParameterTypeSelector selector, Method method, java.lang.reflect.Parameter parameter, String name) throws CommandParseException { - Class<?> type = parameter.getType(); - if (parameter.isVarArgs()) { - type = type.getComponentType(); - } - - Annotation[] annotations = parameter.getAnnotations(); - Flag flag = null; - Annotation typeAnnotation = null; - Desc desc = null; - - for (Annotation annotation : annotations) { - //noinspection StatementWithEmptyBody - if (annotation instanceof NamedArg) { - // do nothing - } else if (annotation instanceof Flag) { - if (flag != null) { - throw new CommandParseException("Multiple flags for the same parameter"); - } - flag = (Flag) annotation; - } else if (annotation instanceof Desc) { - if (desc != null) { - throw new CommandParseException("Multiple descriptions for the same parameter"); - } - desc = (Desc) annotation; - } else { - if (typeAnnotation != null) { - throw new CommandParseException("Multiple parameter type annotations for the same parameter"); - } - typeAnnotation = annotation; - } - } - - if (flag == null && name.startsWith("-")) { - throw new CommandParseException("Non-flag parameter's name starts with -"); - } else if (flag != null && !name.startsWith("-")) { - throw new CommandParseException("Flag parameter's name doesn't start with -"); - } - - ParameterType<Object, Object> parameterType = selector.selectAny(type, typeAnnotation == null ? null : typeAnnotation.getClass()); - if (parameterType == null) { - throw new CommandParseException("IParameter type not found for parameter " + name + " in method " + method.toString()); - } - - Object parameterInfo; - if (typeAnnotation == null) { - parameterInfo = null; - } else try { - parameterInfo = parameterType.getParameterConfig() == null ? null : parameterType.getParameterConfig().getParameterInfo(typeAnnotation); - } catch (Exception ex) { - throw new CommandParseException("Invalid parameter config", ex); - } - - String descString = desc == null ? null : CommandAnnotationUtils.getShortDescription(desc); - - try { - //noinspection unchecked - String flagPermission = flag == null || flag.permission().isEmpty() ? null : flag.permission(); - return new Parameter<>(name, descString, parameterType, parameterInfo, type.isPrimitive(), name.startsWith("-"), flagPermission); - } catch (Exception ex) { - throw new CommandParseException("Invalid parameter", ex); - } - } - - public static void generateCommands(ICommandAddress address, String[] input) { - for (String value : input) { - Consumer<ICommandAddress> consumer = PredefinedCommand.getPredefinedCommandGenerator(value); - if (consumer == null) { - System.out.println("[Command Warning] generated command '" + value + "' could not be found"); - } else { - consumer.accept(address); - } - } - } - - /* - Desired format - - @Cmd({"tp", "tpto"}) - @RequirePermissions("teleport.self") - public (static) String|void onCommand(Player sender, Player target, @Flag("force", permission = "teleport.self.force") boolean force) { - Validate.isTrue(force || !hasTpToggledOff(target), "Target has teleportation disabled. Use -force to ignore"); - sender.teleport(target); - //return - } - - parser needs to: - - see the @Cmd and create a CommandTree for it - - see that it must be a Player executing the command - - add an indexed IParameter for a Player type - - add a flag parameter named force, that consumes no arguments. - - see that setting the force flag requires a permission - */ - - private static void setDescription(ICommandAddress address, String[] array, String shortVersion) { - if (!address.hasCommand()) { - return; - } - - if (array.length == 0) { - address.getCommand().setDescription(shortVersion); - } else { - address.getCommand().setDescription(array); - } - - } - -} +package io.dico.dicore.command.registration.reflect;
+
+import io.dico.dicore.command.*;
+import io.dico.dicore.command.annotation.*;
+import io.dico.dicore.command.annotation.GroupMatchedCommands.GroupEntry;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.ParameterList;
+import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
+import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector;
+import io.dico.dicore.command.parameter.type.ParameterType;
+import io.dico.dicore.command.parameter.type.ParameterTypes;
+import io.dico.dicore.command.predef.PredefinedCommand;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.ConsoleCommandSender;
+import org.bukkit.entity.Player;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+import java.util.function.Consumer;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * Takes care of turning a reflection {@link Method} into a command and more.
+ */
+public class ReflectiveRegistration {
+ /**
+ * This object provides names of the parameters.
+ * Oddly, the AnnotationParanamer extensions require a 'fallback' paranamer to function properly without
+ * requiring ALL parameters to have that flag. This is weird because it should just use the AdaptiveParanamer on an upper level to
+ * determine the name of each individual flag. Oddly this isn't how it works, so the fallback works the same way as the AdaptiveParanamer does.
+ * It's just linked instead of using an array for that part. Then we can use an AdaptiveParanamer for the latest fallback, to get bytecode names
+ * or, finally, to get the Jvm-provided parameter names.
+ */
+ //private static final Paranamer paranamer = new CachingParanamer(new BytecodeReadingParanamer());
+ @SuppressWarnings("StatementWithEmptyBody")
+ private static String[] lookupParameterNames(Method method, java.lang.reflect.Parameter[] parameters, int start) {
+ int n = parameters.length;
+ String[] out = new String[n - start];
+
+ //String[] bytecode;
+ //try {
+ // bytecode = paranamer.lookupParameterNames(method, false);
+ //} catch (Exception ex) {
+ // bytecode = new String[0];
+ // System.err.println("ReflectiveRegistration.lookupParameterNames failed to read bytecode");
+ // //ex.printStackTrace();
+ //}
+ //int bn = bytecode.length;
+
+ for (int i = start; i < n; i++) {
+ java.lang.reflect.Parameter parameter = parameters[i];
+ Flag flag = parameter.getAnnotation(Flag.class);
+ NamedArg namedArg = parameter.getAnnotation(NamedArg.class);
+
+ boolean isFlag = flag != null;
+ String name;
+ if (namedArg != null && !(name = namedArg.value()).isEmpty()) {
+ } else if (isFlag && !(name = flag.value()).isEmpty()) {
+ //} else if (i < bn && (name = bytecode[i]) != null && !name.isEmpty()) {
+ } else {
+ name = parameter.getName();
+ }
+
+ if (isFlag) {
+ name = '-' + name;
+ } else {
+ int idx = 0;
+ while (name.startsWith("-", idx)) {
+ idx++;
+ }
+ name = name.substring(idx);
+ }
+
+ out[i - start] = name;
+ }
+
+ return out;
+ }
+
+ public static void parseCommandGroup(ICommandAddress address, Class<?> clazz, Object instance) throws CommandParseException {
+ parseCommandGroup(address, ParameterTypes.getSelector(), clazz, instance);
+ }
+
+ public static void parseCommandGroup(ICommandAddress address, IParameterTypeSelector selector, Class<?> clazz, Object instance) throws CommandParseException {
+ boolean requireStatic = instance == null;
+ if (!requireStatic && !clazz.isInstance(instance)) {
+ throw new CommandParseException();
+ }
+
+ List<Method> methods = new LinkedList<>(Arrays.asList(clazz.getDeclaredMethods()));
+
+ Iterator<Method> it = methods.iterator();
+ for (Method method; it.hasNext(); ) {
+ method = it.next();
+
+ if (requireStatic && !Modifier.isStatic(method.getModifiers())) {
+ it.remove();
+ continue;
+ }
+
+ if (method.isAnnotationPresent(CmdParamType.class)) {
+ it.remove();
+
+ if (method.getReturnType() != ParameterType.class || method.getParameterCount() != 0) {
+ throw new CommandParseException("Invalid CmdParamType method: must return ParameterType and take no arguments");
+ }
+
+ ParameterType<?, ?> type;
+ try {
+ Object inst = Modifier.isStatic(method.getModifiers()) ? null : instance;
+ type = (ParameterType<?, ?>) method.invoke(inst);
+ Objects.requireNonNull(type, "ParameterType returned is null");
+ } catch (Exception ex) {
+ throw new CommandParseException("Error occurred whilst getting ParameterType from CmdParamType method '" + method.toGenericString() + "'", ex);
+ }
+
+ if (selector == ParameterTypes.getSelector()) {
+ selector = new MapBasedParameterTypeSelector(true);
+ }
+
+ selector.addType(method.getAnnotation(CmdParamType.class).infolessAlias(), type);
+ }
+ }
+
+ GroupMatcherCache groupMatcherCache = new GroupMatcherCache(clazz, address);
+ for (Method method : methods) {
+ if (method.isAnnotationPresent(Cmd.class)) {
+ ICommandAddress parsed = parseCommandMethod(selector, method, instance);
+ groupMatcherCache.getGroupFor(method).addChild(parsed);
+ }
+ }
+
+ }
+
+ private static final class GroupMatcherCache {
+ private ModifiableCommandAddress groupRootAddress;
+ private GroupEntry[] matchEntries;
+ private Pattern[] patterns;
+ private ModifiableCommandAddress[] addresses;
+
+ GroupMatcherCache(Class<?> clazz, ICommandAddress groupRootAddress) throws CommandParseException {
+ this.groupRootAddress = (ModifiableCommandAddress) groupRootAddress;
+
+ GroupMatchedCommands groupMatchedCommands = clazz.getAnnotation(GroupMatchedCommands.class);
+ GroupEntry[] matchEntries = groupMatchedCommands == null ? new GroupEntry[0] : groupMatchedCommands.value();
+
+ Pattern[] patterns = new Pattern[matchEntries.length];
+ for (int i = 0; i < matchEntries.length; i++) {
+ GroupEntry matchEntry = matchEntries[i];
+ if (matchEntry.group().isEmpty() || matchEntry.regex().isEmpty()) {
+ throw new CommandParseException("Empty group or regex in GroupMatchedCommands entry");
+ }
+ try {
+ patterns[i] = Pattern.compile(matchEntry.regex());
+ } catch (PatternSyntaxException ex) {
+ throw new CommandParseException(ex);
+ }
+ }
+
+ this.matchEntries = matchEntries;
+ this.patterns = patterns;
+ this.addresses = new ModifiableCommandAddress[this.matchEntries.length];
+ }
+
+ ModifiableCommandAddress getGroupFor(Method method) {
+ String name = method.getName();
+
+ GroupEntry[] matchEntries = this.matchEntries;
+ Pattern[] patterns = this.patterns;
+ ModifiableCommandAddress[] addresses = this.addresses;
+
+ for (int i = 0; i < matchEntries.length; i++) {
+ GroupEntry matchEntry = matchEntries[i];
+ if (patterns[i].matcher(name).matches()) {
+ if (addresses[i] == null) {
+ ChildCommandAddress placeholder = new ChildCommandAddress();
+ placeholder.setupAsPlaceholder(matchEntry.group(), matchEntry.groupAliases());
+ addresses[i] = placeholder;
+ groupRootAddress.addChild(placeholder);
+ generateCommands(placeholder, matchEntry.generatedCommands());
+ setDescription(placeholder, matchEntry.description(), matchEntry.shortDescription());
+ }
+ return addresses[i];
+ }
+ }
+
+ return groupRootAddress;
+ }
+
+ }
+
+ public static ICommandAddress parseCommandMethod(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException {
+ return new ReflectiveCommand(selector, method, instance).getAddress();
+ }
+
+ static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] parameters) throws CommandParseException {
+ ParameterList list = command.getParameterList();
+
+ boolean hasReceiverParameter = false;
+ boolean hasSenderParameter = false;
+ boolean hasContextParameter = false;
+ boolean hasContinuationParameter = false;
+
+ int start = 0;
+ int end = parameters.length;
+
+ Class<?> senderParameterType = null;
+
+ if (parameters.length > start
+ && command.getInstance() instanceof ICommandInterceptor
+ && ICommandReceiver.class.isAssignableFrom(parameters[start].getType())) {
+ hasReceiverParameter = true;
+ start++;
+ }
+
+ if (parameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = parameters[start].getType())) {
+ hasSenderParameter = true;
+ start++;
+ }
+
+ if (parameters.length > start && parameters[start].getType() == ExecutionContext.class) {
+ hasContextParameter = true;
+ start++;
+ }
+
+ if (parameters.length > start && parameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) {
+ hasContinuationParameter = true;
+ end--;
+ }
+
+ String[] parameterNames = lookupParameterNames(method, parameters, start);
+ for (int i = start, n = end; i < n; i++) {
+ Parameter<?, ?> parameter = parseParameter(selector, method, parameters[i], parameterNames[i - start]);
+ list.addParameter(parameter);
+ }
+ command.setParameterOrder(hasContinuationParameter ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames);
+
+ RequirePermissions cmdPermissions = method.getAnnotation(RequirePermissions.class);
+ if (cmdPermissions != null) {
+ for (String permission : cmdPermissions.value()) {
+ command.addContextFilter(IContextFilter.permission(permission));
+ }
+
+ if (cmdPermissions.inherit()) {
+ command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS);
+ }
+ } else {
+ command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS);
+ }
+
+ RequireParameters reqPar = method.getAnnotation(RequireParameters.class);
+ if (reqPar != null) {
+ list.setRequiredCount(reqPar.value() < 0 ? Integer.MAX_VALUE : reqPar.value());
+ } else {
+ list.setRequiredCount(list.getIndexedParameters().size());
+ }
+
+ /*
+ PreprocessArgs preprocessArgs = method.getAnnotation(PreprocessArgs.class);
+ if (preprocessArgs != null) {
+ IArgumentPreProcessor preProcessor = IArgumentPreProcessor.mergeOnTokens(preprocessArgs.tokens(), preprocessArgs.escapeChar());
+ list.setArgumentPreProcessor(preProcessor);
+ }*/
+
+ Desc desc = method.getAnnotation(Desc.class);
+ if (desc != null) {
+ String[] array = desc.value();
+ if (array.length == 0) {
+ command.setDescription(desc.shortVersion());
+ } else {
+ command.setDescription(array);
+ }
+ } else {
+ command.setDescription();
+ }
+
+ if (hasSenderParameter && Player.class.isAssignableFrom(senderParameterType)) {
+ command.addContextFilter(IContextFilter.PLAYER_ONLY);
+ } else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(senderParameterType)) {
+ command.addContextFilter(IContextFilter.CONSOLE_ONLY);
+ } else if (method.isAnnotationPresent(RequirePlayer.class)) {
+ command.addContextFilter(IContextFilter.PLAYER_ONLY);
+ } else if (method.isAnnotationPresent(RequireConsole.class)) {
+ command.addContextFilter(IContextFilter.CONSOLE_ONLY);
+ }
+
+ list.setRepeatFinalParameter(parameters.length > start && parameters[parameters.length - 1].isVarArgs());
+ list.setFinalParameterMayBeFlag(true);
+
+ int flags = 0;
+ if (hasContinuationParameter) flags |= 1;
+ flags <<= 1;
+ if (hasContextParameter) flags |= 1;
+ flags <<= 1;
+ if (hasSenderParameter) flags |= 1;
+ flags <<= 1;
+ if (hasReceiverParameter) flags |= 1;
+ return flags;
+ }
+
+ public static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command) throws CommandParseException {
+ return parseCommandAttributes(selector, method, command, method.getParameters());
+ }
+
+ public static Parameter<?, ?> parseParameter(IParameterTypeSelector selector, Method method, java.lang.reflect.Parameter parameter, String name) throws CommandParseException {
+ Class<?> type = parameter.getType();
+ if (parameter.isVarArgs()) {
+ type = type.getComponentType();
+ }
+
+ Annotation[] annotations = parameter.getAnnotations();
+ Flag flag = null;
+ Annotation typeAnnotation = null;
+ Desc desc = null;
+
+ for (Annotation annotation : annotations) {
+ //noinspection StatementWithEmptyBody
+ if (annotation instanceof NamedArg) {
+ // do nothing
+ } else if (annotation instanceof Flag) {
+ if (flag != null) {
+ throw new CommandParseException("Multiple flags for the same parameter");
+ }
+ flag = (Flag) annotation;
+ } else if (annotation instanceof Desc) {
+ if (desc != null) {
+ throw new CommandParseException("Multiple descriptions for the same parameter");
+ }
+ desc = (Desc) annotation;
+ } else {
+ if (typeAnnotation != null) {
+ throw new CommandParseException("Multiple parameter type annotations for the same parameter");
+ }
+ typeAnnotation = annotation;
+ }
+ }
+
+ if (flag == null && name.startsWith("-")) {
+ throw new CommandParseException("Non-flag parameter's name starts with -");
+ } else if (flag != null && !name.startsWith("-")) {
+ throw new CommandParseException("Flag parameter's name doesn't start with -");
+ }
+
+ ParameterType<Object, Object> parameterType = selector.selectAny(type, typeAnnotation == null ? null : typeAnnotation.getClass());
+ if (parameterType == null) {
+ throw new CommandParseException("IParameter type not found for parameter " + name + " in method " + method.toString());
+ }
+
+ Object parameterInfo;
+ if (typeAnnotation == null) {
+ parameterInfo = null;
+ } else try {
+ parameterInfo = parameterType.getParameterConfig() == null ? null : parameterType.getParameterConfig().getParameterInfo(typeAnnotation);
+ } catch (Exception ex) {
+ throw new CommandParseException("Invalid parameter config", ex);
+ }
+
+ String descString = desc == null ? null : CommandAnnotationUtils.getShortDescription(desc);
+
+ try {
+ //noinspection unchecked
+ String flagPermission = flag == null || flag.permission().isEmpty() ? null : flag.permission();
+ return new Parameter<>(name, descString, parameterType, parameterInfo, type.isPrimitive(), name.startsWith("-"), flagPermission);
+ } catch (Exception ex) {
+ throw new CommandParseException("Invalid parameter", ex);
+ }
+ }
+
+ public static void generateCommands(ICommandAddress address, String[] input) {
+ for (String value : input) {
+ Consumer<ICommandAddress> consumer = PredefinedCommand.getPredefinedCommandGenerator(value);
+ if (consumer == null) {
+ System.out.println("[Command Warning] generated command '" + value + "' could not be found");
+ } else {
+ consumer.accept(address);
+ }
+ }
+ }
+
+ /*
+ Desired format
+
+ @Cmd({"tp", "tpto"})
+ @RequirePermissions("teleport.self")
+ public (static) String|void onCommand(Player sender, Player target, @Flag("force", permission = "teleport.self.force") boolean force) {
+ Validate.isTrue(force || !hasTpToggledOff(target), "Target has teleportation disabled. Use -force to ignore");
+ sender.teleport(target);
+ //return
+ }
+
+ parser needs to:
+ - see the @Cmd and create a CommandTree for it
+ - see that it must be a Player executing the command
+ - add an indexed IParameter for a Player type
+ - add a flag parameter named force, that consumes no arguments.
+ - see that setting the force flag requires a permission
+ */
+
+ private static void setDescription(ICommandAddress address, String[] array, String shortVersion) {
+ if (!address.hasCommand()) {
+ return;
+ }
+
+ if (array.length == 0) {
+ address.getCommand().setDescription(shortVersion);
+ } else {
+ address.getCommand().setDescription(array);
+ }
+
+ }
+
+}
|