diff --git a/docs/acf-core/co/aikar/commands/BaseCommand.html b/docs/acf-core/co/aikar/commands/BaseCommand.html
index 33226bc8..d311bc91 100644
--- a/docs/acf-core/co/aikar/commands/BaseCommand.html
+++ b/docs/acf-core/co/aikar/commands/BaseCommand.html
@@ -18,7 +18,7 @@
catch(err) {
}
//-->
-var methods = {"i0":42,"i1":10,"i2":10,"i3":10,"i4":42,"i5":10,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10,"i11":10,"i12":10,"i13":10,"i14":10,"i15":10,"i16":10,"i17":10,"i18":10,"i19":10,"i20":10,"i21":10,"i22":10,"i23":42,"i24":10,"i25":10,"i26":10};
+var methods = {"i0":42,"i1":10,"i2":10,"i3":10,"i4":42,"i5":10,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10,"i11":10,"i12":10,"i13":10,"i14":10,"i15":10,"i16":10,"i17":10,"i18":10,"i19":10,"i20":10,"i21":10,"i22":10,"i23":10,"i24":42,"i25":10,"i26":10,"i27":10};
var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"],32:["t6","Deprecated Methods"]};
var altColor = "altColor";
var rowColor = "rowColor";
@@ -234,55 +234,61 @@ extends
+
void |
showCommandHelp()
Deprecated.
@@ -290,12 +296,12 @@ extends
+
void |
showSyntax(CommandIssuer issuer,
RegisteredCommand<?> cmd) |
-
+
List<String> |
tabComplete(CommandIssuer issuer,
String commandLabel,
@@ -303,7 +309,7 @@ extends Gets tab completed data from the given command from the user.
|
-
+
List<String> |
tabComplete(CommandIssuer issuer,
String commandLabel,
@@ -368,13 +374,27 @@ public
+
+
+
+-
+
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
+
+- Returns:
+
+
+
@@ -532,7 +552,7 @@ public
showCommandHelp
@Deprecated
-public void showCommandHelp()
+public void showCommandHelp()
Deprecated.
@@ -542,7 +562,7 @@ public void
help
-public void help(Object issuer,
+public void help(Object issuer,
String[] args)
@@ -552,7 +572,7 @@ public void
help
-public void help(CommandIssuer issuer,
+public void help(CommandIssuer issuer,
String[] args)
@@ -562,7 +582,7 @@ public void
doHelp
-public void doHelp(Object issuer,
+public void doHelp(Object issuer,
String... args)
@@ -572,7 +592,7 @@ public void
doHelp
-public void doHelp(CommandIssuer issuer,
+public void doHelp(CommandIssuer issuer,
String... args)
@@ -582,7 +602,7 @@ public void
showSyntax
-public void showSyntax(CommandIssuer issuer,
+public void showSyntax(CommandIssuer issuer,
RegisteredCommand<?> cmd)
@@ -592,7 +612,7 @@ public void
hasPermission
-public boolean hasPermission(Object issuer)
+public boolean hasPermission(Object issuer)
@@ -601,7 +621,7 @@ public void
hasPermission
-public boolean hasPermission(CommandIssuer issuer)
+public boolean hasPermission(CommandIssuer issuer)
@@ -610,7 +630,7 @@ public void
getRequiredPermissions
-public Set<String> getRequiredPermissions()
+public Set<String> getRequiredPermissions()
@@ -619,7 +639,7 @@ public void
requiresPermission
-public boolean requiresPermission(String permission)
+public boolean requiresPermission(String permission)
@@ -628,7 +648,7 @@ public void
getName
-public String getName()
+public String getName()
@@ -637,7 +657,7 @@ public void
getExceptionHandler
-public ExceptionHandler getExceptionHandler()
+public ExceptionHandler getExceptionHandler()
@@ -646,7 +666,7 @@ public void
setExceptionHandler
-public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler)
+public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler)
@@ -655,7 +675,7 @@ public void
getDefaultRegisteredCommand
-public RegisteredCommand getDefaultRegisteredCommand()
+public RegisteredCommand getDefaultRegisteredCommand()
@@ -664,7 +684,7 @@ public void
setContextFlags
-public String setContextFlags(Class<?> cls,
+public String setContextFlags(Class<?> cls,
String flags)
@@ -674,7 +694,7 @@ public void
getContextFlags
-public String getContextFlags(Class<?> cls)
+public String getContextFlags(Class<?> cls)
@@ -683,7 +703,7 @@ public void
getRegisteredCommands
-public List<RegisteredCommand> getRegisteredCommands()
+public List<RegisteredCommand> getRegisteredCommands()
diff --git a/docs/acf-core/co/aikar/commands/ForwardingCommand.html b/docs/acf-core/co/aikar/commands/ForwardingCommand.html
index 03a6ff1b..b4efb85d 100644
--- a/docs/acf-core/co/aikar/commands/ForwardingCommand.html
+++ b/docs/acf-core/co/aikar/commands/ForwardingCommand.html
@@ -18,7 +18,7 @@
catch(err) {
}
//-->
-var methods = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":10,"i5":10,"i6":10};
+var methods = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":10,"i5":10,"i6":10,"i7":10};
var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"]};
var altColor = "altColor";
var rowColor = "rowColor";
@@ -140,26 +140,32 @@ extends String[] args) |
+CommandOperationContext |
+getLastCommandOperationContext()
+Returns a reference to the last used CommandOperationContext.
+ |
+
+
List<RegisteredCommand> |
getRegisteredCommands() |
-
+
Set<String> |
getRequiredPermissions() |
-
+
boolean |
hasPermission(CommandIssuer sender) |
-
+
boolean |
hasPermission(Object issuer) |
-
+
boolean |
requiresPermission(String permission) |
-
+
List<String> |
tabComplete(CommandIssuer issuer,
String alias,
@@ -210,13 +216,30 @@ extends
+
+
+
-
getRequiredPermissions
-public Set<String> getRequiredPermissions()
+public Set<String> getRequiredPermissions()
- Overrides:
getRequiredPermissions in class BaseCommand
@@ -229,7 +252,7 @@ extends
-
hasPermission
-public boolean hasPermission(Object issuer)
+public boolean hasPermission(Object issuer)
- Overrides:
hasPermission in class BaseCommand
@@ -242,7 +265,7 @@ extends
-
requiresPermission
-public boolean requiresPermission(String permission)
+public boolean requiresPermission(String permission)
- Overrides:
requiresPermission in class BaseCommand
@@ -255,7 +278,7 @@ extends
-
hasPermission
-public boolean hasPermission(CommandIssuer sender)
+public boolean hasPermission(CommandIssuer sender)
- Overrides:
hasPermission in class BaseCommand
@@ -268,7 +291,7 @@ extends
-
tabComplete
-public List<String> tabComplete(CommandIssuer issuer,
+public List<String> tabComplete(CommandIssuer issuer,
String alias,
String[] args,
boolean isAsync)
@@ -297,7 +320,7 @@ extends
-
execute
-public void execute(CommandIssuer issuer,
+public void execute(CommandIssuer issuer,
String commandLabel,
String[] args)
diff --git a/docs/acf-core/co/aikar/commands/class-use/CommandOperationContext.html b/docs/acf-core/co/aikar/commands/class-use/CommandOperationContext.html
index 3594746f..6b644a0a 100644
--- a/docs/acf-core/co/aikar/commands/class-use/CommandOperationContext.html
+++ b/docs/acf-core/co/aikar/commands/class-use/CommandOperationContext.html
@@ -109,6 +109,16 @@
static CommandOperationContext |
CommandManager.getCurrentCommandOperationContext() |
|
+
+CommandOperationContext |
+BaseCommand.getLastCommandOperationContext()
+Returns a reference to the last used CommandOperationContext.
+ |
+
+
+CommandOperationContext |
+ForwardingCommand.getLastCommandOperationContext() |
+
diff --git a/docs/acf-core/index-all.html b/docs/acf-core/index-all.html
index 2449e823..26771837 100644
--- a/docs/acf-core/index-all.html
+++ b/docs/acf-core/index-all.html
@@ -740,6 +740,12 @@
getLastArg() - Method in class co.aikar.commands.CommandExecutionContext
+getLastCommandOperationContext() - Method in class co.aikar.commands.BaseCommand
+
+Returns a reference to the last used CommandOperationContext.
+
+getLastCommandOperationContext() - Method in class co.aikar.commands.ForwardingCommand
+
getLocales() - Method in class co.aikar.commands.CommandManager
Returns a Locales Manager to add and modify language tables for your commands.
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 df236049..d118104f 100644
--- a/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html
+++ b/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html
@@ -161,7 +161,7 @@
153 /**
154 * The last operative context data of this command. This may be null if this command hasn't been run yet.
155 */
-156 @Nullable CommandOperationContext lastCommandOperationContext;
+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 */
@@ -183,40 +183,40 @@
175 }
176
177 /**
-178 * Gets the root command name that the user actually typed
-179 *
-180 * @return Name
-181 */
-182 public String getExecCommandLabel() {
-183 return execLabel;
-184 }
-185
-186 /**
-187 * Gets the actual sub command name the user typed
-188 *
-189 * @return Name
-190 */
-191 public String getExecSubcommand() {
-192 return execSubcommand;
-193 }
-194
-195 /**
-196 * Gets the actual args in string form the user typed
-197 *
-198 * @return Args
-199 */
-200 public String[] getOrigArgs() {
-201 return origArgs;
-202 }
-203
-204 /**
-205 * This should be called whenever the command gets registered.
-206 * It sets all required fields correctly and injects dependencies.
+178 * Returns a reference to the last used CommandOperationContext.
+179 * This method is ThreadLocal, in that it can only be used on a thread that has executed a command
+180 *
+181 * @return
+182 */
+183 public CommandOperationContext getLastCommandOperationContext() {
+184 return lastCommandOperationContext.get();
+185 }
+186
+187 /**
+188 * Gets the root command name that the user actually typed
+189 *
+190 * @return Name
+191 */
+192 public String getExecCommandLabel() {
+193 return execLabel;
+194 }
+195
+196 /**
+197 * Gets the actual sub command name the user typed
+198 *
+199 * @return Name
+200 */
+201 public String getExecSubcommand() {
+202 return execSubcommand;
+203 }
+204
+205 /**
+206 * Gets the actual args in string form the user typed
207 *
-208 * @param manager The manager to register as this command's owner and handler.
+208 * @return Args
209 */
-210 void onRegister(CommandManager manager) {
-211 onRegister(manager, this.commandName);
+210 public String[] getOrigArgs() {
+211 return origArgs;
212 }
213
214 /**
@@ -224,770 +224,780 @@
216 * It sets all required fields correctly and injects dependencies.
217 *
218 * @param manager The manager to register as this command's owner and handler.
-219 * @param cmd The command name to use register with.
-220 */
-221 private void onRegister(CommandManager manager, String cmd) {
-222 manager.injectDependencies(this);
-223 this.manager = manager;
-224
-225 final Annotations annotations = manager.getAnnotations();
-226 final Class<? extends BaseCommand> self = this.getClass();
-227
-228 String[] cmdAliases = annotations.getAnnotationValues(self, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE | Annotations.NO_EMPTY);
-229
-230 if (cmd == null && cmdAliases != null) {
-231 cmd = cmdAliases[0];
-232 }
-233
-234 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase();
-235 this.permission = annotations.getAnnotationValue(self, CommandPermission.class, Annotations.REPLACEMENTS);
-236 this.description = this.commandName + " commands";
-237 this.parentSubcommand = getParentSubcommand(self);
-238 this.conditions = annotations.getAnnotationValue(self, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+219 */
+220 void onRegister(CommandManager manager) {
+221 onRegister(manager, this.commandName);
+222 }
+223
+224 /**
+225 * This should be called whenever the command gets registered.
+226 * It sets all required fields correctly and injects dependencies.
+227 *
+228 * @param manager The manager to register as this command's owner and handler.
+229 * @param cmd The command name to use register with.
+230 */
+231 private void onRegister(CommandManager manager, String cmd) {
+232 manager.injectDependencies(this);
+233 this.manager = manager;
+234
+235 final Annotations annotations = manager.getAnnotations();
+236 final Class<? extends BaseCommand> self = this.getClass();
+237
+238 String[] cmdAliases = annotations.getAnnotationValues(self, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE | Annotations.NO_EMPTY);
239
-240 registerSubcommands();
-241 registerSubclasses(cmd);
-242
-243 if (cmdAliases != null) {
-244 Set<String> cmdList = new HashSet<>();
-245 Collections.addAll(cmdList, cmdAliases);
-246 cmdList.remove(cmd);
-247 for (String cmdAlias : cmdList) {
-248 register(cmdAlias, this);
-249 }
-250 }
-251
-252 if (cmd != null) {
-253 register(cmd, this);
-254 }
-255 }
-256
-257 /**
-258 * This recursively registers all subclasses of the command as subcommands, if they are of type {@link BaseCommand}.
-259 *
-260 * @param cmd The command name of this command.
-261 */
-262 private void registerSubclasses(String cmd) {
-263 for (Class<?> clazz : this.getClass().getDeclaredClasses()) {
-264 if (BaseCommand.class.isAssignableFrom(clazz)) {
-265 try {
-266 BaseCommand subCommand = null;
-267 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
-268 for (Constructor<?> declaredConstructor : declaredConstructors) {
-269
-270 declaredConstructor.setAccessible(true);
-271 Parameter[] parameters = declaredConstructor.getParameters();
-272 if (parameters.length == 1) {
-273 subCommand = (BaseCommand) declaredConstructor.newInstance(this);
-274 } else {
-275 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")");
-276 }
-277 }
-278 if (subCommand != null) {
-279 subCommand.parentCommand = this;
-280 subCommand.onRegister(manager, cmd);
-281 this.subCommands.putAll(subCommand.subCommands);
-282 this.registeredCommands.putAll(subCommand.registeredCommands);
-283 } else {
-284 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName());
-285 }
-286 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
-287 this.manager.log(LogLevel.ERROR, "Error registering subclass", e);
-288 }
-289 }
-290 }
-291 }
-292
-293 /**
-294 * This registers all subcommands of the command.
-295 */
-296 private void registerSubcommands() {
-297 final Annotations annotations = manager.getAnnotations();
-298 boolean foundDefault = false;
-299 boolean foundCatchUnknown = false;
-300 boolean isParentEmpty = parentSubcommand == null || parentSubcommand.isEmpty();
-301
-302 for (Method method : this.getClass().getMethods()) {
-303 method.setAccessible(true);
-304 String sublist = null;
-305 String sub = getSubcommandValue(method);
-306 final boolean def = annotations.hasAnnotation(method, Default.class);
-307 final String helpCommand = annotations.getAnnotationValue(method, HelpCommand.class, Annotations.NOTHING);
-308 final String commandAliases = annotations.getAnnotationValue(method, CommandAlias.class, Annotations.NOTHING);
-309
-310 if (!isParentEmpty && def) {
-311 sub = parentSubcommand;
-312 }
-313 if (isParentEmpty && (def || (!foundDefault && helpCommand != null))) {
-314 if (!foundDefault) {
-315 if (def) {
-316 this.subCommands.get(DEFAULT).clear();
-317 foundDefault = true;
-318 }
-319 registerSubcommand(method, DEFAULT);
-320 } else {
-321 ACFUtil.sneaky(new IllegalStateException("Multiple @Default/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
-322 }
-323 }
-324
-325 if (sub != null) {
-326 sublist = sub;
-327 } else if (commandAliases != null) {
-328 sublist = commandAliases;
-329 } else if (helpCommand != null) {
-330 sublist = helpCommand;
-331 hasHelpCommand = true;
-332 }
-333
-334 boolean preCommand = annotations.hasAnnotation(method, PreCommand.class);
-335 boolean hasCatchUnknown = annotations.hasAnnotation(method, CatchUnknown.class) ||
-336 annotations.hasAnnotation(method, CatchAll.class) ||
-337 annotations.hasAnnotation(method, UnknownHandler.class);
-338
-339 if (hasCatchUnknown || (!foundCatchUnknown && helpCommand != null)) {
-340 if (!foundCatchUnknown) {
-341 if (hasCatchUnknown) {
-342 this.subCommands.get(CATCHUNKNOWN).clear();
-343 foundCatchUnknown = true;
-344 }
-345 registerSubcommand(method, CATCHUNKNOWN);
-346 } else {
-347 ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
-348 }
-349 } else if (preCommand) {
-350 if (this.preCommandHandler == null) {
-351 this.preCommandHandler = method;
-352 } else {
-353 ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
-354 }
-355 }
-356 if (Objects.equals(method.getDeclaringClass(), this.getClass()) && sublist != null) {
-357 registerSubcommand(method, sublist);
-358 }
-359 }
-360 }
-361
-362 /**
-363 * Gets the subcommand name of the method given.
-364 *
-365 * @param method The method to check.
-366 * @return The name of the subcommand. It returns null if the input doesn't have {@link Subcommand} attached.
-367 */
-368 private String getSubcommandValue(Method method) {
-369 final String sub = manager.getAnnotations().getAnnotationValue(method, Subcommand.class, Annotations.NOTHING);
-370 if (sub == null) {
-371 return null;
-372 }
-373 Class<?> clazz = method.getDeclaringClass();
-374 String parent = getParentSubcommand(clazz);
-375 return parent == null || parent.isEmpty() ? sub : parent + " " + sub;
-376 }
-377
-378 private String getParentSubcommand(Class<?> clazz) {
-379 List<String> subList = new ArrayList<>();
-380 while (clazz != null) {
-381 String sub = manager.getAnnotations().getAnnotationValue(clazz, Subcommand.class, Annotations.NOTHING);
-382 if (sub != null) {
-383 subList.add(sub);
-384 }
-385 clazz = clazz.getEnclosingClass();
-386 }
-387 Collections.reverse(subList);
-388 return ACFUtil.join(subList, " ");
-389 }
-390
-391 /**
-392 * Registers the given {@link BaseCommand cmd} as a child of the {@link RootCommand} linked to the name given.
-393 *
-394 * @param name Name of the parent to cmd.
-395 * @param cmd The {@link BaseCommand} to add as a child to the {@link RootCommand} owned name field.
-396 */
-397 private void register(String name, BaseCommand cmd) {
-398 String nameLower = name.toLowerCase();
-399 RootCommand rootCommand = manager.obtainRootCommand(nameLower);
-400 rootCommand.addChild(cmd);
-401
-402 this.registeredCommands.put(nameLower, rootCommand);
-403 }
-404
-405 /**
-406 * Registers the given {@link Method} as a subcommand.
-407 *
-408 * @param method The method to register as a subcommand.
-409 * @param subCommand The subcommand's name(s).
-410 */
-411 private void registerSubcommand(Method method, String subCommand) {
-412 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase());
-413 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand);
-414 // Must run getSubcommandPossibility BEFORE we rewrite it just after this.
-415 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts);
-416
-417 // Strip pipes off for auto complete addition
-418 for (int i = 0; i < subCommandParts.length; i++) {
-419 String[] split = ACFPatterns.PIPE.split(subCommandParts[i]);
-420 if (split.length == 0 || split[0].isEmpty()) {
-421 throw new IllegalArgumentException("Invalid @Subcommand configuration for " + method.getName() + " - parts can not start with | or be empty");
-422 }
-423 subCommandParts[i] = split[0];
-424 }
-425 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " ");
-426 final String[] aliasNames = manager.getAnnotations().getAnnotationValues(method, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE);
-427
-428 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " ";
-429 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand);
-430
-431 for (String subcmd : cmdList) {
-432 subCommands.put(subcmd, cmd);
-433 }
-434 cmd.addSubcommands(cmdList);
-435
-436 if (aliasNames != null) {
-437 for (String name : aliasNames) {
-438 register(name, new ForwardingCommand(this, cmd, subCommandParts));
-439 }
-440 }
-441 }
-442
-443 /**
-444 * Takes a string like "foo|bar baz|qux" and generates a list of
-445 * - foo baz
-446 * - foo qux
-447 * - bar baz
-448 * - bar qux
-449 * <p>
-450 * For every possible sub command combination
-451 *
-452 * @param subCommandParts
-453 * @return List of all sub command possibilities
-454 */
-455 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) {
-456 int i = 0;
-457 Set<String> current = null;
-458 while (true) {
-459 Set<String> newList = new HashSet<>();
-460
-461 if (i < subCommandParts.length) {
-462 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) {
-463 if (current != null) {
-464 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList()));
-465 } else {
-466 newList.add(s1);
-467 }
-468 }
-469 }
+240 if (cmd == null && cmdAliases != null) {
+241 cmd = cmdAliases[0];
+242 }
+243
+244 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase();
+245 this.permission = annotations.getAnnotationValue(self, CommandPermission.class, Annotations.REPLACEMENTS);
+246 this.description = this.commandName + " commands";
+247 this.parentSubcommand = getParentSubcommand(self);
+248 this.conditions = annotations.getAnnotationValue(self, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+249
+250 registerSubcommands();
+251 registerSubclasses(cmd);
+252
+253 if (cmdAliases != null) {
+254 Set<String> cmdList = new HashSet<>();
+255 Collections.addAll(cmdList, cmdAliases);
+256 cmdList.remove(cmd);
+257 for (String cmdAlias : cmdList) {
+258 register(cmdAlias, this);
+259 }
+260 }
+261
+262 if (cmd != null) {
+263 register(cmd, this);
+264 }
+265 }
+266
+267 /**
+268 * This recursively registers all subclasses of the command as subcommands, if they are of type {@link BaseCommand}.
+269 *
+270 * @param cmd The command name of this command.
+271 */
+272 private void registerSubclasses(String cmd) {
+273 for (Class<?> clazz : this.getClass().getDeclaredClasses()) {
+274 if (BaseCommand.class.isAssignableFrom(clazz)) {
+275 try {
+276 BaseCommand subCommand = null;
+277 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
+278 for (Constructor<?> declaredConstructor : declaredConstructors) {
+279
+280 declaredConstructor.setAccessible(true);
+281 Parameter[] parameters = declaredConstructor.getParameters();
+282 if (parameters.length == 1) {
+283 subCommand = (BaseCommand) declaredConstructor.newInstance(this);
+284 } else {
+285 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")");
+286 }
+287 }
+288 if (subCommand != null) {
+289 subCommand.parentCommand = this;
+290 subCommand.onRegister(manager, cmd);
+291 this.subCommands.putAll(subCommand.subCommands);
+292 this.registeredCommands.putAll(subCommand.registeredCommands);
+293 } else {
+294 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName());
+295 }
+296 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+297 this.manager.log(LogLevel.ERROR, "Error registering subclass", e);
+298 }
+299 }
+300 }
+301 }
+302
+303 /**
+304 * This registers all subcommands of the command.
+305 */
+306 private void registerSubcommands() {
+307 final Annotations annotations = manager.getAnnotations();
+308 boolean foundDefault = false;
+309 boolean foundCatchUnknown = false;
+310 boolean isParentEmpty = parentSubcommand == null || parentSubcommand.isEmpty();
+311
+312 for (Method method : this.getClass().getMethods()) {
+313 method.setAccessible(true);
+314 String sublist = null;
+315 String sub = getSubcommandValue(method);
+316 final boolean def = annotations.hasAnnotation(method, Default.class);
+317 final String helpCommand = annotations.getAnnotationValue(method, HelpCommand.class, Annotations.NOTHING);
+318 final String commandAliases = annotations.getAnnotationValue(method, CommandAlias.class, Annotations.NOTHING);
+319
+320 if (!isParentEmpty && def) {
+321 sub = parentSubcommand;
+322 }
+323 if (isParentEmpty && (def || (!foundDefault && helpCommand != null))) {
+324 if (!foundDefault) {
+325 if (def) {
+326 this.subCommands.get(DEFAULT).clear();
+327 foundDefault = true;
+328 }
+329 registerSubcommand(method, DEFAULT);
+330 } else {
+331 ACFUtil.sneaky(new IllegalStateException("Multiple @Default/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
+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 @UnknownHandler/@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 * Gets the subcommand name of the method given.
+374 *
+375 * @param method The method to check.
+376 * @return The name of the subcommand. It returns null if the input doesn't have {@link Subcommand} attached.
+377 */
+378 private String getSubcommandValue(Method method) {
+379 final String sub = manager.getAnnotations().getAnnotationValue(method, Subcommand.class, Annotations.NOTHING);
+380 if (sub == null) {
+381 return null;
+382 }
+383 Class<?> clazz = method.getDeclaringClass();
+384 String parent = getParentSubcommand(clazz);
+385 return parent == null || parent.isEmpty() ? sub : parent + " " + sub;
+386 }
+387
+388 private String getParentSubcommand(Class<?> clazz) {
+389 List<String> subList = new ArrayList<>();
+390 while (clazz != null) {
+391 String sub = manager.getAnnotations().getAnnotationValue(clazz, Subcommand.class, Annotations.NOTHING);
+392 if (sub != null) {
+393 subList.add(sub);
+394 }
+395 clazz = clazz.getEnclosingClass();
+396 }
+397 Collections.reverse(subList);
+398 return ACFUtil.join(subList, " ");
+399 }
+400
+401 /**
+402 * Registers the given {@link BaseCommand cmd} as a child of the {@link RootCommand} linked to the name given.
+403 *
+404 * @param name Name of the parent to cmd.
+405 * @param cmd The {@link BaseCommand} to add as a child to the {@link RootCommand} owned name field.
+406 */
+407 private void register(String name, BaseCommand cmd) {
+408 String nameLower = name.toLowerCase();
+409 RootCommand rootCommand = manager.obtainRootCommand(nameLower);
+410 rootCommand.addChild(cmd);
+411
+412 this.registeredCommands.put(nameLower, rootCommand);
+413 }
+414
+415 /**
+416 * Registers the given {@link Method} as a subcommand.
+417 *
+418 * @param method The method to register as a subcommand.
+419 * @param subCommand The subcommand's name(s).
+420 */
+421 private void registerSubcommand(Method method, String subCommand) {
+422 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase());
+423 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand);
+424 // Must run getSubcommandPossibility BEFORE we rewrite it just after this.
+425 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts);
+426
+427 // Strip pipes off for auto complete addition
+428 for (int i = 0; i < subCommandParts.length; i++) {
+429 String[] split = ACFPatterns.PIPE.split(subCommandParts[i]);
+430 if (split.length == 0 || split[0].isEmpty()) {
+431 throw new IllegalArgumentException("Invalid @Subcommand configuration for " + method.getName() + " - parts can not start with | or be empty");
+432 }
+433 subCommandParts[i] = split[0];
+434 }
+435 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " ");
+436 final String[] aliasNames = manager.getAnnotations().getAnnotationValues(method, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE);
+437
+438 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " ";
+439 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand);
+440
+441 for (String subcmd : cmdList) {
+442 subCommands.put(subcmd, cmd);
+443 }
+444 cmd.addSubcommands(cmdList);
+445
+446 if (aliasNames != null) {
+447 for (String name : aliasNames) {
+448 register(name, new ForwardingCommand(this, cmd, subCommandParts));
+449 }
+450 }
+451 }
+452
+453 /**
+454 * Takes a string like "foo|bar baz|qux" and generates a list of
+455 * - foo baz
+456 * - foo qux
+457 * - bar baz
+458 * - bar qux
+459 * <p>
+460 * For every possible sub command combination
+461 *
+462 * @param subCommandParts
+463 * @return List of all sub command possibilities
+464 */
+465 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) {
+466 int i = 0;
+467 Set<String> current = null;
+468 while (true) {
+469 Set<String> newList = new HashSet<>();
470
-471 if (i + 1 < subCommandParts.length) {
-472 current = newList;
-473 i = i + 1;
-474 continue;
-475 }
-476
-477 return newList;
-478 }
-479 }
+471 if (i < subCommandParts.length) {
+472 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) {
+473 if (current != null) {
+474 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList()));
+475 } else {
+476 newList.add(s1);
+477 }
+478 }
+479 }
480
-481 public void execute(CommandIssuer issuer, String commandLabel, String[] args) {
-482 commandLabel = commandLabel.toLowerCase();
-483 try {
-484 CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args, false);
-485
-486 if (args.length > 0) {
-487 CommandSearch cmd = findSubCommand(args);
-488 if (cmd != null) {
-489 execSubcommand = cmd.getCheckSub();
-490 final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length);
-491 executeCommand(commandContext, issuer, execargs, cmd.cmd);
-492 return;
-493 }
-494 }
+481 if (i + 1 < subCommandParts.length) {
+482 current = newList;
+483 i = i + 1;
+484 continue;
+485 }
+486
+487 return newList;
+488 }
+489 }
+490
+491 public void execute(CommandIssuer issuer, String commandLabel, String[] args) {
+492 commandLabel = commandLabel.toLowerCase();
+493 try {
+494 CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args, false);
495
-496 if (subCommands.get(DEFAULT) != null && args.length == 0) {
-497 findAndExecuteCommand(commandContext, DEFAULT, issuer, args);
-498 } else if (subCommands.get(CATCHUNKNOWN) != null) {
-499 if (!findAndExecuteCommand(commandContext, CATCHUNKNOWN, issuer, args)) {
-500 help(issuer, args);
-501 }
-502 } else if (subCommands.get(DEFAULT) != null) {
-503 findAndExecuteCommand(commandContext, DEFAULT, issuer, args);
+496 if (args.length > 0) {
+497 CommandSearch cmd = findSubCommand(args);
+498 if (cmd != null) {
+499 execSubcommand = cmd.getCheckSub();
+500 final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length);
+501 executeCommand(commandContext, issuer, execargs, cmd.cmd);
+502 return;
+503 }
504 }
505
-506 } finally {
-507 postCommandOperation();
-508 }
-509 }
-510
-511 /**
-512 * Gets the registered command of the given arguments.
-513 *
-514 * @param args The arguments given by the user.
-515 * @return The subcommand or null if none were found.
-516 * @see #findSubCommand(String[])
-517 */
-518 RegisteredCommand<?> getRegisteredCommand(String[] args) {
-519 final CommandSearch cmd = findSubCommand(args);
-520 return cmd != null ? cmd.cmd : null;
-521 }
-522
-523 /**
-524 * This is ran after any command operation has been performed.
-525 */
-526 private void postCommandOperation() {
-527 CommandManager.commandOperationContext.get().pop();
-528 execSubcommand = null;
-529 execLabel = null;
-530 origArgs = new String[]{};
+506 if (subCommands.get(DEFAULT) != null && args.length == 0) {
+507 findAndExecuteCommand(commandContext, DEFAULT, issuer, args);
+508 } else if (subCommands.get(CATCHUNKNOWN) != null) {
+509 if (!findAndExecuteCommand(commandContext, CATCHUNKNOWN, issuer, args)) {
+510 help(issuer, args);
+511 }
+512 } else if (subCommands.get(DEFAULT) != null) {
+513 findAndExecuteCommand(commandContext, DEFAULT, issuer, args);
+514 }
+515
+516 } finally {
+517 postCommandOperation();
+518 }
+519 }
+520
+521 /**
+522 * Gets the registered command of the given arguments.
+523 *
+524 * @param args The arguments given by the user.
+525 * @return The subcommand or null if none were found.
+526 * @see #findSubCommand(String[])
+527 */
+528 RegisteredCommand<?> getRegisteredCommand(String[] args) {
+529 final CommandSearch cmd = findSubCommand(args);
+530 return cmd != null ? cmd.cmd : null;
531 }
532
533 /**
-534 * This is ran before any command operation has been performed.
-535 *
-536 * @param issuer The user who executed the command.
-537 * @param commandLabel The label the user used to execute the command. This is not the command name, but their input.
-538 * When there is multiple aliases, this is which alias was used
-539 * @param args The arguments passed to the command when executing it.
-540 * @param isAsync Whether the command is executed off of the main thread.
-541 * @return The context which is being registered to the {@link CommandManager}'s {@link
-542 * CommandManager#commandOperationContext thread local stack}.
-543 */
-544 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
-545 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get();
-546 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync);
-547 contexts.push(context);
-548 lastCommandOperationContext = context;
-549 execSubcommand = null;
-550 execLabel = commandLabel;
-551 origArgs = args;
-552 return context;
-553 }
-554
-555 /**
-556 * Gets the current command issuer.
-557 *
-558 * @return The current command issuer.
-559 */
-560 public CommandIssuer getCurrentCommandIssuer() {
-561 return CommandManager.getCurrentCommandIssuer();
-562 }
-563
-564 /**
-565 * Gets the current command manager.
-566 *
-567 * @return The current command manager.
-568 */
-569 public CommandManager getCurrentCommandManager() {
-570 return CommandManager.getCurrentCommandManager();
-571 }
-572
-573 /**
-574 * Finds a subcommand of the given arguments.
-575 *
-576 * @param args The arguments the user input.
-577 * @return The identified subcommand.
-578 * @see #findSubCommand(String[], boolean)
-579 */
-580 private CommandSearch findSubCommand(String[] args) {
-581 return findSubCommand(args, false);
-582 }
-583
-584 /**
-585 * Finds a subcommand of the given arguments.
-586 *
-587 * @param args The arguments the user input.
-588 * @param completion Whether or not completion of arguments should kick in. This may end up with worse than wanted results.
-589 * @return The identified subcommand.
-590 */
-591 private CommandSearch findSubCommand(String[] args, boolean completion) {
-592 for (int i = args.length; i >= 0; i--) {
-593 String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase();
-594 Set<RegisteredCommand> cmds = subCommands.get(checkSub);
-595
-596 final int extraArgs = args.length - i;
-597 if (!cmds.isEmpty()) {
-598 RegisteredCommand cmd = null;
-599 if (cmds.size() == 1) {
-600 cmd = ACFUtil.getFirstElement(cmds);
-601 } else {
-602 Optional<RegisteredCommand> optCmd = cmds.stream().filter(c -> {
-603 int required = c.requiredResolvers;
-604 int optional = c.optionalResolvers;
-605 return extraArgs <= required + optional && (completion || extraArgs >= required);
-606 }).min((c1, c2) -> {
-607 int a = c1.consumeInputResolvers;
-608 int b = c2.consumeInputResolvers;
-609
-610 if (a == b) {
-611 return 0;
-612 }
-613 return a < b ? 1 : -1;
-614 });
-615 if (optCmd.isPresent()) {
-616 cmd = optCmd.get();
-617 }
-618 }
-619 if (cmd != null) {
-620 return new CommandSearch(cmd, i, checkSub);
-621 }
-622 }
-623 }
-624 return null;
-625 }
-626
-627 private void executeCommand(CommandOperationContext commandOperationContext,
-628 CommandIssuer issuer, String[] args, RegisteredCommand cmd) {
-629 if (cmd.hasPermission(issuer)) {
-630 commandOperationContext.setRegisteredCommand(cmd);
-631 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) {
-632 return;
-633 }
-634 List<String> sargs = Arrays.asList(args);
-635 cmd.invoke(issuer, sargs, commandOperationContext);
-636 } else {
-637 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED);
-638 }
-639 }
-640
-641 /**
-642 * Please use command conditions for restricting execution
-643 *
-644 * @param issuer
-645 * @param cmd
-646 * @return
-647 * @deprecated See {@link CommandConditions}
-648 */
-649 @SuppressWarnings("DeprecatedIsStillUsed")
-650 @Deprecated
-651 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) {
-652 return true;
-653 }
-654
-655 /**
-656 * Gets tab completed data from the given command from the user.
-657 *
-658 * @param issuer The user who executed the tabcomplete.
-659 * @param commandLabel The label which is being used by the user.
-660 * @param args The arguments the user has typed so far.
-661 * @return All possibilities in the tab complete.
-662 */
-663 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) {
-664 return tabComplete(issuer, commandLabel, args, false);
-665 }
-666
-667 /**
-668 * Gets the tab complete suggestions from a given command. This will automatically find anything
-669 * which is valid for the specified command through the command's implementation.
-670 *
-671 * @param issuer The issuer of the command.
-672 * @param commandLabel The command name as entered by the user instead of the ACF registered name.
-673 * @param args All arguments entered by the user.
-674 * @param isAsync Whether this is run off of the main thread.
-675 * @return The possibilities to tab complete in no particular order.
-676 */
-677 @SuppressWarnings("WeakerAccess")
-678 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync)
-679 throws IllegalArgumentException {
-680
-681 commandLabel = commandLabel.toLowerCase();
-682 if (args.length == 0) {
-683 args = new String[]{""};
-684 }
-685 try {
-686 CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args, isAsync);
-687
-688 final CommandSearch search = findSubCommand(args, true);
-689
+534 * This is ran after any command operation has been performed.
+535 */
+536 private void postCommandOperation() {
+537 CommandManager.commandOperationContext.get().pop();
+538 execSubcommand = null;
+539 execLabel = null;
+540 origArgs = new String[]{};
+541 }
+542
+543 /**
+544 * This is ran before any command operation has been performed.
+545 *
+546 * @param issuer The user who executed the command.
+547 * @param commandLabel The label the user used to execute the command. This is not the command name, but their input.
+548 * When there is multiple aliases, this is which alias was used
+549 * @param args The arguments passed to the command when executing it.
+550 * @param isAsync Whether the command is executed off of the main thread.
+551 * @return The context which is being registered to the {@link CommandManager}'s {@link
+552 * CommandManager#commandOperationContext thread local stack}.
+553 */
+554 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
+555 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get();
+556 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync);
+557 contexts.push(context);
+558 lastCommandOperationContext.set(context);
+559 execSubcommand = null;
+560 execLabel = commandLabel;
+561 origArgs = args;
+562 return context;
+563 }
+564
+565 /**
+566 * Gets the current command issuer.
+567 *
+568 * @return The current command issuer.
+569 */
+570 public CommandIssuer getCurrentCommandIssuer() {
+571 return CommandManager.getCurrentCommandIssuer();
+572 }
+573
+574 /**
+575 * Gets the current command manager.
+576 *
+577 * @return The current command manager.
+578 */
+579 public CommandManager getCurrentCommandManager() {
+580 return CommandManager.getCurrentCommandManager();
+581 }
+582
+583 /**
+584 * Finds a subcommand of the given arguments.
+585 *
+586 * @param args The arguments the user input.
+587 * @return The identified subcommand.
+588 * @see #findSubCommand(String[], boolean)
+589 */
+590 private CommandSearch findSubCommand(String[] args) {
+591 return findSubCommand(args, false);
+592 }
+593
+594 /**
+595 * Finds a subcommand of the given arguments.
+596 *
+597 * @param args The arguments the user input.
+598 * @param completion Whether or not completion of arguments should kick in. This may end up with worse than wanted results.
+599 * @return The identified subcommand.
+600 */
+601 private CommandSearch findSubCommand(String[] args, boolean completion) {
+602 for (int i = args.length; i >= 0; i--) {
+603 String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase();
+604 Set<RegisteredCommand> cmds = subCommands.get(checkSub);
+605
+606 final int extraArgs = args.length - i;
+607 if (!cmds.isEmpty()) {
+608 RegisteredCommand cmd = null;
+609 if (cmds.size() == 1) {
+610 cmd = ACFUtil.getFirstElement(cmds);
+611 } else {
+612 Optional<RegisteredCommand> optCmd = cmds.stream().filter(c -> {
+613 int required = c.requiredResolvers;
+614 int optional = c.optionalResolvers;
+615 return extraArgs <= required + optional && (completion || extraArgs >= required);
+616 }).min((c1, c2) -> {
+617 int a = c1.consumeInputResolvers;
+618 int b = c2.consumeInputResolvers;
+619
+620 if (a == b) {
+621 return 0;
+622 }
+623 return a < b ? 1 : -1;
+624 });
+625 if (optCmd.isPresent()) {
+626 cmd = optCmd.get();
+627 }
+628 }
+629 if (cmd != null) {
+630 return new CommandSearch(cmd, i, checkSub);
+631 }
+632 }
+633 }
+634 return null;
+635 }
+636
+637 private void executeCommand(CommandOperationContext commandOperationContext,
+638 CommandIssuer issuer, String[] args, RegisteredCommand cmd) {
+639 if (cmd.hasPermission(issuer)) {
+640 commandOperationContext.setRegisteredCommand(cmd);
+641 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) {
+642 return;
+643 }
+644 List<String> sargs = Arrays.asList(args);
+645 cmd.invoke(issuer, sargs, commandOperationContext);
+646 } else {
+647 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED);
+648 }
+649 }
+650
+651 /**
+652 * Please use command conditions for restricting execution
+653 *
+654 * @param issuer
+655 * @param cmd
+656 * @return
+657 * @deprecated See {@link CommandConditions}
+658 */
+659 @SuppressWarnings("DeprecatedIsStillUsed")
+660 @Deprecated
+661 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) {
+662 return true;
+663 }
+664
+665 /**
+666 * Gets tab completed data from the given command from the user.
+667 *
+668 * @param issuer The user who executed the tabcomplete.
+669 * @param commandLabel The label which is being used by the user.
+670 * @param args The arguments the user has typed so far.
+671 * @return All possibilities in the tab complete.
+672 */
+673 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) {
+674 return tabComplete(issuer, commandLabel, args, false);
+675 }
+676
+677 /**
+678 * Gets the tab complete suggestions from a given command. This will automatically find anything
+679 * which is valid for the specified command through the command's implementation.
+680 *
+681 * @param issuer The issuer of the command.
+682 * @param commandLabel The command name as entered by the user instead of the ACF registered name.
+683 * @param args All arguments entered by the user.
+684 * @param isAsync Whether this is run off of the main thread.
+685 * @return The possibilities to tab complete in no particular order.
+686 */
+687 @SuppressWarnings("WeakerAccess")
+688 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync)
+689 throws IllegalArgumentException {
690
-691 final List<String> cmds = new ArrayList<>();
-692
-693 if (search != null) {
-694 cmds.addAll(completeCommand(issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel, isAsync));
-695 } else if (subCommands.get(CATCHUNKNOWN).size() == 1) {
-696 cmds.addAll(completeCommand(issuer, ACFUtil.getFirstElement(subCommands.get(CATCHUNKNOWN)), args, commandLabel, isAsync));
-697 } else if (subCommands.get(DEFAULT).size() == 1) {
-698 cmds.addAll(completeCommand(issuer, ACFUtil.getFirstElement(subCommands.get(DEFAULT)), args, commandLabel, isAsync));
-699 }
+691 commandLabel = commandLabel.toLowerCase();
+692 if (args.length == 0) {
+693 args = new String[]{""};
+694 }
+695 try {
+696 CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args, isAsync);
+697
+698 final CommandSearch search = findSubCommand(args, true);
+699
700
-701 return filterTabComplete(args[args.length - 1], cmds);
-702 } finally {
-703 postCommandOperation();
-704 }
-705 }
-706
-707 /**
-708 * Gets all subcommands which are possible to tabcomplete.
-709 *
-710 * @param issuer The command issuer.
-711 * @param args
-712 * @return
-713 */
-714 List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) {
-715 final Set<String> cmds = new HashSet<>();
-716 final int cmdIndex = Math.max(0, args.length - 1);
-717 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase();
-718 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) {
-719 final String key = entry.getKey();
-720 if (key.startsWith(argString) && !CATCHUNKNOWN.equals(key) && !DEFAULT.equals(key)) {
-721 final RegisteredCommand value = entry.getValue();
-722 if (!value.hasPermission(issuer) || value.isPrivate) {
-723 continue;
-724 }
-725
-726 String[] split = ACFPatterns.SPACE.split(value.prefSubCommand);
-727 cmds.add(split[cmdIndex]);
-728 }
-729 }
-730 return new ArrayList<>(cmds);
-731 }
-732
-733 /**
-734 * Complete a command properly per issuer and input.
-735 *
-736 * @param issuer The user who executed this.
-737 * @param cmd The command to be completed.
-738 * @param args All arguments given by the user.
-739 * @param commandLabel The command name the user used.
-740 * @param isAsync Whether the command was executed async.
-741 * @return All results to complete the command.
-742 */
-743 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) {
-744 if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0 || cmd.complete == null) {
-745 return Collections.emptyList();
-746 }
-747
-748 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, args, isAsync);
-749 return filterTabComplete(args[args.length - 1], cmds);
-750 }
-751
-752 /**
-753 * Gets the actual args in string form the user typed
-754 * This returns a list of all tab complete options which are possible with the given argument and commands.
-755 *
-756 * @param arg Argument which was pressed tab on.
-757 * @param cmds The possibilities to return.
-758 * @return All possible options. This may be empty.
-759 */
-760 private static List<String> filterTabComplete(String arg, List<String> cmds) {
-761 return cmds.stream()
-762 .distinct()
-763 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg)))
-764 .collect(Collectors.toList());
-765 }
-766
-767 /**
-768 * Gets a registered command under the given subcommand name.
-769 *
-770 * @param subcommand The name of the subcommand requested.
-771 * @return The subcommand found or null if none.
-772 */
-773 private RegisteredCommand getCommandBySubcommand(String subcommand) {
-774 return getCommandBySubcommand(subcommand, false);
+701 final List<String> cmds = new ArrayList<>();
+702
+703 if (search != null) {
+704 cmds.addAll(completeCommand(issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel, isAsync));
+705 } else if (subCommands.get(CATCHUNKNOWN).size() == 1) {
+706 cmds.addAll(completeCommand(issuer, ACFUtil.getFirstElement(subCommands.get(CATCHUNKNOWN)), args, commandLabel, isAsync));
+707 } else if (subCommands.get(DEFAULT).size() == 1) {
+708 cmds.addAll(completeCommand(issuer, ACFUtil.getFirstElement(subCommands.get(DEFAULT)), args, commandLabel, isAsync));
+709 }
+710
+711 return filterTabComplete(args[args.length - 1], cmds);
+712 } finally {
+713 postCommandOperation();
+714 }
+715 }
+716
+717 /**
+718 * Gets all subcommands which are possible to tabcomplete.
+719 *
+720 * @param issuer The command issuer.
+721 * @param args
+722 * @return
+723 */
+724 List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) {
+725 final Set<String> cmds = new HashSet<>();
+726 final int cmdIndex = Math.max(0, args.length - 1);
+727 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase();
+728 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) {
+729 final String key = entry.getKey();
+730 if (key.startsWith(argString) && !CATCHUNKNOWN.equals(key) && !DEFAULT.equals(key)) {
+731 final RegisteredCommand value = entry.getValue();
+732 if (!value.hasPermission(issuer) || value.isPrivate) {
+733 continue;
+734 }
+735
+736 String[] split = ACFPatterns.SPACE.split(value.prefSubCommand);
+737 cmds.add(split[cmdIndex]);
+738 }
+739 }
+740 return new ArrayList<>(cmds);
+741 }
+742
+743 /**
+744 * Complete a command properly per issuer and input.
+745 *
+746 * @param issuer The user who executed this.
+747 * @param cmd The command to be completed.
+748 * @param args All arguments given by the user.
+749 * @param commandLabel The command name the user used.
+750 * @param isAsync Whether the command was executed async.
+751 * @return All results to complete the command.
+752 */
+753 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) {
+754 if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0 || cmd.complete == null) {
+755 return Collections.emptyList();
+756 }
+757
+758 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, args, isAsync);
+759 return filterTabComplete(args[args.length - 1], cmds);
+760 }
+761
+762 /**
+763 * Gets the actual args in string form the user typed
+764 * This returns a list of all tab complete options which are possible with the given argument and commands.
+765 *
+766 * @param arg Argument which was pressed tab on.
+767 * @param cmds The possibilities to return.
+768 * @return All possible options. This may be empty.
+769 */
+770 private static List<String> filterTabComplete(String arg, List<String> cmds) {
+771 return cmds.stream()
+772 .distinct()
+773 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg)))
+774 .collect(Collectors.toList());
775 }
776
777 /**
-778 * Gets a registered command under the given name.
-779 * If requireOne is true, it won't accept more than a single matching subcommand.
-780 *
-781 * @param subcommand Name of the subcommand wanted.
-782 * @param requireOne Whether to only accept 1 result.
-783 * @return The subcommand found, or null if none/too many.
-784 */
-785 private RegisteredCommand getCommandBySubcommand(String subcommand, boolean requireOne) {
-786 final Set<RegisteredCommand> commands = subCommands.get(subcommand);
-787 if (!commands.isEmpty() && (!requireOne || commands.size() == 1)) {
-788 return commands.iterator().next();
-789 }
-790 return null;
-791 }
-792
-793 /**
-794 * Internally calls {@link #executeCommand(CommandOperationContext, CommandIssuer, String[], RegisteredCommand)}
-795 * and gets through {@link #getCommandBySubcommand(String)}.
-796 *
-797 * @param commandContext The command context to use.
-798 * @param subcommand The subcommand to find the executor of.
-799 * @param issuer The issuer who executed the subcommand.
-800 * @param args All arguments given by the issuer.
-801 * @return Whether it found a command or not.
-802 * @see #executeCommand(CommandOperationContext, CommandIssuer, String[], RegisteredCommand)
-803 * @see #getCommandBySubcommand(String)
-804 * @see RegisteredCommand#invoke(CommandIssuer, List, CommandOperationContext)
-805 */
-806 private boolean findAndExecuteCommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) {
-807 final RegisteredCommand cmd = this.getCommandBySubcommand(subcommand);
-808 if (cmd != null) {
-809 executeCommand(commandContext, issuer, args, cmd);
-810 return true;
-811 }
-812
-813 return false;
-814 }
-815
-816 /**
-817 * Executes the precommand and sees whether something is wrong. Ideally, you get false from this.
-818 *
-819 * @param commandOperationContext The context to use.
-820 * @param cmd The command executed.
-821 * @param issuer The issuer who executed the command.
-822 * @param args The arguments the issuer provided.
-823 * @return Whether something went wrong.
-824 */
-825 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) {
-826 Method pre = this.preCommandHandler;
-827 if (pre != null) {
-828 try {
-829 Class<?>[] types = pre.getParameterTypes();
-830 Object[] parameters = new Object[pre.getParameterCount()];
-831 for (int i = 0; i < parameters.length; i++) {
-832 Class<?> type = types[i];
-833 Object issuerObject = issuer.getIssuer();
-834 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) {
-835 parameters[i] = issuerObject;
-836 } else if (CommandIssuer.class.isAssignableFrom(type)) {
-837 parameters[i] = issuer;
-838 } else if (RegisteredCommand.class.isAssignableFrom(type)) {
-839 parameters[i] = cmd;
-840 } else if (String[].class.isAssignableFrom((type))) {
-841 parameters[i] = args;
-842 } else {
-843 parameters[i] = null;
-844 }
-845 }
-846
-847 return (boolean) pre.invoke(this, parameters);
-848 } catch (IllegalAccessException | InvocationTargetException e) {
-849 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e);
-850 }
-851 }
-852 return false;
-853 }
-854
-855 /**
-856 * @deprecated Unstable API
-857 */
-858 @Deprecated
-859 @UnstableAPI
-860 public CommandHelp getCommandHelp() {
-861 return manager.generateCommandHelp();
-862 }
-863
-864 /**
-865 * @deprecated Unstable API
-866 */
-867 @Deprecated
-868 @UnstableAPI
-869 public void showCommandHelp() {
-870 getCommandHelp().showHelp();
-871 }
-872
-873 public void help(Object issuer, String[] args) {
-874 help(manager.getCommandIssuer(issuer), args);
-875 }
-876
-877 public void help(CommandIssuer issuer, String[] args) {
-878 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND);
-879 }
-880
-881 public void doHelp(Object issuer, String... args) {
-882 doHelp(manager.getCommandIssuer(issuer), args);
-883 }
-884
-885 public void doHelp(CommandIssuer issuer, String... args) {
-886 help(issuer, args);
-887 }
-888
-889 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) {
-890 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX,
-891 "{command}", manager.getCommandPrefix(issuer) + cmd.command,
-892 "{syntax}", cmd.syntaxText
-893 );
-894 }
-895
-896 public boolean hasPermission(Object issuer) {
-897 return hasPermission(manager.getCommandIssuer(issuer));
-898 }
-899
-900 public boolean hasPermission(CommandIssuer issuer) {
-901 return permission == null || permission.isEmpty() || (manager.hasPermission(issuer, permission) && (parentCommand == null || parentCommand.hasPermission(issuer)));
-902 }
-903
-904 public Set<String> getRequiredPermissions() {
-905 Set<String> permissions = new HashSet<>();
-906 if (this.permission != null && !this.permission.isEmpty()) {
-907 permissions.addAll(Arrays.asList(ACFPatterns.COMMA.split(this.permission)));
-908 }
-909 if (parentCommand != null) {
-910 permissions.addAll(parentCommand.getRequiredPermissions());
-911 }
-912
-913 return permissions;
-914 }
-915
-916 public boolean requiresPermission(String permission) {
-917 return getRequiredPermissions().contains(permission);
-918 }
-919
-920 public String getName() {
-921 return commandName;
-922 }
-923
-924 public ExceptionHandler getExceptionHandler() {
-925 return exceptionHandler;
-926 }
-927
-928 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) {
-929 this.exceptionHandler = exceptionHandler;
-930 return this;
-931 }
-932
-933 public RegisteredCommand getDefaultRegisteredCommand() {
-934 return this.getCommandBySubcommand(DEFAULT);
-935 }
-936
-937 public String setContextFlags(Class<?> cls, String flags) {
-938 return this.contextFlags.put(cls, flags);
-939 }
-940
-941 public String getContextFlags(Class<?> cls) {
-942 return this.contextFlags.get(cls);
-943 }
-944
-945 public List<RegisteredCommand> getRegisteredCommands() {
-946 List<RegisteredCommand> registeredCommands = new ArrayList<>();
-947 registeredCommands.addAll(this.subCommands.values());
-948 return registeredCommands;
-949
-950 }
-951
-952 private static class CommandSearch {
-953 RegisteredCommand cmd;
-954 int argIndex;
-955 String checkSub;
-956
-957 CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) {
-958 this.cmd = cmd;
-959 this.argIndex = argIndex;
-960 this.checkSub = checkSub;
-961 }
-962
-963 String getCheckSub() {
-964 return this.checkSub;
-965 }
+778 * Gets a registered command under the given subcommand name.
+779 *
+780 * @param subcommand The name of the subcommand requested.
+781 * @return The subcommand found or null if none.
+782 */
+783 private RegisteredCommand getCommandBySubcommand(String subcommand) {
+784 return getCommandBySubcommand(subcommand, false);
+785 }
+786
+787 /**
+788 * Gets a registered command under the given name.
+789 * If requireOne is true, it won't accept more than a single matching subcommand.
+790 *
+791 * @param subcommand Name of the subcommand wanted.
+792 * @param requireOne Whether to only accept 1 result.
+793 * @return The subcommand found, or null if none/too many.
+794 */
+795 private RegisteredCommand getCommandBySubcommand(String subcommand, boolean requireOne) {
+796 final Set<RegisteredCommand> commands = subCommands.get(subcommand);
+797 if (!commands.isEmpty() && (!requireOne || commands.size() == 1)) {
+798 return commands.iterator().next();
+799 }
+800 return null;
+801 }
+802
+803 /**
+804 * Internally calls {@link #executeCommand(CommandOperationContext, CommandIssuer, String[], RegisteredCommand)}
+805 * and gets through {@link #getCommandBySubcommand(String)}.
+806 *
+807 * @param commandContext The command context to use.
+808 * @param subcommand The subcommand to find the executor of.
+809 * @param issuer The issuer who executed the subcommand.
+810 * @param args All arguments given by the issuer.
+811 * @return Whether it found a command or not.
+812 * @see #executeCommand(CommandOperationContext, CommandIssuer, String[], RegisteredCommand)
+813 * @see #getCommandBySubcommand(String)
+814 * @see RegisteredCommand#invoke(CommandIssuer, List, CommandOperationContext)
+815 */
+816 private boolean findAndExecuteCommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) {
+817 final RegisteredCommand cmd = this.getCommandBySubcommand(subcommand);
+818 if (cmd != null) {
+819 executeCommand(commandContext, issuer, args, cmd);
+820 return true;
+821 }
+822
+823 return false;
+824 }
+825
+826 /**
+827 * Executes the precommand and sees whether something is wrong. Ideally, you get false from this.
+828 *
+829 * @param commandOperationContext The context to use.
+830 * @param cmd The command executed.
+831 * @param issuer The issuer who executed the command.
+832 * @param args The arguments the issuer provided.
+833 * @return Whether something went wrong.
+834 */
+835 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) {
+836 Method pre = this.preCommandHandler;
+837 if (pre != null) {
+838 try {
+839 Class<?>[] types = pre.getParameterTypes();
+840 Object[] parameters = new Object[pre.getParameterCount()];
+841 for (int i = 0; i < parameters.length; i++) {
+842 Class<?> type = types[i];
+843 Object issuerObject = issuer.getIssuer();
+844 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) {
+845 parameters[i] = issuerObject;
+846 } else if (CommandIssuer.class.isAssignableFrom(type)) {
+847 parameters[i] = issuer;
+848 } else if (RegisteredCommand.class.isAssignableFrom(type)) {
+849 parameters[i] = cmd;
+850 } else if (String[].class.isAssignableFrom((type))) {
+851 parameters[i] = args;
+852 } else {
+853 parameters[i] = null;
+854 }
+855 }
+856
+857 return (boolean) pre.invoke(this, parameters);
+858 } catch (IllegalAccessException | InvocationTargetException e) {
+859 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e);
+860 }
+861 }
+862 return false;
+863 }
+864
+865 /**
+866 * @deprecated Unstable API
+867 */
+868 @Deprecated
+869 @UnstableAPI
+870 public CommandHelp getCommandHelp() {
+871 return manager.generateCommandHelp();
+872 }
+873
+874 /**
+875 * @deprecated Unstable API
+876 */
+877 @Deprecated
+878 @UnstableAPI
+879 public void showCommandHelp() {
+880 getCommandHelp().showHelp();
+881 }
+882
+883 public void help(Object issuer, String[] args) {
+884 help(manager.getCommandIssuer(issuer), args);
+885 }
+886
+887 public void help(CommandIssuer issuer, String[] args) {
+888 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND);
+889 }
+890
+891 public void doHelp(Object issuer, String... args) {
+892 doHelp(manager.getCommandIssuer(issuer), args);
+893 }
+894
+895 public void doHelp(CommandIssuer issuer, String... args) {
+896 help(issuer, args);
+897 }
+898
+899 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) {
+900 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX,
+901 "{command}", manager.getCommandPrefix(issuer) + cmd.command,
+902 "{syntax}", cmd.syntaxText
+903 );
+904 }
+905
+906 public boolean hasPermission(Object issuer) {
+907 return hasPermission(manager.getCommandIssuer(issuer));
+908 }
+909
+910 public boolean hasPermission(CommandIssuer issuer) {
+911 return permission == null || permission.isEmpty() || (manager.hasPermission(issuer, permission) && (parentCommand == null || parentCommand.hasPermission(issuer)));
+912 }
+913
+914 public Set<String> getRequiredPermissions() {
+915 Set<String> permissions = new HashSet<>();
+916 if (this.permission != null && !this.permission.isEmpty()) {
+917 permissions.addAll(Arrays.asList(ACFPatterns.COMMA.split(this.permission)));
+918 }
+919 if (parentCommand != null) {
+920 permissions.addAll(parentCommand.getRequiredPermissions());
+921 }
+922
+923 return permissions;
+924 }
+925
+926 public boolean requiresPermission(String permission) {
+927 return getRequiredPermissions().contains(permission);
+928 }
+929
+930 public String getName() {
+931 return commandName;
+932 }
+933
+934 public ExceptionHandler getExceptionHandler() {
+935 return exceptionHandler;
+936 }
+937
+938 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) {
+939 this.exceptionHandler = exceptionHandler;
+940 return this;
+941 }
+942
+943 public RegisteredCommand getDefaultRegisteredCommand() {
+944 return this.getCommandBySubcommand(DEFAULT);
+945 }
+946
+947 public String setContextFlags(Class<?> cls, String flags) {
+948 return this.contextFlags.put(cls, flags);
+949 }
+950
+951 public String getContextFlags(Class<?> cls) {
+952 return this.contextFlags.get(cls);
+953 }
+954
+955 public List<RegisteredCommand> getRegisteredCommands() {
+956 List<RegisteredCommand> registeredCommands = new ArrayList<>();
+957 registeredCommands.addAll(this.subCommands.values());
+958 return registeredCommands;
+959
+960 }
+961
+962 private static class CommandSearch {
+963 RegisteredCommand cmd;
+964 int argIndex;
+965 String checkSub;
966
-967 @Override
-968 public boolean equals(Object o) {
-969 if (this == o) return true;
-970 if (o == null || getClass() != o.getClass()) return false;
-971 CommandSearch that = (CommandSearch) o;
-972 return argIndex == that.argIndex &&
-973 Objects.equals(cmd, that.cmd) &&
-974 Objects.equals(checkSub, that.checkSub);
+967 CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) {
+968 this.cmd = cmd;
+969 this.argIndex = argIndex;
+970 this.checkSub = checkSub;
+971 }
+972
+973 String getCheckSub() {
+974 return this.checkSub;
975 }
976
977 @Override
-978 public int hashCode() {
-979 return Objects.hash(cmd, argIndex, checkSub);
-980 }
-981 }
-982}
+978 public boolean equals(Object o) {
+979 if (this == o) return true;
+980 if (o == null || getClass() != o.getClass()) return false;
+981 CommandSearch that = (CommandSearch) o;
+982 return argIndex == that.argIndex &&
+983 Objects.equals(cmd, that.cmd) &&
+984 Objects.equals(checkSub, that.checkSub);
+985 }
+986
+987 @Override
+988 public int hashCode() {
+989 return Objects.hash(cmd, argIndex, checkSub);
+990 }
+991 }
+992}
diff --git a/docs/acf-core/src-html/co/aikar/commands/ForwardingCommand.html b/docs/acf-core/src-html/co/aikar/commands/ForwardingCommand.html
index 4ff0fd56..b65842b6 100644
--- a/docs/acf-core/src-html/co/aikar/commands/ForwardingCommand.html
+++ b/docs/acf-core/src-html/co/aikar/commands/ForwardingCommand.html
@@ -56,39 +56,44 @@
048 }
049
050 @Override
-051 public Set<String> getRequiredPermissions() {
-052 return command.getRequiredPermissions();
+051 public CommandOperationContext getLastCommandOperationContext() {
+052 return command.getLastCommandOperationContext();
053 }
054
055 @Override
-056 public boolean hasPermission(Object issuer) {
-057 return command.hasPermission(issuer);
+056 public Set<String> getRequiredPermissions() {
+057 return command.getRequiredPermissions();
058 }
059
060 @Override
-061 public boolean requiresPermission(String permission) {
-062 return command.requiresPermission(permission);
+061 public boolean hasPermission(Object issuer) {
+062 return command.hasPermission(issuer);
063 }
064
065 @Override
-066 public boolean hasPermission(CommandIssuer sender) {
-067 return command.hasPermission(sender);
+066 public boolean requiresPermission(String permission) {
+067 return command.requiresPermission(permission);
068 }
069
070 @Override
-071 public List<String> tabComplete(CommandIssuer issuer, String alias, String[] args, boolean isAsync) throws IllegalArgumentException {
-072 return command.tabComplete(issuer, alias, ApacheCommonsLangUtil.addAll(baseArgs, args), isAsync);
+071 public boolean hasPermission(CommandIssuer sender) {
+072 return command.hasPermission(sender);
073 }
074
075 @Override
-076 public void execute(CommandIssuer issuer, String commandLabel, String[] args) {
-077 command.execute(issuer, commandLabel, ApacheCommonsLangUtil.addAll(baseArgs, args));
+076 public List<String> tabComplete(CommandIssuer issuer, String alias, String[] args, boolean isAsync) throws IllegalArgumentException {
+077 return command.tabComplete(issuer, alias, ApacheCommonsLangUtil.addAll(baseArgs, args), isAsync);
078 }
079
-080 BaseCommand getCommand() {
-081 return command;
-082 }
-083}
+080 @Override
+081 public void execute(CommandIssuer issuer, String commandLabel, String[] args) {
+082 command.execute(issuer, commandLabel, ApacheCommonsLangUtil.addAll(baseArgs, args));
+083 }
+084
+085 BaseCommand getCommand() {
+086 return command;
+087 }
+088}
diff --git a/docs/acf-sponge/co/aikar/commands/SpongeCommandContexts.html b/docs/acf-sponge/co/aikar/commands/SpongeCommandContexts.html
index a716ac17..f94a532f 100644
--- a/docs/acf-sponge/co/aikar/commands/SpongeCommandContexts.html
+++ b/docs/acf-sponge/co/aikar/commands/SpongeCommandContexts.html
@@ -107,7 +107,7 @@
-public class SpongeCommandContexts
+public class SpongeCommandContexts
extends co.aikar.commands.CommandContexts<SpongeCommandExecutionContext>
@@ -187,7 +187,7 @@ extends co.aikar.commands.CommandContexts<
SpongeCommandContexts
-public SpongeCommandContexts(SpongeCommandManager manager)
+public SpongeCommandContexts(SpongeCommandManager manager)
diff --git a/docs/acf-sponge/co/aikar/commands/SpongeRootCommand.html b/docs/acf-sponge/co/aikar/commands/SpongeRootCommand.html
index f2e0dc6b..4855aaf9 100644
--- a/docs/acf-sponge/co/aikar/commands/SpongeRootCommand.html
+++ b/docs/acf-sponge/co/aikar/commands/SpongeRootCommand.html
@@ -328,7 +328,7 @@ implements org.spongepowered.api.command.CommandCallable, co.aikar.commands.Root
|