diff --git a/core/src/main/java/co/aikar/commands/BaseCommand.java b/core/src/main/java/co/aikar/commands/BaseCommand.java index 424dcb6a..da3076c9 100644 --- a/core/src/main/java/co/aikar/commands/BaseCommand.java +++ b/core/src/main/java/co/aikar/commands/BaseCommand.java @@ -62,6 +62,17 @@ import java.util.Stack; import java.util.stream.Collectors; import java.util.stream.Stream; +/** + * A Base command is defined as a command group of related commands. + * A BaseCommand does not imply nor enforce that they use the same root command. + * + * It is up to the end user how to organize their command. you could use 1 base command per + * command in your application. + * + * Optionally (and encouraged), you can use the base command to represent a root command, and + * then each actionable command is a sub command + */ + @SuppressWarnings("unused") public abstract class BaseCommand { @@ -76,16 +87,40 @@ public abstract class BaseCommand { */ static final String DEFAULT = "__default"; + /** + * A map of all the registered commands for this base command, keyed to each potential subcommand to access it. + */ final SetMultimap subCommands = HashMultimap.create(); - final Map, String> contextFlags = Maps.newHashMap(); - private Method preCommandHandler; + /** + * A map of flags to pass to Context Resolution for every parameter of the type. This is like an automatic @Flags on each. + */ + final Map, String> contextFlags = Maps.newHashMap(); + + /** + * What method was annoated with {@link PreCommand} to execute before commands. + */ + @Nullable private Method preCommandHandler; + + /** + * What root command the user actually entered to access the currently executing command + */ @SuppressWarnings("WeakerAccess") private String execLabel; + /** + * What subcommand the user actually entered to access the currently executing command + */ @SuppressWarnings("WeakerAccess") private String execSubcommand; + /** + * What arguments the user actually entered after the root command to access the currently executing command + */ @SuppressWarnings("WeakerAccess") private String[] origArgs; + + /** + * The manager this is registered to + */ CommandManager manager = null; /** @@ -125,7 +160,14 @@ public abstract class BaseCommand { @Nullable private String parentSubcommand; public BaseCommand() {} - public BaseCommand(String cmd) { + + /** + * Constructor based defining of commands will be removed in the next version bump. + * @deprecated Please switch to {@link CommandAlias} for defining all root commands. + * @param cmd + */ + @Deprecated + public BaseCommand(@Nullable String cmd) { this.commandName = cmd; } @@ -153,10 +195,6 @@ public abstract class BaseCommand { return origArgs; } - void setParentCommand(BaseCommand command) { - this.parentCommand = command; - } - /** * This should be called whenever the command gets registered. * It sets all required fields correctly and injects dependencies. @@ -177,7 +215,7 @@ public abstract class BaseCommand { * @param cmd * The command name to use register with. */ - void onRegister(CommandManager manager, String cmd) { + private void onRegister(CommandManager manager, String cmd) { manager.injectDependencies(this); this.manager = manager; @@ -237,7 +275,7 @@ public abstract class BaseCommand { } } if (subCommand != null) { - subCommand.setParentCommand(this); + subCommand.parentCommand = this; subCommand.onRegister(manager, cmd); this.subCommands.putAll(subCommand.subCommands); this.registeredCommands.putAll(subCommand.registeredCommands); @@ -258,7 +296,7 @@ public abstract class BaseCommand { final Annotations annotations = manager.getAnnotations(); boolean foundDefault = false; boolean foundCatchUnknown = false; - boolean isParentEmpty = parentSubcommand.isEmpty(); + boolean isParentEmpty = parentSubcommand == null || parentSubcommand.isEmpty(); for (Method method : this.getClass().getMethods()) { method.setAccessible(true); @@ -582,7 +620,7 @@ public abstract class BaseCommand { int required = c.requiredResolvers; int optional = c.optionalResolvers; return extraArgs <= required + optional && (completion || extraArgs >= required); - }).sorted((c1, c2) -> { + }).min((c1, c2) -> { int a = c1.consumeInputResolvers; int b = c2.consumeInputResolvers; @@ -590,7 +628,7 @@ public abstract class BaseCommand { return 0; } return a < b ? 1 : -1; - }).findFirst(); + }); if (optCmd.isPresent()) { cmd = optCmd.get(); } @@ -624,6 +662,7 @@ public abstract class BaseCommand { * @param cmd * @return */ + @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public boolean canExecute(CommandIssuer issuer, RegisteredCommand cmd) { return true; @@ -660,6 +699,7 @@ public abstract class BaseCommand { * * @return The possibilities to tab complete in no particular order. */ + @SuppressWarnings("WeakerAccess") public List tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) throws IllegalArgumentException { diff --git a/core/src/main/java/co/aikar/commands/annotation/CatchUnknown.java b/core/src/main/java/co/aikar/commands/annotation/CatchUnknown.java index 07b16eec..f861c023 100644 --- a/core/src/main/java/co/aikar/commands/annotation/CatchUnknown.java +++ b/core/src/main/java/co/aikar/commands/annotation/CatchUnknown.java @@ -28,6 +28,15 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Defines a method that should receive any unknown command for the related root command. + * + * For example, if a BaseCommand /foo has a method with this, and /foo someunknowncommand is used + * + * If a method is tagged with this annotation, it will catch unknown commands and let you react to them. + * + * Only one instance of this annotation can be used per root command. + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface CatchUnknown { diff --git a/core/src/main/java/co/aikar/commands/annotation/CommandCompletion.java b/core/src/main/java/co/aikar/commands/annotation/CommandCompletion.java index f49979e5..8ad0142e 100644 --- a/core/src/main/java/co/aikar/commands/annotation/CommandCompletion.java +++ b/core/src/main/java/co/aikar/commands/annotation/CommandCompletion.java @@ -28,6 +28,16 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Many implementation platforms have a concept of "Tab Completions", + * where pressing tab will give suggestions on what you can input. + * + * This annotation specifies either static completion values, + * or special @codes that let you define Completion Handlers to dynamically + * populate completion values. + * + * @see {@link co.aikar.commands.CommandCompletions} + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface CommandCompletion { diff --git a/core/src/main/java/co/aikar/commands/annotation/Conditions.java b/core/src/main/java/co/aikar/commands/annotation/Conditions.java index f8192894..00d202c3 100644 --- a/core/src/main/java/co/aikar/commands/annotation/Conditions.java +++ b/core/src/main/java/co/aikar/commands/annotation/Conditions.java @@ -28,6 +28,14 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Specifies conditions that must be met in order to execute this command. + * + * If used on a method or a class, will be checked before parameter context is resolved + * If used on a parameter, will be checked after the context is resolved + * + * @see {@link co.aikar.commands.CommandConditions} + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE}) public @interface Conditions { diff --git a/core/src/main/java/co/aikar/commands/annotation/Flags.java b/core/src/main/java/co/aikar/commands/annotation/Flags.java index 15be0565..b93d9b6f 100644 --- a/core/src/main/java/co/aikar/commands/annotation/Flags.java +++ b/core/src/main/java/co/aikar/commands/annotation/Flags.java @@ -28,6 +28,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Provides configuration options for {@link co.aikar.commands.contexts.ContextResolver}'s to change how they resolve context. + * + * Example: Searching for a player, you might use @Flags("loose") to indicate a fuzzy match instead of an exact match. + * + * If you want to restrict if an issuer can use the command, please use {@link co.aikar.commands.CommandConditions.Condition} instead. + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) public @interface Flags { diff --git a/core/src/main/java/co/aikar/commands/annotation/HelpCommand.java b/core/src/main/java/co/aikar/commands/annotation/HelpCommand.java index 02399354..45e8c73f 100644 --- a/core/src/main/java/co/aikar/commands/annotation/HelpCommand.java +++ b/core/src/main/java/co/aikar/commands/annotation/HelpCommand.java @@ -28,8 +28,18 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * A Shortcut for specifying {@link CatchUnknown}, {@link Default} and {@link Subcommand} on a method. + * Subcommand carries the same value as this annotations value to define the list of subcommands to register for. + * + * a method marked with this annotation should also use a {@link co.aikar.commands.CommandHelp} context parameter to show help. + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface HelpCommand { + /** + * The value to forward to the @Subcommand annotation. Lists which subcommands to register to trigger help + * @return + */ String value() default "help|?|-help|-h|-?"; } diff --git a/core/src/main/java/co/aikar/commands/annotation/HelpSearchTags.java b/core/src/main/java/co/aikar/commands/annotation/HelpSearchTags.java index 0585cedb..0973a0b3 100644 --- a/core/src/main/java/co/aikar/commands/annotation/HelpSearchTags.java +++ b/core/src/main/java/co/aikar/commands/annotation/HelpSearchTags.java @@ -28,6 +28,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Defines additional keywords to feed into the search help system. + * + * For example, if a specific word doesn't make sense to use in the command name or description, but should + * be used for help in discovering the correct command, then you can add it as a tag. + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface HelpSearchTags { diff --git a/core/src/main/java/co/aikar/commands/annotation/Single.java b/core/src/main/java/co/aikar/commands/annotation/Single.java index aab6dfe9..5d061ef1 100644 --- a/core/src/main/java/co/aikar/commands/annotation/Single.java +++ b/core/src/main/java/co/aikar/commands/annotation/Single.java @@ -29,9 +29,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Don't join remaining arguments + * Don't join remaining arguments. Used on String parameters, which normally would combine the remaining arguments */ - @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) public @interface Single {} diff --git a/core/src/main/java/co/aikar/commands/annotation/Subcommand.java b/core/src/main/java/co/aikar/commands/annotation/Subcommand.java index b71d9743..6cb89aee 100644 --- a/core/src/main/java/co/aikar/commands/annotation/Subcommand.java +++ b/core/src/main/java/co/aikar/commands/annotation/Subcommand.java @@ -28,6 +28,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Defines the subcommand that can be used to execute this command. + * This is appended onto the root command for the command group, + * as well as any parent command groups subcommand base. + * + * Defines the part after root command like so: "/rootcommand {@link #value()}". + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface Subcommand { diff --git a/core/src/main/java/co/aikar/commands/annotation/Values.java b/core/src/main/java/co/aikar/commands/annotation/Values.java index 19e5684a..c6cf8194 100644 --- a/core/src/main/java/co/aikar/commands/annotation/Values.java +++ b/core/src/main/java/co/aikar/commands/annotation/Values.java @@ -28,6 +28,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Specifies a list of values that the command input should be validated against, or else show an error. + * + * You may also use {@link CommandCompletion} handler codes here to feed dynamic values and avoid repetition. + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) public @interface Values {