diff --git a/docs/acf-core/co/aikar/commands/BaseCommand.html b/docs/acf-core/co/aikar/commands/BaseCommand.html index cd9997d0..6e88bd26 100644 --- a/docs/acf-core/co/aikar/commands/BaseCommand.html +++ b/docs/acf-core/co/aikar/commands/BaseCommand.html @@ -340,7 +340,7 @@ extends
  • BaseCommand

    -
    public BaseCommand()
    +
    public BaseCommand()
  • @@ -350,7 +350,7 @@ extends

    BaseCommand

    @Deprecated
    -public BaseCommand(@Nullable
    +public BaseCommand(@Nullable
                                    @Nullable String cmd)
    Deprecated. Please switch to CommandAlias for defining all root commands.
    Constructor based defining of commands will be removed in the next version bump.
    @@ -374,7 +374,7 @@ public 
  • getLastCommandOperationContext

    -
    public CommandOperationContext getLastCommandOperationContext()
    +
    public CommandOperationContext getLastCommandOperationContext()
    Returns a reference to the last used CommandOperationContext. This method is ThreadLocal, in that it can only be used on a thread that has executed a command
    @@ -388,7 +388,7 @@ public 
  • getExecCommandLabel

    -
    public String getExecCommandLabel()
    +
    public String getExecCommandLabel()
    Gets the root command name that the user actually typed
    Returns:
    @@ -402,7 +402,7 @@ public 
  • getExecSubcommand

    -
    public String getExecSubcommand()
    +
    public String getExecSubcommand()
    Gets the actual sub command name the user typed
    Returns:
    @@ -416,7 +416,7 @@ public 
  • getOrigArgs

    -
    public String[] getOrigArgs()
    +
    public String[] getOrigArgs()
    Gets the actual args in string form the user typed
    Returns:
    @@ -430,7 +430,7 @@ public 
  • getCurrentCommandIssuer

    -
    public CommandIssuer getCurrentCommandIssuer()
    +
    public CommandIssuer getCurrentCommandIssuer()
    Gets the current command issuer.
    Returns:
    @@ -444,7 +444,7 @@ public 
  • getCurrentCommandManager

    -
    public CommandManager getCurrentCommandManager()
    +
    public CommandManager getCurrentCommandManager()
    Gets the current command manager.
    Returns:
    @@ -459,7 +459,7 @@ public 

    canExecute

    @Deprecated
    -public boolean canExecute(CommandIssuer issuer,
    +public boolean canExecute(CommandIssuer issuer,
                                           RegisteredCommand<?> cmd)
    Deprecated. See CommandConditions
    Please use command conditions for restricting execution
    @@ -477,7 +477,7 @@ public boolean 
  • tabComplete

    -
    public List<StringtabComplete(CommandIssuer issuer,
    +
    public List<StringtabComplete(CommandIssuer issuer,
                                     String commandLabel,
                                     String[] args)
    Gets tab completed data from the given command from the user.
    @@ -497,7 +497,7 @@ public boolean 
  • tabComplete

    -
    public List<StringtabComplete(CommandIssuer issuer,
    +
    public List<StringtabComplete(CommandIssuer issuer,
                                     String commandLabel,
                                     String[] args,
                                     boolean isAsync)
    @@ -524,7 +524,7 @@ public boolean 
     

    getCommandHelp

    @Deprecated
    -public CommandHelp getCommandHelp()
    +public CommandHelp getCommandHelp()
    Deprecated. Unstable API
  • @@ -535,7 +535,7 @@ public 

    showCommandHelp

    @Deprecated
    -public void showCommandHelp()
    +public void showCommandHelp()
    Deprecated. Unstable API
  • @@ -545,7 +545,7 @@ public void 
  • help

    -
    public void help(Object issuer,
    +
    public void help(Object issuer,
                      String[] args)
  • @@ -555,7 +555,7 @@ public void 
  • help

    -
    public void help(CommandIssuer issuer,
    +
    public void help(CommandIssuer issuer,
                      String[] args)
  • @@ -565,7 +565,7 @@ public void 
  • doHelp

    -
    public void doHelp(Object issuer,
    +
    public void doHelp(Object issuer,
                        String... args)
  • @@ -575,7 +575,7 @@ public void 
  • doHelp

    -
    public void doHelp(CommandIssuer issuer,
    +
    public void doHelp(CommandIssuer issuer,
                        String... args)
  • @@ -585,7 +585,7 @@ public void 
  • showSyntax

    -
    public void showSyntax(CommandIssuer issuer,
    +
    public void showSyntax(CommandIssuer issuer,
                            RegisteredCommand<?> cmd)
  • @@ -595,7 +595,7 @@ public void 
  • hasPermission

    -
    public boolean hasPermission(Object issuer)
    +
    public boolean hasPermission(Object issuer)
  • @@ -604,7 +604,7 @@ public void 
  • hasPermission

    -
    public boolean hasPermission(CommandIssuer issuer)
    +
    public boolean hasPermission(CommandIssuer issuer)
  • @@ -613,7 +613,7 @@ public void 
  • getRequiredPermissions

    -
    public Set<StringgetRequiredPermissions()
    +
    public Set<StringgetRequiredPermissions()
  • @@ -622,7 +622,7 @@ public void 
  • requiresPermission

    -
    public boolean requiresPermission(String permission)
    +
    public boolean requiresPermission(String permission)
  • @@ -631,7 +631,7 @@ public void 
  • getName

    -
    public String getName()
    +
    public String getName()
  • @@ -640,7 +640,7 @@ public void 
  • getExceptionHandler

    -
    public ExceptionHandler getExceptionHandler()
    +
    public ExceptionHandler getExceptionHandler()
  • @@ -649,7 +649,7 @@ public void 
  • setExceptionHandler

    -
    public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler)
    +
    public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler)
  • @@ -658,7 +658,7 @@ public void 
  • getDefaultRegisteredCommand

    -
    public RegisteredCommand getDefaultRegisteredCommand()
    +
    public RegisteredCommand getDefaultRegisteredCommand()
  • @@ -667,7 +667,7 @@ public void 
  • setContextFlags

    -
    public String setContextFlags(Class<?> cls,
    +
    public String setContextFlags(Class<?> cls,
                                   String flags)
  • @@ -677,7 +677,7 @@ public void 
  • getContextFlags

    -
    public String getContextFlags(Class<?> cls)
    +
    public String getContextFlags(Class<?> cls)
  • @@ -686,7 +686,7 @@ public void 
  • getRegisteredCommands

    -
    public List<RegisteredCommandgetRegisteredCommands()
    +
    public List<RegisteredCommandgetRegisteredCommands()
  • diff --git a/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html b/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html index 7d8c50d8..2fb89386 100644 --- a/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html +++ b/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html @@ -94,751 +94,755 @@ 086 * A map of all the registered commands for this base command, keyed to each potential subcommand to access it. 087 */ 088 final SetMultimap<String, RegisteredCommand> subCommands = HashMultimap.create(); -089 -090 /** -091 * A map of flags to pass to Context Resolution for every parameter of the type. This is like an automatic @Flags on each. -092 */ -093 final Map<Class<?>, String> contextFlags = new HashMap<>(); -094 -095 /** -096 * What method was annoated with {@link PreCommand} to execute before commands. -097 */ -098 @Nullable -099 private Method preCommandHandler; -100 -101 /** -102 * What root command the user actually entered to access the currently executing command -103 */ -104 @SuppressWarnings("WeakerAccess") -105 private String execLabel; -106 /** -107 * What subcommand the user actually entered to access the currently executing command -108 */ -109 @SuppressWarnings("WeakerAccess") -110 private String execSubcommand; -111 /** -112 * What arguments the user actually entered after the root command to access the currently executing command -113 */ -114 @SuppressWarnings("WeakerAccess") -115 private String[] origArgs; -116 -117 /** -118 * The manager this is registered to -119 */ -120 CommandManager<?, ?, ?, ?, ?, ?> manager = null; -121 -122 /** -123 * The command which owns this. This may be null if there are no owners. -124 */ -125 BaseCommand parentCommand; -126 Map<String, RootCommand> registeredCommands = new HashMap<>(); -127 /** -128 * The description of the command. This may be null if no description has been provided. -129 * Used for help documentation -130 */ -131 @Nullable String description; -132 /** -133 * The name of the command. This may be null if no name has been provided. -134 */ -135 @Nullable String commandName; -136 /** -137 * The permission of the command. This may be null if no permission has been provided. -138 */ -139 @Nullable String permission; -140 /** -141 * The conditions of the command. This may be null if no conditions has been provided. -142 */ -143 @Nullable String conditions; -144 /** -145 * Identifies if the command has an explicit help command annotated with {@link HelpCommand} -146 */ -147 boolean hasHelpCommand; -148 -149 /** -150 * The handler of all uncaught exceptions thrown by the user's command implementation. -151 */ -152 private ExceptionHandler exceptionHandler = null; -153 /** -154 * The last operative context data of this command. This may be null if this command hasn't been run yet. -155 */ -156 private final ThreadLocal<CommandOperationContext> lastCommandOperationContext = new ThreadLocal<>(); -157 /** -158 * If a parent exists to this command, and it has a Subcommand annotation, prefix all subcommands in this class with this -159 */ -160 @Nullable -161 private String parentSubcommand; -162 -163 /** -164 * The permissions of the command. -165 */ -166 private final Set<String> permissions = new HashSet<>(); -167 -168 public BaseCommand() { -169 } -170 -171 /** -172 * Constructor based defining of commands will be removed in the next version bump. -173 * -174 * @param cmd -175 * @deprecated Please switch to {@link CommandAlias} for defining all root commands. -176 */ -177 @Deprecated -178 public BaseCommand(@Nullable String cmd) { -179 this.commandName = cmd; -180 } -181 -182 /** -183 * Returns a reference to the last used CommandOperationContext. -184 * This method is ThreadLocal, in that it can only be used on a thread that has executed a command -185 * -186 * @return -187 */ -188 public CommandOperationContext getLastCommandOperationContext() { -189 return lastCommandOperationContext.get(); -190 } -191 -192 /** -193 * Gets the root command name that the user actually typed -194 * -195 * @return Name -196 */ -197 public String getExecCommandLabel() { -198 return execLabel; -199 } -200 -201 /** -202 * Gets the actual sub command name the user typed -203 * -204 * @return Name -205 */ -206 public String getExecSubcommand() { -207 return execSubcommand; -208 } -209 -210 /** -211 * Gets the actual args in string form the user typed -212 * -213 * @return Args -214 */ -215 public String[] getOrigArgs() { -216 return origArgs; -217 } -218 -219 /** -220 * This should be called whenever the command gets registered. -221 * It sets all required fields correctly and injects dependencies. -222 * -223 * @param manager The manager to register as this command's owner and handler. -224 */ -225 void onRegister(CommandManager manager) { -226 onRegister(manager, this.commandName); -227 } -228 -229 /** -230 * This should be called whenever the command gets registered. -231 * It sets all required fields correctly and injects dependencies. -232 * -233 * @param manager The manager to register as this command's owner and handler. -234 * @param cmd The command name to use register with. -235 */ -236 private void onRegister(CommandManager manager, String cmd) { -237 manager.injectDependencies(this); -238 this.manager = manager; -239 -240 final Annotations annotations = manager.getAnnotations(); -241 final Class<? extends BaseCommand> self = this.getClass(); -242 -243 String[] cmdAliases = annotations.getAnnotationValues(self, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE | Annotations.NO_EMPTY); -244 -245 if (cmd == null && cmdAliases != null) { -246 cmd = cmdAliases[0]; -247 } -248 -249 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase(); -250 this.permission = annotations.getAnnotationValue(self, CommandPermission.class, Annotations.REPLACEMENTS); -251 this.description = annotations.getAnnotationValue(self, Description.class, Annotations.NO_EMPTY | Annotations.REPLACEMENTS); -252 this.parentSubcommand = getParentSubcommand(self); -253 this.conditions = annotations.getAnnotationValue(self, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY); -254 -255 registerSubcommands(); -256 computePermissions(); -257 registerSubclasses(cmd); -258 -259 if (cmdAliases != null) { -260 Set<String> cmdList = new HashSet<>(); -261 Collections.addAll(cmdList, cmdAliases); -262 cmdList.remove(cmd); -263 for (String cmdAlias : cmdList) { -264 register(cmdAlias, this); -265 } -266 } -267 -268 if (cmd != null) { -269 register(cmd, this); -270 } -271 } -272 -273 /** -274 * This recursively registers all subclasses of the command as subcommands, if they are of type {@link BaseCommand}. -275 * -276 * @param cmd The command name of this command. -277 */ -278 private void registerSubclasses(String cmd) { -279 for (Class<?> clazz : this.getClass().getDeclaredClasses()) { -280 if (BaseCommand.class.isAssignableFrom(clazz)) { -281 try { -282 BaseCommand subCommand = null; -283 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); -284 for (Constructor<?> declaredConstructor : declaredConstructors) { -285 -286 declaredConstructor.setAccessible(true); -287 Parameter[] parameters = declaredConstructor.getParameters(); -288 if (parameters.length == 1) { -289 subCommand = (BaseCommand) declaredConstructor.newInstance(this); -290 } else { -291 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")"); -292 } -293 } -294 if (subCommand != null) { -295 subCommand.parentCommand = this; -296 subCommand.onRegister(manager, cmd); -297 this.subCommands.putAll(subCommand.subCommands); -298 this.registeredCommands.putAll(subCommand.registeredCommands); -299 } else { -300 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName()); -301 } -302 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { -303 this.manager.log(LogLevel.ERROR, "Error registering subclass", e); -304 } -305 } -306 } -307 } -308 -309 /** -310 * This registers all subcommands of the command. -311 */ -312 private void registerSubcommands() { -313 final Annotations annotations = manager.getAnnotations(); -314 boolean foundCatchUnknown = false; -315 boolean isParentEmpty = parentSubcommand == null || parentSubcommand.isEmpty(); -316 Set<Method> methods = new LinkedHashSet<>(); -317 Collections.addAll(methods, this.getClass().getDeclaredMethods()); -318 Collections.addAll(methods, this.getClass().getMethods()); -319 -320 for (Method method : methods) { -321 method.setAccessible(true); -322 String sublist = null; -323 String sub = getSubcommandValue(method); -324 final String helpCommand = annotations.getAnnotationValue(method, HelpCommand.class, Annotations.NOTHING); -325 final String commandAliases = annotations.getAnnotationValue(method, CommandAlias.class, Annotations.NOTHING); -326 -327 if (annotations.hasAnnotation(method, Default.class)) { -328 if (!isParentEmpty) { -329 sub = parentSubcommand; -330 } else { -331 registerSubcommand(method, DEFAULT); -332 } -333 } -334 -335 if (sub != null) { -336 sublist = sub; -337 } else if (commandAliases != null) { -338 sublist = commandAliases; -339 } else if (helpCommand != null) { -340 sublist = helpCommand; -341 hasHelpCommand = true; -342 } -343 -344 boolean preCommand = annotations.hasAnnotation(method, PreCommand.class); -345 boolean hasCatchUnknown = annotations.hasAnnotation(method, CatchUnknown.class) || -346 annotations.hasAnnotation(method, CatchAll.class) || -347 annotations.hasAnnotation(method, UnknownHandler.class); -348 -349 if (hasCatchUnknown || (!foundCatchUnknown && helpCommand != null)) { -350 if (!foundCatchUnknown) { -351 if (hasCatchUnknown) { -352 this.subCommands.get(CATCHUNKNOWN).clear(); -353 foundCatchUnknown = true; -354 } -355 registerSubcommand(method, CATCHUNKNOWN); -356 } else { -357 ACFUtil.sneaky(new IllegalStateException("Multiple @CatchUnknown/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); -358 } -359 } else if (preCommand) { -360 if (this.preCommandHandler == null) { -361 this.preCommandHandler = method; -362 } else { -363 ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); -364 } -365 } -366 if (Objects.equals(method.getDeclaringClass(), this.getClass()) && sublist != null) { -367 registerSubcommand(method, sublist); -368 } -369 } -370 } -371 -372 /** -373 * This registers all the permissions required to execute this command. -374 */ -375 private void computePermissions() { -376 this.permissions.clear(); -377 if (this.permission != null && !this.permission.isEmpty()) { -378 this.permissions.addAll(Arrays.asList(ACFPatterns.COMMA.split(this.permission))); -379 } -380 if (this.parentCommand != null) { -381 this.permissions.addAll(this.parentCommand.getRequiredPermissions()); -382 } -383 } -384 -385 /** -386 * Gets the subcommand name of the method given. -387 * -388 * @param method The method to check. -389 * @return The name of the subcommand. It returns null if the input doesn't have {@link Subcommand} attached. -390 */ -391 private String getSubcommandValue(Method method) { -392 final String sub = manager.getAnnotations().getAnnotationValue(method, Subcommand.class, Annotations.NOTHING); -393 if (sub == null) { -394 return null; -395 } -396 Class<?> clazz = method.getDeclaringClass(); -397 String parent = getParentSubcommand(clazz); -398 return parent == null || parent.isEmpty() ? sub : parent + " " + sub; -399 } -400 -401 private String getParentSubcommand(Class<?> clazz) { -402 List<String> subList = new ArrayList<>(); -403 while (clazz != null) { -404 String sub = manager.getAnnotations().getAnnotationValue(clazz, Subcommand.class, Annotations.NOTHING); -405 if (sub != null) { -406 subList.add(sub); -407 } -408 clazz = clazz.getEnclosingClass(); -409 } -410 Collections.reverse(subList); -411 return ACFUtil.join(subList, " "); -412 } -413 -414 /** -415 * Registers the given {@link BaseCommand cmd} as a child of the {@link RootCommand} linked to the name given. -416 * -417 * @param name Name of the parent to cmd. -418 * @param cmd The {@link BaseCommand} to add as a child to the {@link RootCommand} owned name field. -419 */ -420 private void register(String name, BaseCommand cmd) { -421 String nameLower = name.toLowerCase(); -422 RootCommand rootCommand = manager.obtainRootCommand(nameLower); -423 rootCommand.addChild(cmd); -424 -425 this.registeredCommands.put(nameLower, rootCommand); -426 } -427 -428 /** -429 * Registers the given {@link Method} as a subcommand. -430 * -431 * @param method The method to register as a subcommand. -432 * @param subCommand The subcommand's name(s). -433 */ -434 private void registerSubcommand(Method method, String subCommand) { -435 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase()); -436 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand); -437 // Must run getSubcommandPossibility BEFORE we rewrite it just after this. -438 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts); -439 -440 // Strip pipes off for auto complete addition -441 for (int i = 0; i < subCommandParts.length; i++) { -442 String[] split = ACFPatterns.PIPE.split(subCommandParts[i]); -443 if (split.length == 0 || split[0].isEmpty()) { -444 throw new IllegalArgumentException("Invalid @Subcommand configuration for " + method.getName() + " - parts can not start with | or be empty"); -445 } -446 subCommandParts[i] = split[0]; -447 } -448 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " "); -449 final String[] aliasNames = manager.getAnnotations().getAnnotationValues(method, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE); -450 -451 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " "; -452 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand); -453 -454 for (String subcmd : cmdList) { -455 subCommands.put(subcmd, cmd); -456 } -457 cmd.addSubcommands(cmdList); -458 -459 if (aliasNames != null) { -460 for (String name : aliasNames) { -461 register(name, new ForwardingCommand(this, cmd, subCommandParts)); -462 } -463 } -464 } -465 -466 /** -467 * Takes a string like "foo|bar baz|qux" and generates a list of -468 * - foo baz -469 * - foo qux -470 * - bar baz -471 * - bar qux -472 * <p> -473 * For every possible sub command combination -474 * -475 * @param subCommandParts -476 * @return List of all sub command possibilities -477 */ -478 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) { -479 int i = 0; -480 Set<String> current = null; -481 while (true) { -482 Set<String> newList = new HashSet<>(); -483 -484 if (i < subCommandParts.length) { -485 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) { -486 if (current != null) { -487 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList())); -488 } else { -489 newList.add(s1); -490 } -491 } -492 } -493 -494 if (i + 1 < subCommandParts.length) { -495 current = newList; -496 i = i + 1; -497 continue; -498 } -499 -500 return newList; -501 } -502 } +089 final Set<BaseCommand> subScopes = new HashSet<>(); +090 +091 /** +092 * A map of flags to pass to Context Resolution for every parameter of the type. This is like an automatic @Flags on each. +093 */ +094 final Map<Class<?>, String> contextFlags = new HashMap<>(); +095 +096 /** +097 * What method was annoated with {@link PreCommand} to execute before commands. +098 */ +099 @Nullable +100 private Method preCommandHandler; +101 +102 /** +103 * What root command the user actually entered to access the currently executing command +104 */ +105 @SuppressWarnings("WeakerAccess") +106 private String execLabel; +107 /** +108 * What subcommand the user actually entered to access the currently executing command +109 */ +110 @SuppressWarnings("WeakerAccess") +111 private String execSubcommand; +112 /** +113 * What arguments the user actually entered after the root command to access the currently executing command +114 */ +115 @SuppressWarnings("WeakerAccess") +116 private String[] origArgs; +117 +118 /** +119 * The manager this is registered to +120 */ +121 CommandManager<?, ?, ?, ?, ?, ?> manager = null; +122 +123 /** +124 * The command which owns this. This may be null if there are no owners. +125 */ +126 BaseCommand parentCommand; +127 Map<String, RootCommand> registeredCommands = new HashMap<>(); +128 /** +129 * The description of the command. This may be null if no description has been provided. +130 * Used for help documentation +131 */ +132 @Nullable String description; +133 /** +134 * The name of the command. This may be null if no name has been provided. +135 */ +136 @Nullable String commandName; +137 /** +138 * The permission of the command. This may be null if no permission has been provided. +139 */ +140 @Nullable String permission; +141 /** +142 * The conditions of the command. This may be null if no conditions has been provided. +143 */ +144 @Nullable String conditions; +145 /** +146 * Identifies if the command has an explicit help command annotated with {@link HelpCommand} +147 */ +148 boolean hasHelpCommand; +149 +150 /** +151 * The handler of all uncaught exceptions thrown by the user's command implementation. +152 */ +153 private ExceptionHandler exceptionHandler = null; +154 /** +155 * The last operative context data of this command. This may be null if this command hasn't been run yet. +156 */ +157 private final ThreadLocal<CommandOperationContext> lastCommandOperationContext = new ThreadLocal<>(); +158 /** +159 * If a parent exists to this command, and it has a Subcommand annotation, prefix all subcommands in this class with this +160 */ +161 @Nullable +162 private String parentSubcommand; +163 +164 /** +165 * The permissions of the command. +166 */ +167 private final Set<String> permissions = new HashSet<>(); +168 +169 public BaseCommand() { +170 } +171 +172 /** +173 * Constructor based defining of commands will be removed in the next version bump. +174 * +175 * @param cmd +176 * @deprecated Please switch to {@link CommandAlias} for defining all root commands. +177 */ +178 @Deprecated +179 public BaseCommand(@Nullable String cmd) { +180 this.commandName = cmd; +181 } +182 +183 /** +184 * Returns a reference to the last used CommandOperationContext. +185 * This method is ThreadLocal, in that it can only be used on a thread that has executed a command +186 * +187 * @return +188 */ +189 public CommandOperationContext getLastCommandOperationContext() { +190 return lastCommandOperationContext.get(); +191 } +192 +193 /** +194 * Gets the root command name that the user actually typed +195 * +196 * @return Name +197 */ +198 public String getExecCommandLabel() { +199 return execLabel; +200 } +201 +202 /** +203 * Gets the actual sub command name the user typed +204 * +205 * @return Name +206 */ +207 public String getExecSubcommand() { +208 return execSubcommand; +209 } +210 +211 /** +212 * Gets the actual args in string form the user typed +213 * +214 * @return Args +215 */ +216 public String[] getOrigArgs() { +217 return origArgs; +218 } +219 +220 /** +221 * This should be called whenever the command gets registered. +222 * It sets all required fields correctly and injects dependencies. +223 * +224 * @param manager The manager to register as this command's owner and handler. +225 */ +226 void onRegister(CommandManager manager) { +227 onRegister(manager, this.commandName); +228 } +229 +230 /** +231 * This should be called whenever the command gets registered. +232 * It sets all required fields correctly and injects dependencies. +233 * +234 * @param manager The manager to register as this command's owner and handler. +235 * @param cmd The command name to use register with. +236 */ +237 private void onRegister(CommandManager manager, String cmd) { +238 manager.injectDependencies(this); +239 this.manager = manager; +240 +241 final Annotations annotations = manager.getAnnotations(); +242 final Class<? extends BaseCommand> self = this.getClass(); +243 +244 String[] cmdAliases = annotations.getAnnotationValues(self, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE | Annotations.NO_EMPTY); +245 +246 if (cmd == null && cmdAliases != null) { +247 cmd = cmdAliases[0]; +248 } +249 +250 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase(); +251 this.permission = annotations.getAnnotationValue(self, CommandPermission.class, Annotations.REPLACEMENTS); +252 this.description = annotations.getAnnotationValue(self, Description.class, Annotations.NO_EMPTY | Annotations.REPLACEMENTS); +253 this.parentSubcommand = getParentSubcommand(self); +254 this.conditions = annotations.getAnnotationValue(self, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY); +255 +256 computePermissions(); // Must be before any subcommands so they can inherit permissions +257 registerSubcommands(); +258 registerSubclasses(cmd); +259 +260 if (cmdAliases != null) { +261 Set<String> cmdList = new HashSet<>(); +262 Collections.addAll(cmdList, cmdAliases); +263 cmdList.remove(cmd); +264 for (String cmdAlias : cmdList) { +265 register(cmdAlias, this); +266 } +267 } +268 +269 if (cmd != null) { +270 register(cmd, this); +271 } +272 } +273 +274 /** +275 * This recursively registers all subclasses of the command as subcommands, if they are of type {@link BaseCommand}. +276 * +277 * @param cmd The command name of this command. +278 */ +279 private void registerSubclasses(String cmd) { +280 for (Class<?> clazz : this.getClass().getDeclaredClasses()) { +281 if (BaseCommand.class.isAssignableFrom(clazz)) { +282 try { +283 BaseCommand subScope = null; +284 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); +285 for (Constructor<?> declaredConstructor : declaredConstructors) { +286 +287 declaredConstructor.setAccessible(true); +288 Parameter[] parameters = declaredConstructor.getParameters(); +289 if (parameters.length == 1) { +290 subScope = (BaseCommand) declaredConstructor.newInstance(this); +291 } else { +292 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")"); +293 } +294 } +295 if (subScope != null) { +296 subScope.parentCommand = this; +297 this.subScopes.add(subScope); +298 subScope.onRegister(manager, cmd); +299 this.subCommands.putAll(subScope.subCommands); +300 this.registeredCommands.putAll(subScope.registeredCommands); +301 } else { +302 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName()); +303 } +304 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { +305 this.manager.log(LogLevel.ERROR, "Error registering subclass", e); +306 } +307 } +308 } +309 } +310 +311 /** +312 * This registers all subcommands of the command. +313 */ +314 private void registerSubcommands() { +315 final Annotations annotations = manager.getAnnotations(); +316 boolean foundCatchUnknown = false; +317 boolean isParentEmpty = parentSubcommand == null || parentSubcommand.isEmpty(); +318 Set<Method> methods = new LinkedHashSet<>(); +319 Collections.addAll(methods, this.getClass().getDeclaredMethods()); +320 Collections.addAll(methods, this.getClass().getMethods()); +321 +322 for (Method method : methods) { +323 method.setAccessible(true); +324 String sublist = null; +325 String sub = getSubcommandValue(method); +326 final String helpCommand = annotations.getAnnotationValue(method, HelpCommand.class, Annotations.NOTHING); +327 final String commandAliases = annotations.getAnnotationValue(method, CommandAlias.class, Annotations.NOTHING); +328 +329 if (annotations.hasAnnotation(method, Default.class)) { +330 if (!isParentEmpty) { +331 sub = parentSubcommand; +332 } else { +333 registerSubcommand(method, DEFAULT); +334 } +335 } +336 +337 if (sub != null) { +338 sublist = sub; +339 } else if (commandAliases != null) { +340 sublist = commandAliases; +341 } else if (helpCommand != null) { +342 sublist = helpCommand; +343 hasHelpCommand = true; +344 } +345 +346 boolean preCommand = annotations.hasAnnotation(method, PreCommand.class); +347 boolean hasCatchUnknown = annotations.hasAnnotation(method, CatchUnknown.class) || +348 annotations.hasAnnotation(method, CatchAll.class) || +349 annotations.hasAnnotation(method, UnknownHandler.class); +350 +351 if (hasCatchUnknown || (!foundCatchUnknown && helpCommand != null)) { +352 if (!foundCatchUnknown) { +353 if (hasCatchUnknown) { +354 this.subCommands.get(CATCHUNKNOWN).clear(); +355 foundCatchUnknown = true; +356 } +357 registerSubcommand(method, CATCHUNKNOWN); +358 } else { +359 ACFUtil.sneaky(new IllegalStateException("Multiple @CatchUnknown/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); +360 } +361 } else if (preCommand) { +362 if (this.preCommandHandler == null) { +363 this.preCommandHandler = method; +364 } else { +365 ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); +366 } +367 } +368 if (Objects.equals(method.getDeclaringClass(), this.getClass()) && sublist != null) { +369 registerSubcommand(method, sublist); +370 } +371 } +372 } +373 +374 /** +375 * This registers all the permissions required to execute this command. +376 */ +377 private void computePermissions() { +378 this.permissions.clear(); +379 if (this.permission != null && !this.permission.isEmpty()) { +380 this.permissions.addAll(Arrays.asList(ACFPatterns.COMMA.split(this.permission))); +381 } +382 if (this.parentCommand != null) { +383 this.permissions.addAll(this.parentCommand.getRequiredPermissions()); +384 } +385 this.subCommands.values().forEach(RegisteredCommand::computePermissions); +386 this.subScopes.forEach(BaseCommand::computePermissions); +387 } +388 +389 /** +390 * Gets the subcommand name of the method given. +391 * +392 * @param method The method to check. +393 * @return The name of the subcommand. It returns null if the input doesn't have {@link Subcommand} attached. +394 */ +395 private String getSubcommandValue(Method method) { +396 final String sub = manager.getAnnotations().getAnnotationValue(method, Subcommand.class, Annotations.NOTHING); +397 if (sub == null) { +398 return null; +399 } +400 Class<?> clazz = method.getDeclaringClass(); +401 String parent = getParentSubcommand(clazz); +402 return parent == null || parent.isEmpty() ? sub : parent + " " + sub; +403 } +404 +405 private String getParentSubcommand(Class<?> clazz) { +406 List<String> subList = new ArrayList<>(); +407 while (clazz != null) { +408 String sub = manager.getAnnotations().getAnnotationValue(clazz, Subcommand.class, Annotations.NOTHING); +409 if (sub != null) { +410 subList.add(sub); +411 } +412 clazz = clazz.getEnclosingClass(); +413 } +414 Collections.reverse(subList); +415 return ACFUtil.join(subList, " "); +416 } +417 +418 /** +419 * Registers the given {@link BaseCommand cmd} as a child of the {@link RootCommand} linked to the name given. +420 * +421 * @param name Name of the parent to cmd. +422 * @param cmd The {@link BaseCommand} to add as a child to the {@link RootCommand} owned name field. +423 */ +424 private void register(String name, BaseCommand cmd) { +425 String nameLower = name.toLowerCase(); +426 RootCommand rootCommand = manager.obtainRootCommand(nameLower); +427 rootCommand.addChild(cmd); +428 +429 this.registeredCommands.put(nameLower, rootCommand); +430 } +431 +432 /** +433 * Registers the given {@link Method} as a subcommand. +434 * +435 * @param method The method to register as a subcommand. +436 * @param subCommand The subcommand's name(s). +437 */ +438 private void registerSubcommand(Method method, String subCommand) { +439 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase()); +440 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand); +441 // Must run getSubcommandPossibility BEFORE we rewrite it just after this. +442 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts); +443 +444 // Strip pipes off for auto complete addition +445 for (int i = 0; i < subCommandParts.length; i++) { +446 String[] split = ACFPatterns.PIPE.split(subCommandParts[i]); +447 if (split.length == 0 || split[0].isEmpty()) { +448 throw new IllegalArgumentException("Invalid @Subcommand configuration for " + method.getName() + " - parts can not start with | or be empty"); +449 } +450 subCommandParts[i] = split[0]; +451 } +452 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " "); +453 final String[] aliasNames = manager.getAnnotations().getAnnotationValues(method, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE); +454 +455 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " "; +456 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand); +457 +458 for (String subcmd : cmdList) { +459 subCommands.put(subcmd, cmd); +460 } +461 cmd.addSubcommands(cmdList); +462 +463 if (aliasNames != null) { +464 for (String name : aliasNames) { +465 register(name, new ForwardingCommand(this, cmd, subCommandParts)); +466 } +467 } +468 } +469 +470 /** +471 * Takes a string like "foo|bar baz|qux" and generates a list of +472 * - foo baz +473 * - foo qux +474 * - bar baz +475 * - bar qux +476 * <p> +477 * For every possible sub command combination +478 * +479 * @param subCommandParts +480 * @return List of all sub command possibilities +481 */ +482 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) { +483 int i = 0; +484 Set<String> current = null; +485 while (true) { +486 Set<String> newList = new HashSet<>(); +487 +488 if (i < subCommandParts.length) { +489 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) { +490 if (current != null) { +491 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList())); +492 } else { +493 newList.add(s1); +494 } +495 } +496 } +497 +498 if (i + 1 < subCommandParts.length) { +499 current = newList; +500 i = i + 1; +501 continue; +502 } 503 -504 void execute(CommandIssuer issuer, CommandRouter.CommandRouteResult command) { -505 try { -506 CommandOperationContext commandContext = preCommandOperation(issuer, command.commandLabel, command.args, false); -507 execSubcommand = command.subcommand; -508 executeCommand(commandContext, issuer, command.args, command.cmd); -509 } finally { -510 postCommandOperation(); -511 } -512 } -513 -514 /** -515 * This is ran after any command operation has been performed. -516 */ -517 private void postCommandOperation() { -518 CommandManager.commandOperationContext.get().pop(); -519 execSubcommand = null; -520 execLabel = null; -521 origArgs = new String[]{}; -522 } -523 -524 /** -525 * This is ran before any command operation has been performed. -526 * -527 * @param issuer The user who executed the command. -528 * @param commandLabel The label the user used to execute the command. This is not the command name, but their input. -529 * When there is multiple aliases, this is which alias was used -530 * @param args The arguments passed to the command when executing it. -531 * @param isAsync Whether the command is executed off of the main thread. -532 * @return The context which is being registered to the {@link CommandManager}'s {@link -533 * CommandManager#commandOperationContext thread local stack}. -534 */ -535 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) { -536 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get(); -537 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync); -538 contexts.push(context); -539 lastCommandOperationContext.set(context); -540 execSubcommand = null; -541 execLabel = commandLabel; -542 origArgs = args; -543 return context; -544 } -545 -546 /** -547 * Gets the current command issuer. -548 * -549 * @return The current command issuer. -550 */ -551 public CommandIssuer getCurrentCommandIssuer() { -552 return CommandManager.getCurrentCommandIssuer(); -553 } -554 -555 /** -556 * Gets the current command manager. -557 * -558 * @return The current command manager. -559 */ -560 public CommandManager getCurrentCommandManager() { -561 return CommandManager.getCurrentCommandManager(); -562 } -563 -564 private void executeCommand(CommandOperationContext commandOperationContext, -565 CommandIssuer issuer, String[] args, RegisteredCommand cmd) { -566 if (cmd.hasPermission(issuer)) { -567 commandOperationContext.setRegisteredCommand(cmd); -568 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) { -569 return; -570 } -571 List<String> sargs = Arrays.asList(args); -572 cmd.invoke(issuer, sargs, commandOperationContext); -573 } else { -574 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED); -575 } -576 } -577 -578 /** -579 * Please use command conditions for restricting execution -580 * -581 * @param issuer -582 * @param cmd -583 * @return -584 * @deprecated See {@link CommandConditions} -585 */ -586 @SuppressWarnings("DeprecatedIsStillUsed") -587 @Deprecated -588 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) { -589 return true; -590 } -591 -592 /** -593 * Gets tab completed data from the given command from the user. -594 * -595 * @param issuer The user who executed the tabcomplete. -596 * @param commandLabel The label which is being used by the user. -597 * @param args The arguments the user has typed so far. -598 * @return All possibilities in the tab complete. -599 */ -600 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) { -601 return tabComplete(issuer, commandLabel, args, false); -602 } -603 -604 /** -605 * Gets the tab complete suggestions from a given command. This will automatically find anything -606 * which is valid for the specified command through the command's implementation. -607 * -608 * @param issuer The issuer of the command. -609 * @param commandLabel The command name as entered by the user instead of the ACF registered name. -610 * @param args All arguments entered by the user. -611 * @param isAsync Whether this is run off of the main thread. -612 * @return The possibilities to tab complete in no particular order. -613 */ -614 @SuppressWarnings("WeakerAccess") -615 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) -616 throws IllegalArgumentException { -617 return tabComplete(issuer, manager.getRootCommand(commandLabel.toLowerCase()), args, isAsync); -618 } -619 -620 List<String> tabComplete(CommandIssuer issuer, RootCommand rootCommand, String[] args, boolean isAsync) -621 throws IllegalArgumentException { -622 if (args.length == 0) { -623 args = new String[]{""}; -624 } -625 String commandLabel = rootCommand.getCommandName(); -626 try { -627 CommandRouter router = manager.getRouter(); -628 -629 preCommandOperation(issuer, commandLabel, args, isAsync); -630 -631 final RouteSearch search = router.routeCommand(rootCommand, commandLabel, args, true); +504 return newList; +505 } +506 } +507 +508 void execute(CommandIssuer issuer, CommandRouter.CommandRouteResult command) { +509 try { +510 CommandOperationContext commandContext = preCommandOperation(issuer, command.commandLabel, command.args, false); +511 execSubcommand = command.subcommand; +512 executeCommand(commandContext, issuer, command.args, command.cmd); +513 } finally { +514 postCommandOperation(); +515 } +516 } +517 +518 /** +519 * This is ran after any command operation has been performed. +520 */ +521 private void postCommandOperation() { +522 CommandManager.commandOperationContext.get().pop(); +523 execSubcommand = null; +524 execLabel = null; +525 origArgs = new String[]{}; +526 } +527 +528 /** +529 * This is ran before any command operation has been performed. +530 * +531 * @param issuer The user who executed the command. +532 * @param commandLabel The label the user used to execute the command. This is not the command name, but their input. +533 * When there is multiple aliases, this is which alias was used +534 * @param args The arguments passed to the command when executing it. +535 * @param isAsync Whether the command is executed off of the main thread. +536 * @return The context which is being registered to the {@link CommandManager}'s {@link +537 * CommandManager#commandOperationContext thread local stack}. +538 */ +539 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) { +540 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get(); +541 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync); +542 contexts.push(context); +543 lastCommandOperationContext.set(context); +544 execSubcommand = null; +545 execLabel = commandLabel; +546 origArgs = args; +547 return context; +548 } +549 +550 /** +551 * Gets the current command issuer. +552 * +553 * @return The current command issuer. +554 */ +555 public CommandIssuer getCurrentCommandIssuer() { +556 return CommandManager.getCurrentCommandIssuer(); +557 } +558 +559 /** +560 * Gets the current command manager. +561 * +562 * @return The current command manager. +563 */ +564 public CommandManager getCurrentCommandManager() { +565 return CommandManager.getCurrentCommandManager(); +566 } +567 +568 private void executeCommand(CommandOperationContext commandOperationContext, +569 CommandIssuer issuer, String[] args, RegisteredCommand cmd) { +570 if (cmd.hasPermission(issuer)) { +571 commandOperationContext.setRegisteredCommand(cmd); +572 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) { +573 return; +574 } +575 List<String> sargs = Arrays.asList(args); +576 cmd.invoke(issuer, sargs, commandOperationContext); +577 } else { +578 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED); +579 } +580 } +581 +582 /** +583 * Please use command conditions for restricting execution +584 * +585 * @param issuer +586 * @param cmd +587 * @return +588 * @deprecated See {@link CommandConditions} +589 */ +590 @SuppressWarnings("DeprecatedIsStillUsed") +591 @Deprecated +592 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) { +593 return true; +594 } +595 +596 /** +597 * Gets tab completed data from the given command from the user. +598 * +599 * @param issuer The user who executed the tabcomplete. +600 * @param commandLabel The label which is being used by the user. +601 * @param args The arguments the user has typed so far. +602 * @return All possibilities in the tab complete. +603 */ +604 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) { +605 return tabComplete(issuer, commandLabel, args, false); +606 } +607 +608 /** +609 * Gets the tab complete suggestions from a given command. This will automatically find anything +610 * which is valid for the specified command through the command's implementation. +611 * +612 * @param issuer The issuer of the command. +613 * @param commandLabel The command name as entered by the user instead of the ACF registered name. +614 * @param args All arguments entered by the user. +615 * @param isAsync Whether this is run off of the main thread. +616 * @return The possibilities to tab complete in no particular order. +617 */ +618 @SuppressWarnings("WeakerAccess") +619 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) +620 throws IllegalArgumentException { +621 return tabComplete(issuer, manager.getRootCommand(commandLabel.toLowerCase()), args, isAsync); +622 } +623 +624 List<String> tabComplete(CommandIssuer issuer, RootCommand rootCommand, String[] args, boolean isAsync) +625 throws IllegalArgumentException { +626 if (args.length == 0) { +627 args = new String[]{""}; +628 } +629 String commandLabel = rootCommand.getCommandName(); +630 try { +631 CommandRouter router = manager.getRouter(); 632 -633 final List<String> cmds = new ArrayList<>(); -634 if (search != null) { -635 CommandRouter.CommandRouteResult result = router.matchCommand(search, true); -636 if (result != null) { -637 cmds.addAll(completeCommand(issuer, result.cmd, result.args, commandLabel, isAsync)); -638 } -639 } -640 -641 return filterTabComplete(args[args.length - 1], cmds); -642 } finally { -643 postCommandOperation(); -644 } -645 } -646 -647 /** -648 * Gets all subcommands which are possible to tabcomplete. -649 * -650 * @param issuer The command issuer. -651 * @param args -652 * @return -653 */ -654 List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) { -655 final Set<String> cmds = new HashSet<>(); -656 final int cmdIndex = Math.max(0, args.length - 1); -657 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase(); -658 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) { -659 final String key = entry.getKey(); -660 if (key.startsWith(argString) && !CATCHUNKNOWN.equals(key) && !DEFAULT.equals(key)) { -661 final RegisteredCommand value = entry.getValue(); -662 if (!value.hasPermission(issuer) || value.isPrivate) { -663 continue; -664 } -665 -666 String[] split = ACFPatterns.SPACE.split(value.prefSubCommand); -667 cmds.add(split[cmdIndex]); -668 } -669 } -670 return new ArrayList<>(cmds); -671 } -672 -673 /** -674 * Complete a command properly per issuer and input. -675 * -676 * @param issuer The user who executed this. -677 * @param cmd The command to be completed. -678 * @param args All arguments given by the user. -679 * @param commandLabel The command name the user used. -680 * @param isAsync Whether the command was executed async. -681 * @return All results to complete the command. -682 */ -683 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) { -684 if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0) { -685 return Collections.emptyList(); -686 } -687 -688 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, args, isAsync); -689 return filterTabComplete(args[args.length - 1], cmds); -690 } +633 preCommandOperation(issuer, commandLabel, args, isAsync); +634 +635 final RouteSearch search = router.routeCommand(rootCommand, commandLabel, args, true); +636 +637 final List<String> cmds = new ArrayList<>(); +638 if (search != null) { +639 CommandRouter.CommandRouteResult result = router.matchCommand(search, true); +640 if (result != null) { +641 cmds.addAll(completeCommand(issuer, result.cmd, result.args, commandLabel, isAsync)); +642 } +643 } +644 +645 return filterTabComplete(args[args.length - 1], cmds); +646 } finally { +647 postCommandOperation(); +648 } +649 } +650 +651 /** +652 * Gets all subcommands which are possible to tabcomplete. +653 * +654 * @param issuer The command issuer. +655 * @param args +656 * @return +657 */ +658 List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) { +659 final Set<String> cmds = new HashSet<>(); +660 final int cmdIndex = Math.max(0, args.length - 1); +661 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase(); +662 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) { +663 final String key = entry.getKey(); +664 if (key.startsWith(argString) && !CATCHUNKNOWN.equals(key) && !DEFAULT.equals(key)) { +665 final RegisteredCommand value = entry.getValue(); +666 if (!value.hasPermission(issuer) || value.isPrivate) { +667 continue; +668 } +669 +670 String[] split = ACFPatterns.SPACE.split(value.prefSubCommand); +671 cmds.add(split[cmdIndex]); +672 } +673 } +674 return new ArrayList<>(cmds); +675 } +676 +677 /** +678 * Complete a command properly per issuer and input. +679 * +680 * @param issuer The user who executed this. +681 * @param cmd The command to be completed. +682 * @param args All arguments given by the user. +683 * @param commandLabel The command name the user used. +684 * @param isAsync Whether the command was executed async. +685 * @return All results to complete the command. +686 */ +687 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) { +688 if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0) { +689 return Collections.emptyList(); +690 } 691 -692 /** -693 * Gets the actual args in string form the user typed -694 * This returns a list of all tab complete options which are possible with the given argument and commands. -695 * -696 * @param arg Argument which was pressed tab on. -697 * @param cmds The possibilities to return. -698 * @return All possible options. This may be empty. -699 */ -700 private static List<String> filterTabComplete(String arg, List<String> cmds) { -701 return cmds.stream() -702 .distinct() -703 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg))) -704 .collect(Collectors.toList()); -705 } -706 -707 /** -708 * Executes the precommand and sees whether something is wrong. Ideally, you get false from this. -709 * -710 * @param commandOperationContext The context to use. -711 * @param cmd The command executed. -712 * @param issuer The issuer who executed the command. -713 * @param args The arguments the issuer provided. -714 * @return Whether something went wrong. -715 */ -716 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) { -717 Method pre = this.preCommandHandler; -718 if (pre != null) { -719 try { -720 Class<?>[] types = pre.getParameterTypes(); -721 Object[] parameters = new Object[pre.getParameterCount()]; -722 for (int i = 0; i < parameters.length; i++) { -723 Class<?> type = types[i]; -724 Object issuerObject = issuer.getIssuer(); -725 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) { -726 parameters[i] = issuerObject; -727 } else if (CommandIssuer.class.isAssignableFrom(type)) { -728 parameters[i] = issuer; -729 } else if (RegisteredCommand.class.isAssignableFrom(type)) { -730 parameters[i] = cmd; -731 } else if (String[].class.isAssignableFrom((type))) { -732 parameters[i] = args; -733 } else { -734 parameters[i] = null; -735 } -736 } -737 -738 return (boolean) pre.invoke(this, parameters); -739 } catch (IllegalAccessException | InvocationTargetException e) { -740 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e); -741 } -742 } -743 return false; -744 } -745 -746 /** -747 * @deprecated Unstable API -748 */ -749 @Deprecated -750 @UnstableAPI -751 public CommandHelp getCommandHelp() { -752 return manager.generateCommandHelp(); -753 } -754 -755 /** -756 * @deprecated Unstable API -757 */ -758 @Deprecated -759 @UnstableAPI -760 public void showCommandHelp() { -761 getCommandHelp().showHelp(); -762 } -763 -764 public void help(Object issuer, String[] args) { -765 help(manager.getCommandIssuer(issuer), args); +692 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, args, isAsync); +693 return filterTabComplete(args[args.length - 1], cmds); +694 } +695 +696 /** +697 * Gets the actual args in string form the user typed +698 * This returns a list of all tab complete options which are possible with the given argument and commands. +699 * +700 * @param arg Argument which was pressed tab on. +701 * @param cmds The possibilities to return. +702 * @return All possible options. This may be empty. +703 */ +704 private static List<String> filterTabComplete(String arg, List<String> cmds) { +705 return cmds.stream() +706 .distinct() +707 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg))) +708 .collect(Collectors.toList()); +709 } +710 +711 /** +712 * Executes the precommand and sees whether something is wrong. Ideally, you get false from this. +713 * +714 * @param commandOperationContext The context to use. +715 * @param cmd The command executed. +716 * @param issuer The issuer who executed the command. +717 * @param args The arguments the issuer provided. +718 * @return Whether something went wrong. +719 */ +720 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) { +721 Method pre = this.preCommandHandler; +722 if (pre != null) { +723 try { +724 Class<?>[] types = pre.getParameterTypes(); +725 Object[] parameters = new Object[pre.getParameterCount()]; +726 for (int i = 0; i < parameters.length; i++) { +727 Class<?> type = types[i]; +728 Object issuerObject = issuer.getIssuer(); +729 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) { +730 parameters[i] = issuerObject; +731 } else if (CommandIssuer.class.isAssignableFrom(type)) { +732 parameters[i] = issuer; +733 } else if (RegisteredCommand.class.isAssignableFrom(type)) { +734 parameters[i] = cmd; +735 } else if (String[].class.isAssignableFrom((type))) { +736 parameters[i] = args; +737 } else { +738 parameters[i] = null; +739 } +740 } +741 +742 return (boolean) pre.invoke(this, parameters); +743 } catch (IllegalAccessException | InvocationTargetException e) { +744 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e); +745 } +746 } +747 return false; +748 } +749 +750 /** +751 * @deprecated Unstable API +752 */ +753 @Deprecated +754 @UnstableAPI +755 public CommandHelp getCommandHelp() { +756 return manager.generateCommandHelp(); +757 } +758 +759 /** +760 * @deprecated Unstable API +761 */ +762 @Deprecated +763 @UnstableAPI +764 public void showCommandHelp() { +765 getCommandHelp().showHelp(); 766 } 767 -768 public void help(CommandIssuer issuer, String[] args) { -769 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND); +768 public void help(Object issuer, String[] args) { +769 help(manager.getCommandIssuer(issuer), args); 770 } 771 -772 public void doHelp(Object issuer, String... args) { -773 doHelp(manager.getCommandIssuer(issuer), args); +772 public void help(CommandIssuer issuer, String[] args) { +773 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND); 774 } 775 -776 public void doHelp(CommandIssuer issuer, String... args) { -777 help(issuer, args); +776 public void doHelp(Object issuer, String... args) { +777 doHelp(manager.getCommandIssuer(issuer), args); 778 } 779 -780 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) { -781 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX, -782 "{command}", manager.getCommandPrefix(issuer) + cmd.command, -783 "{syntax}", cmd.syntaxText -784 ); -785 } -786 -787 public boolean hasPermission(Object issuer) { -788 return hasPermission(manager.getCommandIssuer(issuer)); +780 public void doHelp(CommandIssuer issuer, String... args) { +781 help(issuer, args); +782 } +783 +784 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) { +785 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX, +786 "{command}", manager.getCommandPrefix(issuer) + cmd.command, +787 "{syntax}", cmd.syntaxText +788 ); 789 } 790 -791 public boolean hasPermission(CommandIssuer issuer) { -792 return this.manager.hasPermission(issuer, getRequiredPermissions()); +791 public boolean hasPermission(Object issuer) { +792 return hasPermission(manager.getCommandIssuer(issuer)); 793 } 794 -795 public Set<String> getRequiredPermissions() { -796 return this.permissions; +795 public boolean hasPermission(CommandIssuer issuer) { +796 return this.manager.hasPermission(issuer, getRequiredPermissions()); 797 } 798 -799 public boolean requiresPermission(String permission) { -800 return getRequiredPermissions().contains(permission); +799 public Set<String> getRequiredPermissions() { +800 return this.permissions; 801 } 802 -803 public String getName() { -804 return commandName; +803 public boolean requiresPermission(String permission) { +804 return getRequiredPermissions().contains(permission); 805 } 806 -807 public ExceptionHandler getExceptionHandler() { -808 return exceptionHandler; +807 public String getName() { +808 return commandName; 809 } 810 -811 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) { -812 this.exceptionHandler = exceptionHandler; -813 return this; -814 } -815 -816 public RegisteredCommand getDefaultRegisteredCommand() { -817 return ACFUtil.getFirstElement(this.subCommands.get(DEFAULT)); +811 public ExceptionHandler getExceptionHandler() { +812 return exceptionHandler; +813 } +814 +815 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) { +816 this.exceptionHandler = exceptionHandler; +817 return this; 818 } 819 -820 public String setContextFlags(Class<?> cls, String flags) { -821 return this.contextFlags.put(cls, flags); +820 public RegisteredCommand getDefaultRegisteredCommand() { +821 return ACFUtil.getFirstElement(this.subCommands.get(DEFAULT)); 822 } 823 -824 public String getContextFlags(Class<?> cls) { -825 return this.contextFlags.get(cls); +824 public String setContextFlags(Class<?> cls, String flags) { +825 return this.contextFlags.put(cls, flags); 826 } 827 -828 public List<RegisteredCommand> getRegisteredCommands() { -829 List<RegisteredCommand> registeredCommands = new ArrayList<>(); -830 registeredCommands.addAll(this.subCommands.values()); -831 return registeredCommands; -832 } -833} +828 public String getContextFlags(Class<?> cls) { +829 return this.contextFlags.get(cls); +830 } +831 +832 public List<RegisteredCommand> getRegisteredCommands() { +833 List<RegisteredCommand> registeredCommands = new ArrayList<>(); +834 registeredCommands.addAll(this.subCommands.values()); +835 return registeredCommands; +836 } +837} diff --git a/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html b/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html index 20a17f21..0439ef71 100644 --- a/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html +++ b/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html @@ -312,7 +312,7 @@ 304 return ACFPatterns.COMMA.split(this.permission)[0]; 305 } 306 -307 private void computePermissions() { +307 void computePermissions() { 308 this.permissions.clear(); 309 this.permissions.addAll(this.scope.getRequiredPermissions()); 310 if (this.permission != null && !this.permission.isEmpty()) {