Summary:
Nested |
-Field |
+Field |
Constr |
Method
diff --git a/docs/acf-core/deprecated-list.html b/docs/acf-core/deprecated-list.html
index 82a28d1c..5881f90a 100644
--- a/docs/acf-core/deprecated-list.html
+++ b/docs/acf-core/deprecated-list.html
@@ -185,6 +185,16 @@
+| co.aikar.commands.CommandOperationContext.getAnnotation(Class<T>)
+
+ |
+
+
+| co.aikar.commands.CommandExecutionContext.getAnnotation(Class<T>)
+
+ |
+
+
| co.aikar.commands.BaseCommand.getCommandHelp()
|
@@ -195,28 +205,31 @@
-| co.aikar.commands.RegisteredCommand.getPermission() |
+co.aikar.commands.CommandExecutionContext.getParam() |
-| co.aikar.commands.ACFUtil.random(Class<? extends T>) |
+co.aikar.commands.RegisteredCommand.getPermission() |
+| co.aikar.commands.ACFUtil.random(Class<? extends T>) |
+
+
| co.aikar.commands.CommandContexts.registerSenderAwareContext(Class<T>, IssuerAwareContextResolver<T, R>)
|
-
+
| co.aikar.commands.CommandIssuer.sendMessageInternal(String)
|
-
+
| co.aikar.commands.CommandManager.setDefaultHelpPerPage(int)
|
-
+
| co.aikar.commands.BaseCommand.showCommandHelp()
|
diff --git a/docs/acf-core/index-all.html b/docs/acf-core/index-all.html
index 462dace6..ac1f733b 100644
--- a/docs/acf-core/index-all.html
+++ b/docs/acf-core/index-all.html
@@ -447,8 +447,24 @@
GERMAN - Static variable in class co.aikar.commands.Locales
getAnnotation(Class<T>) - Method in class co.aikar.commands.CommandExecutionContext
-
+
+
+
getAnnotation(Class<T>) - Method in class co.aikar.commands.CommandOperationContext
+
+
+
+getAnnotationValue(Class<T>) - Method in class co.aikar.commands.CommandExecutionContext
+
+getAnnotationValue(Class<T>, int) - Method in class co.aikar.commands.CommandExecutionContext
+
+getAnnotationValue(Class<T>) - Method in class co.aikar.commands.CommandOperationContext
+
+getAnnotationValue(Class<T>, int) - Method in class co.aikar.commands.CommandOperationContext
getApplicableParameters() - Method in interface co.aikar.commands.AnnotationProcessor
@@ -516,6 +532,8 @@
getCompletions(C) - Method in interface co.aikar.commands.CommandCompletions.CommandCompletionHandler
+getConditions() - Method in class co.aikar.commands.CommandParameter
+
getConfig(String) - Method in class co.aikar.commands.CommandCompletionContext
getConfig(String, String) - Method in class co.aikar.commands.CommandCompletionContext
@@ -684,7 +702,9 @@
Gets the actual args in string form the user typed
getParam() - Method in class co.aikar.commands.CommandExecutionContext
-
+
+Deprecated.
+
getParameter() - Method in class co.aikar.commands.CommandParameter
getParameterSyntax() - Method in class co.aikar.commands.HelpEntry
@@ -826,6 +846,8 @@
HelpSearchTags - Annotation Type in co.aikar.commands.annotation
+helpSearchTags - Variable in class co.aikar.commands.RegisteredCommand
+
HINDI - Static variable in class co.aikar.commands.Locales
@@ -1489,6 +1511,8 @@
setCommandIssuer(boolean) - Method in class co.aikar.commands.CommandParameter
+setConditions(String) - Method in class co.aikar.commands.CommandParameter
+
setContextFlags(Class<?>, String) - Method in class co.aikar.commands.BaseCommand
setDefaultExceptionHandler(ExceptionHandler) - Method in class co.aikar.commands.CommandManager
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 682e1fee..22ba3f62 100644
--- a/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html
+++ b/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html
@@ -35,666 +35,680 @@
027import co.aikar.commands.annotation.CatchUnknown;
028import co.aikar.commands.annotation.CommandAlias;
029import co.aikar.commands.annotation.CommandPermission;
-030import co.aikar.commands.annotation.Default;
-031import co.aikar.commands.annotation.HelpCommand;
-032import co.aikar.commands.annotation.PreCommand;
-033import co.aikar.commands.annotation.Subcommand;
-034import co.aikar.commands.annotation.UnknownHandler;
-035import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil;
-036import com.google.common.collect.HashMultimap;
-037import com.google.common.collect.ImmutableList;
-038import com.google.common.collect.ImmutableSet;
-039import com.google.common.collect.Iterables;
-040import com.google.common.collect.Lists;
-041import com.google.common.collect.Maps;
-042import com.google.common.collect.SetMultimap;
-043import com.google.common.collect.Sets;
-044
-045import java.lang.reflect.Constructor;
-046import java.lang.reflect.InvocationTargetException;
-047import java.lang.reflect.Method;
-048import java.lang.reflect.Parameter;
-049import java.util.ArrayList;
-050import java.util.Arrays;
-051import java.util.Collections;
-052import java.util.HashMap;
-053import java.util.HashSet;
-054import java.util.List;
-055import java.util.Map;
-056import java.util.Objects;
-057import java.util.Optional;
-058import java.util.Set;
-059import java.util.Stack;
-060import java.util.stream.Collectors;
-061import java.util.stream.Stream;
-062
-063@SuppressWarnings("unused")
-064public abstract class BaseCommand {
-065
-066 public static final String CATCHUNKNOWN = "__catchunknown";
-067 public static final String DEFAULT = "__default";
-068 final SetMultimap<String, RegisteredCommand> subCommands = HashMultimap.create();
-069 final Map<Class<?>, String> contextFlags = Maps.newHashMap();
-070 private Method preCommandHandler;
-071
-072 @SuppressWarnings("WeakerAccess")
-073 private String execLabel;
-074 @SuppressWarnings("WeakerAccess")
-075 private String execSubcommand;
-076 @SuppressWarnings("WeakerAccess")
-077 private String[] origArgs;
-078 CommandManager<?, ?, ?, ?, ?, ?> manager = null;
-079 BaseCommand parentCommand;
-080 Map<String, RootCommand> registeredCommands = new HashMap<>();
-081 String description;
-082 String commandName;
-083 String usageMessage;
-084 String permission;
-085
-086 private ExceptionHandler exceptionHandler = null;
-087 CommandOperationContext lastCommandOperationContext;
-088 private String parentSubcommand;
-089
-090 public BaseCommand() {}
-091 public BaseCommand(String cmd) {
-092 this.commandName = cmd;
-093 }
-094
-095 /**
-096 * Gets the root command name that the user actually typed
-097 * @return Name
-098 */
-099 public String getExecCommandLabel() {
-100 return execLabel;
-101 }
-102
-103 /**
-104 * Gets the actual sub command name the user typed
-105 * @return Name
-106 */
-107 public String getExecSubcommand() {
-108 return execSubcommand;
-109 }
-110
-111 /**
-112 * Gets the actual args in string form the user typed
-113 * @return Args
-114 */
-115 public String[] getOrigArgs() {
-116 return origArgs;
-117 }
-118
-119 void setParentCommand(BaseCommand command) {
-120 this.parentCommand = command;
-121 }
-122 void onRegister(CommandManager manager) {
-123 onRegister(manager, this.commandName);
-124 }
-125 void onRegister(CommandManager manager, String cmd) {
-126 manager.injectDependencies(this);
-127 this.manager = manager;
-128 final Class<? extends BaseCommand> self = this.getClass();
-129 CommandAlias rootCmdAliasAnno = self.getAnnotation(CommandAlias.class);
-130 String rootCmdAlias = rootCmdAliasAnno != null ? manager.getCommandReplacements().replace(rootCmdAliasAnno.value()).toLowerCase() : null;
-131 if (cmd == null && rootCmdAlias != null) {
-132 cmd = ACFPatterns.PIPE.split(rootCmdAlias)[0];
-133 }
-134 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase();
+030import co.aikar.commands.annotation.Conditions;
+031import co.aikar.commands.annotation.Default;
+032import co.aikar.commands.annotation.HelpCommand;
+033import co.aikar.commands.annotation.PreCommand;
+034import co.aikar.commands.annotation.Subcommand;
+035import co.aikar.commands.annotation.UnknownHandler;
+036import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil;
+037import com.google.common.collect.HashMultimap;
+038import com.google.common.collect.ImmutableList;
+039import com.google.common.collect.ImmutableSet;
+040import com.google.common.collect.Iterables;
+041import com.google.common.collect.Lists;
+042import com.google.common.collect.Maps;
+043import com.google.common.collect.SetMultimap;
+044import com.google.common.collect.Sets;
+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 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[] origArgs;
+079 CommandManager<?, ?, ?, ?, ?, ?> manager = null;
+080 BaseCommand parentCommand;
+081 Map<String, RootCommand> registeredCommands = new HashMap<>();
+082 String description;
+083 String commandName;
+084 String usageMessage;
+085 String permission;
+086 String conditions;
+087
+088 private ExceptionHandler exceptionHandler = null;
+089 CommandOperationContext lastCommandOperationContext;
+090 private String parentSubcommand;
+091
+092 public BaseCommand() {}
+093 public BaseCommand(String cmd) {
+094 this.commandName = cmd;
+095 }
+096
+097 /**
+098 * Gets the root command name that the user actually typed
+099 * @return Name
+100 */
+101 public String getExecCommandLabel() {
+102 return execLabel;
+103 }
+104
+105 /**
+106 * Gets the actual sub command name the user typed
+107 * @return Name
+108 */
+109 public String getExecSubcommand() {
+110 return execSubcommand;
+111 }
+112
+113 /**
+114 * Gets the actual args in string form the user typed
+115 * @return Args
+116 */
+117 public String[] getOrigArgs() {
+118 return origArgs;
+119 }
+120
+121 void setParentCommand(BaseCommand command) {
+122 this.parentCommand = command;
+123 }
+124 void onRegister(CommandManager manager) {
+125 onRegister(manager, this.commandName);
+126 }
+127 void onRegister(CommandManager manager, String cmd) {
+128 manager.injectDependencies(this);
+129 this.manager = manager;
+130
+131 final Annotations annotations = manager.getAnnotations();
+132 final Class<? extends BaseCommand> self = this.getClass();
+133
+134 String[] cmdAliases = annotations.getAnnotationValues(self, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE | Annotations.NO_EMPTY);
135
-136 this.description = this.commandName + " commands";
-137 this.usageMessage = "/" + this.commandName;
-138 this.parentSubcommand = getParentSubcommand(this.getClass());
+136 if (cmd == null && cmdAliases != null) {
+137 cmd = cmdAliases[0];
+138 }
139
-140 final CommandPermission perm = self.getAnnotation(CommandPermission.class);
-141 if (perm != null) {
-142 this.permission = manager.getCommandReplacements().replace(perm.value());
-143 }
-144
-145 boolean foundDefault = false;
-146 boolean foundCatchUnknown = false;
-147 boolean isParentEmpty = parentSubcommand.isEmpty();
-148 for (Method method : self.getMethods()) {
-149 method.setAccessible(true);
-150 String sublist = null;
-151 String sub = getSubcommandValue(method);
-152 final Default def = method.getAnnotation(Default.class);
-153 final HelpCommand helpCommand = method.getAnnotation(HelpCommand.class);
-154 final CommandAlias commandAliases = method.getAnnotation(CommandAlias.class);
-155
-156 if (!isParentEmpty && def != null) {
-157 sub = parentSubcommand;
-158 }
-159 if (isParentEmpty && (def != null || (!foundDefault && helpCommand != null))) {
-160 if (!foundDefault) {
-161 if (def != null) {
-162 this.subCommands.get(DEFAULT).clear();
-163 foundDefault = true;
-164 }
-165 registerSubcommand(method, DEFAULT);
-166 } else {
-167 ACFUtil.sneaky(new IllegalStateException("Multiple @Default/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
-168 }
-169 }
-170
-171 if (sub != null) {
-172 sublist = sub;
-173 } else if (commandAliases != null) {
-174 sublist = commandAliases.value();
-175 } else if (helpCommand != null) {
-176 sublist = helpCommand.value();
-177 }
-178
-179 PreCommand preCommand = method.getAnnotation(PreCommand.class);
-180 boolean hasCatchUnknown = method.isAnnotationPresent(CatchUnknown.class) || method.isAnnotationPresent(CatchAll.class) || method.isAnnotationPresent(UnknownHandler.class);
-181 if (hasCatchUnknown || (!foundCatchUnknown && helpCommand != null)) {
-182 if (!foundCatchUnknown) {
-183 if (hasCatchUnknown) {
-184 this.subCommands.get(CATCHUNKNOWN).clear();
-185 foundCatchUnknown = true;
-186 }
-187 registerSubcommand(method, CATCHUNKNOWN);
-188 } else {
-189 ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
-190 }
-191 } else if (preCommand != null) {
-192 if (this.preCommandHandler == null) {
-193 this.preCommandHandler = method;
-194 } else {
-195 ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
-196 }
-197 }
-198 if (Objects.equals(method.getDeclaringClass(), this.getClass()) && sublist != null) {
-199 registerSubcommand(method, sublist);
-200 }
-201 }
-202
-203 if (rootCmdAlias != null) {
-204 Set<String> cmdList = new HashSet<>();
-205 Collections.addAll(cmdList, ACFPatterns.PIPE.split(rootCmdAlias));
-206 cmdList.remove(cmd);
-207 for (String cmdAlias : cmdList) {
-208 register(cmdAlias, this);
-209 }
-210 }
-211
-212 if (cmd != null) {
-213 register(cmd, this);
-214 }
-215 for (Class<?> clazz : this.getClass().getDeclaredClasses()) {
-216 if (BaseCommand.class.isAssignableFrom(clazz)) {
-217 try {
-218 BaseCommand subCommand = null;
-219 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
-220 for (Constructor<?> declaredConstructor : declaredConstructors) {
-221
-222 declaredConstructor.setAccessible(true);
-223 Parameter[] parameters = declaredConstructor.getParameters();
-224 if (parameters.length == 1) {
-225 subCommand = (BaseCommand) declaredConstructor.newInstance(this);
-226 } else {
-227 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")");
-228 }
-229 }
-230 if (subCommand != null) {
-231 subCommand.setParentCommand(this);
-232 subCommand.onRegister(manager, cmd);
-233 this.subCommands.putAll(subCommand.subCommands);
-234 this.registeredCommands.putAll(subCommand.registeredCommands);
-235 } else {
-236 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName());
-237 }
-238 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
-239 e.printStackTrace();
-240 }
-241 }
-242 }
-243
-244 }
-245
-246 private String getSubcommandValue(Method method) {
-247 final Subcommand sub = method.getAnnotation(Subcommand.class);
-248 if (sub == null) {
-249 return null;
-250 }
-251 Class<?> clazz = method.getDeclaringClass();
-252 String parent = getParentSubcommand(clazz);
-253 return parent == null || parent.isEmpty() ? sub.value() : parent + " " + sub.value();
-254 }
-255
-256 private String getParentSubcommand(Class<?> clazz) {
-257 List<String> subList = new ArrayList<>();
-258 while (clazz != null) {
-259 Subcommand classSub = clazz.getAnnotation(Subcommand.class);
-260 if (classSub != null) {
-261 subList.add(classSub.value());
-262 }
-263 clazz = clazz.getEnclosingClass();
-264 }
-265 Collections.reverse(subList);
-266 return ACFUtil.join(subList, " ");
-267 }
-268
-269 private void register(String name, BaseCommand cmd) {
-270 String nameLower = name.toLowerCase();
-271 RootCommand rootCommand = manager.obtainRootCommand(nameLower);
-272 rootCommand.addChild(cmd);
-273
-274 this.registeredCommands.put(nameLower, rootCommand);
-275 }
-276
-277 private void registerSubcommand(Method method, String subCommand) {
-278 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase());
-279 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand);
-280 // Must run getSubcommandPossibility BEFORE we rewrite it just after this.
-281 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts);
-282
-283 // Strip pipes off for auto complete addition
-284 for (int i = 0; i < subCommandParts.length; i++) {
-285 String[] split = ACFPatterns.PIPE.split(subCommandParts[i]);
-286 if (split.length == 0 || split[0].isEmpty()) {
-287 throw new IllegalArgumentException("Invalid @Subcommand configuration for " + method.getName() + " - parts can not start with | or be empty");
-288 }
-289 subCommandParts[i] = split[0];
-290 }
-291 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " ");
-292 final CommandAlias cmdAlias = method.getAnnotation(CommandAlias.class);
-293
-294 final String[] aliasNames = cmdAlias != null ? ACFPatterns.PIPE.split(manager.getCommandReplacements().replace(cmdAlias.value().toLowerCase())) : null;
-295 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " ";
-296 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand);
+140 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase();
+141 this.permission = annotations.getAnnotationValue(self, CommandPermission.class, Annotations.REPLACEMENTS);
+142 this.description = this.commandName + " commands";
+143 this.usageMessage = "/" + this.commandName;
+144 this.parentSubcommand = getParentSubcommand(self);
+145 this.conditions = annotations.getAnnotationValue(self, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+146
+147 registerSubcommands();
+148
+149 if (cmdAliases != null) {
+150 Set<String> cmdList = new HashSet<>();
+151 Collections.addAll(cmdList, cmdAliases);
+152 cmdList.remove(cmd);
+153 for (String cmdAlias : cmdList) {
+154 register(cmdAlias, this);
+155 }
+156 }
+157
+158 if (cmd != null) {
+159 register(cmd, this);
+160 }
+161 registerSubclasses(cmd);
+162
+163 }
+164
+165 private void registerSubclasses(String cmd) {
+166 for (Class<?> clazz : this.getClass().getDeclaredClasses()) {
+167 if (BaseCommand.class.isAssignableFrom(clazz)) {
+168 try {
+169 BaseCommand subCommand = null;
+170 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
+171 for (Constructor<?> declaredConstructor : declaredConstructors) {
+172
+173 declaredConstructor.setAccessible(true);
+174 Parameter[] parameters = declaredConstructor.getParameters();
+175 if (parameters.length == 1) {
+176 subCommand = (BaseCommand) declaredConstructor.newInstance(this);
+177 } else {
+178 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")");
+179 }
+180 }
+181 if (subCommand != null) {
+182 subCommand.setParentCommand(this);
+183 subCommand.onRegister(manager, cmd);
+184 this.subCommands.putAll(subCommand.subCommands);
+185 this.registeredCommands.putAll(subCommand.registeredCommands);
+186 } else {
+187 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName());
+188 }
+189 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+190 this.manager.log(LogLevel.ERROR, "Error registering subclass", e);
+191 }
+192 }
+193 }
+194 }
+195
+196 private void registerSubcommands() {
+197 final Annotations annotations = manager.getAnnotations();
+198 boolean foundDefault = false;
+199 boolean foundCatchUnknown = false;
+200 boolean isParentEmpty = parentSubcommand.isEmpty();
+201
+202 for (Method method : this.getClass().getMethods()) {
+203 method.setAccessible(true);
+204 String sublist = null;
+205 String sub = getSubcommandValue(method);
+206 final boolean def = annotations.hasAnnotation(method, Default.class);
+207 final String helpCommand = annotations.getAnnotationValue(method, HelpCommand.class, Annotations.NOTHING);
+208 final String commandAliases = annotations.getAnnotationValue(method, CommandAlias.class, Annotations.NOTHING);
+209
+210 if (!isParentEmpty && def) {
+211 sub = parentSubcommand;
+212 }
+213 if (isParentEmpty && (def || (!foundDefault && helpCommand != null))) {
+214 if (!foundDefault) {
+215 if (def) {
+216 this.subCommands.get(DEFAULT).clear();
+217 foundDefault = true;
+218 }
+219 registerSubcommand(method, DEFAULT);
+220 } else {
+221 ACFUtil.sneaky(new IllegalStateException("Multiple @Default/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
+222 }
+223 }
+224
+225 if (sub != null) {
+226 sublist = sub;
+227 } else if (commandAliases != null) {
+228 sublist = commandAliases;
+229 } else if (helpCommand != null) {
+230 sublist = helpCommand;
+231 }
+232
+233 boolean preCommand = annotations.hasAnnotation(method, PreCommand.class);
+234 boolean hasCatchUnknown = annotations.hasAnnotation(method, CatchUnknown.class) ||
+235 annotations.hasAnnotation(method, CatchAll.class) ||
+236 annotations.hasAnnotation(method, UnknownHandler.class);
+237
+238 if (hasCatchUnknown || (!foundCatchUnknown && helpCommand != null)) {
+239 if (!foundCatchUnknown) {
+240 if (hasCatchUnknown) {
+241 this.subCommands.get(CATCHUNKNOWN).clear();
+242 foundCatchUnknown = true;
+243 }
+244 registerSubcommand(method, CATCHUNKNOWN);
+245 } else {
+246 ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
+247 }
+248 } else if (preCommand) {
+249 if (this.preCommandHandler == null) {
+250 this.preCommandHandler = method;
+251 } else {
+252 ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
+253 }
+254 }
+255 if (Objects.equals(method.getDeclaringClass(), this.getClass()) && sublist != null) {
+256 registerSubcommand(method, sublist);
+257 }
+258 }
+259 }
+260
+261 private String getSubcommandValue(Method method) {
+262 final String sub = manager.getAnnotations().getAnnotationValue(method, Subcommand.class, Annotations.NOTHING);
+263 if (sub == null) {
+264 return null;
+265 }
+266 Class<?> clazz = method.getDeclaringClass();
+267 String parent = getParentSubcommand(clazz);
+268 return parent == null || parent.isEmpty() ? sub : parent + " " + sub;
+269 }
+270
+271 private String getParentSubcommand(Class<?> clazz) {
+272 List<String> subList = new ArrayList<>();
+273 while (clazz != null) {
+274 String sub = manager.getAnnotations().getAnnotationValue(clazz, Subcommand.class, Annotations.NOTHING);
+275 if (sub != null) {
+276 subList.add(sub);
+277 }
+278 clazz = clazz.getEnclosingClass();
+279 }
+280 Collections.reverse(subList);
+281 return ACFUtil.join(subList, " ");
+282 }
+283
+284 private void register(String name, BaseCommand cmd) {
+285 String nameLower = name.toLowerCase();
+286 RootCommand rootCommand = manager.obtainRootCommand(nameLower);
+287 rootCommand.addChild(cmd);
+288
+289 this.registeredCommands.put(nameLower, rootCommand);
+290 }
+291
+292 private void registerSubcommand(Method method, String subCommand) {
+293 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase());
+294 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand);
+295 // Must run getSubcommandPossibility BEFORE we rewrite it just after this.
+296 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts);
297
-298 for (String subcmd : cmdList) {
-299 subCommands.put(subcmd, cmd);
-300 }
-301 cmd.addSubcommands(cmdList);
-302
-303 if (aliasNames != null) {
-304 for (String name : aliasNames) {
-305 register(name, new ForwardingCommand(this, subCommandParts));
-306 }
-307 }
-308 }
-309
-310 /**
-311 * Takes a string like "foo|bar baz|qux" and generates a list of
-312 * - foo baz
-313 * - foo qux
-314 * - bar baz
-315 * - bar qux
-316 *
-317 * For every possible sub command combination
-318 *
-319 * @param subCommandParts
-320 * @return List of all sub command possibilities
-321 */
-322 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) {
-323 int i = 0;
-324 Set<String> current = null;
-325 while (true) {
-326 Set<String> newList = new HashSet<>();
-327
-328 if (i < subCommandParts.length) {
-329 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) {
-330 if (current != null) {
-331 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList()));
-332 } else {
-333 newList.add(s1);
-334 }
-335 }
-336 }
-337
-338 if (i + 1 < subCommandParts.length) {
-339 current = newList;
-340 i = i + 1;
-341 continue;
-342 }
-343
-344 return newList;
-345 }
-346 }
-347
-348 public void execute(CommandIssuer issuer, String commandLabel, String[] args) {
-349 commandLabel = commandLabel.toLowerCase();
-350 try {
-351 CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args, false);
-352
-353 if (args.length > 0) {
-354 CommandSearch cmd = findSubCommand(args);
-355 if (cmd != null) {
-356 execSubcommand = cmd.getCheckSub();
-357 final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length);
-358 executeCommand(commandContext, issuer, execargs, cmd.cmd);
-359 return;
-360 }
-361 }
-362
-363 if (subCommands.get(DEFAULT) != null && args.length == 0) {
-364 executeSubcommand(commandContext, DEFAULT, issuer, args);
-365 } else if (subCommands.get(CATCHUNKNOWN) != null) {
-366 if (!executeSubcommand(commandContext, CATCHUNKNOWN, issuer, args)) {
-367 help(issuer, args);
-368 }
-369 } else if (subCommands.get(DEFAULT) != null) {
-370 executeSubcommand(commandContext, DEFAULT, issuer, args);
-371 }
-372
-373 } finally {
-374 postCommandOperation();
-375 }
-376 }
-377
-378 RegisteredCommand<?> getRegisteredCommand(String[] args) {
-379 final CommandSearch cmd = findSubCommand(args);
-380 return cmd != null ? cmd.cmd : null;
-381 }
-382
-383 private void postCommandOperation() {
-384 CommandManager.commandOperationContext.get().pop();
-385 execSubcommand = null;
-386 execLabel = null;
-387 origArgs = new String[]{};
-388 }
-389
-390 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
-391 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get();
-392 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync);
-393 contexts.push(context);
-394 lastCommandOperationContext = context;
-395 execSubcommand = null;
-396 execLabel = commandLabel;
-397 origArgs = args;
-398 return context;
-399 }
-400
-401 public CommandIssuer getCurrentCommandIssuer() {
-402 return CommandManager.getCurrentCommandIssuer();
-403 }
-404 public CommandManager getCurrentCommandManager() {
-405 return CommandManager.getCurrentCommandManager();
-406 }
-407
-408 private CommandSearch findSubCommand(String[] args) {
-409 return findSubCommand(args, false);
-410 }
-411 private CommandSearch findSubCommand(String[] args, boolean completion) {
-412 for (int i = args.length; i >= 0; i--) {
-413 String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase();
-414 Set<RegisteredCommand> cmds = subCommands.get(checkSub);
-415
-416 final int extraArgs = args.length - i;
-417 if (!cmds.isEmpty()) {
-418 RegisteredCommand cmd = null;
-419 if (cmds.size() == 1) {
-420 cmd = Iterables.getOnlyElement(cmds);
-421 } else {
-422 Optional<RegisteredCommand> optCmd = cmds.stream().filter(c -> {
-423 int required = c.requiredResolvers;
-424 int optional = c.optionalResolvers;
-425 return extraArgs <= required + optional && (completion || extraArgs >= required);
-426 }).sorted((c1, c2) -> {
-427 int a = c1.requiredResolvers + c1.optionalResolvers;
-428 int b = c2.requiredResolvers + c2.optionalResolvers;
+298 // Strip pipes off for auto complete addition
+299 for (int i = 0; i < subCommandParts.length; i++) {
+300 String[] split = ACFPatterns.PIPE.split(subCommandParts[i]);
+301 if (split.length == 0 || split[0].isEmpty()) {
+302 throw new IllegalArgumentException("Invalid @Subcommand configuration for " + method.getName() + " - parts can not start with | or be empty");
+303 }
+304 subCommandParts[i] = split[0];
+305 }
+306 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " ");
+307 final String[] aliasNames = manager.getAnnotations().getAnnotationValues(method, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE);
+308
+309 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " ";
+310 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand);
+311
+312 for (String subcmd : cmdList) {
+313 subCommands.put(subcmd, cmd);
+314 }
+315 cmd.addSubcommands(cmdList);
+316
+317 if (aliasNames != null) {
+318 for (String name : aliasNames) {
+319 register(name, new ForwardingCommand(this, subCommandParts));
+320 }
+321 }
+322 }
+323
+324 /**
+325 * Takes a string like "foo|bar baz|qux" and generates a list of
+326 * - foo baz
+327 * - foo qux
+328 * - bar baz
+329 * - bar qux
+330 *
+331 * For every possible sub command combination
+332 *
+333 * @param subCommandParts
+334 * @return List of all sub command possibilities
+335 */
+336 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) {
+337 int i = 0;
+338 Set<String> current = null;
+339 while (true) {
+340 Set<String> newList = new HashSet<>();
+341
+342 if (i < subCommandParts.length) {
+343 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) {
+344 if (current != null) {
+345 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList()));
+346 } else {
+347 newList.add(s1);
+348 }
+349 }
+350 }
+351
+352 if (i + 1 < subCommandParts.length) {
+353 current = newList;
+354 i = i + 1;
+355 continue;
+356 }
+357
+358 return newList;
+359 }
+360 }
+361
+362 public void execute(CommandIssuer issuer, String commandLabel, String[] args) {
+363 commandLabel = commandLabel.toLowerCase();
+364 try {
+365 CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args, false);
+366
+367 if (args.length > 0) {
+368 CommandSearch cmd = findSubCommand(args);
+369 if (cmd != null) {
+370 execSubcommand = cmd.getCheckSub();
+371 final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length);
+372 executeCommand(commandContext, issuer, execargs, cmd.cmd);
+373 return;
+374 }
+375 }
+376
+377 if (subCommands.get(DEFAULT) != null && args.length == 0) {
+378 executeSubcommand(commandContext, DEFAULT, issuer, args);
+379 } else if (subCommands.get(CATCHUNKNOWN) != null) {
+380 if (!executeSubcommand(commandContext, CATCHUNKNOWN, issuer, args)) {
+381 help(issuer, args);
+382 }
+383 } else if (subCommands.get(DEFAULT) != null) {
+384 executeSubcommand(commandContext, DEFAULT, issuer, args);
+385 }
+386
+387 } finally {
+388 postCommandOperation();
+389 }
+390 }
+391
+392 RegisteredCommand<?> getRegisteredCommand(String[] args) {
+393 final CommandSearch cmd = findSubCommand(args);
+394 return cmd != null ? cmd.cmd : null;
+395 }
+396
+397 private void postCommandOperation() {
+398 CommandManager.commandOperationContext.get().pop();
+399 execSubcommand = null;
+400 execLabel = null;
+401 origArgs = new String[]{};
+402 }
+403
+404 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
+405 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get();
+406 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync);
+407 contexts.push(context);
+408 lastCommandOperationContext = context;
+409 execSubcommand = null;
+410 execLabel = commandLabel;
+411 origArgs = args;
+412 return context;
+413 }
+414
+415 public CommandIssuer getCurrentCommandIssuer() {
+416 return CommandManager.getCurrentCommandIssuer();
+417 }
+418 public CommandManager getCurrentCommandManager() {
+419 return CommandManager.getCurrentCommandManager();
+420 }
+421
+422 private CommandSearch findSubCommand(String[] args) {
+423 return findSubCommand(args, false);
+424 }
+425 private CommandSearch findSubCommand(String[] args, boolean completion) {
+426 for (int i = args.length; i >= 0; i--) {
+427 String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase();
+428 Set<RegisteredCommand> cmds = subCommands.get(checkSub);
429
-430 if (a == b) {
-431 return 0;
-432 }
-433 return a < b ? 1 : -1;
-434 }).findFirst();
-435 if (optCmd.isPresent()) {
-436 cmd = optCmd.get();
-437 }
-438 }
-439 if (cmd != null) {
-440 return new CommandSearch(cmd, i, checkSub);
-441 }
-442 }
-443 }
-444 return null;
-445 }
-446
-447 private void executeCommand(CommandOperationContext commandOperationContext,
-448 CommandIssuer issuer, String[] args, RegisteredCommand cmd) {
-449 if (cmd.hasPermission(issuer)) {
-450 commandOperationContext.setRegisteredCommand(cmd);
-451 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) {
-452 return;
-453 }
-454 List<String> sargs = Lists.newArrayList(args);
-455 cmd.invoke(issuer, sargs, commandOperationContext);
-456 } else {
-457 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED);
-458 }
+430 final int extraArgs = args.length - i;
+431 if (!cmds.isEmpty()) {
+432 RegisteredCommand cmd = null;
+433 if (cmds.size() == 1) {
+434 cmd = Iterables.getOnlyElement(cmds);
+435 } else {
+436 Optional<RegisteredCommand> optCmd = cmds.stream().filter(c -> {
+437 int required = c.requiredResolvers;
+438 int optional = c.optionalResolvers;
+439 return extraArgs <= required + optional && (completion || extraArgs >= required);
+440 }).sorted((c1, c2) -> {
+441 int a = c1.requiredResolvers + c1.optionalResolvers;
+442 int b = c2.requiredResolvers + c2.optionalResolvers;
+443
+444 if (a == b) {
+445 return 0;
+446 }
+447 return a < b ? 1 : -1;
+448 }).findFirst();
+449 if (optCmd.isPresent()) {
+450 cmd = optCmd.get();
+451 }
+452 }
+453 if (cmd != null) {
+454 return new CommandSearch(cmd, i, checkSub);
+455 }
+456 }
+457 }
+458 return null;
459 }
460
-461 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) {
-462 return true;
-463 }
-464
-465 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) {
-466 return tabComplete(issuer, commandLabel, args, false);
-467 }
-468 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync)
-469 throws IllegalArgumentException {
-470
-471 commandLabel = commandLabel.toLowerCase();
-472 if (args.length == 0) {
-473 args = new String[]{""};
-474 }
-475 try {
-476 CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args, isAsync);
-477
-478 final CommandSearch search = findSubCommand(args, true);
-479
-480
-481 final List<String> cmds = new ArrayList<>();
-482
-483 if (search != null) {
-484 cmds.addAll(completeCommand(issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel, isAsync));
-485 } else if (subCommands.get(CATCHUNKNOWN).size() == 1) {
-486 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(CATCHUNKNOWN)), args, commandLabel, isAsync));
-487 } else if (subCommands.get(DEFAULT).size() == 1) {
-488 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(DEFAULT)), args, commandLabel, isAsync));
-489 }
-490
-491 return filterTabComplete(args[args.length - 1], cmds);
-492 } finally {
-493 postCommandOperation();
-494 }
-495 }
+461 private void executeCommand(CommandOperationContext commandOperationContext,
+462 CommandIssuer issuer, String[] args, RegisteredCommand cmd) {
+463 if (cmd.hasPermission(issuer)) {
+464 commandOperationContext.setRegisteredCommand(cmd);
+465 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) {
+466 return;
+467 }
+468 List<String> sargs = Lists.newArrayList(args);
+469 cmd.invoke(issuer, sargs, commandOperationContext);
+470 } else {
+471 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED);
+472 }
+473 }
+474
+475 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) {
+476 return true;
+477 }
+478
+479 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) {
+480 return tabComplete(issuer, commandLabel, args, false);
+481 }
+482 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync)
+483 throws IllegalArgumentException {
+484
+485 commandLabel = commandLabel.toLowerCase();
+486 if (args.length == 0) {
+487 args = new String[]{""};
+488 }
+489 try {
+490 CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args, isAsync);
+491
+492 final CommandSearch search = findSubCommand(args, true);
+493
+494
+495 final List<String> cmds = new ArrayList<>();
496
-497 List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) {
-498 final Set<String> cmds = new HashSet<>();
-499 final int cmdIndex = Math.max(0, args.length - 1);
-500 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase();
-501 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) {
-502 final String key = entry.getKey();
-503 if (key.startsWith(argString) && !CATCHUNKNOWN.equals(key) && !DEFAULT.equals(key)) {
-504 final RegisteredCommand value = entry.getValue();
-505 if (!value.hasPermission(issuer)) {
-506 continue;
-507 }
-508
-509 String[] split = ACFPatterns.SPACE.split(value.prefSubCommand);
-510 cmds.add(split[cmdIndex]);
-511 }
-512 }
-513 return new ArrayList<>(cmds);
-514 }
-515
-516 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) {
-517 if (!cmd.hasPermission(issuer) || args.length > cmd.requiredResolvers + cmd.optionalResolvers || args.length == 0
-518 || cmd.complete == null) {
-519 return ImmutableList.of();
-520 }
-521
-522 String[] completions = ACFPatterns.SPACE.split(cmd.complete);
-523
-524 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, completions, args, isAsync);
-525 return filterTabComplete(args[args.length-1], cmds);
-526 }
-527
-528 private static List<String> filterTabComplete(String arg, List<String> cmds) {
-529 return cmds.stream()
-530 .distinct()
-531 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg)))
-532 .collect(Collectors.toList());
-533 }
-534
-535 RegisteredCommand getSubcommand(String subcommand) {
-536 return getSubcommand(subcommand, false);
-537 }
-538
-539 RegisteredCommand getSubcommand(String subcommand, boolean requireOne) {
-540 final Set<RegisteredCommand> commands = subCommands.get(subcommand);
-541 if (!commands.isEmpty() && (!requireOne || commands.size() == 1)) {
-542 return commands.iterator().next();
-543 }
-544 return null;
-545 }
-546
-547 private boolean executeSubcommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) {
-548 final RegisteredCommand cmd = this.getSubcommand(subcommand);
-549 if (cmd != null) {
-550 executeCommand(commandContext, issuer, args, cmd);
-551 return true;
-552 }
-553
-554 return false;
-555 }
-556
-557 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) {
-558 Method pre = this.preCommandHandler;
-559 if (pre != null) {
-560 try {
-561 Class<?>[] types = pre.getParameterTypes();
-562 Object[] parameters = new Object[pre.getParameterCount()];
-563 for (int i = 0; i < parameters.length; i++) {
-564 Class<?> type = types[i];
-565 Object issuerObject = issuer.getIssuer();
-566 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) {
-567 parameters[i] = issuerObject;
-568 } else if (CommandIssuer.class.isAssignableFrom(type)) {
-569 parameters[i] = issuer;
-570 } else if (RegisteredCommand.class.isAssignableFrom(type)) {
-571 parameters[i] = cmd;
-572 } else if (String[].class.isAssignableFrom((type))) {
-573 parameters[i] = args;
-574 } else {
-575 parameters[i] = null;
-576 }
-577 }
-578
-579 return (boolean) pre.invoke(this, parameters);
-580 } catch (IllegalAccessException | InvocationTargetException e) {
-581 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e);
-582 }
-583 }
-584 return false;
-585 }
-586
-587 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-588 public CommandHelp getCommandHelp() {
-589 return manager.generateCommandHelp();
-590 }
-591
-592 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-593 public void showCommandHelp() {
-594 getCommandHelp().showHelp();
-595 }
-596
-597 public void help(Object issuer, String[] args) {
-598 help(manager.getCommandIssuer(issuer), args);
+497 if (search != null) {
+498 cmds.addAll(completeCommand(issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel, isAsync));
+499 } else if (subCommands.get(CATCHUNKNOWN).size() == 1) {
+500 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(CATCHUNKNOWN)), args, commandLabel, isAsync));
+501 } else if (subCommands.get(DEFAULT).size() == 1) {
+502 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(DEFAULT)), args, commandLabel, isAsync));
+503 }
+504
+505 return filterTabComplete(args[args.length - 1], cmds);
+506 } finally {
+507 postCommandOperation();
+508 }
+509 }
+510
+511 List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) {
+512 final Set<String> cmds = new HashSet<>();
+513 final int cmdIndex = Math.max(0, args.length - 1);
+514 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase();
+515 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) {
+516 final String key = entry.getKey();
+517 if (key.startsWith(argString) && !CATCHUNKNOWN.equals(key) && !DEFAULT.equals(key)) {
+518 final RegisteredCommand value = entry.getValue();
+519 if (!value.hasPermission(issuer)) {
+520 continue;
+521 }
+522
+523 String[] split = ACFPatterns.SPACE.split(value.prefSubCommand);
+524 cmds.add(split[cmdIndex]);
+525 }
+526 }
+527 return new ArrayList<>(cmds);
+528 }
+529
+530 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) {
+531 if (!cmd.hasPermission(issuer) || args.length > cmd.requiredResolvers + cmd.optionalResolvers || args.length == 0
+532 || cmd.complete == null) {
+533 return ImmutableList.of();
+534 }
+535
+536 String[] completions = ACFPatterns.SPACE.split(cmd.complete);
+537
+538 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, completions, args, isAsync);
+539 return filterTabComplete(args[args.length-1], cmds);
+540 }
+541
+542 private static List<String> filterTabComplete(String arg, List<String> cmds) {
+543 return cmds.stream()
+544 .distinct()
+545 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg)))
+546 .collect(Collectors.toList());
+547 }
+548
+549 RegisteredCommand getSubcommand(String subcommand) {
+550 return getSubcommand(subcommand, false);
+551 }
+552
+553 RegisteredCommand getSubcommand(String subcommand, boolean requireOne) {
+554 final Set<RegisteredCommand> commands = subCommands.get(subcommand);
+555 if (!commands.isEmpty() && (!requireOne || commands.size() == 1)) {
+556 return commands.iterator().next();
+557 }
+558 return null;
+559 }
+560
+561 private boolean executeSubcommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) {
+562 final RegisteredCommand cmd = this.getSubcommand(subcommand);
+563 if (cmd != null) {
+564 executeCommand(commandContext, issuer, args, cmd);
+565 return true;
+566 }
+567
+568 return false;
+569 }
+570
+571 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) {
+572 Method pre = this.preCommandHandler;
+573 if (pre != null) {
+574 try {
+575 Class<?>[] types = pre.getParameterTypes();
+576 Object[] parameters = new Object[pre.getParameterCount()];
+577 for (int i = 0; i < parameters.length; i++) {
+578 Class<?> type = types[i];
+579 Object issuerObject = issuer.getIssuer();
+580 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) {
+581 parameters[i] = issuerObject;
+582 } else if (CommandIssuer.class.isAssignableFrom(type)) {
+583 parameters[i] = issuer;
+584 } else if (RegisteredCommand.class.isAssignableFrom(type)) {
+585 parameters[i] = cmd;
+586 } else if (String[].class.isAssignableFrom((type))) {
+587 parameters[i] = args;
+588 } else {
+589 parameters[i] = null;
+590 }
+591 }
+592
+593 return (boolean) pre.invoke(this, parameters);
+594 } catch (IllegalAccessException | InvocationTargetException e) {
+595 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e);
+596 }
+597 }
+598 return false;
599 }
-600 public void help(CommandIssuer issuer, String[] args) {
-601 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND);
-602 }
-603 public void doHelp(Object issuer, String... args) {
-604 doHelp(manager.getCommandIssuer(issuer), args);
-605 }
-606 public void doHelp(CommandIssuer issuer, String... args) {
-607 help(issuer, args);
-608 }
-609
-610 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) {
-611 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX,
-612 "{command}", "/" + cmd.command,
-613 "{syntax}", cmd.syntaxText
-614 );
-615 }
-616
-617 public boolean hasPermission(Object issuer) {
-618 return hasPermission(manager.getCommandIssuer(issuer));
+600
+601 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
+602 public CommandHelp getCommandHelp() {
+603 return manager.generateCommandHelp();
+604 }
+605
+606 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
+607 public void showCommandHelp() {
+608 getCommandHelp().showHelp();
+609 }
+610
+611 public void help(Object issuer, String[] args) {
+612 help(manager.getCommandIssuer(issuer), args);
+613 }
+614 public void help(CommandIssuer issuer, String[] args) {
+615 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND);
+616 }
+617 public void doHelp(Object issuer, String... args) {
+618 doHelp(manager.getCommandIssuer(issuer), args);
619 }
-620
-621 public boolean hasPermission(CommandIssuer issuer) {
-622 return permission == null || permission.isEmpty() || (manager.hasPermission(issuer, permission) && (parentCommand == null || parentCommand.hasPermission(issuer)));
-623 }
-624
-625
-626 public Set<String> getRequiredPermissions() {
-627 if (this.permission == null || this.permission.isEmpty()) {
-628 return ImmutableSet.of();
-629 }
-630 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission));
-631 }
-632
-633 public boolean requiresPermission(String permission) {
-634 return getRequiredPermissions().contains(permission) || this.parentCommand != null && parentCommand.requiresPermission(permission);
-635 }
-636
-637 public String getName() {
-638 return commandName;
-639 }
-640
-641 public ExceptionHandler getExceptionHandler() {
-642 return exceptionHandler;
-643 }
-644
-645 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) {
-646 this.exceptionHandler = exceptionHandler;
-647 return this;
-648 }
-649
-650 public RegisteredCommand getDefaultRegisteredCommand() {
-651 return this.getSubcommand(DEFAULT);
-652 }
-653
-654 public String setContextFlags(Class<?> cls, String flags) {
-655 return this.contextFlags.put(cls, flags);
-656 }
-657
-658 public String getContextFlags(Class<?> cls) {
-659 return this.contextFlags.get(cls);
-660 }
-661
-662 private static class CommandSearch { RegisteredCommand cmd; int argIndex; String checkSub;
+620 public void doHelp(CommandIssuer issuer, String... args) {
+621 help(issuer, args);
+622 }
+623
+624 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) {
+625 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX,
+626 "{command}", "/" + cmd.command,
+627 "{syntax}", cmd.syntaxText
+628 );
+629 }
+630
+631 public boolean hasPermission(Object issuer) {
+632 return hasPermission(manager.getCommandIssuer(issuer));
+633 }
+634
+635 public boolean hasPermission(CommandIssuer issuer) {
+636 return permission == null || permission.isEmpty() || (manager.hasPermission(issuer, permission) && (parentCommand == null || parentCommand.hasPermission(issuer)));
+637 }
+638
+639
+640 public Set<String> getRequiredPermissions() {
+641 if (this.permission == null || this.permission.isEmpty()) {
+642 return ImmutableSet.of();
+643 }
+644 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission));
+645 }
+646
+647 public boolean requiresPermission(String permission) {
+648 return getRequiredPermissions().contains(permission) || this.parentCommand != null && parentCommand.requiresPermission(permission);
+649 }
+650
+651 public String getName() {
+652 return commandName;
+653 }
+654
+655 public ExceptionHandler getExceptionHandler() {
+656 return exceptionHandler;
+657 }
+658
+659 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) {
+660 this.exceptionHandler = exceptionHandler;
+661 return this;
+662 }
663
-664 CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) {
-665 this.cmd = cmd;
-666 this.argIndex = argIndex;
-667 this.checkSub = checkSub;
-668 }
-669
-670 String getCheckSub() {
-671 return this.checkSub;
-672 }
-673
-674 @Override
-675 public boolean equals(Object o) {
-676 if (this == o) return true;
-677 if (o == null || getClass() != o.getClass()) return false;
-678 CommandSearch that = (CommandSearch) o;
-679 return argIndex == that.argIndex &&
-680 Objects.equals(cmd, that.cmd) &&
-681 Objects.equals(checkSub, that.checkSub);
+664 public RegisteredCommand getDefaultRegisteredCommand() {
+665 return this.getSubcommand(DEFAULT);
+666 }
+667
+668 public String setContextFlags(Class<?> cls, String flags) {
+669 return this.contextFlags.put(cls, flags);
+670 }
+671
+672 public String getContextFlags(Class<?> cls) {
+673 return this.contextFlags.get(cls);
+674 }
+675
+676 private static class CommandSearch { RegisteredCommand cmd; int argIndex; String checkSub;
+677
+678 CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) {
+679 this.cmd = cmd;
+680 this.argIndex = argIndex;
+681 this.checkSub = checkSub;
682 }
683
-684 @Override
-685 public int hashCode() {
-686 return Objects.hash(cmd, argIndex, checkSub);
-687 }
-688 }
-689}
+684 String getCheckSub() {
+685 return this.checkSub;
+686 }
+687
+688 @Override
+689 public boolean equals(Object o) {
+690 if (this == o) return true;
+691 if (o == null || getClass() != o.getClass()) return false;
+692 CommandSearch that = (CommandSearch) o;
+693 return argIndex == that.argIndex &&
+694 Objects.equals(cmd, that.cmd) &&
+695 Objects.equals(checkSub, that.checkSub);
+696 }
+697
+698 @Override
+699 public int hashCode() {
+700 return Objects.hash(cmd, argIndex, checkSub);
+701 }
+702 }
+703}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandConditions.Condition.html b/docs/acf-core/src-html/co/aikar/commands/CommandConditions.Condition.html
index 289a3b0c..6ff13c3a 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandConditions.Condition.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandConditions.Condition.html
@@ -62,96 +62,90 @@
054 return this.paramConditions.put(clazz, id.toLowerCase(), handler);
055 }
056
-057 void validateConditions(CEC cec, Object value) throws InvalidCommandArgument {
-058 Conditions conditions = cec.getParam().getAnnotation(Conditions.class);
-059 validateConditions(conditions, cec, value);
-060 }
-061
-062 void validateConditions(CommandOperationContext context) throws InvalidCommandArgument {
-063 RegisteredCommand cmd = context.getRegisteredCommand();
-064 Conditions conditions = cmd.method.getAnnotation(Conditions.class);
-065 validateConditions(conditions, context);
-066 validateConditions(cmd.scope, context);
-067 }
-068
-069
-070 private void validateConditions(BaseCommand scope, CommandOperationContext operationContext) throws InvalidCommandArgument {
-071 Conditions conditions = scope.getClass().getAnnotation(Conditions.class);
-072 validateConditions(conditions, operationContext);
-073
-074 if (scope.parentCommand != null) {
-075 validateConditions(scope.parentCommand, operationContext);
-076 }
-077 }
-078
-079 private void validateConditions(Conditions condAnno, CommandOperationContext context) throws InvalidCommandArgument {
-080 if (condAnno == null) {
-081 return;
-082 }
-083
-084 String conditions = this.manager.getCommandReplacements().replace(condAnno.value());
-085 CommandIssuer issuer = context.getCommandIssuer();
-086 for (String cond : ACFPatterns.PIPE.split(conditions)) {
-087 String[] split = ACFPatterns.COLON.split(cond, 2);
-088 String id = split[0].toLowerCase();
-089 Condition<I> condition = this.conditions.get(id);
-090 if (condition == null) {
-091 RegisteredCommand cmd = context.getRegisteredCommand();
-092 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName());
-093 continue;
-094 }
-095
-096 String config = split.length == 2 ? split[1] : null;
-097 //noinspection unchecked
-098 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
-099 //noinspection unchecked
-100 condition.validateCondition(conditionContext);
+057 void validateConditions(CommandOperationContext context) throws InvalidCommandArgument {
+058 RegisteredCommand cmd = context.getRegisteredCommand();
+059
+060 validateConditions(cmd.conditions, context);
+061 validateConditions(cmd.scope, context);
+062 }
+063
+064 private void validateConditions(BaseCommand scope, CommandOperationContext operationContext) throws InvalidCommandArgument {
+065 validateConditions(scope.conditions, operationContext);
+066
+067 if (scope.parentCommand != null) {
+068 validateConditions(scope.parentCommand, operationContext);
+069 }
+070 }
+071
+072 private void validateConditions(String conditions, CommandOperationContext context) throws InvalidCommandArgument {
+073 if (conditions == null) {
+074 return;
+075 }
+076
+077 conditions = this.manager.getCommandReplacements().replace(conditions);
+078 CommandIssuer issuer = context.getCommandIssuer();
+079 for (String cond : ACFPatterns.PIPE.split(conditions)) {
+080 String[] split = ACFPatterns.COLON.split(cond, 2);
+081 String id = split[0].toLowerCase();
+082 Condition<I> condition = this.conditions.get(id);
+083 if (condition == null) {
+084 RegisteredCommand cmd = context.getRegisteredCommand();
+085 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName());
+086 continue;
+087 }
+088
+089 String config = split.length == 2 ? split[1] : null;
+090 //noinspection unchecked
+091 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
+092 //noinspection unchecked
+093 condition.validateCondition(conditionContext);
+094 }
+095 }
+096
+097 void validateConditions(CEC execContext, Object value) throws InvalidCommandArgument {
+098 String conditions = execContext.getCommandParameter().getConditions();
+099 if (conditions == null) {
+100 return;
101 }
-102 }
-103
-104 private void validateConditions(Conditions condAnno, CEC execContext, Object value) throws InvalidCommandArgument {
-105 if (condAnno == null) {
-106 return;
-107 }
-108 String conditions = this.manager.getCommandReplacements().replace(condAnno.value());
-109 I issuer = execContext.getIssuer();
-110 for (String cond : ACFPatterns.PIPE.split(conditions)) {
-111 String[] split = ACFPatterns.COLON.split(cond, 2);
-112 ParameterCondition condition;
-113 Class<?> cls = execContext.getParam().getType();
-114 String id = split[0].toLowerCase();
-115 do {
-116 condition = this.paramConditions.get(cls, id);
-117 if (condition == null && cls.getSuperclass() != null && cls.getSuperclass() != Object.class) {
-118 cls = cls.getSuperclass();
-119 } else {
-120 break;
-121 }
-122 } while (cls != null);
-123
-124
-125 if (condition == null) {
-126 RegisteredCommand cmd = execContext.getCmd();
-127 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName() + "::" +execContext.getParam().getName());
-128 continue;
-129 }
-130 String config = split.length == 2 ? split[1] : null;
-131 //noinspection unchecked
-132 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
-133
-134 //noinspection unchecked
-135 condition.validateCondition(conditionContext, execContext, value);
-136 }
-137 }
-138
-139 public interface Condition <I extends CommandIssuer> {
-140 void validateCondition(ConditionContext<I> context) throws InvalidCommandArgument;
-141 }
-142
-143 public interface ParameterCondition <P, CEC extends CommandExecutionContext, I extends CommandIssuer> {
-144 void validateCondition(ConditionContext<I> context, CEC execContext, P value) throws InvalidCommandArgument;
-145 }
-146}
+102 conditions = this.manager.getCommandReplacements().replace(conditions);
+103 I issuer = execContext.getIssuer();
+104 for (String cond : ACFPatterns.PIPE.split(conditions)) {
+105 String[] split = ACFPatterns.COLON.split(cond, 2);
+106 ParameterCondition condition;
+107 Class<?> cls = execContext.getParam().getType();
+108 String id = split[0].toLowerCase();
+109 do {
+110 condition = this.paramConditions.get(cls, id);
+111 if (condition == null && cls.getSuperclass() != null && cls.getSuperclass() != Object.class) {
+112 cls = cls.getSuperclass();
+113 } else {
+114 break;
+115 }
+116 } while (cls != null);
+117
+118
+119 if (condition == null) {
+120 RegisteredCommand cmd = execContext.getCmd();
+121 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName() + "::" +execContext.getParam().getName());
+122 continue;
+123 }
+124 String config = split.length == 2 ? split[1] : null;
+125 //noinspection unchecked
+126 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
+127
+128 //noinspection unchecked
+129 condition.validateCondition(conditionContext, execContext, value);
+130 }
+131 }
+132
+133 public interface Condition <I extends CommandIssuer> {
+134 void validateCondition(ConditionContext<I> context) throws InvalidCommandArgument;
+135 }
+136
+137 public interface ParameterCondition <P, CEC extends CommandExecutionContext, I extends CommandIssuer> {
+138 void validateCondition(ConditionContext<I> context, CEC execContext, P value) throws InvalidCommandArgument;
+139 }
+140}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandConditions.ParameterCondition.html b/docs/acf-core/src-html/co/aikar/commands/CommandConditions.ParameterCondition.html
index 289a3b0c..6ff13c3a 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandConditions.ParameterCondition.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandConditions.ParameterCondition.html
@@ -62,96 +62,90 @@
054 return this.paramConditions.put(clazz, id.toLowerCase(), handler);
055 }
056
-057 void validateConditions(CEC cec, Object value) throws InvalidCommandArgument {
-058 Conditions conditions = cec.getParam().getAnnotation(Conditions.class);
-059 validateConditions(conditions, cec, value);
-060 }
-061
-062 void validateConditions(CommandOperationContext context) throws InvalidCommandArgument {
-063 RegisteredCommand cmd = context.getRegisteredCommand();
-064 Conditions conditions = cmd.method.getAnnotation(Conditions.class);
-065 validateConditions(conditions, context);
-066 validateConditions(cmd.scope, context);
-067 }
-068
-069
-070 private void validateConditions(BaseCommand scope, CommandOperationContext operationContext) throws InvalidCommandArgument {
-071 Conditions conditions = scope.getClass().getAnnotation(Conditions.class);
-072 validateConditions(conditions, operationContext);
-073
-074 if (scope.parentCommand != null) {
-075 validateConditions(scope.parentCommand, operationContext);
-076 }
-077 }
-078
-079 private void validateConditions(Conditions condAnno, CommandOperationContext context) throws InvalidCommandArgument {
-080 if (condAnno == null) {
-081 return;
-082 }
-083
-084 String conditions = this.manager.getCommandReplacements().replace(condAnno.value());
-085 CommandIssuer issuer = context.getCommandIssuer();
-086 for (String cond : ACFPatterns.PIPE.split(conditions)) {
-087 String[] split = ACFPatterns.COLON.split(cond, 2);
-088 String id = split[0].toLowerCase();
-089 Condition<I> condition = this.conditions.get(id);
-090 if (condition == null) {
-091 RegisteredCommand cmd = context.getRegisteredCommand();
-092 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName());
-093 continue;
-094 }
-095
-096 String config = split.length == 2 ? split[1] : null;
-097 //noinspection unchecked
-098 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
-099 //noinspection unchecked
-100 condition.validateCondition(conditionContext);
+057 void validateConditions(CommandOperationContext context) throws InvalidCommandArgument {
+058 RegisteredCommand cmd = context.getRegisteredCommand();
+059
+060 validateConditions(cmd.conditions, context);
+061 validateConditions(cmd.scope, context);
+062 }
+063
+064 private void validateConditions(BaseCommand scope, CommandOperationContext operationContext) throws InvalidCommandArgument {
+065 validateConditions(scope.conditions, operationContext);
+066
+067 if (scope.parentCommand != null) {
+068 validateConditions(scope.parentCommand, operationContext);
+069 }
+070 }
+071
+072 private void validateConditions(String conditions, CommandOperationContext context) throws InvalidCommandArgument {
+073 if (conditions == null) {
+074 return;
+075 }
+076
+077 conditions = this.manager.getCommandReplacements().replace(conditions);
+078 CommandIssuer issuer = context.getCommandIssuer();
+079 for (String cond : ACFPatterns.PIPE.split(conditions)) {
+080 String[] split = ACFPatterns.COLON.split(cond, 2);
+081 String id = split[0].toLowerCase();
+082 Condition<I> condition = this.conditions.get(id);
+083 if (condition == null) {
+084 RegisteredCommand cmd = context.getRegisteredCommand();
+085 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName());
+086 continue;
+087 }
+088
+089 String config = split.length == 2 ? split[1] : null;
+090 //noinspection unchecked
+091 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
+092 //noinspection unchecked
+093 condition.validateCondition(conditionContext);
+094 }
+095 }
+096
+097 void validateConditions(CEC execContext, Object value) throws InvalidCommandArgument {
+098 String conditions = execContext.getCommandParameter().getConditions();
+099 if (conditions == null) {
+100 return;
101 }
-102 }
-103
-104 private void validateConditions(Conditions condAnno, CEC execContext, Object value) throws InvalidCommandArgument {
-105 if (condAnno == null) {
-106 return;
-107 }
-108 String conditions = this.manager.getCommandReplacements().replace(condAnno.value());
-109 I issuer = execContext.getIssuer();
-110 for (String cond : ACFPatterns.PIPE.split(conditions)) {
-111 String[] split = ACFPatterns.COLON.split(cond, 2);
-112 ParameterCondition condition;
-113 Class<?> cls = execContext.getParam().getType();
-114 String id = split[0].toLowerCase();
-115 do {
-116 condition = this.paramConditions.get(cls, id);
-117 if (condition == null && cls.getSuperclass() != null && cls.getSuperclass() != Object.class) {
-118 cls = cls.getSuperclass();
-119 } else {
-120 break;
-121 }
-122 } while (cls != null);
-123
-124
-125 if (condition == null) {
-126 RegisteredCommand cmd = execContext.getCmd();
-127 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName() + "::" +execContext.getParam().getName());
-128 continue;
-129 }
-130 String config = split.length == 2 ? split[1] : null;
-131 //noinspection unchecked
-132 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
-133
-134 //noinspection unchecked
-135 condition.validateCondition(conditionContext, execContext, value);
-136 }
-137 }
-138
-139 public interface Condition <I extends CommandIssuer> {
-140 void validateCondition(ConditionContext<I> context) throws InvalidCommandArgument;
-141 }
-142
-143 public interface ParameterCondition <P, CEC extends CommandExecutionContext, I extends CommandIssuer> {
-144 void validateCondition(ConditionContext<I> context, CEC execContext, P value) throws InvalidCommandArgument;
-145 }
-146}
+102 conditions = this.manager.getCommandReplacements().replace(conditions);
+103 I issuer = execContext.getIssuer();
+104 for (String cond : ACFPatterns.PIPE.split(conditions)) {
+105 String[] split = ACFPatterns.COLON.split(cond, 2);
+106 ParameterCondition condition;
+107 Class<?> cls = execContext.getParam().getType();
+108 String id = split[0].toLowerCase();
+109 do {
+110 condition = this.paramConditions.get(cls, id);
+111 if (condition == null && cls.getSuperclass() != null && cls.getSuperclass() != Object.class) {
+112 cls = cls.getSuperclass();
+113 } else {
+114 break;
+115 }
+116 } while (cls != null);
+117
+118
+119 if (condition == null) {
+120 RegisteredCommand cmd = execContext.getCmd();
+121 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName() + "::" +execContext.getParam().getName());
+122 continue;
+123 }
+124 String config = split.length == 2 ? split[1] : null;
+125 //noinspection unchecked
+126 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
+127
+128 //noinspection unchecked
+129 condition.validateCondition(conditionContext, execContext, value);
+130 }
+131 }
+132
+133 public interface Condition <I extends CommandIssuer> {
+134 void validateCondition(ConditionContext<I> context) throws InvalidCommandArgument;
+135 }
+136
+137 public interface ParameterCondition <P, CEC extends CommandExecutionContext, I extends CommandIssuer> {
+138 void validateCondition(ConditionContext<I> context, CEC execContext, P value) throws InvalidCommandArgument;
+139 }
+140}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandConditions.html b/docs/acf-core/src-html/co/aikar/commands/CommandConditions.html
index 289a3b0c..6ff13c3a 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandConditions.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandConditions.html
@@ -62,96 +62,90 @@
054 return this.paramConditions.put(clazz, id.toLowerCase(), handler);
055 }
056
-057 void validateConditions(CEC cec, Object value) throws InvalidCommandArgument {
-058 Conditions conditions = cec.getParam().getAnnotation(Conditions.class);
-059 validateConditions(conditions, cec, value);
-060 }
-061
-062 void validateConditions(CommandOperationContext context) throws InvalidCommandArgument {
-063 RegisteredCommand cmd = context.getRegisteredCommand();
-064 Conditions conditions = cmd.method.getAnnotation(Conditions.class);
-065 validateConditions(conditions, context);
-066 validateConditions(cmd.scope, context);
-067 }
-068
-069
-070 private void validateConditions(BaseCommand scope, CommandOperationContext operationContext) throws InvalidCommandArgument {
-071 Conditions conditions = scope.getClass().getAnnotation(Conditions.class);
-072 validateConditions(conditions, operationContext);
-073
-074 if (scope.parentCommand != null) {
-075 validateConditions(scope.parentCommand, operationContext);
-076 }
-077 }
-078
-079 private void validateConditions(Conditions condAnno, CommandOperationContext context) throws InvalidCommandArgument {
-080 if (condAnno == null) {
-081 return;
-082 }
-083
-084 String conditions = this.manager.getCommandReplacements().replace(condAnno.value());
-085 CommandIssuer issuer = context.getCommandIssuer();
-086 for (String cond : ACFPatterns.PIPE.split(conditions)) {
-087 String[] split = ACFPatterns.COLON.split(cond, 2);
-088 String id = split[0].toLowerCase();
-089 Condition<I> condition = this.conditions.get(id);
-090 if (condition == null) {
-091 RegisteredCommand cmd = context.getRegisteredCommand();
-092 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName());
-093 continue;
-094 }
-095
-096 String config = split.length == 2 ? split[1] : null;
-097 //noinspection unchecked
-098 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
-099 //noinspection unchecked
-100 condition.validateCondition(conditionContext);
+057 void validateConditions(CommandOperationContext context) throws InvalidCommandArgument {
+058 RegisteredCommand cmd = context.getRegisteredCommand();
+059
+060 validateConditions(cmd.conditions, context);
+061 validateConditions(cmd.scope, context);
+062 }
+063
+064 private void validateConditions(BaseCommand scope, CommandOperationContext operationContext) throws InvalidCommandArgument {
+065 validateConditions(scope.conditions, operationContext);
+066
+067 if (scope.parentCommand != null) {
+068 validateConditions(scope.parentCommand, operationContext);
+069 }
+070 }
+071
+072 private void validateConditions(String conditions, CommandOperationContext context) throws InvalidCommandArgument {
+073 if (conditions == null) {
+074 return;
+075 }
+076
+077 conditions = this.manager.getCommandReplacements().replace(conditions);
+078 CommandIssuer issuer = context.getCommandIssuer();
+079 for (String cond : ACFPatterns.PIPE.split(conditions)) {
+080 String[] split = ACFPatterns.COLON.split(cond, 2);
+081 String id = split[0].toLowerCase();
+082 Condition<I> condition = this.conditions.get(id);
+083 if (condition == null) {
+084 RegisteredCommand cmd = context.getRegisteredCommand();
+085 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName());
+086 continue;
+087 }
+088
+089 String config = split.length == 2 ? split[1] : null;
+090 //noinspection unchecked
+091 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
+092 //noinspection unchecked
+093 condition.validateCondition(conditionContext);
+094 }
+095 }
+096
+097 void validateConditions(CEC execContext, Object value) throws InvalidCommandArgument {
+098 String conditions = execContext.getCommandParameter().getConditions();
+099 if (conditions == null) {
+100 return;
101 }
-102 }
-103
-104 private void validateConditions(Conditions condAnno, CEC execContext, Object value) throws InvalidCommandArgument {
-105 if (condAnno == null) {
-106 return;
-107 }
-108 String conditions = this.manager.getCommandReplacements().replace(condAnno.value());
-109 I issuer = execContext.getIssuer();
-110 for (String cond : ACFPatterns.PIPE.split(conditions)) {
-111 String[] split = ACFPatterns.COLON.split(cond, 2);
-112 ParameterCondition condition;
-113 Class<?> cls = execContext.getParam().getType();
-114 String id = split[0].toLowerCase();
-115 do {
-116 condition = this.paramConditions.get(cls, id);
-117 if (condition == null && cls.getSuperclass() != null && cls.getSuperclass() != Object.class) {
-118 cls = cls.getSuperclass();
-119 } else {
-120 break;
-121 }
-122 } while (cls != null);
-123
-124
-125 if (condition == null) {
-126 RegisteredCommand cmd = execContext.getCmd();
-127 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName() + "::" +execContext.getParam().getName());
-128 continue;
-129 }
-130 String config = split.length == 2 ? split[1] : null;
-131 //noinspection unchecked
-132 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
-133
-134 //noinspection unchecked
-135 condition.validateCondition(conditionContext, execContext, value);
-136 }
-137 }
-138
-139 public interface Condition <I extends CommandIssuer> {
-140 void validateCondition(ConditionContext<I> context) throws InvalidCommandArgument;
-141 }
-142
-143 public interface ParameterCondition <P, CEC extends CommandExecutionContext, I extends CommandIssuer> {
-144 void validateCondition(ConditionContext<I> context, CEC execContext, P value) throws InvalidCommandArgument;
-145 }
-146}
+102 conditions = this.manager.getCommandReplacements().replace(conditions);
+103 I issuer = execContext.getIssuer();
+104 for (String cond : ACFPatterns.PIPE.split(conditions)) {
+105 String[] split = ACFPatterns.COLON.split(cond, 2);
+106 ParameterCondition condition;
+107 Class<?> cls = execContext.getParam().getType();
+108 String id = split[0].toLowerCase();
+109 do {
+110 condition = this.paramConditions.get(cls, id);
+111 if (condition == null && cls.getSuperclass() != null && cls.getSuperclass() != Object.class) {
+112 cls = cls.getSuperclass();
+113 } else {
+114 break;
+115 }
+116 } while (cls != null);
+117
+118
+119 if (condition == null) {
+120 RegisteredCommand cmd = execContext.getCmd();
+121 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName() + "::" +execContext.getParam().getName());
+122 continue;
+123 }
+124 String config = split.length == 2 ? split[1] : null;
+125 //noinspection unchecked
+126 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
+127
+128 //noinspection unchecked
+129 condition.validateCondition(conditionContext, execContext, value);
+130 }
+131 }
+132
+133 public interface Condition <I extends CommandIssuer> {
+134 void validateCondition(ConditionContext<I> context) throws InvalidCommandArgument;
+135 }
+136
+137 public interface ParameterCondition <P, CEC extends CommandExecutionContext, I extends CommandIssuer> {
+138 void validateCondition(ConditionContext<I> context, CEC execContext, P value) throws InvalidCommandArgument;
+139 }
+140}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandContexts.html b/docs/acf-core/src-html/co/aikar/commands/CommandContexts.html
index 428bfac0..5f696747 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandContexts.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandContexts.html
@@ -158,11 +158,11 @@
150 return s.charAt(0);
151 });
152 registerContext(String.class, (c) -> {
-153 final Values values = c.getParam().getAnnotation(Values.class);
-154 if (values != null) {
+153 // This will fail fast, its either in the values or its not
+154 if (c.hasAnnotation(Values.class)) {
155 return c.popFirstArg();
156 }
-157 String ret = (c.isLastArg() && c.getParam().getAnnotation(Single.class) == null) ?
+157 String ret = (c.isLastArg() && !c.hasAnnotation(Single.class)) ?
158 ACFUtil.join(c.getArgs())
159 :
160 c.popFirstArg();
@@ -187,17 +187,17 @@
179 // Go home IDEA, you're drunk
180 //noinspection unchecked
181 List<String> args = c.getArgs();
-182 if (c.isLastArg() && c.getParam().getAnnotation(Single.class) == null) {
+182 if (c.isLastArg() && !c.hasAnnotation(Single.class)) {
183 val = ACFUtil.join(args);
184 } else {
185 val = c.popFirstArg();
186 }
-187 Split split = c.getParam().getAnnotation(Split.class);
+187 String split = c.getAnnotationValue(Split.class, Annotations.NOTHING | Annotations.NO_EMPTY);
188 if (split != null) {
189 if (val.isEmpty()) {
190 throw new InvalidCommandArgument();
191 }
-192 return ACFPatterns.getPattern(split.value()).split(val);
+192 return ACFPatterns.getPattern(split).split(val);
193 } else if (!c.isLastArg()) {
194 ACFUtil.sneaky(new IllegalStateException("Weird Command signature... String[] should be last or @Split"));
195 }
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html b/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html
index 1eb64bda..e6a1d341 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html
@@ -178,49 +178,68 @@
170 return ACFUtil.parseDouble(this.flags.get(flag), def != null ? def.doubleValue() : null);
171 }
172
-173 public <T extends Annotation> T getAnnotation(Class<T> cls) {
-174 return param.getParameter().getAnnotation(cls);
-175 }
-176
-177 public <T extends Annotation> boolean hasAnnotation(Class<T> cls) {
-178 return param.getParameter().isAnnotationPresent(cls);
-179 }
-180
-181 public RegisteredCommand getCmd() {
-182 return this.cmd;
-183 }
-184
-185 public Parameter getParam() {
-186 return this.param.getParameter();
-187 }
-188
-189 public I getIssuer() {
-190 return this.issuer;
-191 }
-192
-193 public List<String> getArgs() {
-194 return this.args;
-195 }
-196
-197 public int getIndex() {
-198 return this.index;
-199 }
-200
-201 public Map<String, Object> getPassedArgs() {
-202 return this.passedArgs;
-203 }
-204
-205 public Map<String, String> getFlags() {
-206 return this.flags;
-207 }
-208
-209 public String joinArgs() {
-210 return ACFUtil.join(args, " ");
-211 }
-212 public String joinArgs(String sep) {
-213 return ACFUtil.join(args, sep);
+173 /**
+174 * This method will not support annotation processors!! use getAnnotationValue or hasAnnotation
+175 * @deprecated Use {@link #getAnnotationValue(Class)}
+176 */
+177 @Deprecated
+178 public <T extends Annotation> T getAnnotation(Class<T> cls) {
+179 return param.getParameter().getAnnotation(cls);
+180 }
+181
+182 public <T extends Annotation> String getAnnotationValue(Class<T> cls) {
+183 return manager.getAnnotations().getAnnotationValue(param.getParameter(), cls);
+184 }
+185
+186 public <T extends Annotation> String getAnnotationValue(Class<T> cls, int options) {
+187 return manager.getAnnotations().getAnnotationValue(param.getParameter(), cls, options);
+188 }
+189
+190 public <T extends Annotation> boolean hasAnnotation(Class<T> cls) {
+191 return manager.getAnnotations().hasAnnotation(param.getParameter(), cls);
+192 }
+193
+194 public RegisteredCommand getCmd() {
+195 return this.cmd;
+196 }
+197
+198 @UnstableAPI
+199 CommandParameter getCommandParameter() {
+200 return this.param;
+201 }
+202
+203 @Deprecated
+204 public Parameter getParam() {
+205 return this.param.getParameter();
+206 }
+207
+208 public I getIssuer() {
+209 return this.issuer;
+210 }
+211
+212 public List<String> getArgs() {
+213 return this.args;
214 }
-215}
+215
+216 public int getIndex() {
+217 return this.index;
+218 }
+219
+220 public Map<String, Object> getPassedArgs() {
+221 return this.passedArgs;
+222 }
+223
+224 public Map<String, String> getFlags() {
+225 return this.flags;
+226 }
+227
+228 public String joinArgs() {
+229 return ACFUtil.join(args, " ");
+230 }
+231 public String joinArgs(String sep) {
+232 return ACFUtil.join(args, sep);
+233 }
+234}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandManager.html b/docs/acf-core/src-html/co/aikar/commands/CommandManager.html
index dc79274a..32c77313 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandManager.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandManager.html
@@ -32,469 +32,477 @@
024package co.aikar.commands;
025
026import co.aikar.commands.annotation.Dependency;
-027import co.aikar.locales.MessageKeyProvider;
-028import com.google.common.collect.HashBasedTable;
-029import com.google.common.collect.Lists;
-030import com.google.common.collect.Maps;
-031import com.google.common.collect.Sets;
-032import com.google.common.collect.Table;
-033import org.jetbrains.annotations.NotNull;
-034
-035import java.lang.reflect.Field;
-036import java.lang.reflect.InvocationTargetException;
-037import java.lang.reflect.Method;
-038import java.util.HashMap;
-039import java.util.IdentityHashMap;
-040import java.util.List;
-041import java.util.Locale;
-042import java.util.Map;
-043import java.util.Objects;
-044import java.util.Set;
-045import java.util.Stack;
-046import java.util.UUID;
-047
-048
-049@SuppressWarnings("WeakerAccess")
-050public abstract class CommandManager <
-051 IT,
-052 I extends CommandIssuer,
-053 FT,
-054 MF extends MessageFormatter<FT>,
-055 CEC extends CommandExecutionContext<CEC, I>,
-056 CC extends ConditionContext<I>
-057 > {
-058
-059 /**
-060 * This is a stack incase a command calls a command
-061 */
-062 static ThreadLocal<Stack<CommandOperationContext>> commandOperationContext = ThreadLocal.withInitial(() -> new Stack<CommandOperationContext>() {
-063 @Override
-064 public synchronized CommandOperationContext peek() {
-065 return super.size() == 0 ? null : super.peek();
-066 }
-067 });
-068 protected Map<String, RootCommand> rootCommands = new HashMap<>();
-069 protected final CommandReplacements replacements = new CommandReplacements(this);
-070 protected final CommandConditions<I, CEC, CC> conditions = new CommandConditions<>(this);
-071 protected ExceptionHandler defaultExceptionHandler = null;
-072 protected Table<Class<?>, String, Object> dependencies = HashBasedTable.create();
-073
-074 protected boolean usePerIssuerLocale = false;
-075 protected List<IssuerLocaleChangedCallback<I>> localeChangedCallbacks = Lists.newArrayList();
-076 protected Set<Locale> supportedLanguages = Sets.newHashSet(Locales.ENGLISH, Locales.GERMAN, Locales.SPANISH, Locales.CZECH, Locales.PORTUGUESE);
-077 protected Map<MessageType, MF> formatters = new IdentityHashMap<>();
-078 protected MF defaultFormatter;
-079 protected int defaultHelpPerPage = 10;
-080
-081 protected Map<UUID, Locale> issuersLocale = Maps.newConcurrentMap();
+027import co.aikar.commands.annotation.HelpCommand;
+028import co.aikar.locales.MessageKeyProvider;
+029import com.google.common.collect.HashBasedTable;
+030import com.google.common.collect.Lists;
+031import com.google.common.collect.Maps;
+032import com.google.common.collect.Sets;
+033import com.google.common.collect.Table;
+034import org.jetbrains.annotations.NotNull;
+035
+036import java.lang.annotation.Annotation;
+037import java.lang.reflect.Field;
+038import java.lang.reflect.InvocationTargetException;
+039import java.lang.reflect.Method;
+040import java.util.HashMap;
+041import java.util.IdentityHashMap;
+042import java.util.List;
+043import java.util.Locale;
+044import java.util.Map;
+045import java.util.Objects;
+046import java.util.Set;
+047import java.util.Stack;
+048import java.util.UUID;
+049
+050
+051@SuppressWarnings("WeakerAccess")
+052public abstract class CommandManager <
+053 IT,
+054 I extends CommandIssuer,
+055 FT,
+056 MF extends MessageFormatter<FT>,
+057 CEC extends CommandExecutionContext<CEC, I>,
+058 CC extends ConditionContext<I>
+059 > {
+060
+061 /**
+062 * This is a stack incase a command calls a command
+063 */
+064 static ThreadLocal<Stack<CommandOperationContext>> commandOperationContext = ThreadLocal.withInitial(() -> new Stack<CommandOperationContext>() {
+065 @Override
+066 public synchronized CommandOperationContext peek() {
+067 return super.size() == 0 ? null : super.peek();
+068 }
+069 });
+070 protected Map<String, RootCommand> rootCommands = new HashMap<>();
+071 protected final CommandReplacements replacements = new CommandReplacements(this);
+072 protected final CommandConditions<I, CEC, CC> conditions = new CommandConditions<>(this);
+073 protected ExceptionHandler defaultExceptionHandler = null;
+074 protected Table<Class<?>, String, Object> dependencies = HashBasedTable.create();
+075
+076 protected boolean usePerIssuerLocale = false;
+077 protected List<IssuerLocaleChangedCallback<I>> localeChangedCallbacks = Lists.newArrayList();
+078 protected Set<Locale> supportedLanguages = Sets.newHashSet(Locales.ENGLISH, Locales.GERMAN, Locales.SPANISH, Locales.CZECH, Locales.PORTUGUESE);
+079 protected Map<MessageType, MF> formatters = new IdentityHashMap<>();
+080 protected MF defaultFormatter;
+081 protected int defaultHelpPerPage = 10;
082
-083 private Set<String> unstableAPIs = Sets.newHashSet();
+083 protected Map<UUID, Locale> issuersLocale = Maps.newConcurrentMap();
084
-085 public static CommandOperationContext getCurrentCommandOperationContext() {
-086 return commandOperationContext.get().peek();
-087 }
+085 private Set<String> unstableAPIs = Sets.newHashSet();
+086
+087 private Annotations annotations = new Annotations<>(this);
088
-089 public static CommandIssuer getCurrentCommandIssuer() {
-090 CommandOperationContext context = commandOperationContext.get().peek();
-091 return context != null ? context.getCommandIssuer() : null;
-092 }
-093
-094 public static CommandManager getCurrentCommandManager() {
-095 CommandOperationContext context = commandOperationContext.get().peek();
-096 return context != null ? context.getCommandManager() : null;
-097 }
-098
-099 public MF setFormat(MessageType type, MF formatter) {
-100 return formatters.put(type, formatter);
+089 public static CommandOperationContext getCurrentCommandOperationContext() {
+090 return commandOperationContext.get().peek();
+091 }
+092
+093 public static CommandIssuer getCurrentCommandIssuer() {
+094 CommandOperationContext context = commandOperationContext.get().peek();
+095 return context != null ? context.getCommandIssuer() : null;
+096 }
+097
+098 public static CommandManager getCurrentCommandManager() {
+099 CommandOperationContext context = commandOperationContext.get().peek();
+100 return context != null ? context.getCommandManager() : null;
101 }
102
-103 public MF getFormat(MessageType type) {
-104 return formatters.getOrDefault(type, defaultFormatter);
+103 public MF setFormat(MessageType type, MF formatter) {
+104 return formatters.put(type, formatter);
105 }
106
-107 public void setFormat(MessageType type, FT... colors) {
-108 MF format = getFormat(type);
-109 for (int i = 1; i <= colors.length; i++) {
-110 format.setColor(i, colors[i-1]);
-111 }
-112 }
-113
-114 public void setFormat(MessageType type, int i, FT color) {
-115 MF format = getFormat(type);
-116 format.setColor(i, color);
-117 }
-118
-119 public MF getDefaultFormatter() {
-120 return defaultFormatter;
+107 public MF getFormat(MessageType type) {
+108 return formatters.getOrDefault(type, defaultFormatter);
+109 }
+110
+111 public void setFormat(MessageType type, FT... colors) {
+112 MF format = getFormat(type);
+113 for (int i = 1; i <= colors.length; i++) {
+114 format.setColor(i, colors[i-1]);
+115 }
+116 }
+117
+118 public void setFormat(MessageType type, int i, FT color) {
+119 MF format = getFormat(type);
+120 format.setColor(i, color);
121 }
122
-123 public void setDefaultFormatter(MF defaultFormatter) {
-124 this.defaultFormatter = defaultFormatter;
+123 public MF getDefaultFormatter() {
+124 return defaultFormatter;
125 }
126
-127 public CommandConditions<I, CEC, CC> getCommandConditions() {
-128 return conditions;
+127 public void setDefaultFormatter(MF defaultFormatter) {
+128 this.defaultFormatter = defaultFormatter;
129 }
130
-131 /**
-132 * Gets the command contexts manager
-133 * @return Command Contexts
-134 */
-135 public abstract CommandContexts<?> getCommandContexts();
-136
-137 /**
-138 * Gets the command completions manager
-139 * @return Command Completions
-140 */
-141 public abstract CommandCompletions<?> getCommandCompletions();
-142
-143 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-144 public CommandHelp generateCommandHelp(@NotNull String command) {
-145 verifyUnstableAPI("help");
-146 CommandOperationContext context = getCurrentCommandOperationContext();
-147 if (context == null) {
-148 throw new IllegalStateException("This method can only be called as part of a command execution.");
-149 }
-150 return generateCommandHelp(context.getCommandIssuer(), command);
-151 }
-152
-153 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-154 public CommandHelp generateCommandHelp(CommandIssuer issuer, @NotNull String command) {
-155 verifyUnstableAPI("help");
-156 return generateCommandHelp(issuer, obtainRootCommand(command));
-157 }
-158
-159 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-160 public CommandHelp generateCommandHelp() {
-161 verifyUnstableAPI("help");
-162 CommandOperationContext context = getCurrentCommandOperationContext();
-163 if (context == null) {
-164 throw new IllegalStateException("This method can only be called as part of a command execution.");
-165 }
-166 String commandLabel = context.getCommandLabel();
-167 return generateCommandHelp(context.getCommandIssuer(), this.obtainRootCommand(commandLabel));
-168 }
-169
-170 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-171 public CommandHelp generateCommandHelp(CommandIssuer issuer, RootCommand rootCommand) {
-172 verifyUnstableAPI("help");
-173 return new CommandHelp(this, rootCommand, issuer);
-174 }
-175
-176 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-177 public int getDefaultHelpPerPage() {
-178 verifyUnstableAPI("help");
-179 return defaultHelpPerPage;
-180 }
-181
-182 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-183 public void setDefaultHelpPerPage(int defaultHelpPerPage) {
-184 verifyUnstableAPI("help");
-185 this.defaultHelpPerPage = defaultHelpPerPage;
-186 }
-187
-188 /**
-189 * Registers a command with ACF
-190 *
-191 * @param command The command to register
-192 * @return boolean
-193 */
-194 public abstract void registerCommand(BaseCommand command);
-195 public abstract boolean hasRegisteredCommands();
-196 public abstract boolean isCommandIssuer(Class<?> type);
-197
-198 // TODO: Change this to IT if we make a breaking change
-199 public abstract I getCommandIssuer(Object issuer);
-200
-201 public abstract RootCommand createRootCommand(String cmd);
-202
-203 /**
-204 * Returns a Locales Manager to add and modify language tables for your commands.
-205 * @return
-206 */
-207 public abstract Locales getLocales();
-208
-209 public boolean usingPerIssuerLocale() {
-210 return usePerIssuerLocale;
-211 }
+131 public CommandConditions<I, CEC, CC> getCommandConditions() {
+132 return conditions;
+133 }
+134
+135 /**
+136 * Gets the command contexts manager
+137 * @return Command Contexts
+138 */
+139 public abstract CommandContexts<?> getCommandContexts();
+140
+141 /**
+142 * Gets the command completions manager
+143 * @return Command Completions
+144 */
+145 public abstract CommandCompletions<?> getCommandCompletions();
+146
+147 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
+148 public CommandHelp generateCommandHelp(@NotNull String command) {
+149 verifyUnstableAPI("help");
+150 CommandOperationContext context = getCurrentCommandOperationContext();
+151 if (context == null) {
+152 throw new IllegalStateException("This method can only be called as part of a command execution.");
+153 }
+154 return generateCommandHelp(context.getCommandIssuer(), command);
+155 }
+156
+157 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
+158 public CommandHelp generateCommandHelp(CommandIssuer issuer, @NotNull String command) {
+159 verifyUnstableAPI("help");
+160 return generateCommandHelp(issuer, obtainRootCommand(command));
+161 }
+162
+163 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
+164 public CommandHelp generateCommandHelp() {
+165 verifyUnstableAPI("help");
+166 CommandOperationContext context = getCurrentCommandOperationContext();
+167 if (context == null) {
+168 throw new IllegalStateException("This method can only be called as part of a command execution.");
+169 }
+170 String commandLabel = context.getCommandLabel();
+171 return generateCommandHelp(context.getCommandIssuer(), this.obtainRootCommand(commandLabel));
+172 }
+173
+174 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
+175 public CommandHelp generateCommandHelp(CommandIssuer issuer, RootCommand rootCommand) {
+176 verifyUnstableAPI("help");
+177 return new CommandHelp(this, rootCommand, issuer);
+178 }
+179
+180 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
+181 public int getDefaultHelpPerPage() {
+182 verifyUnstableAPI("help");
+183 return defaultHelpPerPage;
+184 }
+185
+186 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
+187 public void setDefaultHelpPerPage(int defaultHelpPerPage) {
+188 verifyUnstableAPI("help");
+189 this.defaultHelpPerPage = defaultHelpPerPage;
+190 }
+191
+192 /**
+193 * Registers a command with ACF
+194 *
+195 * @param command The command to register
+196 * @return boolean
+197 */
+198 public abstract void registerCommand(BaseCommand command);
+199 public abstract boolean hasRegisteredCommands();
+200 public abstract boolean isCommandIssuer(Class<?> type);
+201
+202 // TODO: Change this to IT if we make a breaking change
+203 public abstract I getCommandIssuer(Object issuer);
+204
+205 public abstract RootCommand createRootCommand(String cmd);
+206
+207 /**
+208 * Returns a Locales Manager to add and modify language tables for your commands.
+209 * @return
+210 */
+211 public abstract Locales getLocales();
212
-213 public boolean usePerIssuerLocale(boolean setting) {
-214 boolean old = usePerIssuerLocale;
-215 usePerIssuerLocale = setting;
-216 return old;
-217 }
-218
-219 public ConditionContext createConditionContext(CommandIssuer issuer, String config) {
-220 //noinspection unchecked
-221 return new ConditionContext(issuer, config);
-222 }
-223
-224 public abstract CommandExecutionContext createCommandContext(RegisteredCommand command, CommandParameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs);
-225
-226 public abstract CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args);
+213 public boolean usingPerIssuerLocale() {
+214 return usePerIssuerLocale;
+215 }
+216
+217 public boolean usePerIssuerLocale(boolean setting) {
+218 boolean old = usePerIssuerLocale;
+219 usePerIssuerLocale = setting;
+220 return old;
+221 }
+222
+223 public ConditionContext createConditionContext(CommandIssuer issuer, String config) {
+224 //noinspection unchecked
+225 return new ConditionContext(issuer, config);
+226 }
227
-228 public abstract void log(final LogLevel level, final String message, final Throwable throwable);
+228 public abstract CommandExecutionContext createCommandContext(RegisteredCommand command, CommandParameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs);
229
-230 public void log(final LogLevel level, final String message) {
-231 log(level, message, null);
-232 }
+230 public abstract CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args);
+231
+232 public abstract void log(final LogLevel level, final String message, final Throwable throwable);
233
-234 /**
-235 * Lets you add custom string replacements that can be applied to annotation values,
-236 * to reduce duplication/repetition of common values such as permission nodes and command prefixes.
-237 *
-238 * Any replacement registered starts with a %
-239 *
-240 * So for ex @CommandPermission("%staff")
-241 * @return Replacements Manager
-242 */
-243 public CommandReplacements getCommandReplacements() {
-244 return replacements;
-245 }
-246
-247 public boolean hasPermission(CommandIssuer issuer, String permission) {
-248 if (permission == null || permission.isEmpty()) {
-249 return true;
-250 }
-251 for (String perm : ACFPatterns.COMMA.split(permission)) {
-252 if (!perm.isEmpty() && !issuer.hasPermission(perm)) {
-253 return false;
-254 }
-255 }
-256 return true;
-257 }
-258
-259 BaseCommand getBaseCommand(String commandLabel, @NotNull String[] args) {
-260 RootCommand rootCommand = obtainRootCommand(commandLabel);
-261 if (rootCommand == null) {
-262 return null;
-263 }
-264 return rootCommand.getBaseCommand(args);
-265 }
-266
-267 public synchronized RootCommand getRootCommand(@NotNull String cmd) {
-268 return rootCommands.get(ACFPatterns.SPACE.split(cmd.toLowerCase(), 2)[0]);
+234 public void log(final LogLevel level, final String message) {
+235 log(level, message, null);
+236 }
+237
+238 /**
+239 * Lets you add custom string replacements that can be applied to annotation values,
+240 * to reduce duplication/repetition of common values such as permission nodes and command prefixes.
+241 *
+242 * Any replacement registered starts with a %
+243 *
+244 * So for ex @CommandPermission("%staff")
+245 * @return Replacements Manager
+246 */
+247 public CommandReplacements getCommandReplacements() {
+248 return replacements;
+249 }
+250
+251 public boolean hasPermission(CommandIssuer issuer, String permission) {
+252 if (permission == null || permission.isEmpty()) {
+253 return true;
+254 }
+255 for (String perm : ACFPatterns.COMMA.split(permission)) {
+256 if (!perm.isEmpty() && !issuer.hasPermission(perm)) {
+257 return false;
+258 }
+259 }
+260 return true;
+261 }
+262
+263 BaseCommand getBaseCommand(String commandLabel, @NotNull String[] args) {
+264 RootCommand rootCommand = obtainRootCommand(commandLabel);
+265 if (rootCommand == null) {
+266 return null;
+267 }
+268 return rootCommand.getBaseCommand(args);
269 }
270
-271 public synchronized RootCommand obtainRootCommand(@NotNull String cmd) {
-272 return rootCommands.computeIfAbsent(ACFPatterns.SPACE.split(cmd.toLowerCase(), 2)[0], this::createRootCommand);
+271 public synchronized RootCommand getRootCommand(@NotNull String cmd) {
+272 return rootCommands.get(ACFPatterns.SPACE.split(cmd.toLowerCase(), 2)[0]);
273 }
274
-275 public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) {
-276 return new RegisteredCommand(command, cmdName, method, prefSubCommand);
+275 public synchronized RootCommand obtainRootCommand(@NotNull String cmd) {
+276 return rootCommands.computeIfAbsent(ACFPatterns.SPACE.split(cmd.toLowerCase(), 2)[0], this::createRootCommand);
277 }
278
-279 /**
-280 * Sets the default {@link ExceptionHandler} that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered.
-281 *
-282 * @param exceptionHandler the handler that should handle uncaught exceptions
-283 */
-284 public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler) {
-285 defaultExceptionHandler = exceptionHandler;
-286 }
-287
-288 /**
-289 * Gets the current default exception handler, might be null.
-290 *
-291 * @return the default exception handler
-292 */
-293 public ExceptionHandler getDefaultExceptionHandler() {
-294 return defaultExceptionHandler;
-295 }
-296
-297 protected boolean handleUncaughtException(BaseCommand scope, RegisteredCommand registeredCommand, CommandIssuer sender, List<String> args, Throwable t) {
-298 if (t instanceof InvocationTargetException && t.getCause() != null) {
-299 t = t.getCause();
-300 }
-301 boolean result = false;
-302 if (scope.getExceptionHandler() != null) {
-303 result = scope.getExceptionHandler().execute(scope, registeredCommand, sender, args, t);
-304 } else if (defaultExceptionHandler != null) {
-305 result = defaultExceptionHandler.execute(scope, registeredCommand, sender, args, t);
-306 }
-307 return result;
-308 }
-309
-310 public void sendMessage(IT issuerArg, MessageType type, MessageKeyProvider key, String... replacements) {
-311 sendMessage(getCommandIssuer(issuerArg), type, key, replacements);
+279 public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) {
+280 return new RegisteredCommand(command, cmdName, method, prefSubCommand);
+281 }
+282
+283 /**
+284 * Sets the default {@link ExceptionHandler} that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered.
+285 *
+286 * @param exceptionHandler the handler that should handle uncaught exceptions
+287 */
+288 public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler) {
+289 defaultExceptionHandler = exceptionHandler;
+290 }
+291
+292 /**
+293 * Gets the current default exception handler, might be null.
+294 *
+295 * @return the default exception handler
+296 */
+297 public ExceptionHandler getDefaultExceptionHandler() {
+298 return defaultExceptionHandler;
+299 }
+300
+301 protected boolean handleUncaughtException(BaseCommand scope, RegisteredCommand registeredCommand, CommandIssuer sender, List<String> args, Throwable t) {
+302 if (t instanceof InvocationTargetException && t.getCause() != null) {
+303 t = t.getCause();
+304 }
+305 boolean result = false;
+306 if (scope.getExceptionHandler() != null) {
+307 result = scope.getExceptionHandler().execute(scope, registeredCommand, sender, args, t);
+308 } else if (defaultExceptionHandler != null) {
+309 result = defaultExceptionHandler.execute(scope, registeredCommand, sender, args, t);
+310 }
+311 return result;
312 }
313
-314 public void sendMessage(CommandIssuer issuer, MessageType type, MessageKeyProvider key, String... replacements) {
-315 String message = formatMessage(issuer, type, key, replacements);
-316
-317 for (String msg : ACFPatterns.NEWLINE.split(message)) {
-318 issuer.sendMessageInternal(ACFUtil.rtrim(msg));
-319 }
-320 }
-321
-322 public String formatMessage(CommandIssuer issuer, MessageType type, MessageKeyProvider key, String... replacements) {
-323 String message = getLocales().getMessage(issuer, key.getMessageKey());
-324 if (replacements.length > 0) {
-325 message = ACFUtil.replaceStrings(message, replacements);
-326 }
-327
-328 message = getCommandReplacements().replace(message);
-329
-330 MessageFormatter formatter = formatters.getOrDefault(type, defaultFormatter);
-331 if (formatter != null) {
-332 message = formatter.format(message);
-333 }
-334 return message;
-335 }
-336
-337 public void onLocaleChange(IssuerLocaleChangedCallback<I> onChange) {
-338 localeChangedCallbacks.add(onChange);
+314 public void sendMessage(IT issuerArg, MessageType type, MessageKeyProvider key, String... replacements) {
+315 sendMessage(getCommandIssuer(issuerArg), type, key, replacements);
+316 }
+317
+318 public void sendMessage(CommandIssuer issuer, MessageType type, MessageKeyProvider key, String... replacements) {
+319 String message = formatMessage(issuer, type, key, replacements);
+320
+321 for (String msg : ACFPatterns.NEWLINE.split(message)) {
+322 issuer.sendMessageInternal(ACFUtil.rtrim(msg));
+323 }
+324 }
+325
+326 public String formatMessage(CommandIssuer issuer, MessageType type, MessageKeyProvider key, String... replacements) {
+327 String message = getLocales().getMessage(issuer, key.getMessageKey());
+328 if (replacements.length > 0) {
+329 message = ACFUtil.replaceStrings(message, replacements);
+330 }
+331
+332 message = getCommandReplacements().replace(message);
+333
+334 MessageFormatter formatter = formatters.getOrDefault(type, defaultFormatter);
+335 if (formatter != null) {
+336 message = formatter.format(message);
+337 }
+338 return message;
339 }
340
-341 public void notifyLocaleChange(I issuer, Locale oldLocale, Locale newLocale) {
-342 localeChangedCallbacks.forEach(cb -> {
-343 try {
-344 cb.onIssuerLocaleChange(issuer, oldLocale, newLocale);
-345 } catch (Exception e) {
-346 this.log(LogLevel.ERROR, "Error in notifyLocaleChange", e);
-347 }
-348 });
-349 }
-350
-351 public Locale setIssuerLocale(IT issuer, Locale locale) {
-352 I commandIssuer = getCommandIssuer(issuer);
-353
-354 Locale old = issuersLocale.put(commandIssuer.getUniqueId(), locale);
-355 if (!Objects.equals(old, locale)) {
-356 this.notifyLocaleChange(commandIssuer, old, locale);
-357 }
-358
-359 return old;
-360 }
-361
-362 public Locale getIssuerLocale(CommandIssuer issuer) {
-363 if (usingPerIssuerLocale()) {
-364 Locale locale = issuersLocale.get(issuer.getUniqueId());
-365 if (locale != null) {
-366 return locale;
-367 }
-368 }
-369
-370 return getLocales().getDefaultLocale();
-371 }
-372
-373 CommandOperationContext<I> createCommandOperationContext(BaseCommand command, CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
-374 //noinspection unchecked
-375 return new CommandOperationContext<>(
-376 this,
-377 (I) issuer,
-378 command,
-379 commandLabel,
-380 args,
-381 isAsync
-382 );
-383 }
-384
-385 /**
-386 * Gets a list of all currently supported languages for this manager.
-387 * These locales will be automatically loaded from
-388 * @return
-389 */
-390 public Set<Locale> getSupportedLanguages() {
-391 return supportedLanguages;
-392 }
-393
-394 /**
-395 * Adds a new locale to the list of automatic Locales to load Message Bundles for.
-396 * All bundles loaded under the previous supported languages will now automatically load for this new locale too.
-397 *
-398 * @param locale
-399 */
-400 public void addSupportedLanguage(Locale locale) {
-401 supportedLanguages.add(locale);
-402 getLocales().loadMissingBundles();
-403 }
-404
-405 /**
-406 * Registers an instance of a class to be registered as an injectable dependency.<br>
-407 * The command manager will attempt to inject all fields in a command class that are annotated with
-408 * {@link co.aikar.commands.annotation.Dependency} with the provided instance.
-409 *
-410 * @param clazz the class the injector should look for when injecting
-411 * @param instance the instance of the class that should be injected
-412 * @throws IllegalStateException when there is already an instance for the provided class registered
-413 */
-414 public <T> void registerDependency(Class<? extends T> clazz, T instance){
-415 registerDependency(clazz, clazz.getName(), instance);
-416 }
-417
-418 /**
-419 * Registers an instance of a class to be registered as an injectable dependency.<br>
-420 * The command manager will attempt to inject all fields in a command class that are annotated with
-421 * {@link co.aikar.commands.annotation.Dependency} with the provided instance.
-422 *
-423 * @param clazz the class the injector should look for when injecting
-424 * @param key the key which needs to be present if that
-425 * @param instance the instance of the class that should be injected
-426 * @throws IllegalStateException when there is already an instance for the provided class registered
-427 */
-428 public <T> void registerDependency(Class<? extends T> clazz, String key, T instance){
-429 if(dependencies.containsRow(clazz) && dependencies.containsColumn(key)){
-430 throw new IllegalStateException("There is already an instance of " + clazz.getName() + " with the key " + key + " registered!");
-431 }
-432
-433 dependencies.put(clazz, key, instance);
-434 }
-435
-436 /**
-437 * Attempts to inject instances of classes registered with {@link CommandManager#registerDependency(Class, Object)}
-438 * into all fields of the class and its superclasses that are marked with {@link Dependency}.
-439 *
-440 * @param baseCommand the instance which fields should be filled
-441 */
-442 void injectDependencies(BaseCommand baseCommand) {
-443 Class clazz = baseCommand.getClass();
-444 do {
-445 for (Field field : clazz.getDeclaredFields()) {
-446 if (field.isAnnotationPresent(Dependency.class)) {
-447 Dependency dependency = field.getAnnotation(Dependency.class);
-448 String key = (key = dependency.value()).equals("") ? field.getType().getName() : key;
-449 Object object = dependencies.row(field.getType()).get(key);
-450 if (object == null) {
-451 throw new UnresolvedDependencyException("Could not find a registered instance of " +
-452 field.getType().getName() + " with key " + key + " for field " + field.getName() +
-453 " in class " + baseCommand.getClass().getName());
-454 }
-455
-456 try {
-457 boolean accessible = field.isAccessible();
-458 if (!accessible) {
-459 field.setAccessible(true);
-460 }
-461 field.set(baseCommand, object);
-462 field.setAccessible(accessible);
-463 } catch (IllegalAccessException e) {
-464 e.printStackTrace(); //TODO should we print our own exception here to make a more descriptive error?
-465 }
-466 }
-467 }
-468 clazz = clazz.getSuperclass();
-469 } while (!clazz.equals(BaseCommand.class));
-470 }
-471
-472 /**
-473 * @deprecated Use this with caution! If you enable and use Unstable API's, your next compile using ACF
-474 * may require you to update your implementation to those unstable API's
-475 */
-476 @Deprecated
-477 public void enableUnstableAPI(String api) {
-478 unstableAPIs.add(api);
-479 }
-480 void verifyUnstableAPI(String api) {
-481 if (!unstableAPIs.contains(api)) {
-482 throw new IllegalStateException("Using an unstable API that has not been enabled ( " + api + "). See https://acfunstable.emc.gs");
-483 }
-484 }
-485
-486 boolean hasUnstableAPI(String api) {
-487 return unstableAPIs.contains(api);
+341 public void onLocaleChange(IssuerLocaleChangedCallback<I> onChange) {
+342 localeChangedCallbacks.add(onChange);
+343 }
+344
+345 public void notifyLocaleChange(I issuer, Locale oldLocale, Locale newLocale) {
+346 localeChangedCallbacks.forEach(cb -> {
+347 try {
+348 cb.onIssuerLocaleChange(issuer, oldLocale, newLocale);
+349 } catch (Exception e) {
+350 this.log(LogLevel.ERROR, "Error in notifyLocaleChange", e);
+351 }
+352 });
+353 }
+354
+355 public Locale setIssuerLocale(IT issuer, Locale locale) {
+356 I commandIssuer = getCommandIssuer(issuer);
+357
+358 Locale old = issuersLocale.put(commandIssuer.getUniqueId(), locale);
+359 if (!Objects.equals(old, locale)) {
+360 this.notifyLocaleChange(commandIssuer, old, locale);
+361 }
+362
+363 return old;
+364 }
+365
+366 public Locale getIssuerLocale(CommandIssuer issuer) {
+367 if (usingPerIssuerLocale()) {
+368 Locale locale = issuersLocale.get(issuer.getUniqueId());
+369 if (locale != null) {
+370 return locale;
+371 }
+372 }
+373
+374 return getLocales().getDefaultLocale();
+375 }
+376
+377 CommandOperationContext<I> createCommandOperationContext(BaseCommand command, CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
+378 //noinspection unchecked
+379 return new CommandOperationContext<>(
+380 this,
+381 (I) issuer,
+382 command,
+383 commandLabel,
+384 args,
+385 isAsync
+386 );
+387 }
+388
+389 /**
+390 * Gets a list of all currently supported languages for this manager.
+391 * These locales will be automatically loaded from
+392 * @return
+393 */
+394 public Set<Locale> getSupportedLanguages() {
+395 return supportedLanguages;
+396 }
+397
+398 /**
+399 * Adds a new locale to the list of automatic Locales to load Message Bundles for.
+400 * All bundles loaded under the previous supported languages will now automatically load for this new locale too.
+401 *
+402 * @param locale
+403 */
+404 public void addSupportedLanguage(Locale locale) {
+405 supportedLanguages.add(locale);
+406 getLocales().loadMissingBundles();
+407 }
+408
+409 /**
+410 * Registers an instance of a class to be registered as an injectable dependency.<br>
+411 * The command manager will attempt to inject all fields in a command class that are annotated with
+412 * {@link co.aikar.commands.annotation.Dependency} with the provided instance.
+413 *
+414 * @param clazz the class the injector should look for when injecting
+415 * @param instance the instance of the class that should be injected
+416 * @throws IllegalStateException when there is already an instance for the provided class registered
+417 */
+418 public <T> void registerDependency(Class<? extends T> clazz, T instance){
+419 registerDependency(clazz, clazz.getName(), instance);
+420 }
+421
+422 /**
+423 * Registers an instance of a class to be registered as an injectable dependency.<br>
+424 * The command manager will attempt to inject all fields in a command class that are annotated with
+425 * {@link co.aikar.commands.annotation.Dependency} with the provided instance.
+426 *
+427 * @param clazz the class the injector should look for when injecting
+428 * @param key the key which needs to be present if that
+429 * @param instance the instance of the class that should be injected
+430 * @throws IllegalStateException when there is already an instance for the provided class registered
+431 */
+432 public <T> void registerDependency(Class<? extends T> clazz, String key, T instance){
+433 if(dependencies.containsRow(clazz) && dependencies.containsColumn(key)){
+434 throw new IllegalStateException("There is already an instance of " + clazz.getName() + " with the key " + key + " registered!");
+435 }
+436
+437 dependencies.put(clazz, key, instance);
+438 }
+439
+440 /**
+441 * Attempts to inject instances of classes registered with {@link CommandManager#registerDependency(Class, Object)}
+442 * into all fields of the class and its superclasses that are marked with {@link Dependency}.
+443 *
+444 * @param baseCommand the instance which fields should be filled
+445 */
+446 void injectDependencies(BaseCommand baseCommand) {
+447 Class clazz = baseCommand.getClass();
+448 do {
+449 for (Field field : clazz.getDeclaredFields()) {
+450 if (annotations.hasAnnotation(field, Dependency.class)) {
+451 String dependency = annotations.getAnnotationValue(field, Dependency.class);
+452 String key = (key = dependency).isEmpty() ? field.getType().getName() : key;
+453 Object object = dependencies.row(field.getType()).get(key);
+454 if (object == null) {
+455 throw new UnresolvedDependencyException("Could not find a registered instance of " +
+456 field.getType().getName() + " with key " + key + " for field " + field.getName() +
+457 " in class " + baseCommand.getClass().getName());
+458 }
+459
+460 try {
+461 boolean accessible = field.isAccessible();
+462 if (!accessible) {
+463 field.setAccessible(true);
+464 }
+465 field.set(baseCommand, object);
+466 field.setAccessible(accessible);
+467 } catch (IllegalAccessException e) {
+468 e.printStackTrace(); //TODO should we print our own exception here to make a more descriptive error?
+469 }
+470 }
+471 }
+472 clazz = clazz.getSuperclass();
+473 } while (!clazz.equals(BaseCommand.class));
+474 }
+475
+476 /**
+477 * @deprecated Use this with caution! If you enable and use Unstable API's, your next compile using ACF
+478 * may require you to update your implementation to those unstable API's
+479 */
+480 @Deprecated
+481 public void enableUnstableAPI(String api) {
+482 unstableAPIs.add(api);
+483 }
+484 void verifyUnstableAPI(String api) {
+485 if (!unstableAPIs.contains(api)) {
+486 throw new IllegalStateException("Using an unstable API that has not been enabled ( " + api + "). See https://acfunstable.emc.gs");
+487 }
488 }
-489}
+489
+490 boolean hasUnstableAPI(String api) {
+491 return unstableAPIs.contains(api);
+492 }
+493
+494 Annotations getAnnotations() {
+495 return annotations;
+496 }
+497}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandOperationContext.html b/docs/acf-core/src-html/co/aikar/commands/CommandOperationContext.html
index e9678f7f..5852da15 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandOperationContext.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandOperationContext.html
@@ -87,15 +87,28 @@
079 return registeredCommand;
080 }
081
-082 public <T extends Annotation> T getAnnotation(Class<T> anno) {
-083 return registeredCommand.method.getAnnotation(anno);
-084 }
-085
-086 public boolean hasAnnotation(Class<? extends Annotation> anno) {
-087 return getAnnotation(anno) != null;
-088 }
-089
-090}
+082 /**
+083 * This method will not support annotation processors!! use getAnnotationValue or hasAnnotation
+084 * @deprecated Use {@link #getAnnotationValue(Class)}
+085 */
+086 @Deprecated
+087 public <T extends Annotation> T getAnnotation(Class<T> anno) {
+088 return registeredCommand.method.getAnnotation(anno);
+089 }
+090
+091 public <T extends Annotation> String getAnnotationValue(Class<T> cls) {
+092 return manager.getAnnotations().getAnnotationValue(registeredCommand.method, cls);
+093 }
+094
+095 public <T extends Annotation> String getAnnotationValue(Class<T> cls, int options) {
+096 return manager.getAnnotations().getAnnotationValue(registeredCommand.method, cls, options);
+097 }
+098
+099 public boolean hasAnnotation(Class<? extends Annotation> anno) {
+100 return getAnnotation(anno) != null;
+101 }
+102
+103}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandParameter.html b/docs/acf-core/src-html/co/aikar/commands/CommandParameter.html
index e9433039..9a5d4608 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandParameter.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandParameter.html
@@ -31,237 +31,238 @@
023
024package co.aikar.commands;
025
-026import co.aikar.commands.annotation.Default;
-027import co.aikar.commands.annotation.Description;
-028import co.aikar.commands.annotation.Flags;
-029import co.aikar.commands.annotation.Optional;
-030import co.aikar.commands.annotation.Syntax;
-031import co.aikar.commands.annotation.Values;
-032import co.aikar.commands.contexts.ContextResolver;
-033import co.aikar.commands.contexts.IssuerAwareContextResolver;
-034import co.aikar.commands.contexts.IssuerOnlyContextResolver;
-035import co.aikar.commands.contexts.OptionalContextResolver;
-036import com.google.common.collect.Maps;
-037
-038import java.lang.reflect.Parameter;
-039import java.util.Map;
-040
-041public class CommandParameter <CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> {
-042 private final Parameter parameter;
-043 private final Class<?> type;
-044 private final String name;
-045 private final CommandManager manager;
-046 private final int paramIndex;
-047
-048 private ContextResolver<?, CEC> resolver;
-049 private boolean optional;
-050 private String description;
-051 private String defaultValue;
-052 private String syntax;
-053 private boolean requiresInput;
-054 private boolean commandIssuer;
-055 private String[] values;
-056 private Map<String, String> flags;
-057 private boolean canConsumeInput;
-058 private boolean optionalResolver;
-059
-060 public CommandParameter(RegisteredCommand<CEC> command, Parameter param, int paramIndex) {
-061 this.parameter = param;
-062 this.type = param.getType();
-063 this.name = param.getName(); // do we care for an annotation to supply name?
-064 //noinspection unchecked
-065 this.manager = command.manager;
-066 this.paramIndex = paramIndex;
-067 CommandReplacements replacements = manager.getCommandReplacements();
-068
-069 Default defaultAnno = param.getAnnotation(Default.class);
-070 Description descAnno = param.getAnnotation(Description.class);
-071
-072 //noinspection unchecked
-073 this.resolver = manager.getCommandContexts().getResolver(type);
-074 if (this.resolver == null) {
-075 ACFUtil.sneaky(new InvalidCommandContextException(
-076 "Parameter " + type.getSimpleName() + " of " + command + " has no applicable context resolver"
-077 ));
-078 }
-079
-080 this.description = descAnno != null ? descAnno.value() : null;
-081 String defaultValue = defaultAnno != null ? replacements.replace(defaultAnno.value()) : null;
-082 this.defaultValue = defaultValue != null && (type == String.class || !defaultValue.isEmpty()) ? defaultValue : null;
-083
-084
-085 this.optional = param.isAnnotationPresent(Optional.class) || this.defaultValue != null;
-086 this.optionalResolver = isOptionalResolver(resolver);
-087 this.requiresInput = !this.optional && !this.optionalResolver;
-088 //noinspection unchecked
-089 this.commandIssuer = manager.isCommandIssuer(type);
-090 this.canConsumeInput = !(resolver instanceof IssuerOnlyContextResolver);
+026import co.aikar.commands.annotation.Conditions;
+027import co.aikar.commands.annotation.Default;
+028import co.aikar.commands.annotation.Description;
+029import co.aikar.commands.annotation.Flags;
+030import co.aikar.commands.annotation.Optional;
+031import co.aikar.commands.annotation.Syntax;
+032import co.aikar.commands.annotation.Values;
+033import co.aikar.commands.contexts.ContextResolver;
+034import co.aikar.commands.contexts.IssuerAwareContextResolver;
+035import co.aikar.commands.contexts.IssuerOnlyContextResolver;
+036import co.aikar.commands.contexts.OptionalContextResolver;
+037import com.google.common.collect.Maps;
+038
+039import java.lang.reflect.Parameter;
+040import java.util.Map;
+041
+042public class CommandParameter <CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> {
+043 private final Parameter parameter;
+044 private final Class<?> type;
+045 private final String name;
+046 private final CommandManager manager;
+047 private final int paramIndex;
+048
+049 private ContextResolver<?, CEC> resolver;
+050 private boolean optional;
+051 private String description;
+052 private String defaultValue;
+053 private String syntax;
+054 private String conditions;
+055 private boolean requiresInput;
+056 private boolean commandIssuer;
+057 private String[] values;
+058 private Map<String, String> flags;
+059 private boolean canConsumeInput;
+060 private boolean optionalResolver;
+061
+062 public CommandParameter(RegisteredCommand<CEC> command, Parameter param, int paramIndex) {
+063 this.parameter = param;
+064 this.type = param.getType();
+065 this.name = param.getName(); // do we care for an annotation to supply name?
+066 //noinspection unchecked
+067 this.manager = command.manager;
+068 this.paramIndex = paramIndex;
+069 Annotations annotations = manager.getAnnotations();
+070
+071 this.defaultValue = annotations.getAnnotationValue(param, Default.class, Annotations.REPLACEMENTS | (type != String.class ? Annotations.NO_EMPTY : 0));
+072 this.description = annotations.getAnnotationValue(param, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY);
+073 this.conditions = annotations.getAnnotationValue(param, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+074
+075 //noinspection unchecked
+076 this.resolver = manager.getCommandContexts().getResolver(type);
+077 if (this.resolver == null) {
+078 ACFUtil.sneaky(new InvalidCommandContextException(
+079 "Parameter " + type.getSimpleName() + " of " + command + " has no applicable context resolver"
+080 ));
+081 }
+082
+083 this.optional = annotations.hasAnnotation(param, Optional.class) || this.defaultValue != null;
+084 this.optionalResolver = isOptionalResolver(resolver);
+085 this.requiresInput = !this.optional && !this.optionalResolver;
+086 //noinspection unchecked
+087 this.commandIssuer = manager.isCommandIssuer(type);
+088 this.canConsumeInput = !(resolver instanceof IssuerOnlyContextResolver);
+089
+090 this.values = annotations.getAnnotationValues(param, Values.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
091
-092 final Values values = param.getAnnotation(Values.class);
-093 if (values != null) {
-094 this.values = ACFPatterns.PIPE.split(manager.getCommandReplacements().replace(values.value()));
-095 } else {
-096 this.values = null;
-097 }
-098
-099 this.syntax = null;
-100 if (!commandIssuer) {
-101 Syntax syntaxAnno = param.getAnnotation(Syntax.class);
-102 if (syntaxAnno != null) {
-103 this.syntax = replacements.replace(syntaxAnno.value());
-104 } else if (!requiresInput && canConsumeInput) {
-105 this.syntax = "[" + name + "]";
-106 } else if (requiresInput) {
-107 this.syntax = "<" + name + ">";
-108 }
-109 }
-110
-111 this.flags = Maps.newHashMap();
-112 Flags flags = param.getAnnotation(Flags.class);
-113 if (flags != null) {
-114 parseFlags(flags.value());
-115 }
-116 inheritContextFlags(command.scope);
-117 }
-118
-119 private void inheritContextFlags(BaseCommand scope) {
-120 if (!scope.contextFlags.isEmpty()) {
-121 Class<?> pCls = this.type;
-122 do {
-123 parseFlags(scope.contextFlags.get(pCls));
-124 } while ((pCls = pCls.getSuperclass()) != null);
-125 }
-126 if (scope.parentCommand != null) {
-127 inheritContextFlags(scope.parentCommand);
-128 }
-129 }
-130
-131 private void parseFlags(String flags) {
-132 if (flags != null) {
-133 for (String s : ACFPatterns.COMMA.split(manager.getCommandReplacements().replace(flags))) {
-134 String[] v = ACFPatterns.EQUALS.split(s, 2);
-135 if (!this.flags.containsKey(v[0])) {
-136 this.flags.put(v[0], v.length > 1 ? v[1] : null);
-137 }
-138 }
-139 }
-140 }
+092 this.syntax = null;
+093 if (!commandIssuer) {
+094 this.syntax = annotations.getAnnotationValue(param, Syntax.class);
+095 if (syntax == null) {
+096 if (!requiresInput && canConsumeInput) {
+097 this.syntax = "[" + name + "]";
+098 } else if (requiresInput) {
+099 this.syntax = "<" + name + ">";
+100 }
+101 }
+102 }
+103
+104 this.flags = Maps.newHashMap();
+105 String flags = annotations.getAnnotationValue(param, Flags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+106 if (flags != null) {
+107 parseFlags(flags);
+108 }
+109 inheritContextFlags(command.scope);
+110 }
+111
+112 private void inheritContextFlags(BaseCommand scope) {
+113 if (!scope.contextFlags.isEmpty()) {
+114 Class<?> pCls = this.type;
+115 do {
+116 parseFlags(scope.contextFlags.get(pCls));
+117 } while ((pCls = pCls.getSuperclass()) != null);
+118 }
+119 if (scope.parentCommand != null) {
+120 inheritContextFlags(scope.parentCommand);
+121 }
+122 }
+123
+124 private void parseFlags(String flags) {
+125 if (flags != null) {
+126 for (String s : ACFPatterns.COMMA.split(manager.getCommandReplacements().replace(flags))) {
+127 String[] v = ACFPatterns.EQUALS.split(s, 2);
+128 if (!this.flags.containsKey(v[0])) {
+129 this.flags.put(v[0], v.length > 1 ? v[1] : null);
+130 }
+131 }
+132 }
+133 }
+134
+135 private boolean isOptionalResolver(ContextResolver<?, CEC> resolver) {
+136 return resolver instanceof IssuerAwareContextResolver
+137 || resolver instanceof IssuerOnlyContextResolver
+138 || resolver instanceof OptionalContextResolver;
+139 }
+140
141
-142 private boolean isOptionalResolver(ContextResolver<?, CEC> resolver) {
-143 return resolver instanceof IssuerAwareContextResolver
-144 || resolver instanceof IssuerOnlyContextResolver
-145 || resolver instanceof OptionalContextResolver;
-146 }
-147
-148
-149 public Parameter getParameter() {
-150 return parameter;
-151 }
-152
-153 public Class<?> getType() {
-154 return type;
-155 }
-156
-157 public String getName() {
-158 return name;
-159 }
-160
-161 public CommandManager getManager() {
-162 return manager;
-163 }
-164
-165 public int getParamIndex() {
-166 return paramIndex;
-167 }
-168
-169 public ContextResolver<?, CEC> getResolver() {
-170 return resolver;
-171 }
-172
-173 public void setResolver(ContextResolver<?, CEC> resolver) {
-174 this.resolver = resolver;
-175 }
-176
-177 public boolean isOptional() {
-178 return optional;
-179 }
-180
-181 public void setOptional(boolean optional) {
-182 this.optional = optional;
-183 }
-184
-185 public String getDescription() {
-186 return description;
-187 }
-188
-189 public void setDescription(String description) {
-190 this.description = description;
-191 }
-192
-193 public String getDefaultValue() {
-194 return defaultValue;
-195 }
-196
-197 public void setDefaultValue(String defaultValue) {
-198 this.defaultValue = defaultValue;
-199 }
-200
-201 public boolean isCommandIssuer() {
-202 return commandIssuer;
-203 }
-204
-205 public void setCommandIssuer(boolean commandIssuer) {
-206 this.commandIssuer = commandIssuer;
-207 }
-208
-209 public String[] getValues() {
-210 return values;
-211 }
-212
-213 public void setValues(String[] values) {
-214 this.values = values;
-215 }
-216
-217 public Map<String, String> getFlags() {
-218 return flags;
-219 }
-220
-221 public void setFlags(Map<String, String> flags) {
-222 this.flags = flags;
-223 }
-224
-225 public boolean canConsumeInput() {
-226 return canConsumeInput;
-227 }
-228
-229 public void setCanConsumeInput(boolean canConsumeInput) {
-230 this.canConsumeInput = canConsumeInput;
-231 }
-232
-233 public void setOptionalResolver(boolean optionalResolver) {
-234 this.optionalResolver = optionalResolver;
-235 }
-236
-237 public boolean isOptionalResolver() {
-238 return optionalResolver;
-239 }
-240
-241 public boolean requiresInput() {
-242 return requiresInput;
-243 }
-244
-245 public void setRequiresInput(boolean requiresInput) {
-246 this.requiresInput = requiresInput;
-247 }
-248
-249 public String getSyntax() {
-250 return syntax;
-251 }
-252
-253 public void setSyntax(String syntax) {
-254 this.syntax = syntax;
-255 }
-256}
+142 public Parameter getParameter() {
+143 return parameter;
+144 }
+145
+146 public Class<?> getType() {
+147 return type;
+148 }
+149
+150 public String getName() {
+151 return name;
+152 }
+153
+154 public CommandManager getManager() {
+155 return manager;
+156 }
+157
+158 public int getParamIndex() {
+159 return paramIndex;
+160 }
+161
+162 public ContextResolver<?, CEC> getResolver() {
+163 return resolver;
+164 }
+165
+166 public void setResolver(ContextResolver<?, CEC> resolver) {
+167 this.resolver = resolver;
+168 }
+169
+170 public boolean isOptional() {
+171 return optional;
+172 }
+173
+174 public void setOptional(boolean optional) {
+175 this.optional = optional;
+176 }
+177
+178 public String getDescription() {
+179 return description;
+180 }
+181
+182 public void setDescription(String description) {
+183 this.description = description;
+184 }
+185
+186 public String getDefaultValue() {
+187 return defaultValue;
+188 }
+189
+190 public void setDefaultValue(String defaultValue) {
+191 this.defaultValue = defaultValue;
+192 }
+193
+194 public boolean isCommandIssuer() {
+195 return commandIssuer;
+196 }
+197
+198 public void setCommandIssuer(boolean commandIssuer) {
+199 this.commandIssuer = commandIssuer;
+200 }
+201
+202 public String[] getValues() {
+203 return values;
+204 }
+205
+206 public void setValues(String[] values) {
+207 this.values = values;
+208 }
+209
+210 public Map<String, String> getFlags() {
+211 return flags;
+212 }
+213
+214 public void setFlags(Map<String, String> flags) {
+215 this.flags = flags;
+216 }
+217
+218 public boolean canConsumeInput() {
+219 return canConsumeInput;
+220 }
+221
+222 public void setCanConsumeInput(boolean canConsumeInput) {
+223 this.canConsumeInput = canConsumeInput;
+224 }
+225
+226 public void setOptionalResolver(boolean optionalResolver) {
+227 this.optionalResolver = optionalResolver;
+228 }
+229
+230 public boolean isOptionalResolver() {
+231 return optionalResolver;
+232 }
+233
+234 public boolean requiresInput() {
+235 return requiresInput;
+236 }
+237
+238 public void setRequiresInput(boolean requiresInput) {
+239 this.requiresInput = requiresInput;
+240 }
+241
+242 public String getSyntax() {
+243 return syntax;
+244 }
+245
+246 public void setSyntax(String syntax) {
+247 this.syntax = syntax;
+248 }
+249
+250 public String getConditions() {
+251 return conditions;
+252 }
+253
+254 public void setConditions(String conditions) {
+255 this.conditions = conditions;
+256 }
+257}
diff --git a/docs/acf-core/src-html/co/aikar/commands/HelpEntry.html b/docs/acf-core/src-html/co/aikar/commands/HelpEntry.html
index ca803606..e3aa7651 100644
--- a/docs/acf-core/src-html/co/aikar/commands/HelpEntry.html
+++ b/docs/acf-core/src-html/co/aikar/commands/HelpEntry.html
@@ -36,48 +36,45 @@
028public class HelpEntry {
029
030 private final RegisteredCommand command;
-031 private final String searchTags;
-032 private int searchScore = 1;
-033
-034 HelpEntry(RegisteredCommand command) {
-035 this.command = command;
-036 HelpSearchTags tagsAnno = command.method.getAnnotation(HelpSearchTags.class);
-037 this.searchTags = tagsAnno != null ? tagsAnno.value() : null;
-038 }
-039
-040 RegisteredCommand getRegisteredCommand() {
-041 return this.command;
-042 }
-043
-044
-045 public String getCommand(){
-046 return "/" + this.command.command;
-047 }
-048
-049 public String getParameterSyntax(){
-050 return this.command.syntaxText;
-051 }
-052
-053 public String getDescription(){
-054 return this.command.helpText;
-055 }
-056
-057 public void setSearchScore(int searchScore) {
-058 this.searchScore = searchScore;
-059 }
-060
-061 public boolean shouldShow() {
-062 return this.searchScore > 0;
-063 }
-064
-065 public int getSearchScore() {
-066 return searchScore;
-067 }
-068
-069 public String getSearchTags() {
-070 return searchTags;
-071 }
-072}
+031 private int searchScore = 1;
+032
+033 HelpEntry(RegisteredCommand command) {
+034 this.command = command;
+035 }
+036
+037 RegisteredCommand getRegisteredCommand() {
+038 return this.command;
+039 }
+040
+041
+042 public String getCommand(){
+043 return "/" + this.command.command;
+044 }
+045
+046 public String getParameterSyntax(){
+047 return this.command.syntaxText;
+048 }
+049
+050 public String getDescription(){
+051 return this.command.helpText;
+052 }
+053
+054 public void setSearchScore(int searchScore) {
+055 this.searchScore = searchScore;
+056 }
+057
+058 public boolean shouldShow() {
+059 return this.searchScore > 0;
+060 }
+061
+062 public int getSearchScore() {
+063 return searchScore;
+064 }
+065
+066 public String getSearchTags() {
+067 return command.helpSearchTags;
+068 }
+069}
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 3aeec899..00af2ab1 100644
--- a/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html
+++ b/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html
@@ -34,266 +34,269 @@
026import co.aikar.commands.annotation.CommandAlias;
027import co.aikar.commands.annotation.CommandCompletion;
028import co.aikar.commands.annotation.CommandPermission;
-029import co.aikar.commands.annotation.Description;
-030import co.aikar.commands.annotation.Syntax;
-031import co.aikar.commands.contexts.ContextResolver;
-032import com.google.common.collect.ImmutableSet;
-033import com.google.common.collect.Lists;
-034import com.google.common.collect.Maps;
-035import com.google.common.collect.Sets;
-036import org.jetbrains.annotations.Nullable;
-037
-038import java.lang.reflect.InvocationTargetException;
-039import java.lang.reflect.Method;
-040import java.lang.reflect.Parameter;
-041import java.util.ArrayList;
-042import java.util.Collection;
-043import java.util.List;
-044import java.util.Map;
-045import java.util.Set;
-046import java.util.stream.Collectors;
-047
-048@SuppressWarnings("WeakerAccess")
-049public class RegisteredCommand <CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> {
-050 final BaseCommand scope;
-051 final String command;
-052 final Method method;
-053 final String prefSubCommand;
+029import co.aikar.commands.annotation.Conditions;
+030import co.aikar.commands.annotation.Description;
+031import co.aikar.commands.annotation.HelpSearchTags;
+032import co.aikar.commands.annotation.Syntax;
+033import co.aikar.commands.contexts.ContextResolver;
+034import com.google.common.collect.ImmutableSet;
+035import com.google.common.collect.Lists;
+036import com.google.common.collect.Maps;
+037import com.google.common.collect.Sets;
+038import org.jetbrains.annotations.Nullable;
+039
+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 String syntaxText;
-056 final String helpText;
+055 final CommandManager manager;
+056 final List<String> registeredSubcommands = new ArrayList<>();
057
-058 private final String permission;
-059 final String complete;
-060 final int requiredResolvers;
-061 final int optionalResolvers;
-062 final List<String> registeredSubcommands = new ArrayList<>();
-063 final CommandManager manager;
-064
-065 RegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) {
-066 this.scope = scope;
-067 this.manager = this.scope.manager;
-068 CommandReplacements replacements = scope.manager.getCommandReplacements();
+058 String command;
+059 String prefSubCommand;
+060 String syntaxText;
+061 String helpText;
+062 String permission;
+063 String complete;
+064 String conditions;
+065
+066 final int requiredResolvers;
+067 final int optionalResolvers;
+068 public String helpSearchTags;
069
-070 if (BaseCommand.CATCHUNKNOWN.equals(prefSubCommand) || BaseCommand.DEFAULT.equals(prefSubCommand)) {
-071 prefSubCommand = "";
-072 }
-073 this.command = command + (!method.isAnnotationPresent(CommandAlias.class) && !prefSubCommand.isEmpty() ? prefSubCommand : "");
-074 this.method = method;
-075 this.prefSubCommand = prefSubCommand;
-076
-077 final CommandPermission permissionAnno = method.getAnnotation(CommandPermission.class);
-078 final CommandCompletion completionAnno = method.getAnnotation(CommandCompletion.class);
-079 final Description descriptionAnno = method.getAnnotation(Description.class);
-080 final Syntax syntaxStr = method.getAnnotation(Syntax.class);
+070 RegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) {
+071 this.scope = scope;
+072 this.manager = this.scope.manager;
+073 final Annotations annotations = this.manager.getAnnotations();
+074
+075 if (BaseCommand.CATCHUNKNOWN.equals(prefSubCommand) || BaseCommand.DEFAULT.equals(prefSubCommand)) {
+076 prefSubCommand = "";
+077 }
+078 this.command = command + (!annotations.hasAnnotation(method, CommandAlias.class, false) && !prefSubCommand.isEmpty() ? prefSubCommand : "");
+079 this.method = method;
+080 this.prefSubCommand = prefSubCommand;
081
-082 this.permission = permissionAnno != null ? replacements.replace(permissionAnno.value()) : null;
-083 this.complete = completionAnno != null ? replacements.replace(completionAnno.value()) : null;
-084 Parameter[] parameters = method.getParameters();
-085 //noinspection unchecked
-086 this.parameters = new CommandParameter[parameters.length];
-087 this.helpText = descriptionAnno != null ? descriptionAnno.value() : "";
-088
-089
-090 int requiredResolvers = 0;
-091 int optionalResolvers = 0;
-092 StringBuilder syntaxBuilder = new StringBuilder(64);
-093
-094 for (int i = 0; i < parameters.length; i++) {
-095 CommandParameter<CEC> parameter = this.parameters[i] = new CommandParameter<>(this, parameters[i], i);
-096 if (!parameter.isCommandIssuer()) {
-097 if (!parameter.requiresInput()) {
-098 optionalResolvers++;
-099 } else {
-100 requiredResolvers++;
-101 }
-102 }
-103 if (parameter.getSyntax() != null) {
-104 if (syntaxBuilder.length() > 0) {
-105 syntaxBuilder.append(' ');
-106 }
-107 syntaxBuilder.append(parameter.getSyntax());
-108 }
-109 }
-110 String syntaxText = syntaxBuilder.toString().trim();
-111 this.syntaxText = replacements.replace(syntaxStr != null ?
-112 ACFUtil.replace(syntaxStr.value(), "@syntax", syntaxText) : syntaxText);
-113 this.requiredResolvers = requiredResolvers;
-114 this.optionalResolvers = optionalResolvers;
-115 }
-116
-117
-118 void invoke(CommandIssuer sender, List<String> args, CommandOperationContext context) {
-119 if (!scope.canExecute(sender, this)) {
-120 return;
-121 }
-122 preCommand();
-123 try {
-124 this.manager.conditions.validateConditions(context);
-125 Map<String, Object> passedArgs = resolveContexts(sender, args);
-126 if (passedArgs == null) return;
-127
-128 method.invoke(scope, passedArgs.values().toArray());
-129 } catch (Exception e) {
-130 handleException(sender, args, e);
-131 }
-132 postCommand();
-133 }
-134 public void preCommand() {}
-135 public void postCommand() {}
-136
-137 void handleException(CommandIssuer sender, List<String> args, Exception e) {
-138 if (e instanceof InvocationTargetException && e.getCause() instanceof InvalidCommandArgument) {
-139 e = (Exception) e.getCause();
-140 }
-141 if (e instanceof ShowCommandHelp) {
-142 ShowCommandHelp showHelp = (ShowCommandHelp) e;
-143 CommandHelp commandHelp = manager.generateCommandHelp();
-144 if (showHelp.search) {
-145 commandHelp.setSearch(showHelp.searchArgs == null ? args : showHelp.searchArgs);
-146 }
-147 commandHelp.showHelp(sender);
-148 } else if (e instanceof InvalidCommandArgument) {
-149 InvalidCommandArgument invalidCommandArg = (InvalidCommandArgument) e;
-150 if (invalidCommandArg.key != null) {
-151 sender.sendMessage(MessageType.ERROR, invalidCommandArg.key, invalidCommandArg.replacements);
-152 } else if (e.getMessage() != null && !e.getMessage().isEmpty()) {
-153 sender.sendMessage(MessageType.ERROR, MessageKeys.ERROR_PREFIX, "{message}", e.getMessage());
-154 }
-155 if (invalidCommandArg.showSyntax) {
-156 scope.showSyntax(sender, this);
+082 this.permission = annotations.getAnnotationValue(method, CommandPermission.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+083 this.complete = annotations.getAnnotationValue(method, CommandCompletion.class);
+084 this.helpText = annotations.getAnnotationValue(method, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY);
+085 this.conditions = annotations.getAnnotationValue(method, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+086 this.helpSearchTags = annotations.getAnnotationValue(method, HelpSearchTags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+087
+088 Parameter[] parameters = method.getParameters();
+089 //noinspection unchecked
+090 this.parameters = new CommandParameter[parameters.length];
+091
+092
+093 int requiredResolvers = 0;
+094 int optionalResolvers = 0;
+095 StringBuilder syntaxBuilder = new StringBuilder(64);
+096
+097 for (int i = 0; i < parameters.length; i++) {
+098 CommandParameter<CEC> parameter = this.parameters[i] = new CommandParameter<>(this, parameters[i], i);
+099 if (!parameter.isCommandIssuer()) {
+100 if (!parameter.requiresInput()) {
+101 optionalResolvers++;
+102 } else {
+103 requiredResolvers++;
+104 }
+105 }
+106 if (parameter.getSyntax() != null) {
+107 if (syntaxBuilder.length() > 0) {
+108 syntaxBuilder.append(' ');
+109 }
+110 syntaxBuilder.append(parameter.getSyntax());
+111 }
+112 }
+113 String syntaxText = syntaxBuilder.toString().trim();
+114 final String syntaxStr = annotations.getAnnotationValue(method, Syntax.class);
+115 this.syntaxText = syntaxStr != null ? ACFUtil.replace(syntaxStr, "@syntax", syntaxText) : syntaxText;
+116 this.requiredResolvers = requiredResolvers;
+117 this.optionalResolvers = optionalResolvers;
+118 }
+119
+120
+121 void invoke(CommandIssuer sender, List<String> args, CommandOperationContext context) {
+122 if (!scope.canExecute(sender, this)) {
+123 return;
+124 }
+125 preCommand();
+126 try {
+127 this.manager.conditions.validateConditions(context);
+128 Map<String, Object> passedArgs = resolveContexts(sender, args);
+129 if (passedArgs == null) return;
+130
+131 method.invoke(scope, passedArgs.values().toArray());
+132 } catch (Exception e) {
+133 handleException(sender, args, e);
+134 }
+135 postCommand();
+136 }
+137 public void preCommand() {}
+138 public void postCommand() {}
+139
+140 void handleException(CommandIssuer sender, List<String> args, Exception e) {
+141 if (e instanceof InvocationTargetException && e.getCause() instanceof InvalidCommandArgument) {
+142 e = (Exception) e.getCause();
+143 }
+144 if (e instanceof ShowCommandHelp) {
+145 ShowCommandHelp showHelp = (ShowCommandHelp) e;
+146 CommandHelp commandHelp = manager.generateCommandHelp();
+147 if (showHelp.search) {
+148 commandHelp.setSearch(showHelp.searchArgs == null ? args : showHelp.searchArgs);
+149 }
+150 commandHelp.showHelp(sender);
+151 } else if (e instanceof InvalidCommandArgument) {
+152 InvalidCommandArgument invalidCommandArg = (InvalidCommandArgument) e;
+153 if (invalidCommandArg.key != null) {
+154 sender.sendMessage(MessageType.ERROR, invalidCommandArg.key, invalidCommandArg.replacements);
+155 } else if (e.getMessage() != null && !e.getMessage().isEmpty()) {
+156 sender.sendMessage(MessageType.ERROR, MessageKeys.ERROR_PREFIX, "{message}", e.getMessage());
157 }
-158 } else {
-159 try {
-160 if (!this.manager.handleUncaughtException(scope, this, sender, args, e)) {
-161 sender.sendMessage(MessageType.ERROR, MessageKeys.ERROR_PERFORMING_COMMAND);
-162 }
-163 this.manager.log(LogLevel.ERROR, "Exception in command: " + command + " " + ACFUtil.join(args), e);
-164 } catch (Exception e2) {
-165 this.manager.log(LogLevel.ERROR, "Exception in handleException for command: " + command + " " + ACFUtil.join(args), e);
-166 this.manager.log(LogLevel.ERROR, "Exception triggered by exception handler:", e2);
-167 }
-168 }
-169 }
-170
-171 @Nullable
-172 Map<String, Object> resolveContexts(CommandIssuer sender, List<String> args) throws InvalidCommandArgument {
-173 return resolveContexts(sender, args, parameters.length);
-174 }
-175 @Nullable
-176 Map<String, Object> resolveContexts(CommandIssuer sender, List<String> args, int argLimit) throws InvalidCommandArgument {
-177 args = Lists.newArrayList(args);
-178 String[] origArgs = args.toArray(new String[args.size()]);
-179 Map<String, Object> passedArgs = Maps.newLinkedHashMap();
-180 int remainingRequired = requiredResolvers;
-181 CommandOperationContext opContext = CommandManager.getCurrentCommandOperationContext();
-182 for (int i = 0; i < parameters.length && i < argLimit; i++) {
-183 boolean isLast = i == parameters.length - 1;
-184 boolean allowOptional = remainingRequired == 0;
-185 final CommandParameter<CEC> parameter = parameters[i];
-186 final String parameterName = parameter.getName();
-187 final Class<?> type = parameter.getType();
-188 //noinspection unchecked
-189 final ContextResolver<?, CEC> resolver = parameter.getResolver();
-190 //noinspection unchecked
-191 CEC context = (CEC) this.manager.createCommandContext(this, parameter, sender, args, i, passedArgs);
-192 boolean requiresInput = parameter.requiresInput();
-193 if (requiresInput && remainingRequired > 0) {
-194 remainingRequired--;
-195 }
-196 if (args.isEmpty() && !(isLast && type == String[].class)) {
-197 if (allowOptional && parameter.getDefaultValue() != null) {
-198 args.add(parameter.getDefaultValue());
-199 } else if (allowOptional && parameter.isOptional()) {
-200 Object value = parameter.isOptionalResolver() ? resolver.getContext(context) : null;
-201 if (value == null && parameter.getClass().isPrimitive()) {
-202 throw new IllegalStateException("Parameter " + parameter.getName() + " is primitive and does not support Optional.");
-203 }
-204 //noinspection unchecked
-205 this.manager.conditions.validateConditions(context, value);
-206 passedArgs.put(parameterName, value);
-207 //noinspection UnnecessaryContinue
-208 continue;
-209 } else if (requiresInput) {
-210 scope.showSyntax(sender, this);
-211 return null;
-212 }
-213 }
-214 if (parameter.getValues() != null) {
-215 String arg = !args.isEmpty() ? args.get(0) : "";
-216
-217 Set<String> possible = Sets.newHashSet();
-218 CommandCompletions commandCompletions = this.manager.getCommandCompletions();
-219 for (String s : parameter.getValues()) {
-220 //noinspection unchecked
-221 List<String> check = commandCompletions.getCompletionValues(this, sender, s, origArgs, opContext.isAsync());
-222 if (!check.isEmpty()) {
-223 possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList()));
-224 } else {
-225 possible.add(s.toLowerCase());
-226 }
-227 }
-228
-229 if (!possible.contains(arg.toLowerCase())) {
-230 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF,
-231 "{valid}", ACFUtil.join(possible, ", "));
-232 }
-233 }
-234 Object paramValue = resolver.getContext(context);
-235 //noinspection unchecked
-236 this.manager.conditions.validateConditions(context, paramValue);
-237 passedArgs.put(parameterName, paramValue);
-238 }
-239 return passedArgs;
-240 }
-241
-242 boolean hasPermission(CommandIssuer issuer) {
-243 return (permission == null || permission.isEmpty() || scope.manager.hasPermission(issuer, permission)) && scope.hasPermission(issuer);
-244 }
-245
-246
-247 /**
-248 * @see #getRequiredPermissions()
-249 * @deprecated
-250 */
-251 @Deprecated
-252 public String getPermission() {
-253 if (this.permission == null || this.permission.isEmpty()) {
-254 return null;
-255 }
-256 return ACFPatterns.COMMA.split(this.permission)[0];
-257 }
-258
-259 public Set<String> getRequiredPermissions() {
-260 if (this.permission == null || this.permission.isEmpty()) {
-261 return ImmutableSet.of();
-262 }
-263 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission));
-264 }
-265
-266 public boolean requiresPermission(String permission) {
-267 return getRequiredPermissions().contains(permission) || scope.requiresPermission(permission);
-268 }
-269
-270 public String getPrefSubCommand() {
-271 return prefSubCommand;
-272 }
-273
-274 public String getSyntaxText() {
-275 return syntaxText;
-276 }
-277
-278 public String getCommand() {
-279 return command;
-280 }
-281
-282 public void addSubcommand(String cmd) {
-283 this.registeredSubcommands.add(cmd);
-284 }
-285 public void addSubcommands(Collection<String> cmd) {
-286 this.registeredSubcommands.addAll(cmd);
+158 if (invalidCommandArg.showSyntax) {
+159 scope.showSyntax(sender, this);
+160 }
+161 } else {
+162 try {
+163 if (!this.manager.handleUncaughtException(scope, this, sender, args, e)) {
+164 sender.sendMessage(MessageType.ERROR, MessageKeys.ERROR_PERFORMING_COMMAND);
+165 }
+166 this.manager.log(LogLevel.ERROR, "Exception in command: " + command + " " + ACFUtil.join(args), e);
+167 } catch (Exception e2) {
+168 this.manager.log(LogLevel.ERROR, "Exception in handleException for command: " + command + " " + ACFUtil.join(args), e);
+169 this.manager.log(LogLevel.ERROR, "Exception triggered by exception handler:", e2);
+170 }
+171 }
+172 }
+173
+174 @Nullable
+175 Map<String, Object> resolveContexts(CommandIssuer sender, List<String> args) throws InvalidCommandArgument {
+176 return resolveContexts(sender, args, parameters.length);
+177 }
+178 @Nullable
+179 Map<String, Object> resolveContexts(CommandIssuer sender, List<String> args, int argLimit) throws InvalidCommandArgument {
+180 args = Lists.newArrayList(args);
+181 String[] origArgs = args.toArray(new String[args.size()]);
+182 Map<String, Object> passedArgs = Maps.newLinkedHashMap();
+183 int remainingRequired = requiredResolvers;
+184 CommandOperationContext opContext = CommandManager.getCurrentCommandOperationContext();
+185 for (int i = 0; i < parameters.length && i < argLimit; i++) {
+186 boolean isLast = i == parameters.length - 1;
+187 boolean allowOptional = remainingRequired == 0;
+188 final CommandParameter<CEC> parameter = parameters[i];
+189 final String parameterName = parameter.getName();
+190 final Class<?> type = parameter.getType();
+191 //noinspection unchecked
+192 final ContextResolver<?, CEC> resolver = parameter.getResolver();
+193 //noinspection unchecked
+194 CEC context = (CEC) this.manager.createCommandContext(this, parameter, sender, args, i, passedArgs);
+195 boolean requiresInput = parameter.requiresInput();
+196 if (requiresInput && remainingRequired > 0) {
+197 remainingRequired--;
+198 }
+199 if (args.isEmpty() && !(isLast && type == String[].class)) {
+200 if (allowOptional && parameter.getDefaultValue() != null) {
+201 args.add(parameter.getDefaultValue());
+202 } else if (allowOptional && parameter.isOptional()) {
+203 Object value = parameter.isOptionalResolver() ? resolver.getContext(context) : null;
+204 if (value == null && parameter.getClass().isPrimitive()) {
+205 throw new IllegalStateException("Parameter " + parameter.getName() + " is primitive and does not support Optional.");
+206 }
+207 //noinspection unchecked
+208 this.manager.conditions.validateConditions(context, value);
+209 passedArgs.put(parameterName, value);
+210 //noinspection UnnecessaryContinue
+211 continue;
+212 } else if (requiresInput) {
+213 scope.showSyntax(sender, this);
+214 return null;
+215 }
+216 }
+217 if (parameter.getValues() != null) {
+218 String arg = !args.isEmpty() ? args.get(0) : "";
+219
+220 Set<String> possible = Sets.newHashSet();
+221 CommandCompletions commandCompletions = this.manager.getCommandCompletions();
+222 for (String s : parameter.getValues()) {
+223 //noinspection unchecked
+224 List<String> check = commandCompletions.getCompletionValues(this, sender, s, origArgs, opContext.isAsync());
+225 if (!check.isEmpty()) {
+226 possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList()));
+227 } else {
+228 possible.add(s.toLowerCase());
+229 }
+230 }
+231
+232 if (!possible.contains(arg.toLowerCase())) {
+233 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF,
+234 "{valid}", ACFUtil.join(possible, ", "));
+235 }
+236 }
+237 Object paramValue = resolver.getContext(context);
+238 //noinspection unchecked
+239 this.manager.conditions.validateConditions(context, paramValue);
+240 passedArgs.put(parameterName, paramValue);
+241 }
+242 return passedArgs;
+243 }
+244
+245 boolean hasPermission(CommandIssuer issuer) {
+246 return (permission == null || permission.isEmpty() || scope.manager.hasPermission(issuer, permission)) && scope.hasPermission(issuer);
+247 }
+248
+249
+250 /**
+251 * @see #getRequiredPermissions()
+252 * @deprecated
+253 */
+254 @Deprecated
+255 public String getPermission() {
+256 if (this.permission == null || this.permission.isEmpty()) {
+257 return null;
+258 }
+259 return ACFPatterns.COMMA.split(this.permission)[0];
+260 }
+261
+262 public Set<String> getRequiredPermissions() {
+263 if (this.permission == null || this.permission.isEmpty()) {
+264 return ImmutableSet.of();
+265 }
+266 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission));
+267 }
+268
+269 public boolean requiresPermission(String permission) {
+270 return getRequiredPermissions().contains(permission) || scope.requiresPermission(permission);
+271 }
+272
+273 public String getPrefSubCommand() {
+274 return prefSubCommand;
+275 }
+276
+277 public String getSyntaxText() {
+278 return syntaxText;
+279 }
+280
+281 public String getCommand() {
+282 return command;
+283 }
+284
+285 public void addSubcommand(String cmd) {
+286 this.registeredSubcommands.add(cmd);
287 }
-288}
+288 public void addSubcommands(Collection<String> cmd) {
+289 this.registeredSubcommands.addAll(cmd);
+290 }
+291}
diff --git a/docs/acf-jda/co/aikar/commands/JDACommandExecutionContext.html b/docs/acf-jda/co/aikar/commands/JDACommandExecutionContext.html
index 6081687f..dcad747b 100644
--- a/docs/acf-jda/co/aikar/commands/JDACommandExecutionContext.html
+++ b/docs/acf-jda/co/aikar/commands/JDACommandExecutionContext.html
@@ -140,7 +140,7 @@ extends co.aikar.commands.CommandExecutionContext<
diff --git a/docs/acf-jda/co/aikar/commands/JDARootCommand.html b/docs/acf-jda/co/aikar/commands/JDARootCommand.html
index 673aa68f..8f4f258c 100644
--- a/docs/acf-jda/co/aikar/commands/JDARootCommand.html
+++ b/docs/acf-jda/co/aikar/commands/JDARootCommand.html
@@ -18,7 +18,7 @@
catch(err) {
}
//-->
-var methods = {"i0":10,"i1":18,"i2":18,"i3":18,"i4":10,"i5":10,"i6":18,"i7":10,"i8":10,"i9":10,"i10":18,"i11":18};
+var methods = {"i0":10,"i1":18,"i2":18,"i3":18,"i4":10,"i5":10,"i6":18,"i7":10,"i8":18,"i9":10,"i10":10,"i11":18,"i12":18,"i13":18};
var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"],16:["t5","Default Methods"]};
var altColor = "altColor";
var rowColor = "rowColor";
@@ -164,26 +164,34 @@ extends getDefCommand()
+default String |
+getDescription() |
+
+
co.aikar.commands.CommandManager |
getManager() |
-
+
com.google.common.collect.SetMultimap<String,co.aikar.commands.RegisteredCommand> |
getSubCommands() |
-
+
default List<String> |
getTabCompletions(co.aikar.commands.CommandIssuer arg0,
String arg1,
String[] arg2) |
-
+
default List<String> |
getTabCompletions(co.aikar.commands.CommandIssuer arg0,
String arg1,
String[] arg2,
boolean arg3) |
+
+default String |
+getUsage() |
+
diff --git a/docs/acf-jda/src-html/co/aikar/commands/JDACommandContexts.html b/docs/acf-jda/src-html/co/aikar/commands/JDACommandContexts.html
index 3e3e1a8e..fc8df8eb 100644
--- a/docs/acf-jda/src-html/co/aikar/commands/JDACommandContexts.html
+++ b/docs/acf-jda/src-html/co/aikar/commands/JDACommandContexts.html
@@ -35,7 +35,7 @@
027 });