From 0f196f59c6a4cb76ab8409da62ff1f35505f94a8 Mon Sep 17 00:00:00 2001 From: Dico Karssiens Date: Sun, 11 Nov 2018 14:06:45 +0000 Subject: Changes I made before breaking my local repository. Hoping this works. --- .../reflect/ReflectiveRegistration.java | 830 ++++++++++----------- 1 file changed, 415 insertions(+), 415 deletions(-) (limited to 'dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java') 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 methods = new LinkedList<>(Arrays.asList(clazz.getDeclaredMethods())); - - Iterator 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 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 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 methods = new LinkedList<>(Arrays.asList(clazz.getDeclaredMethods())); + + Iterator 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 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 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); + } + + } + +} -- cgit v1.2.3 From 5ef2584fdb6e4db482aa4c57e6ecf0202c67a48d Mon Sep 17 00:00:00 2001 From: Dico Karssiens Date: Sat, 17 Nov 2018 21:32:43 +0000 Subject: Tweak some command stuff, clear/swap entities --- .../reflect/ReflectiveRegistration.java | 821 ++++++++++----------- 1 file changed, 406 insertions(+), 415 deletions(-) (limited to 'dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java') 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 93ac0ee..ddd5420 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,406 @@ -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 methods = new LinkedList<>(Arrays.asList(clazz.getDeclaredMethods())); - - Iterator 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 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 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; + +import static io.dico.dicore.command.registration.reflect.ReflectiveCallFlags.*; + +/** + * 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 methods = new LinkedList<>(Arrays.asList(clazz.getDeclaredMethods())); + + Iterator 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[] callParameters) throws CommandParseException { + ParameterList list = command.getParameterList(); + + Class senderParameterType = null; + int flags = 0; + int start = 0; + int end = callParameters.length; + + if (callParameters.length > start + && command.getInstance() instanceof ICommandInterceptor + && ICommandReceiver.class.isAssignableFrom(callParameters[start].getType())) { + flags |= RECEIVER_BIT; + ++start; + } + + if (callParameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = callParameters[start].getType())) { + flags |= SENDER_BIT; + ++start; + } + + if (callParameters.length > start && callParameters[start].getType() == ExecutionContext.class) { + flags |= CONTEXT_BIT; + ++start; + } + + if (callParameters.length > start && callParameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) { + flags |= CONTINUATION_BIT; + --end; + } + + String[] parameterNames = lookupParameterNames(method, callParameters, start); + for (int i = start, n = end; i < n; i++) { + Parameter parameter = parseParameter(selector, method, callParameters[i], parameterNames[i - start]); + list.addParameter(parameter); + } + + command.setParameterOrder(hasCallArg(flags, CONTINUATION_BIT) ? 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(); + } + + boolean hasSenderParameter = hasCallArg(flags, SENDER_BIT); + 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(callParameters.length > start && callParameters[callParameters.length - 1].isVarArgs()); + list.setFinalParameterMayBeFlag(true); + + 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 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 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); + } + + } + +} -- cgit v1.2.3