diff --git a/docs/acf-core/co/aikar/commands/BaseCommand.html b/docs/acf-core/co/aikar/commands/BaseCommand.html
index f0f0b409..6e48b63d 100644
--- a/docs/acf-core/co/aikar/commands/BaseCommand.html
+++ b/docs/acf-core/co/aikar/commands/BaseCommand.html
@@ -112,7 +112,7 @@ var activeTableTab = "activeTableTab";
-public abstract class BaseCommand
+public abstract class BaseCommand
extends Object
@@ -335,7 +335,7 @@ extends
CATCHUNKNOWN
-public static final String CATCHUNKNOWN
+public static final String CATCHUNKNOWN
- See Also:
- Constant Field Values
@@ -348,7 +348,7 @@ extends
-
DEFAULT
-public static final String DEFAULT
+public static final String DEFAULT
- See Also:
- Constant Field Values
@@ -369,7 +369,7 @@ extends
-
BaseCommand
-public BaseCommand()
+public BaseCommand()
@@ -378,7 +378,7 @@ extends
-
BaseCommand
-public BaseCommand(String cmd)
+public BaseCommand(String cmd)
@@ -395,7 +395,7 @@ extends
-
getExecCommandLabel
-public String getExecCommandLabel()
+public String getExecCommandLabel()
Gets the root command name that the user actually typed
- Returns:
@@ -409,7 +409,7 @@ extends
-
getExecSubcommand
-public String getExecSubcommand()
+public String getExecSubcommand()
Gets the actual sub command name the user typed
- Returns:
@@ -423,7 +423,7 @@ extends
-
getOrigArgs
-public String[] getOrigArgs()
+public String[] getOrigArgs()
Gets the actual args in string form the user typed
- Returns:
@@ -437,7 +437,7 @@ extends
-
execute
-public void execute(CommandIssuer issuer,
+public void execute(CommandIssuer issuer,
String commandLabel,
String[] args)
@@ -448,7 +448,7 @@ extends
-
getCurrentCommandIssuer
-public CommandIssuer getCurrentCommandIssuer()
+public CommandIssuer getCurrentCommandIssuer()
@@ -457,7 +457,7 @@ extends
-
getCurrentCommandManager
-public CommandManager getCurrentCommandManager()
+public CommandManager getCurrentCommandManager()
@@ -466,7 +466,7 @@ extends
-
canExecute
-public boolean canExecute(CommandIssuer issuer,
+public boolean canExecute(CommandIssuer issuer,
RegisteredCommand<?> cmd)
@@ -476,7 +476,7 @@ extends
-
tabComplete
-public List<String> tabComplete(CommandIssuer issuer,
+public List<String> tabComplete(CommandIssuer issuer,
String commandLabel,
String[] args)
@@ -487,7 +487,7 @@ extends
-
tabComplete
-public List<String> tabComplete(CommandIssuer issuer,
+public List<String> tabComplete(CommandIssuer issuer,
String commandLabel,
String[] args,
boolean isAsync)
@@ -505,7 +505,7 @@ extends
getCommandHelp
@Deprecated
-public CommandHelp getCommandHelp()
+public CommandHelp getCommandHelp()
Deprecated.
@@ -516,7 +516,7 @@ public
showCommandHelp
@Deprecated
-public void showCommandHelp()
+public void showCommandHelp()
Deprecated.
@@ -526,7 +526,7 @@ public void
help
-public void help(Object issuer,
+public void help(Object issuer,
String[] args)
@@ -536,7 +536,7 @@ public void
help
-public void help(CommandIssuer issuer,
+public void help(CommandIssuer issuer,
String[] args)
@@ -546,7 +546,7 @@ public void
doHelp
-public void doHelp(Object issuer,
+public void doHelp(Object issuer,
String... args)
@@ -556,7 +556,7 @@ public void
doHelp
-public void doHelp(CommandIssuer issuer,
+public void doHelp(CommandIssuer issuer,
String... args)
@@ -566,7 +566,7 @@ public void
showSyntax
-public void showSyntax(CommandIssuer issuer,
+public void showSyntax(CommandIssuer issuer,
RegisteredCommand<?> cmd)
@@ -576,7 +576,7 @@ public void
hasPermission
-public boolean hasPermission(Object issuer)
+public boolean hasPermission(Object issuer)
@@ -585,7 +585,7 @@ public void
hasPermission
-public boolean hasPermission(CommandIssuer issuer)
+public boolean hasPermission(CommandIssuer issuer)
@@ -594,7 +594,7 @@ public void
getRequiredPermissions
-public Set<String> getRequiredPermissions()
+public Set<String> getRequiredPermissions()
@@ -603,7 +603,7 @@ public void
requiresPermission
-public boolean requiresPermission(String permission)
+public boolean requiresPermission(String permission)
@@ -612,7 +612,7 @@ public void
getName
-public String getName()
+public String getName()
@@ -621,7 +621,7 @@ public void
getExceptionHandler
-public ExceptionHandler getExceptionHandler()
+public ExceptionHandler getExceptionHandler()
@@ -630,7 +630,7 @@ public void
setExceptionHandler
-public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler)
+public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler)
@@ -639,7 +639,7 @@ public void
getDefaultRegisteredCommand
-public RegisteredCommand getDefaultRegisteredCommand()
+public RegisteredCommand getDefaultRegisteredCommand()
@@ -648,7 +648,7 @@ public void
setContextFlags
-public String setContextFlags(Class<?> cls,
+public String setContextFlags(Class<?> cls,
String flags)
@@ -658,7 +658,7 @@ public void
getContextFlags
-public String getContextFlags(Class<?> cls)
+public String getContextFlags(Class<?> cls)
diff --git a/docs/acf-core/co/aikar/commands/RegisteredCommand.html b/docs/acf-core/co/aikar/commands/RegisteredCommand.html
index 56efa0fb..9fd85eea 100644
--- a/docs/acf-core/co/aikar/commands/RegisteredCommand.html
+++ b/docs/acf-core/co/aikar/commands/RegisteredCommand.html
@@ -108,7 +108,7 @@ var activeTableTab = "activeTableTab";
-public class RegisteredCommand<CEC extends CommandExecutionContext<CEC,? extends CommandIssuer>>
+public class RegisteredCommand<CEC extends CommandExecutionContext<CEC,? extends CommandIssuer>>
extends Object
@@ -217,7 +217,7 @@ extends
helpSearchTags
-public String helpSearchTags
+public String helpSearchTags
@@ -234,7 +234,7 @@ extends
preCommand
-public void preCommand()
+public void preCommand()
@@ -243,7 +243,7 @@ extends
postCommand
-public void postCommand()
+public void postCommand()
@@ -253,7 +253,7 @@ extends
getPermission
@Deprecated
-public String getPermission()
+public String getPermission()
Deprecated.
- See Also:
@@ -267,7 +267,7 @@ public
-
getRequiredPermissions
-public Set<String> getRequiredPermissions()
+public Set<String> getRequiredPermissions()
@@ -276,7 +276,7 @@ public
-
requiresPermission
-public boolean requiresPermission(String permission)
+public boolean requiresPermission(String permission)
@@ -285,7 +285,7 @@ public
-
getPrefSubCommand
-public String getPrefSubCommand()
+public String getPrefSubCommand()
@@ -294,7 +294,7 @@ public
-
getSyntaxText
-public String getSyntaxText()
+public String getSyntaxText()
@@ -303,7 +303,7 @@ public
-
getCommand
-public String getCommand()
+public String getCommand()
@@ -312,7 +312,7 @@ public
-
addSubcommand
-public void addSubcommand(String cmd)
+public void addSubcommand(String cmd)
@@ -321,7 +321,7 @@ public
-
addSubcommands
-public void addSubcommands(Collection<String> cmd)
+public void addSubcommands(Collection<String> cmd)
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 af31eaac..6a243136 100644
--- a/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html
+++ b/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html
@@ -50,672 +50,660 @@
042import com.google.common.collect.Maps;
043import com.google.common.collect.SetMultimap;
044import com.google.common.collect.Sets;
-045import org.jetbrains.annotations.Nullable;
-046
-047import java.lang.invoke.MethodHandle;
-048import java.lang.invoke.MethodHandles;
-049import java.lang.reflect.Constructor;
-050import java.lang.reflect.InvocationTargetException;
-051import java.lang.reflect.Method;
-052import java.lang.reflect.Parameter;
-053import java.util.ArrayList;
-054import java.util.Arrays;
-055import java.util.Collections;
-056import java.util.HashMap;
-057import java.util.HashSet;
-058import java.util.List;
-059import java.util.Map;
-060import java.util.Objects;
-061import java.util.Optional;
-062import java.util.Set;
-063import java.util.Stack;
-064import java.util.stream.Collectors;
-065import java.util.stream.Stream;
+045
+046import java.lang.reflect.Constructor;
+047import java.lang.reflect.InvocationTargetException;
+048import java.lang.reflect.Method;
+049import java.lang.reflect.Parameter;
+050import java.util.ArrayList;
+051import java.util.Arrays;
+052import java.util.Collections;
+053import java.util.HashMap;
+054import java.util.HashSet;
+055import java.util.List;
+056import java.util.Map;
+057import java.util.Objects;
+058import java.util.Optional;
+059import java.util.Set;
+060import java.util.Stack;
+061import java.util.stream.Collectors;
+062import java.util.stream.Stream;
+063
+064@SuppressWarnings("unused")
+065public abstract class BaseCommand {
066
-067@SuppressWarnings("unused")
-068public abstract class BaseCommand {
-069
-070 public static final String CATCHUNKNOWN = "__catchunknown";
-071 public static final String DEFAULT = "__default";
-072 final SetMultimap<String, RegisteredCommand> subCommands = HashMultimap.create();
-073 final Map<Class<?>, String> contextFlags = Maps.newHashMap();
-074 @Nullable private MethodHandle preCommandHandler = null;
-075 private Method preCommandReflectiveHandler = null;
-076
+067 public static final String CATCHUNKNOWN = "__catchunknown";
+068 public static final String DEFAULT = "__default";
+069 final SetMultimap<String, RegisteredCommand> subCommands = HashMultimap.create();
+070 final Map<Class<?>, String> contextFlags = Maps.newHashMap();
+071 private Method preCommandHandler;
+072
+073 @SuppressWarnings("WeakerAccess")
+074 private String execLabel;
+075 @SuppressWarnings("WeakerAccess")
+076 private String execSubcommand;
077 @SuppressWarnings("WeakerAccess")
-078 private String execLabel;
-079 @SuppressWarnings("WeakerAccess")
-080 private String execSubcommand;
-081 @SuppressWarnings("WeakerAccess")
-082 private String[] origArgs;
-083 CommandManager<?, ?, ?, ?, ?, ?> manager = null;
-084 BaseCommand parentCommand;
-085 Map<String, RootCommand> registeredCommands = new HashMap<>();
-086 String description;
-087 String commandName;
-088 String permission;
-089 String conditions;
+078 private String[] origArgs;
+079 CommandManager<?, ?, ?, ?, ?, ?> manager = null;
+080 BaseCommand parentCommand;
+081 Map<String, RootCommand> registeredCommands = new HashMap<>();
+082 String description;
+083 String commandName;
+084 String permission;
+085 String conditions;
+086
+087 private ExceptionHandler exceptionHandler = null;
+088 CommandOperationContext lastCommandOperationContext;
+089 private String parentSubcommand;
090
-091 private ExceptionHandler exceptionHandler = null;
-092 CommandOperationContext lastCommandOperationContext;
-093 private String parentSubcommand;
-094
-095 public BaseCommand() {}
-096 public BaseCommand(String cmd) {
-097 this.commandName = cmd;
-098 }
-099
-100 /**
-101 * Gets the root command name that the user actually typed
-102 * @return Name
-103 */
-104 public String getExecCommandLabel() {
-105 return execLabel;
-106 }
-107
-108 /**
-109 * Gets the actual sub command name the user typed
-110 * @return Name
-111 */
-112 public String getExecSubcommand() {
-113 return execSubcommand;
-114 }
-115
-116 /**
-117 * Gets the actual args in string form the user typed
-118 * @return Args
-119 */
-120 public String[] getOrigArgs() {
-121 return origArgs;
+091 public BaseCommand() {}
+092 public BaseCommand(String cmd) {
+093 this.commandName = cmd;
+094 }
+095
+096 /**
+097 * Gets the root command name that the user actually typed
+098 * @return Name
+099 */
+100 public String getExecCommandLabel() {
+101 return execLabel;
+102 }
+103
+104 /**
+105 * Gets the actual sub command name the user typed
+106 * @return Name
+107 */
+108 public String getExecSubcommand() {
+109 return execSubcommand;
+110 }
+111
+112 /**
+113 * Gets the actual args in string form the user typed
+114 * @return Args
+115 */
+116 public String[] getOrigArgs() {
+117 return origArgs;
+118 }
+119
+120 void setParentCommand(BaseCommand command) {
+121 this.parentCommand = command;
122 }
-123
-124 void setParentCommand(BaseCommand command) {
-125 this.parentCommand = command;
-126 }
-127 void onRegister(CommandManager manager) {
-128 onRegister(manager, this.commandName);
-129 }
-130 void onRegister(CommandManager manager, String cmd) {
-131 manager.injectDependencies(this);
-132 this.manager = manager;
-133
-134 final Annotations annotations = manager.getAnnotations();
-135 final Class<? extends BaseCommand> self = this.getClass();
-136
-137 String[] cmdAliases = annotations.getAnnotationValues(self, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE | Annotations.NO_EMPTY);
+123 void onRegister(CommandManager manager) {
+124 onRegister(manager, this.commandName);
+125 }
+126 void onRegister(CommandManager manager, String cmd) {
+127 manager.injectDependencies(this);
+128 this.manager = manager;
+129
+130 final Annotations annotations = manager.getAnnotations();
+131 final Class<? extends BaseCommand> self = this.getClass();
+132
+133 String[] cmdAliases = annotations.getAnnotationValues(self, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE | Annotations.NO_EMPTY);
+134
+135 if (cmd == null && cmdAliases != null) {
+136 cmd = cmdAliases[0];
+137 }
138
-139 if (cmd == null && cmdAliases != null) {
-140 cmd = cmdAliases[0];
-141 }
-142
-143 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase();
-144 this.permission = annotations.getAnnotationValue(self, CommandPermission.class, Annotations.REPLACEMENTS);
-145 this.description = this.commandName + " commands";
-146 this.parentSubcommand = getParentSubcommand(self);
-147 this.conditions = annotations.getAnnotationValue(self, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
-148
-149 registerSubcommands();
-150
-151 if (cmdAliases != null) {
-152 Set<String> cmdList = new HashSet<>();
-153 Collections.addAll(cmdList, cmdAliases);
-154 cmdList.remove(cmd);
-155 for (String cmdAlias : cmdList) {
-156 register(cmdAlias, this);
-157 }
+139 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase();
+140 this.permission = annotations.getAnnotationValue(self, CommandPermission.class, Annotations.REPLACEMENTS);
+141 this.description = this.commandName + " commands";
+142 this.parentSubcommand = getParentSubcommand(self);
+143 this.conditions = annotations.getAnnotationValue(self, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+144
+145 registerSubcommands();
+146
+147 if (cmdAliases != null) {
+148 Set<String> cmdList = new HashSet<>();
+149 Collections.addAll(cmdList, cmdAliases);
+150 cmdList.remove(cmd);
+151 for (String cmdAlias : cmdList) {
+152 register(cmdAlias, this);
+153 }
+154 }
+155
+156 if (cmd != null) {
+157 register(cmd, this);
158 }
-159
-160 if (cmd != null) {
-161 register(cmd, this);
-162 }
-163 registerSubclasses(cmd);
-164
-165 }
-166
-167 private void registerSubclasses(String cmd) {
-168 for (Class<?> clazz : this.getClass().getDeclaredClasses()) {
-169 if (BaseCommand.class.isAssignableFrom(clazz)) {
-170 try {
-171 BaseCommand subCommand = null;
-172 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
-173 for (Constructor<?> declaredConstructor : declaredConstructors) {
-174
-175 declaredConstructor.setAccessible(true);
-176 Parameter[] parameters = declaredConstructor.getParameters();
-177 if (parameters.length == 1) {
-178 subCommand = (BaseCommand) declaredConstructor.newInstance(this);
-179 } else {
-180 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")");
-181 }
-182 }
-183 if (subCommand != null) {
-184 subCommand.setParentCommand(this);
-185 subCommand.onRegister(manager, cmd);
-186 this.subCommands.putAll(subCommand.subCommands);
-187 this.registeredCommands.putAll(subCommand.registeredCommands);
-188 } else {
-189 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName());
-190 }
-191 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
-192 this.manager.log(LogLevel.ERROR, "Error registering subclass", e);
-193 }
-194 }
-195 }
-196 }
-197
-198 private void registerSubcommands() {
-199 final Annotations annotations = manager.getAnnotations();
-200 boolean foundDefault = false;
-201 boolean foundCatchUnknown = false;
-202 boolean isParentEmpty = parentSubcommand.isEmpty();
-203
-204 for (Method method : this.getClass().getMethods()) {
-205 method.setAccessible(true);
-206 String sublist = null;
-207 String sub = getSubcommandValue(method);
-208 final boolean def = annotations.hasAnnotation(method, Default.class);
-209 final String helpCommand = annotations.getAnnotationValue(method, HelpCommand.class, Annotations.NOTHING);
-210 final String commandAliases = annotations.getAnnotationValue(method, CommandAlias.class, Annotations.NOTHING);
-211
-212 if (!isParentEmpty && def) {
-213 sub = parentSubcommand;
-214 }
-215 if (isParentEmpty && (def || (!foundDefault && helpCommand != null))) {
-216 if (!foundDefault) {
-217 if (def) {
-218 this.subCommands.get(DEFAULT).clear();
-219 foundDefault = true;
-220 }
-221 registerSubcommand(method, DEFAULT);
-222 } else {
-223 ACFUtil.sneaky(new IllegalStateException("Multiple @Default/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
-224 }
-225 }
-226
-227 if (sub != null) {
-228 sublist = sub;
-229 } else if (commandAliases != null) {
-230 sublist = commandAliases;
-231 } else if (helpCommand != null) {
-232 sublist = helpCommand;
-233 }
-234
-235 boolean preCommand = annotations.hasAnnotation(method, PreCommand.class);
-236 boolean hasCatchUnknown = annotations.hasAnnotation(method, CatchUnknown.class) ||
-237 annotations.hasAnnotation(method, CatchAll.class) ||
-238 annotations.hasAnnotation(method, UnknownHandler.class);
-239
-240 if (hasCatchUnknown || (!foundCatchUnknown && helpCommand != null)) {
-241 if (!foundCatchUnknown) {
-242 if (hasCatchUnknown) {
-243 this.subCommands.get(CATCHUNKNOWN).clear();
-244 foundCatchUnknown = true;
-245 }
-246 registerSubcommand(method, CATCHUNKNOWN);
-247 } else {
-248 ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
-249 }
-250 } else if (preCommand) {
-251 if (this.preCommandHandler == null) {
-252 this.preCommandReflectiveHandler = method;
-253 try {
-254 this.preCommandHandler = MethodHandles.lookup().unreflect(method);
-255 } catch (IllegalAccessException ignored) {
-256 // We use preCommandReflectiveHandler whenever the methodhandle is used instead
-257 }
-258 } else {
-259 ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
-260 }
-261 }
-262 if (Objects.equals(method.getDeclaringClass(), this.getClass()) && sublist != null) {
-263 registerSubcommand(method, sublist);
-264 }
-265 }
-266 }
-267
-268 private String getSubcommandValue(Method method) {
-269 final String sub = manager.getAnnotations().getAnnotationValue(method, Subcommand.class, Annotations.NOTHING);
-270 if (sub == null) {
-271 return null;
-272 }
-273 Class<?> clazz = method.getDeclaringClass();
-274 String parent = getParentSubcommand(clazz);
-275 return parent == null || parent.isEmpty() ? sub : parent + " " + sub;
-276 }
-277
-278 private String getParentSubcommand(Class<?> clazz) {
-279 List<String> subList = new ArrayList<>();
-280 while (clazz != null) {
-281 String sub = manager.getAnnotations().getAnnotationValue(clazz, Subcommand.class, Annotations.NOTHING);
-282 if (sub != null) {
-283 subList.add(sub);
-284 }
-285 clazz = clazz.getEnclosingClass();
-286 }
-287 Collections.reverse(subList);
-288 return ACFUtil.join(subList, " ");
-289 }
-290
-291 private void register(String name, BaseCommand cmd) {
-292 String nameLower = name.toLowerCase();
-293 RootCommand rootCommand = manager.obtainRootCommand(nameLower);
-294 rootCommand.addChild(cmd);
+159 registerSubclasses(cmd);
+160
+161 }
+162
+163 private void registerSubclasses(String cmd) {
+164 for (Class<?> clazz : this.getClass().getDeclaredClasses()) {
+165 if (BaseCommand.class.isAssignableFrom(clazz)) {
+166 try {
+167 BaseCommand subCommand = null;
+168 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
+169 for (Constructor<?> declaredConstructor : declaredConstructors) {
+170
+171 declaredConstructor.setAccessible(true);
+172 Parameter[] parameters = declaredConstructor.getParameters();
+173 if (parameters.length == 1) {
+174 subCommand = (BaseCommand) declaredConstructor.newInstance(this);
+175 } else {
+176 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")");
+177 }
+178 }
+179 if (subCommand != null) {
+180 subCommand.setParentCommand(this);
+181 subCommand.onRegister(manager, cmd);
+182 this.subCommands.putAll(subCommand.subCommands);
+183 this.registeredCommands.putAll(subCommand.registeredCommands);
+184 } else {
+185 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName());
+186 }
+187 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+188 this.manager.log(LogLevel.ERROR, "Error registering subclass", e);
+189 }
+190 }
+191 }
+192 }
+193
+194 private void registerSubcommands() {
+195 final Annotations annotations = manager.getAnnotations();
+196 boolean foundDefault = false;
+197 boolean foundCatchUnknown = false;
+198 boolean isParentEmpty = parentSubcommand.isEmpty();
+199
+200 for (Method method : this.getClass().getMethods()) {
+201 method.setAccessible(true);
+202 String sublist = null;
+203 String sub = getSubcommandValue(method);
+204 final boolean def = annotations.hasAnnotation(method, Default.class);
+205 final String helpCommand = annotations.getAnnotationValue(method, HelpCommand.class, Annotations.NOTHING);
+206 final String commandAliases = annotations.getAnnotationValue(method, CommandAlias.class, Annotations.NOTHING);
+207
+208 if (!isParentEmpty && def) {
+209 sub = parentSubcommand;
+210 }
+211 if (isParentEmpty && (def || (!foundDefault && helpCommand != null))) {
+212 if (!foundDefault) {
+213 if (def) {
+214 this.subCommands.get(DEFAULT).clear();
+215 foundDefault = true;
+216 }
+217 registerSubcommand(method, DEFAULT);
+218 } else {
+219 ACFUtil.sneaky(new IllegalStateException("Multiple @Default/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
+220 }
+221 }
+222
+223 if (sub != null) {
+224 sublist = sub;
+225 } else if (commandAliases != null) {
+226 sublist = commandAliases;
+227 } else if (helpCommand != null) {
+228 sublist = helpCommand;
+229 }
+230
+231 boolean preCommand = annotations.hasAnnotation(method, PreCommand.class);
+232 boolean hasCatchUnknown = annotations.hasAnnotation(method, CatchUnknown.class) ||
+233 annotations.hasAnnotation(method, CatchAll.class) ||
+234 annotations.hasAnnotation(method, UnknownHandler.class);
+235
+236 if (hasCatchUnknown || (!foundCatchUnknown && helpCommand != null)) {
+237 if (!foundCatchUnknown) {
+238 if (hasCatchUnknown) {
+239 this.subCommands.get(CATCHUNKNOWN).clear();
+240 foundCatchUnknown = true;
+241 }
+242 registerSubcommand(method, CATCHUNKNOWN);
+243 } else {
+244 ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
+245 }
+246 } else if (preCommand) {
+247 if (this.preCommandHandler == null) {
+248 this.preCommandHandler = method;
+249 } else {
+250 ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
+251 }
+252 }
+253 if (Objects.equals(method.getDeclaringClass(), this.getClass()) && sublist != null) {
+254 registerSubcommand(method, sublist);
+255 }
+256 }
+257 }
+258
+259 private String getSubcommandValue(Method method) {
+260 final String sub = manager.getAnnotations().getAnnotationValue(method, Subcommand.class, Annotations.NOTHING);
+261 if (sub == null) {
+262 return null;
+263 }
+264 Class<?> clazz = method.getDeclaringClass();
+265 String parent = getParentSubcommand(clazz);
+266 return parent == null || parent.isEmpty() ? sub : parent + " " + sub;
+267 }
+268
+269 private String getParentSubcommand(Class<?> clazz) {
+270 List<String> subList = new ArrayList<>();
+271 while (clazz != null) {
+272 String sub = manager.getAnnotations().getAnnotationValue(clazz, Subcommand.class, Annotations.NOTHING);
+273 if (sub != null) {
+274 subList.add(sub);
+275 }
+276 clazz = clazz.getEnclosingClass();
+277 }
+278 Collections.reverse(subList);
+279 return ACFUtil.join(subList, " ");
+280 }
+281
+282 private void register(String name, BaseCommand cmd) {
+283 String nameLower = name.toLowerCase();
+284 RootCommand rootCommand = manager.obtainRootCommand(nameLower);
+285 rootCommand.addChild(cmd);
+286
+287 this.registeredCommands.put(nameLower, rootCommand);
+288 }
+289
+290 private void registerSubcommand(Method method, String subCommand) {
+291 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase());
+292 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand);
+293 // Must run getSubcommandPossibility BEFORE we rewrite it just after this.
+294 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts);
295
-296 this.registeredCommands.put(nameLower, rootCommand);
-297 }
-298
-299 private void registerSubcommand(Method method, String subCommand) {
-300 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase());
-301 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand);
-302 // Must run getSubcommandPossibility BEFORE we rewrite it just after this.
-303 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts);
-304
-305 // Strip pipes off for auto complete addition
-306 for (int i = 0; i < subCommandParts.length; i++) {
-307 String[] split = ACFPatterns.PIPE.split(subCommandParts[i]);
-308 if (split.length == 0 || split[0].isEmpty()) {
-309 throw new IllegalArgumentException("Invalid @Subcommand configuration for " + method.getName() + " - parts can not start with | or be empty");
-310 }
-311 subCommandParts[i] = split[0];
+296 // Strip pipes off for auto complete addition
+297 for (int i = 0; i < subCommandParts.length; i++) {
+298 String[] split = ACFPatterns.PIPE.split(subCommandParts[i]);
+299 if (split.length == 0 || split[0].isEmpty()) {
+300 throw new IllegalArgumentException("Invalid @Subcommand configuration for " + method.getName() + " - parts can not start with | or be empty");
+301 }
+302 subCommandParts[i] = split[0];
+303 }
+304 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " ");
+305 final String[] aliasNames = manager.getAnnotations().getAnnotationValues(method, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE);
+306
+307 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " ";
+308 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand);
+309
+310 for (String subcmd : cmdList) {
+311 subCommands.put(subcmd, cmd);
312 }
-313 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " ");
-314 final String[] aliasNames = manager.getAnnotations().getAnnotationValues(method, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE);
-315
-316 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " ";
-317 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand);
-318
-319 for (String subcmd : cmdList) {
-320 subCommands.put(subcmd, cmd);
-321 }
-322 cmd.addSubcommands(cmdList);
-323
-324 if (aliasNames != null) {
-325 for (String name : aliasNames) {
-326 register(name, new ForwardingCommand(this, subCommandParts));
-327 }
-328 }
-329 }
-330
-331 /**
-332 * Takes a string like "foo|bar baz|qux" and generates a list of
-333 * - foo baz
-334 * - foo qux
-335 * - bar baz
-336 * - bar qux
-337 *
-338 * For every possible sub command combination
-339 *
-340 * @param subCommandParts
-341 * @return List of all sub command possibilities
-342 */
-343 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) {
-344 int i = 0;
-345 Set<String> current = null;
-346 while (true) {
-347 Set<String> newList = new HashSet<>();
-348
-349 if (i < subCommandParts.length) {
-350 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) {
-351 if (current != null) {
-352 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList()));
-353 } else {
-354 newList.add(s1);
-355 }
-356 }
-357 }
-358
-359 if (i + 1 < subCommandParts.length) {
-360 current = newList;
-361 i = i + 1;
-362 continue;
-363 }
+313 cmd.addSubcommands(cmdList);
+314
+315 if (aliasNames != null) {
+316 for (String name : aliasNames) {
+317 register(name, new ForwardingCommand(this, subCommandParts));
+318 }
+319 }
+320 }
+321
+322 /**
+323 * Takes a string like "foo|bar baz|qux" and generates a list of
+324 * - foo baz
+325 * - foo qux
+326 * - bar baz
+327 * - bar qux
+328 *
+329 * For every possible sub command combination
+330 *
+331 * @param subCommandParts
+332 * @return List of all sub command possibilities
+333 */
+334 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) {
+335 int i = 0;
+336 Set<String> current = null;
+337 while (true) {
+338 Set<String> newList = new HashSet<>();
+339
+340 if (i < subCommandParts.length) {
+341 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) {
+342 if (current != null) {
+343 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList()));
+344 } else {
+345 newList.add(s1);
+346 }
+347 }
+348 }
+349
+350 if (i + 1 < subCommandParts.length) {
+351 current = newList;
+352 i = i + 1;
+353 continue;
+354 }
+355
+356 return newList;
+357 }
+358 }
+359
+360 public void execute(CommandIssuer issuer, String commandLabel, String[] args) {
+361 commandLabel = commandLabel.toLowerCase();
+362 try {
+363 CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args, false);
364
-365 return newList;
-366 }
-367 }
-368
-369 public void execute(CommandIssuer issuer, String commandLabel, String[] args) {
-370 commandLabel = commandLabel.toLowerCase();
-371 try {
-372 CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args, false);
-373
-374 if (args.length > 0) {
-375 CommandSearch cmd = findSubCommand(args);
-376 if (cmd != null) {
-377 execSubcommand = cmd.getCheckSub();
-378 final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length);
-379 executeCommand(commandContext, issuer, execargs, cmd.cmd);
-380 return;
-381 }
-382 }
-383
-384 if (subCommands.get(DEFAULT) != null && args.length == 0) {
-385 executeSubcommand(commandContext, DEFAULT, issuer, args);
-386 } else if (subCommands.get(CATCHUNKNOWN) != null) {
-387 if (!executeSubcommand(commandContext, CATCHUNKNOWN, issuer, args)) {
-388 help(issuer, args);
-389 }
-390 } else if (subCommands.get(DEFAULT) != null) {
-391 executeSubcommand(commandContext, DEFAULT, issuer, args);
-392 }
-393
-394 } finally {
-395 postCommandOperation();
-396 }
-397 }
-398
-399 RegisteredCommand<?> getRegisteredCommand(String[] args) {
-400 final CommandSearch cmd = findSubCommand(args);
-401 return cmd != null ? cmd.cmd : null;
-402 }
-403
-404 private void postCommandOperation() {
-405 CommandManager.commandOperationContext.get().pop();
-406 execSubcommand = null;
-407 execLabel = null;
-408 origArgs = new String[]{};
-409 }
-410
-411 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
-412 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get();
-413 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync);
-414 contexts.push(context);
-415 lastCommandOperationContext = context;
-416 execSubcommand = null;
-417 execLabel = commandLabel;
-418 origArgs = args;
-419 return context;
-420 }
-421
-422 public CommandIssuer getCurrentCommandIssuer() {
-423 return CommandManager.getCurrentCommandIssuer();
-424 }
-425 public CommandManager getCurrentCommandManager() {
-426 return CommandManager.getCurrentCommandManager();
-427 }
-428
-429 private CommandSearch findSubCommand(String[] args) {
-430 return findSubCommand(args, false);
-431 }
-432 private CommandSearch findSubCommand(String[] args, boolean completion) {
-433 for (int i = args.length; i >= 0; i--) {
-434 String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase();
-435 Set<RegisteredCommand> cmds = subCommands.get(checkSub);
-436
-437 final int extraArgs = args.length - i;
-438 if (!cmds.isEmpty()) {
-439 RegisteredCommand cmd = null;
-440 if (cmds.size() == 1) {
-441 cmd = Iterables.getOnlyElement(cmds);
-442 } else {
-443 Optional<RegisteredCommand> optCmd = cmds.stream().filter(c -> {
-444 int required = c.requiredResolvers;
-445 int optional = c.optionalResolvers;
-446 return extraArgs <= required + optional && (completion || extraArgs >= required);
-447 }).sorted((c1, c2) -> {
-448 int a = c1.consumeInputResolvers;
-449 int b = c2.consumeInputResolvers;
-450
-451 if (a == b) {
-452 return 0;
-453 }
-454 return a < b ? 1 : -1;
-455 }).findFirst();
-456 if (optCmd.isPresent()) {
-457 cmd = optCmd.get();
-458 }
-459 }
-460 if (cmd != null) {
-461 return new CommandSearch(cmd, i, checkSub);
-462 }
-463 }
-464 }
-465 return null;
-466 }
-467
-468 private void executeCommand(CommandOperationContext commandOperationContext,
-469 CommandIssuer issuer, String[] args, RegisteredCommand cmd) {
-470 if (cmd.hasPermission(issuer)) {
-471 commandOperationContext.setRegisteredCommand(cmd);
-472 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) {
-473 return;
-474 }
-475 List<String> sargs = Lists.newArrayList(args);
-476 cmd.invoke(issuer, sargs, commandOperationContext);
-477 } else {
-478 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED);
-479 }
-480 }
-481
-482 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) {
-483 return true;
-484 }
-485
-486 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) {
-487 return tabComplete(issuer, commandLabel, args, false);
-488 }
-489 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync)
-490 throws IllegalArgumentException {
+365 if (args.length > 0) {
+366 CommandSearch cmd = findSubCommand(args);
+367 if (cmd != null) {
+368 execSubcommand = cmd.getCheckSub();
+369 final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length);
+370 executeCommand(commandContext, issuer, execargs, cmd.cmd);
+371 return;
+372 }
+373 }
+374
+375 if (subCommands.get(DEFAULT) != null && args.length == 0) {
+376 executeSubcommand(commandContext, DEFAULT, issuer, args);
+377 } else if (subCommands.get(CATCHUNKNOWN) != null) {
+378 if (!executeSubcommand(commandContext, CATCHUNKNOWN, issuer, args)) {
+379 help(issuer, args);
+380 }
+381 } else if (subCommands.get(DEFAULT) != null) {
+382 executeSubcommand(commandContext, DEFAULT, issuer, args);
+383 }
+384
+385 } finally {
+386 postCommandOperation();
+387 }
+388 }
+389
+390 RegisteredCommand<?> getRegisteredCommand(String[] args) {
+391 final CommandSearch cmd = findSubCommand(args);
+392 return cmd != null ? cmd.cmd : null;
+393 }
+394
+395 private void postCommandOperation() {
+396 CommandManager.commandOperationContext.get().pop();
+397 execSubcommand = null;
+398 execLabel = null;
+399 origArgs = new String[]{};
+400 }
+401
+402 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
+403 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get();
+404 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync);
+405 contexts.push(context);
+406 lastCommandOperationContext = context;
+407 execSubcommand = null;
+408 execLabel = commandLabel;
+409 origArgs = args;
+410 return context;
+411 }
+412
+413 public CommandIssuer getCurrentCommandIssuer() {
+414 return CommandManager.getCurrentCommandIssuer();
+415 }
+416 public CommandManager getCurrentCommandManager() {
+417 return CommandManager.getCurrentCommandManager();
+418 }
+419
+420 private CommandSearch findSubCommand(String[] args) {
+421 return findSubCommand(args, false);
+422 }
+423 private CommandSearch findSubCommand(String[] args, boolean completion) {
+424 for (int i = args.length; i >= 0; i--) {
+425 String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase();
+426 Set<RegisteredCommand> cmds = subCommands.get(checkSub);
+427
+428 final int extraArgs = args.length - i;
+429 if (!cmds.isEmpty()) {
+430 RegisteredCommand cmd = null;
+431 if (cmds.size() == 1) {
+432 cmd = Iterables.getOnlyElement(cmds);
+433 } else {
+434 Optional<RegisteredCommand> optCmd = cmds.stream().filter(c -> {
+435 int required = c.requiredResolvers;
+436 int optional = c.optionalResolvers;
+437 return extraArgs <= required + optional && (completion || extraArgs >= required);
+438 }).sorted((c1, c2) -> {
+439 int a = c1.consumeInputResolvers;
+440 int b = c2.consumeInputResolvers;
+441
+442 if (a == b) {
+443 return 0;
+444 }
+445 return a < b ? 1 : -1;
+446 }).findFirst();
+447 if (optCmd.isPresent()) {
+448 cmd = optCmd.get();
+449 }
+450 }
+451 if (cmd != null) {
+452 return new CommandSearch(cmd, i, checkSub);
+453 }
+454 }
+455 }
+456 return null;
+457 }
+458
+459 private void executeCommand(CommandOperationContext commandOperationContext,
+460 CommandIssuer issuer, String[] args, RegisteredCommand cmd) {
+461 if (cmd.hasPermission(issuer)) {
+462 commandOperationContext.setRegisteredCommand(cmd);
+463 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) {
+464 return;
+465 }
+466 List<String> sargs = Lists.newArrayList(args);
+467 cmd.invoke(issuer, sargs, commandOperationContext);
+468 } else {
+469 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED);
+470 }
+471 }
+472
+473 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) {
+474 return true;
+475 }
+476
+477 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) {
+478 return tabComplete(issuer, commandLabel, args, false);
+479 }
+480 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync)
+481 throws IllegalArgumentException {
+482
+483 commandLabel = commandLabel.toLowerCase();
+484 if (args.length == 0) {
+485 args = new String[]{""};
+486 }
+487 try {
+488 CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args, isAsync);
+489
+490 final CommandSearch search = findSubCommand(args, true);
491
-492 commandLabel = commandLabel.toLowerCase();
-493 if (args.length == 0) {
-494 args = new String[]{""};
-495 }
-496 try {
-497 CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args, isAsync);
-498
-499 final CommandSearch search = findSubCommand(args, true);
-500
-501
-502 final List<String> cmds = new ArrayList<>();
-503
-504 if (search != null) {
-505 cmds.addAll(completeCommand(issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel, isAsync));
-506 } else if (subCommands.get(CATCHUNKNOWN).size() == 1) {
-507 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(CATCHUNKNOWN)), args, commandLabel, isAsync));
-508 } else if (subCommands.get(DEFAULT).size() == 1) {
-509 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(DEFAULT)), args, commandLabel, isAsync));
-510 }
-511
-512 return filterTabComplete(args[args.length - 1], cmds);
-513 } finally {
-514 postCommandOperation();
-515 }
-516 }
-517
-518 List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) {
-519 final Set<String> cmds = new HashSet<>();
-520 final int cmdIndex = Math.max(0, args.length - 1);
-521 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase();
-522 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) {
-523 final String key = entry.getKey();
-524 if (key.startsWith(argString) && !CATCHUNKNOWN.equals(key) && !DEFAULT.equals(key)) {
-525 final RegisteredCommand value = entry.getValue();
-526 if (!value.hasPermission(issuer)) {
-527 continue;
-528 }
-529
-530 String[] split = ACFPatterns.SPACE.split(value.prefSubCommand);
-531 cmds.add(split[cmdIndex]);
-532 }
-533 }
-534 return new ArrayList<>(cmds);
+492
+493 final List<String> cmds = new ArrayList<>();
+494
+495 if (search != null) {
+496 cmds.addAll(completeCommand(issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel, isAsync));
+497 } else if (subCommands.get(CATCHUNKNOWN).size() == 1) {
+498 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(CATCHUNKNOWN)), args, commandLabel, isAsync));
+499 } else if (subCommands.get(DEFAULT).size() == 1) {
+500 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(DEFAULT)), args, commandLabel, isAsync));
+501 }
+502
+503 return filterTabComplete(args[args.length - 1], cmds);
+504 } finally {
+505 postCommandOperation();
+506 }
+507 }
+508
+509 List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) {
+510 final Set<String> cmds = new HashSet<>();
+511 final int cmdIndex = Math.max(0, args.length - 1);
+512 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase();
+513 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) {
+514 final String key = entry.getKey();
+515 if (key.startsWith(argString) && !CATCHUNKNOWN.equals(key) && !DEFAULT.equals(key)) {
+516 final RegisteredCommand value = entry.getValue();
+517 if (!value.hasPermission(issuer)) {
+518 continue;
+519 }
+520
+521 String[] split = ACFPatterns.SPACE.split(value.prefSubCommand);
+522 cmds.add(split[cmdIndex]);
+523 }
+524 }
+525 return new ArrayList<>(cmds);
+526 }
+527
+528 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) {
+529 if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0 || cmd.complete == null) {
+530 return ImmutableList.of();
+531 }
+532
+533 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, args, isAsync);
+534 return filterTabComplete(args[args.length-1], cmds);
535 }
536
-537 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) {
-538 if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0 || cmd.complete == null) {
-539 return ImmutableList.of();
-540 }
-541
-542 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, args, isAsync);
-543 return filterTabComplete(args[args.length-1], cmds);
-544 }
-545
-546 private static List<String> filterTabComplete(String arg, List<String> cmds) {
-547 return cmds.stream()
-548 .distinct()
-549 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg)))
-550 .collect(Collectors.toList());
-551 }
-552
-553 RegisteredCommand getSubcommand(String subcommand) {
-554 return getSubcommand(subcommand, false);
-555 }
-556
-557 RegisteredCommand getSubcommand(String subcommand, boolean requireOne) {
-558 final Set<RegisteredCommand> commands = subCommands.get(subcommand);
-559 if (!commands.isEmpty() && (!requireOne || commands.size() == 1)) {
-560 return commands.iterator().next();
+537 private static List<String> filterTabComplete(String arg, List<String> cmds) {
+538 return cmds.stream()
+539 .distinct()
+540 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg)))
+541 .collect(Collectors.toList());
+542 }
+543
+544 RegisteredCommand getSubcommand(String subcommand) {
+545 return getSubcommand(subcommand, false);
+546 }
+547
+548 RegisteredCommand getSubcommand(String subcommand, boolean requireOne) {
+549 final Set<RegisteredCommand> commands = subCommands.get(subcommand);
+550 if (!commands.isEmpty() && (!requireOne || commands.size() == 1)) {
+551 return commands.iterator().next();
+552 }
+553 return null;
+554 }
+555
+556 private boolean executeSubcommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) {
+557 final RegisteredCommand cmd = this.getSubcommand(subcommand);
+558 if (cmd != null) {
+559 executeCommand(commandContext, issuer, args, cmd);
+560 return true;
561 }
-562 return null;
-563 }
-564
-565 private boolean executeSubcommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) {
-566 final RegisteredCommand cmd = this.getSubcommand(subcommand);
-567 if (cmd != null) {
-568 executeCommand(commandContext, issuer, args, cmd);
-569 return true;
-570 }
-571
-572 return false;
-573 }
-574
-575 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) {
-576 Method pre = this.preCommandReflectiveHandler;
-577 if (pre != null) {
-578 try {
-579 Class<?>[] types = pre.getParameterTypes();
-580 Object[] parameters = new Object[pre.getParameterCount()];
-581 for (int i = 0; i < parameters.length; i++) {
-582 Class<?> type = types[i];
-583 Object issuerObject = issuer.getIssuer();
-584 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) {
-585 parameters[i] = issuerObject;
-586 } else if (CommandIssuer.class.isAssignableFrom(type)) {
-587 parameters[i] = issuer;
-588 } else if (RegisteredCommand.class.isAssignableFrom(type)) {
-589 parameters[i] = cmd;
-590 } else if (String[].class.isAssignableFrom((type))) {
-591 parameters[i] = args;
-592 } else {
-593 parameters[i] = null;
-594 }
-595 }
-596
-597 if (false) { // preCommandHandler != null) { //TODO: MethodHandle disabled due to WrongMethodTypeException
-598 return (boolean) preCommandHandler.invoke(parameters);
-599 }
-600 return (boolean) pre.invoke(this, parameters);
-601 } catch (Throwable e) {
-602 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e);
-603 }
-604 }
-605 return false;
-606 }
-607
-608 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-609 public CommandHelp getCommandHelp() {
-610 return manager.generateCommandHelp();
+562
+563 return false;
+564 }
+565
+566 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) {
+567 Method pre = this.preCommandHandler;
+568 if (pre != null) {
+569 try {
+570 Class<?>[] types = pre.getParameterTypes();
+571 Object[] parameters = new Object[pre.getParameterCount()];
+572 for (int i = 0; i < parameters.length; i++) {
+573 Class<?> type = types[i];
+574 Object issuerObject = issuer.getIssuer();
+575 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) {
+576 parameters[i] = issuerObject;
+577 } else if (CommandIssuer.class.isAssignableFrom(type)) {
+578 parameters[i] = issuer;
+579 } else if (RegisteredCommand.class.isAssignableFrom(type)) {
+580 parameters[i] = cmd;
+581 } else if (String[].class.isAssignableFrom((type))) {
+582 parameters[i] = args;
+583 } else {
+584 parameters[i] = null;
+585 }
+586 }
+587
+588 return (boolean) pre.invoke(this, parameters);
+589 } catch (IllegalAccessException | InvocationTargetException e) {
+590 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e);
+591 }
+592 }
+593 return false;
+594 }
+595
+596 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
+597 public CommandHelp getCommandHelp() {
+598 return manager.generateCommandHelp();
+599 }
+600
+601 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
+602 public void showCommandHelp() {
+603 getCommandHelp().showHelp();
+604 }
+605
+606 public void help(Object issuer, String[] args) {
+607 help(manager.getCommandIssuer(issuer), args);
+608 }
+609 public void help(CommandIssuer issuer, String[] args) {
+610 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND);
611 }
-612
-613 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-614 public void showCommandHelp() {
-615 getCommandHelp().showHelp();
-616 }
-617
-618 public void help(Object issuer, String[] args) {
-619 help(manager.getCommandIssuer(issuer), args);
-620 }
-621 public void help(CommandIssuer issuer, String[] args) {
-622 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND);
-623 }
-624 public void doHelp(Object issuer, String... args) {
-625 doHelp(manager.getCommandIssuer(issuer), args);
-626 }
-627 public void doHelp(CommandIssuer issuer, String... args) {
-628 help(issuer, args);
-629 }
-630
-631 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) {
-632 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX,
-633 "{command}", manager.getCommandPrefix(issuer) + cmd.command,
-634 "{syntax}", cmd.syntaxText
-635 );
-636 }
-637
-638 public boolean hasPermission(Object issuer) {
-639 return hasPermission(manager.getCommandIssuer(issuer));
+612 public void doHelp(Object issuer, String... args) {
+613 doHelp(manager.getCommandIssuer(issuer), args);
+614 }
+615 public void doHelp(CommandIssuer issuer, String... args) {
+616 help(issuer, args);
+617 }
+618
+619 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) {
+620 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX,
+621 "{command}", manager.getCommandPrefix(issuer) + cmd.command,
+622 "{syntax}", cmd.syntaxText
+623 );
+624 }
+625
+626 public boolean hasPermission(Object issuer) {
+627 return hasPermission(manager.getCommandIssuer(issuer));
+628 }
+629
+630 public boolean hasPermission(CommandIssuer issuer) {
+631 return permission == null || permission.isEmpty() || (manager.hasPermission(issuer, permission) && (parentCommand == null || parentCommand.hasPermission(issuer)));
+632 }
+633
+634
+635 public Set<String> getRequiredPermissions() {
+636 if (this.permission == null || this.permission.isEmpty()) {
+637 return ImmutableSet.of();
+638 }
+639 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission));
640 }
641
-642 public boolean hasPermission(CommandIssuer issuer) {
-643 return permission == null || permission.isEmpty() || (manager.hasPermission(issuer, permission) && (parentCommand == null || parentCommand.hasPermission(issuer)));
+642 public boolean requiresPermission(String permission) {
+643 return getRequiredPermissions().contains(permission) || this.parentCommand != null && parentCommand.requiresPermission(permission);
644 }
645
-646
-647 public Set<String> getRequiredPermissions() {
-648 if (this.permission == null || this.permission.isEmpty()) {
-649 return ImmutableSet.of();
-650 }
-651 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission));
+646 public String getName() {
+647 return commandName;
+648 }
+649
+650 public ExceptionHandler getExceptionHandler() {
+651 return exceptionHandler;
652 }
653
-654 public boolean requiresPermission(String permission) {
-655 return getRequiredPermissions().contains(permission) || this.parentCommand != null && parentCommand.requiresPermission(permission);
-656 }
-657
-658 public String getName() {
-659 return commandName;
-660 }
-661
-662 public ExceptionHandler getExceptionHandler() {
-663 return exceptionHandler;
-664 }
-665
-666 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) {
-667 this.exceptionHandler = exceptionHandler;
-668 return this;
+654 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) {
+655 this.exceptionHandler = exceptionHandler;
+656 return this;
+657 }
+658
+659 public RegisteredCommand getDefaultRegisteredCommand() {
+660 return this.getSubcommand(DEFAULT);
+661 }
+662
+663 public String setContextFlags(Class<?> cls, String flags) {
+664 return this.contextFlags.put(cls, flags);
+665 }
+666
+667 public String getContextFlags(Class<?> cls) {
+668 return this.contextFlags.get(cls);
669 }
670
-671 public RegisteredCommand getDefaultRegisteredCommand() {
-672 return this.getSubcommand(DEFAULT);
-673 }
-674
-675 public String setContextFlags(Class<?> cls, String flags) {
-676 return this.contextFlags.put(cls, flags);
-677 }
+671 private static class CommandSearch { RegisteredCommand cmd; int argIndex; String checkSub;
+672
+673 CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) {
+674 this.cmd = cmd;
+675 this.argIndex = argIndex;
+676 this.checkSub = checkSub;
+677 }
678
-679 public String getContextFlags(Class<?> cls) {
-680 return this.contextFlags.get(cls);
-681 }
+679 String getCheckSub() {
+680 return this.checkSub;
+681 }
682
-683 private static class CommandSearch { RegisteredCommand cmd; int argIndex; String checkSub;
-684
-685 CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) {
-686 this.cmd = cmd;
-687 this.argIndex = argIndex;
-688 this.checkSub = checkSub;
-689 }
-690
-691 String getCheckSub() {
-692 return this.checkSub;
-693 }
-694
-695 @Override
-696 public boolean equals(Object o) {
-697 if (this == o) return true;
-698 if (o == null || getClass() != o.getClass()) return false;
-699 CommandSearch that = (CommandSearch) o;
-700 return argIndex == that.argIndex &&
-701 Objects.equals(cmd, that.cmd) &&
-702 Objects.equals(checkSub, that.checkSub);
-703 }
-704
-705 @Override
-706 public int hashCode() {
-707 return Objects.hash(cmd, argIndex, checkSub);
-708 }
-709 }
-710}
+683 @Override
+684 public boolean equals(Object o) {
+685 if (this == o) return true;
+686 if (o == null || getClass() != o.getClass()) return false;
+687 CommandSearch that = (CommandSearch) o;
+688 return argIndex == that.argIndex &&
+689 Objects.equals(cmd, that.cmd) &&
+690 Objects.equals(checkSub, that.checkSub);
+691 }
+692
+693 @Override
+694 public int hashCode() {
+695 return Objects.hash(cmd, argIndex, checkSub);
+696 }
+697 }
+698}
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 1f4356f4..99c9e7d0 100644
--- a/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html
+++ b/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html
@@ -45,288 +45,272 @@
037import com.google.common.collect.Sets;
038import org.jetbrains.annotations.Nullable;
039
-040import java.lang.invoke.MethodHandle;
-041import java.lang.invoke.MethodHandles;
-042import java.lang.reflect.InvocationTargetException;
-043import java.lang.reflect.Method;
-044import java.lang.reflect.Parameter;
-045import java.util.ArrayList;
-046import java.util.Collection;
-047import java.util.List;
-048import java.util.Map;
-049import java.util.Set;
-050import java.util.stream.Collectors;
-051
-052@SuppressWarnings("WeakerAccess")
-053public class RegisteredCommand <CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> {
-054 final BaseCommand scope;
-055 final Method method;
-056 @Nullable final MethodHandle methodHandle;
-057 final CommandParameter<CEC>[] parameters;
-058 final CommandManager manager;
-059 final List<String> registeredSubcommands = new ArrayList<>();
-060
-061 String command;
-062 String prefSubCommand;
-063 String syntaxText;
-064 String helpText;
-065 String permission;
-066 String complete;
-067 String conditions;
-068 public String helpSearchTags;
-069
-070 final int requiredResolvers;
-071 final int consumeInputResolvers;
-072 final int doesNotConsumeInputResolvers;
-073 final int optionalResolvers;
-074
-075 RegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) {
-076 this.scope = scope;
-077 this.manager = this.scope.manager;
-078 final Annotations annotations = this.manager.getAnnotations();
-079
-080 if (BaseCommand.CATCHUNKNOWN.equals(prefSubCommand) || BaseCommand.DEFAULT.equals(prefSubCommand)) {
-081 prefSubCommand = "";
-082 }
-083 this.command = command + (!annotations.hasAnnotation(method, CommandAlias.class, false) && !prefSubCommand.isEmpty() ? prefSubCommand : "");
-084 this.method = method;
-085 MethodHandle methodHandle = null;
-086 try {
-087 methodHandle = MethodHandles.lookup().unreflect(method);
-088 } catch (IllegalAccessException ignored) {
-089 // As the method has been found, it cannot not have permission for it.
-090 // In the case that it does however manage to fail this, keep it null and handle it later on.
-091 }
-092 this.methodHandle = methodHandle;
-093 this.prefSubCommand = prefSubCommand;
+040import java.lang.reflect.InvocationTargetException;
+041import java.lang.reflect.Method;
+042import java.lang.reflect.Parameter;
+043import java.util.ArrayList;
+044import java.util.Collection;
+045import java.util.List;
+046import java.util.Map;
+047import java.util.Set;
+048import java.util.stream.Collectors;
+049
+050@SuppressWarnings("WeakerAccess")
+051public class RegisteredCommand <CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> {
+052 final BaseCommand scope;
+053 final Method method;
+054 final CommandParameter<CEC>[] parameters;
+055 final CommandManager manager;
+056 final List<String> registeredSubcommands = new ArrayList<>();
+057
+058 String command;
+059 String prefSubCommand;
+060 String syntaxText;
+061 String helpText;
+062 String permission;
+063 String complete;
+064 String conditions;
+065 public String helpSearchTags;
+066
+067 final int requiredResolvers;
+068 final int consumeInputResolvers;
+069 final int doesNotConsumeInputResolvers;
+070 final int optionalResolvers;
+071
+072 RegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) {
+073 this.scope = scope;
+074 this.manager = this.scope.manager;
+075 final Annotations annotations = this.manager.getAnnotations();
+076
+077 if (BaseCommand.CATCHUNKNOWN.equals(prefSubCommand) || BaseCommand.DEFAULT.equals(prefSubCommand)) {
+078 prefSubCommand = "";
+079 }
+080 this.command = command + (!annotations.hasAnnotation(method, CommandAlias.class, false) && !prefSubCommand.isEmpty() ? prefSubCommand : "");
+081 this.method = method;
+082 this.prefSubCommand = prefSubCommand;
+083
+084 this.permission = annotations.getAnnotationValue(method, CommandPermission.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+085 this.complete = annotations.getAnnotationValue(method, CommandCompletion.class);
+086 this.helpText = annotations.getAnnotationValue(method, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY);
+087 this.conditions = annotations.getAnnotationValue(method, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+088 this.helpSearchTags = annotations.getAnnotationValue(method, HelpSearchTags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+089
+090 Parameter[] parameters = method.getParameters();
+091 //noinspection unchecked
+092 this.parameters = new CommandParameter[parameters.length];
+093
094
-095 this.permission = annotations.getAnnotationValue(method, CommandPermission.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
-096 this.complete = annotations.getAnnotationValue(method, CommandCompletion.class);
-097 this.helpText = annotations.getAnnotationValue(method, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY);
-098 this.conditions = annotations.getAnnotationValue(method, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
-099 this.helpSearchTags = annotations.getAnnotationValue(method, HelpSearchTags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+095 int requiredResolvers = 0;
+096 int consumeInputResolvers = 0;
+097 int doesNotConsumeInputResolvers = 0;
+098 int optionalResolvers = 0;
+099 StringBuilder syntaxBuilder = new StringBuilder(64);
100
-101 Parameter[] parameters = method.getParameters();
-102 //noinspection unchecked
-103 this.parameters = new CommandParameter[parameters.length];
-104
-105
-106 int requiredResolvers = 0;
-107 int consumeInputResolvers = 0;
-108 int doesNotConsumeInputResolvers = 0;
-109 int optionalResolvers = 0;
-110 StringBuilder syntaxBuilder = new StringBuilder(64);
-111
-112 for (int i = 0; i < parameters.length; i++) {
-113 CommandParameter<CEC> parameter = this.parameters[i] = new CommandParameter<>(this, parameters[i], i);
-114 if (!parameter.isCommandIssuer()) {
-115 if (!parameter.requiresInput()) {
-116 optionalResolvers++;
-117 } else {
-118 requiredResolvers++;
-119 }
-120 if (parameter.canConsumeInput()) {
-121 consumeInputResolvers++;
-122 } else {
-123 doesNotConsumeInputResolvers++;
-124 }
-125 }
-126 if (parameter.getSyntax() != null) {
-127 if (syntaxBuilder.length() > 0) {
-128 syntaxBuilder.append(' ');
-129 }
-130 syntaxBuilder.append(parameter.getSyntax());
-131 }
-132 }
-133 String syntaxText = syntaxBuilder.toString().trim();
-134 final String syntaxStr = annotations.getAnnotationValue(method, Syntax.class);
-135 this.syntaxText = syntaxStr != null ? ACFUtil.replace(syntaxStr, "@syntax", syntaxText) : syntaxText;
-136 this.requiredResolvers = requiredResolvers;
-137 this.consumeInputResolvers = consumeInputResolvers;
-138 this.doesNotConsumeInputResolvers = doesNotConsumeInputResolvers;
-139 this.optionalResolvers = optionalResolvers;
-140 }
+101 for (int i = 0; i < parameters.length; i++) {
+102 CommandParameter<CEC> parameter = this.parameters[i] = new CommandParameter<>(this, parameters[i], i);
+103 if (!parameter.isCommandIssuer()) {
+104 if (!parameter.requiresInput()) {
+105 optionalResolvers++;
+106 } else {
+107 requiredResolvers++;
+108 }
+109 if (parameter.canConsumeInput()) {
+110 consumeInputResolvers++;
+111 } else {
+112 doesNotConsumeInputResolvers++;
+113 }
+114 }
+115 if (parameter.getSyntax() != null) {
+116 if (syntaxBuilder.length() > 0) {
+117 syntaxBuilder.append(' ');
+118 }
+119 syntaxBuilder.append(parameter.getSyntax());
+120 }
+121 }
+122 String syntaxText = syntaxBuilder.toString().trim();
+123 final String syntaxStr = annotations.getAnnotationValue(method, Syntax.class);
+124 this.syntaxText = syntaxStr != null ? ACFUtil.replace(syntaxStr, "@syntax", syntaxText) : syntaxText;
+125 this.requiredResolvers = requiredResolvers;
+126 this.consumeInputResolvers = consumeInputResolvers;
+127 this.doesNotConsumeInputResolvers = doesNotConsumeInputResolvers;
+128 this.optionalResolvers = optionalResolvers;
+129 }
+130
+131
+132 void invoke(CommandIssuer sender, List<String> args, CommandOperationContext context) {
+133 if (!scope.canExecute(sender, this)) {
+134 return;
+135 }
+136 preCommand();
+137 try {
+138 this.manager.conditions.validateConditions(context);
+139 Map<String, Object> passedArgs = resolveContexts(sender, args);
+140 if (passedArgs == null) return;
141
-142
-143 void invoke(CommandIssuer sender, List<String> args, CommandOperationContext context) {
-144 if (!scope.canExecute(sender, this)) {
-145 return;
-146 }
-147 preCommand();
-148 try {
-149 this.manager.conditions.validateConditions(context);
-150 Map<String, Object> passedArgs = resolveContexts(sender, args);
-151 if (passedArgs == null) return;
-152
-153 if (false) {// methodHandle != null) { //TODO: MethodHandle disabled due to WrongMethodTypeException
-154 methodHandle.invokeWithArguments(passedArgs.values().toArray());
-155 } else {
-156 // The offchance the method handle wasn't unreflected, we're going to use the slower, working reflection.
-157 method.invoke(scope, passedArgs.values().toArray());
-158 }
-159 } catch (Throwable throwable) {
-160 handleException(sender, args, throwable instanceof Exception ? (Exception) throwable : new Exception(throwable));
-161 }
-162 postCommand();
-163 }
-164 public void preCommand() {}
-165 public void postCommand() {}
-166
-167 void handleException(CommandIssuer sender, List<String> args, Exception e) {
-168 if (e instanceof InvocationTargetException && e.getCause() instanceof InvalidCommandArgument) {
-169 e = (Exception) e.getCause();
-170 }
-171 if (e instanceof ShowCommandHelp) {
-172 ShowCommandHelp showHelp = (ShowCommandHelp) e;
-173 CommandHelp commandHelp = manager.generateCommandHelp();
-174 if (showHelp.search) {
-175 commandHelp.setSearch(showHelp.searchArgs == null ? args : showHelp.searchArgs);
-176 }
-177 commandHelp.showHelp(sender);
-178 } else if (e instanceof InvalidCommandArgument) {
-179 InvalidCommandArgument invalidCommandArg = (InvalidCommandArgument) e;
-180 if (invalidCommandArg.key != null) {
-181 sender.sendMessage(MessageType.ERROR, invalidCommandArg.key, invalidCommandArg.replacements);
-182 } else if (e.getMessage() != null && !e.getMessage().isEmpty()) {
-183 sender.sendMessage(MessageType.ERROR, MessageKeys.ERROR_PREFIX, "{message}", e.getMessage());
-184 }
-185 if (invalidCommandArg.showSyntax) {
-186 scope.showSyntax(sender, this);
-187 }
-188 } else {
-189 try {
-190 if (!this.manager.handleUncaughtException(scope, this, sender, args, e)) {
-191 sender.sendMessage(MessageType.ERROR, MessageKeys.ERROR_PERFORMING_COMMAND);
-192 }
-193 this.manager.log(LogLevel.ERROR, "Exception in command: " + command + " " + ACFUtil.join(args), e);
-194 } catch (Exception e2) {
-195 this.manager.log(LogLevel.ERROR, "Exception in handleException for command: " + command + " " + ACFUtil.join(args), e);
-196 this.manager.log(LogLevel.ERROR, "Exception triggered by exception handler:", e2);
-197 }
-198 }
-199 }
-200
-201 @Nullable
-202 Map<String, Object> resolveContexts(CommandIssuer sender, List<String> args) throws InvalidCommandArgument {
-203 return resolveContexts(sender, args, parameters.length);
-204 }
-205 @Nullable
-206 Map<String, Object> resolveContexts(CommandIssuer sender, List<String> args, int argLimit) throws InvalidCommandArgument {
-207 args = Lists.newArrayList(args);
-208 String[] origArgs = args.toArray(new String[args.size()]);
-209 Map<String, Object> passedArgs = Maps.newLinkedHashMap();
-210 int remainingRequired = requiredResolvers;
-211 CommandOperationContext opContext = CommandManager.getCurrentCommandOperationContext();
-212 for (int i = 0; i < parameters.length && i < argLimit; i++) {
-213 boolean isLast = i == parameters.length - 1;
-214 boolean allowOptional = remainingRequired == 0;
-215 final CommandParameter<CEC> parameter = parameters[i];
-216 if (parameter.isCommandIssuer()) {
-217 argLimit++;
-218 }
-219 final String parameterName = parameter.getName();
-220 final Class<?> type = parameter.getType();
-221 //noinspection unchecked
-222 final ContextResolver<?, CEC> resolver = parameter.getResolver();
-223 //noinspection unchecked
-224 CEC context = (CEC) this.manager.createCommandContext(this, parameter, sender, args, i, passedArgs);
-225 boolean requiresInput = parameter.requiresInput();
-226 if (requiresInput && remainingRequired > 0) {
-227 remainingRequired--;
-228 }
-229 if (args.isEmpty() && !(isLast && type == String[].class)) {
-230 if (allowOptional && parameter.getDefaultValue() != null) {
-231 args.add(parameter.getDefaultValue());
-232 } else if (allowOptional && parameter.isOptional()) {
-233 Object value = parameter.isOptionalResolver() ? resolver.getContext(context) : null;
-234 if (value == null && parameter.getClass().isPrimitive()) {
-235 throw new IllegalStateException("Parameter " + parameter.getName() + " is primitive and does not support Optional.");
-236 }
+142 method.invoke(scope, passedArgs.values().toArray());
+143 } catch (Exception e) {
+144 handleException(sender, args, e);
+145 }
+146 postCommand();
+147 }
+148 public void preCommand() {}
+149 public void postCommand() {}
+150
+151 void handleException(CommandIssuer sender, List<String> args, Exception e) {
+152 if (e instanceof InvocationTargetException && e.getCause() instanceof InvalidCommandArgument) {
+153 e = (Exception) e.getCause();
+154 }
+155 if (e instanceof ShowCommandHelp) {
+156 ShowCommandHelp showHelp = (ShowCommandHelp) e;
+157 CommandHelp commandHelp = manager.generateCommandHelp();
+158 if (showHelp.search) {
+159 commandHelp.setSearch(showHelp.searchArgs == null ? args : showHelp.searchArgs);
+160 }
+161 commandHelp.showHelp(sender);
+162 } else if (e instanceof InvalidCommandArgument) {
+163 InvalidCommandArgument invalidCommandArg = (InvalidCommandArgument) e;
+164 if (invalidCommandArg.key != null) {
+165 sender.sendMessage(MessageType.ERROR, invalidCommandArg.key, invalidCommandArg.replacements);
+166 } else if (e.getMessage() != null && !e.getMessage().isEmpty()) {
+167 sender.sendMessage(MessageType.ERROR, MessageKeys.ERROR_PREFIX, "{message}", e.getMessage());
+168 }
+169 if (invalidCommandArg.showSyntax) {
+170 scope.showSyntax(sender, this);
+171 }
+172 } else {
+173 try {
+174 if (!this.manager.handleUncaughtException(scope, this, sender, args, e)) {
+175 sender.sendMessage(MessageType.ERROR, MessageKeys.ERROR_PERFORMING_COMMAND);
+176 }
+177 this.manager.log(LogLevel.ERROR, "Exception in command: " + command + " " + ACFUtil.join(args), e);
+178 } catch (Exception e2) {
+179 this.manager.log(LogLevel.ERROR, "Exception in handleException for command: " + command + " " + ACFUtil.join(args), e);
+180 this.manager.log(LogLevel.ERROR, "Exception triggered by exception handler:", e2);
+181 }
+182 }
+183 }
+184
+185 @Nullable
+186 Map<String, Object> resolveContexts(CommandIssuer sender, List<String> args) throws InvalidCommandArgument {
+187 return resolveContexts(sender, args, parameters.length);
+188 }
+189 @Nullable
+190 Map<String, Object> resolveContexts(CommandIssuer sender, List<String> args, int argLimit) throws InvalidCommandArgument {
+191 args = Lists.newArrayList(args);
+192 String[] origArgs = args.toArray(new String[args.size()]);
+193 Map<String, Object> passedArgs = Maps.newLinkedHashMap();
+194 int remainingRequired = requiredResolvers;
+195 CommandOperationContext opContext = CommandManager.getCurrentCommandOperationContext();
+196 for (int i = 0; i < parameters.length && i < argLimit; i++) {
+197 boolean isLast = i == parameters.length - 1;
+198 boolean allowOptional = remainingRequired == 0;
+199 final CommandParameter<CEC> parameter = parameters[i];
+200 if (parameter.isCommandIssuer()) {
+201 argLimit++;
+202 }
+203 final String parameterName = parameter.getName();
+204 final Class<?> type = parameter.getType();
+205 //noinspection unchecked
+206 final ContextResolver<?, CEC> resolver = parameter.getResolver();
+207 //noinspection unchecked
+208 CEC context = (CEC) this.manager.createCommandContext(this, parameter, sender, args, i, passedArgs);
+209 boolean requiresInput = parameter.requiresInput();
+210 if (requiresInput && remainingRequired > 0) {
+211 remainingRequired--;
+212 }
+213 if (args.isEmpty() && !(isLast && type == String[].class)) {
+214 if (allowOptional && parameter.getDefaultValue() != null) {
+215 args.add(parameter.getDefaultValue());
+216 } else if (allowOptional && parameter.isOptional()) {
+217 Object value = parameter.isOptionalResolver() ? resolver.getContext(context) : null;
+218 if (value == null && parameter.getClass().isPrimitive()) {
+219 throw new IllegalStateException("Parameter " + parameter.getName() + " is primitive and does not support Optional.");
+220 }
+221 //noinspection unchecked
+222 this.manager.conditions.validateConditions(context, value);
+223 passedArgs.put(parameterName, value);
+224 //noinspection UnnecessaryContinue
+225 continue;
+226 } else if (requiresInput) {
+227 scope.showSyntax(sender, this);
+228 return null;
+229 }
+230 }
+231 if (parameter.getValues() != null) {
+232 String arg = !args.isEmpty() ? args.get(0) : "";
+233
+234 Set<String> possible = Sets.newHashSet();
+235 CommandCompletions commandCompletions = this.manager.getCommandCompletions();
+236 for (String s : parameter.getValues()) {
237 //noinspection unchecked
-238 this.manager.conditions.validateConditions(context, value);
-239 passedArgs.put(parameterName, value);
-240 //noinspection UnnecessaryContinue
-241 continue;
-242 } else if (requiresInput) {
-243 scope.showSyntax(sender, this);
-244 return null;
-245 }
-246 }
-247 if (parameter.getValues() != null) {
-248 String arg = !args.isEmpty() ? args.get(0) : "";
-249
-250 Set<String> possible = Sets.newHashSet();
-251 CommandCompletions commandCompletions = this.manager.getCommandCompletions();
-252 for (String s : parameter.getValues()) {
-253 //noinspection unchecked
-254 List<String> check = commandCompletions.getCompletionValues(this, sender, s, origArgs, opContext.isAsync());
-255 if (!check.isEmpty()) {
-256 possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList()));
-257 } else {
-258 possible.add(s.toLowerCase());
-259 }
-260 }
-261
-262 if (!possible.contains(arg.toLowerCase())) {
-263 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF,
-264 "{valid}", ACFUtil.join(possible, ", "));
-265 }
-266 }
-267 Object paramValue = resolver.getContext(context);
-268 //noinspection unchecked
-269 this.manager.conditions.validateConditions(context, paramValue);
-270 passedArgs.put(parameterName, paramValue);
-271 }
-272 return passedArgs;
-273 }
-274
-275 boolean hasPermission(CommandIssuer issuer) {
-276 return (permission == null || permission.isEmpty() || scope.manager.hasPermission(issuer, permission)) && scope.hasPermission(issuer);
-277 }
-278
-279
-280 /**
-281 * @see #getRequiredPermissions()
-282 * @deprecated
-283 */
-284 @Deprecated
-285 public String getPermission() {
-286 if (this.permission == null || this.permission.isEmpty()) {
-287 return null;
-288 }
-289 return ACFPatterns.COMMA.split(this.permission)[0];
-290 }
-291
-292 public Set<String> getRequiredPermissions() {
-293 if (this.permission == null || this.permission.isEmpty()) {
-294 return ImmutableSet.of();
-295 }
-296 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission));
+238 List<String> check = commandCompletions.getCompletionValues(this, sender, s, origArgs, opContext.isAsync());
+239 if (!check.isEmpty()) {
+240 possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList()));
+241 } else {
+242 possible.add(s.toLowerCase());
+243 }
+244 }
+245
+246 if (!possible.contains(arg.toLowerCase())) {
+247 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF,
+248 "{valid}", ACFUtil.join(possible, ", "));
+249 }
+250 }
+251 Object paramValue = resolver.getContext(context);
+252 //noinspection unchecked
+253 this.manager.conditions.validateConditions(context, paramValue);
+254 passedArgs.put(parameterName, paramValue);
+255 }
+256 return passedArgs;
+257 }
+258
+259 boolean hasPermission(CommandIssuer issuer) {
+260 return (permission == null || permission.isEmpty() || scope.manager.hasPermission(issuer, permission)) && scope.hasPermission(issuer);
+261 }
+262
+263
+264 /**
+265 * @see #getRequiredPermissions()
+266 * @deprecated
+267 */
+268 @Deprecated
+269 public String getPermission() {
+270 if (this.permission == null || this.permission.isEmpty()) {
+271 return null;
+272 }
+273 return ACFPatterns.COMMA.split(this.permission)[0];
+274 }
+275
+276 public Set<String> getRequiredPermissions() {
+277 if (this.permission == null || this.permission.isEmpty()) {
+278 return ImmutableSet.of();
+279 }
+280 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission));
+281 }
+282
+283 public boolean requiresPermission(String permission) {
+284 return getRequiredPermissions().contains(permission) || scope.requiresPermission(permission);
+285 }
+286
+287 public String getPrefSubCommand() {
+288 return prefSubCommand;
+289 }
+290
+291 public String getSyntaxText() {
+292 return syntaxText;
+293 }
+294
+295 public String getCommand() {
+296 return command;
297 }
298
-299 public boolean requiresPermission(String permission) {
-300 return getRequiredPermissions().contains(permission) || scope.requiresPermission(permission);
+299 public void addSubcommand(String cmd) {
+300 this.registeredSubcommands.add(cmd);
301 }
-302
-303 public String getPrefSubCommand() {
-304 return prefSubCommand;
-305 }
-306
-307 public String getSyntaxText() {
-308 return syntaxText;
-309 }
-310
-311 public String getCommand() {
-312 return command;
-313 }
-314
-315 public void addSubcommand(String cmd) {
-316 this.registeredSubcommands.add(cmd);
-317 }
-318 public void addSubcommands(Collection<String> cmd) {
-319 this.registeredSubcommands.addAll(cmd);
-320 }
-321}
+302 public void addSubcommands(Collection<String> cmd) {
+303 this.registeredSubcommands.addAll(cmd);
+304 }
+305}