mirror of
https://github.com/aikar/commands.git
synced 2026-06-06 17:02:18 +00:00
Updated JavaDocs from PR #116
This commit is contained in:
@@ -60,6 +60,18 @@ final class ACFPatterns {
|
||||
.expirationPolicy(ExpirationPolicy.ACCESSED)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Gets a pattern and compiles it.
|
||||
* If the pattern is stored already in {@link #patternCache}, it will simply fetch it from there.
|
||||
* If it is not, it will store it there for further use.
|
||||
* <p>
|
||||
* The {@link #patternCache} does not contain the constant patterns defined in this class.
|
||||
*
|
||||
* @param pattern
|
||||
* The raw pattern in a String.
|
||||
*
|
||||
* @return The pattern which has been cached.
|
||||
*/
|
||||
public static Pattern getPattern(String pattern) {
|
||||
return patternCache.computeIfAbsent(pattern, s -> Pattern.compile(pattern));
|
||||
}
|
||||
|
||||
@@ -29,25 +29,111 @@ import java.util.regex.Pattern;
|
||||
|
||||
abstract class AnnotationLookups {
|
||||
|
||||
/**
|
||||
* This checks whether the {@link AnnotatedElement} given has an annotation of the type given as annoClass.
|
||||
*
|
||||
* Runs through annotation processing
|
||||
*
|
||||
* @param object
|
||||
* The element to check whether has an annotation of annoClass.
|
||||
* @param annoClass
|
||||
* The annotation type in form of a class.
|
||||
*
|
||||
* @return Whether an annotation of annoClass is attached to element.
|
||||
*
|
||||
* @see #hasAnnotation(AnnotatedElement, Class, boolean)
|
||||
*/
|
||||
boolean hasAnnotation(AnnotatedElement object, Class<? extends Annotation> annoClass) {
|
||||
return getAnnotationValue(object, annoClass, Annotations.NOTHING) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This checks whether the {@link AnnotatedElement} given has an annotation of the type given as annoClass.
|
||||
* If the value is empty/null and allowEmpty is false, it will return false.
|
||||
*
|
||||
* @param object
|
||||
* The element to check whether has an annotation of annoClass.
|
||||
* @param annoClass
|
||||
* The annotation type in form of a class.
|
||||
* @param allowEmpty
|
||||
* Whether or not to allow empty/null values.
|
||||
*
|
||||
* @return Whether an annotation of annoClass is attached to element, and if allowEmpty is false, whether it has a value.
|
||||
*/
|
||||
boolean hasAnnotation(AnnotatedElement object, Class<? extends Annotation> annoClass, boolean allowEmpty) {
|
||||
return getAnnotationValue(object, annoClass, Annotations.NOTHING | (allowEmpty ? 0 : Annotations.NO_EMPTY)) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This fetches all the values the {@link AnnotatedElement}'s annotation of type annoClass has.
|
||||
* If the value contains a pipe (|), it will split on this and return an array of more indicies than 1.
|
||||
*
|
||||
* @param object
|
||||
* The element to check the value of the annotation of type annoClass.
|
||||
* @param annoClass
|
||||
* The annotation type in form of a class.
|
||||
*
|
||||
* @return All the values of annoClass on the object split by pipe (|). If the value is empty, this is null.
|
||||
*
|
||||
* @see #getAnnotationValues(AnnotatedElement, Class, Pattern, int)
|
||||
*/
|
||||
String[] getAnnotationValues(AnnotatedElement object, Class<? extends Annotation> annoClass) {
|
||||
return getAnnotationValues(object, annoClass, ACFPatterns.PIPE, Annotations.REPLACEMENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* This fetches all the values the {@link AnnotatedElement}'s annotation of type annoClass has.
|
||||
* The value is split by the pattern given and return an array of more indicies than 1.
|
||||
*
|
||||
* @param object
|
||||
* The element to check the value of the annotation of type annoClass.
|
||||
* @param annoClass
|
||||
* The annotation type in form of a class.
|
||||
* @param pattern
|
||||
* The pattern the value element is split on.
|
||||
*
|
||||
* @return All the values of annoClass on the object split by the pattern given. If the value is empty, this is null.
|
||||
*
|
||||
* @see #getAnnotationValues(AnnotatedElement, Class, Pattern, int)
|
||||
*/
|
||||
String[] getAnnotationValues(AnnotatedElement object, Class<? extends Annotation> annoClass, Pattern pattern) {
|
||||
return getAnnotationValues(object, annoClass, pattern, Annotations.REPLACEMENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* This fetches all the values the {@link AnnotatedElement}'s annotation of type annoClass has.
|
||||
* The value is split by all pipes (|), but must follow the options.
|
||||
*
|
||||
* @param object
|
||||
* The element to check the value of the annotation of type annoClass.
|
||||
* @param annoClass
|
||||
* The annotation type in form of a class.
|
||||
* @param options
|
||||
* The options to use. If several options are wanted, use the OR operator (opt1 | opt2).
|
||||
*
|
||||
* @return All the values of annoClass on the object split by a pipe (|). Nullability depends on options.
|
||||
*
|
||||
* @see #getAnnotationValues(AnnotatedElement, Class, Pattern, int)
|
||||
*/
|
||||
String[] getAnnotationValues(AnnotatedElement object, Class<? extends Annotation> annoClass, int options) {
|
||||
return getAnnotationValues(object, annoClass, ACFPatterns.PIPE, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* This fetches all the values the {@link AnnotatedElement}'s annotation of type annoClass has.
|
||||
* The value is split by the pattern given, and must also follow the options.
|
||||
*
|
||||
* @param object
|
||||
* The element to check the value of the annotation of type annoClass.
|
||||
* @param annoClass
|
||||
* The annotation type in form of a class.
|
||||
* @param options
|
||||
* The options to use. If several options are wanted, use the OR operator (opt1 | opt2).
|
||||
* @param pattern
|
||||
* The pattern to split by each occurrence of.
|
||||
*
|
||||
* @return All the values of annoClass on the object split by the pattern given. Nullability depends on options.
|
||||
*/
|
||||
String[] getAnnotationValues(AnnotatedElement object, Class<? extends Annotation> annoClass, Pattern pattern, int options) {
|
||||
String value = getAnnotationValue(object, annoClass, options);
|
||||
if (value == null) {
|
||||
@@ -56,9 +142,32 @@ abstract class AnnotationLookups {
|
||||
return pattern.split(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the {@link AnnotatedElement}'s annotation of type annoClass as a string.
|
||||
*
|
||||
* @param object
|
||||
* The element to check the value of the annotation of type annoClass.
|
||||
* @param annoClass
|
||||
* The annotation type in form of a class.
|
||||
*
|
||||
* @return The value of the annotation on the object given.
|
||||
*/
|
||||
String getAnnotationValue(AnnotatedElement object, Class<? extends Annotation> annoClass) {
|
||||
return getAnnotationValue(object, annoClass, Annotations.REPLACEMENTS);
|
||||
}
|
||||
|
||||
abstract String getAnnotationValue(AnnotatedElement annotation, Class<? extends Annotation> annoClass, int options);
|
||||
/**
|
||||
* Gets the value of the {@link AnnotatedElement}'s annotation of type annoClass as a string.
|
||||
* The value has to follow the given options.
|
||||
*
|
||||
* @param object
|
||||
* The element to check the value of the annotation of type annoClass.
|
||||
* @param annoClass
|
||||
* The annotation type in form of a class.
|
||||
* @param options
|
||||
* The options to use. If several options are wanted, use the OR operator (opt1 | opt2).
|
||||
*
|
||||
* @return The value of the annotation on the object given. Nullability depends on options.
|
||||
*/
|
||||
abstract String getAnnotationValue(AnnotatedElement object, Class<? extends Annotation> annoClass, int options);
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ class Annotations <M extends CommandManager> extends AnnotationLookups {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
String getAnnotationValue(AnnotatedElement element, Class<? extends Annotation> annoClass, int options) {
|
||||
Annotation annotation = element.getAnnotation(annoClass);
|
||||
String getAnnotationValue(AnnotatedElement object, Class<? extends Annotation> annoClass, int options) {
|
||||
Annotation annotation = object.getAnnotation(annoClass);
|
||||
String value = null;
|
||||
|
||||
if (annotation != null) {
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -64,8 +65,17 @@ import java.util.stream.Stream;
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class BaseCommand {
|
||||
|
||||
public static final String CATCHUNKNOWN = "__catchunknown";
|
||||
public static final String DEFAULT = "__default";
|
||||
/**
|
||||
* This is a field which contains the magic key in the {@link #subCommands} map for the method to catch any unknown
|
||||
* argument to command states.
|
||||
*/
|
||||
static final String CATCHUNKNOWN = "__catchunknown";
|
||||
/**
|
||||
* This is a field which contains the magic key in the {@link #subCommands} map for the method which is default for the
|
||||
* entire base command.
|
||||
*/
|
||||
static final String DEFAULT = "__default";
|
||||
|
||||
final SetMultimap<String, RegisteredCommand> subCommands = HashMultimap.create();
|
||||
final Map<Class<?>, String> contextFlags = Maps.newHashMap();
|
||||
private Method preCommandHandler;
|
||||
@@ -77,16 +87,42 @@ public abstract class BaseCommand {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
private String[] origArgs;
|
||||
CommandManager<?, ?, ?, ?, ?, ?> manager = null;
|
||||
|
||||
/**
|
||||
* The command which owns this. This may be null if there are no owners.
|
||||
*/
|
||||
BaseCommand parentCommand;
|
||||
Map<String, RootCommand> registeredCommands = new HashMap<>();
|
||||
String description;
|
||||
String commandName;
|
||||
String permission;
|
||||
String conditions;
|
||||
/**
|
||||
* The description of the command. This may be null if no description has been provided.
|
||||
* Used for help documentation
|
||||
*/
|
||||
@Nullable String description;
|
||||
/**
|
||||
* The name of the command. This may be null if no name has been provided.
|
||||
*/
|
||||
@Nullable String commandName;
|
||||
/**
|
||||
* The permission of the command. This may be null if no permission has been provided.
|
||||
*/
|
||||
@Nullable String permission;
|
||||
/**
|
||||
* The conditions of the command. This may be null if no conditions has been provided.
|
||||
*/
|
||||
@Nullable String conditions;
|
||||
|
||||
/**
|
||||
* The handler of all uncaught exceptions thrown by the user's command implementation.
|
||||
*/
|
||||
private ExceptionHandler exceptionHandler = null;
|
||||
CommandOperationContext lastCommandOperationContext;
|
||||
private String parentSubcommand;
|
||||
/**
|
||||
* The last operative context data of this command. This may be null if this command hasn't been run yet.
|
||||
*/
|
||||
@Nullable CommandOperationContext lastCommandOperationContext;
|
||||
/**
|
||||
* If a parent exists to this command, and it has a Subcommand annotation, prefix all subcommands in this class with this
|
||||
*/
|
||||
@Nullable private String parentSubcommand;
|
||||
|
||||
public BaseCommand() {}
|
||||
public BaseCommand(String cmd) {
|
||||
@@ -120,9 +156,27 @@ public abstract class BaseCommand {
|
||||
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.
|
||||
*
|
||||
* @param manager
|
||||
* The manager to register as this command's owner and handler.
|
||||
*/
|
||||
void onRegister(CommandManager manager) {
|
||||
onRegister(manager, this.commandName);
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called whenever the command gets registered.
|
||||
* It sets all required fields correctly and injects dependencies.
|
||||
*
|
||||
* @param manager
|
||||
* The manager to register as this command's owner and handler.
|
||||
* @param cmd
|
||||
* The command name to use register with.
|
||||
*/
|
||||
void onRegister(CommandManager manager, String cmd) {
|
||||
manager.injectDependencies(this);
|
||||
this.manager = manager;
|
||||
@@ -160,6 +214,12 @@ public abstract class BaseCommand {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This recursively registers all subclasses of the command as subcommands, if they are of type {@link BaseCommand}.
|
||||
*
|
||||
* @param cmd
|
||||
* The command name of this command.
|
||||
*/
|
||||
private void registerSubclasses(String cmd) {
|
||||
for (Class<?> clazz : this.getClass().getDeclaredClasses()) {
|
||||
if (BaseCommand.class.isAssignableFrom(clazz)) {
|
||||
@@ -191,6 +251,9 @@ public abstract class BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This registers all subcommands of the command.
|
||||
*/
|
||||
private void registerSubcommands() {
|
||||
final Annotations annotations = manager.getAnnotations();
|
||||
boolean foundDefault = false;
|
||||
@@ -256,6 +319,14 @@ public abstract class BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subcommand name of the method given.
|
||||
*
|
||||
* @param method
|
||||
* The method to check.
|
||||
*
|
||||
* @return The name of the subcommand. It returns null if the input doesn't have {@link Subcommand} attached.
|
||||
*/
|
||||
private String getSubcommandValue(Method method) {
|
||||
final String sub = manager.getAnnotations().getAnnotationValue(method, Subcommand.class, Annotations.NOTHING);
|
||||
if (sub == null) {
|
||||
@@ -279,6 +350,14 @@ public abstract class BaseCommand {
|
||||
return ACFUtil.join(subList, " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given {@link BaseCommand cmd} as a child of the {@link RootCommand} linked to the name given.
|
||||
*
|
||||
* @param name
|
||||
* Name of the parent to cmd.
|
||||
* @param cmd
|
||||
* The {@link BaseCommand} to add as a child to the {@link RootCommand} owned name field.
|
||||
*/
|
||||
private void register(String name, BaseCommand cmd) {
|
||||
String nameLower = name.toLowerCase();
|
||||
RootCommand rootCommand = manager.obtainRootCommand(nameLower);
|
||||
@@ -287,6 +366,14 @@ public abstract class BaseCommand {
|
||||
this.registeredCommands.put(nameLower, rootCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given {@link Method} as a subcommand.
|
||||
*
|
||||
* @param method
|
||||
* The method to register as a subcommand.
|
||||
* @param subCommand
|
||||
* The subcommand's name(s).
|
||||
*/
|
||||
private void registerSubcommand(Method method, String subCommand) {
|
||||
subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase());
|
||||
final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand);
|
||||
@@ -373,13 +460,13 @@ public abstract class BaseCommand {
|
||||
}
|
||||
|
||||
if (subCommands.get(DEFAULT) != null && args.length == 0) {
|
||||
executeSubcommand(commandContext, DEFAULT, issuer, args);
|
||||
findAndExecuteCommand(commandContext, DEFAULT, issuer, args);
|
||||
} else if (subCommands.get(CATCHUNKNOWN) != null) {
|
||||
if (!executeSubcommand(commandContext, CATCHUNKNOWN, issuer, args)) {
|
||||
if (!findAndExecuteCommand(commandContext, CATCHUNKNOWN, issuer, args)) {
|
||||
help(issuer, args);
|
||||
}
|
||||
} else if (subCommands.get(DEFAULT) != null) {
|
||||
executeSubcommand(commandContext, DEFAULT, issuer, args);
|
||||
findAndExecuteCommand(commandContext, DEFAULT, issuer, args);
|
||||
}
|
||||
|
||||
} finally {
|
||||
@@ -387,11 +474,23 @@ public abstract class BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the registered command of the given arguments.
|
||||
* @param args
|
||||
* The arguments given by the user.
|
||||
*
|
||||
* @return The subcommand or null if none were found.
|
||||
*
|
||||
* @see #findSubCommand(String[])
|
||||
*/
|
||||
RegisteredCommand<?> getRegisteredCommand(String[] args) {
|
||||
final CommandSearch cmd = findSubCommand(args);
|
||||
return cmd != null ? cmd.cmd : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is ran after any command operation has been performed.
|
||||
*/
|
||||
private void postCommandOperation() {
|
||||
CommandManager.commandOperationContext.get().pop();
|
||||
execSubcommand = null;
|
||||
@@ -399,6 +498,22 @@ public abstract class BaseCommand {
|
||||
origArgs = new String[]{};
|
||||
}
|
||||
|
||||
/**
|
||||
* This is ran before any command operation has been performed.
|
||||
*
|
||||
* @param issuer
|
||||
* The user who executed the command.
|
||||
* @param commandLabel
|
||||
* The label the user used to execute the command. This is not the command name, but their input.
|
||||
* When there is multiple aliases, this is which alias was used
|
||||
* @param args
|
||||
* The arguments passed to the command when executing it.
|
||||
* @param isAsync
|
||||
* Whether the command is executed off of the main thread.
|
||||
*
|
||||
* @return The context which is being registered to the {@link CommandManager}'s {@link
|
||||
* CommandManager#commandOperationContext thread local stack}.
|
||||
*/
|
||||
private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
|
||||
Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get();
|
||||
CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync);
|
||||
@@ -410,16 +525,48 @@ public abstract class BaseCommand {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current command issuer.
|
||||
*
|
||||
* @return The current command issuer.
|
||||
*/
|
||||
public CommandIssuer getCurrentCommandIssuer() {
|
||||
return CommandManager.getCurrentCommandIssuer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current command manager.
|
||||
*
|
||||
* @return The current command manager.
|
||||
*/
|
||||
public CommandManager getCurrentCommandManager() {
|
||||
return CommandManager.getCurrentCommandManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a subcommand of the given arguments.
|
||||
*
|
||||
* @param args
|
||||
* The arguments the user input.
|
||||
*
|
||||
* @return The identified subcommand.
|
||||
*
|
||||
* @see #findSubCommand(String[], boolean)
|
||||
*/
|
||||
private CommandSearch findSubCommand(String[] args) {
|
||||
return findSubCommand(args, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a subcommand of the given arguments.
|
||||
*
|
||||
* @param args
|
||||
* The arguments the user input.
|
||||
* @param completion
|
||||
* Whether or not completion of arguments should kick in. This may end up with worse than wanted results.
|
||||
*
|
||||
* @return The identified subcommand.
|
||||
*/
|
||||
private CommandSearch findSubCommand(String[] args, boolean completion) {
|
||||
for (int i = args.length; i >= 0; i--) {
|
||||
String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase();
|
||||
@@ -470,13 +617,49 @@ public abstract class BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Please use command conditions for restricting execution
|
||||
* @deprecated See {@link CommandConditions}
|
||||
* @param issuer
|
||||
* @param cmd
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets tab completed data from the given command from the user.
|
||||
*
|
||||
* @param issuer
|
||||
* The user who executed the tabcomplete.
|
||||
* @param commandLabel
|
||||
* The label which is being used by the user.
|
||||
* @param args
|
||||
* The arguments the user has typed so far.
|
||||
*
|
||||
* @return All possibilities in the tab complete.
|
||||
*/
|
||||
public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) {
|
||||
return tabComplete(issuer, commandLabel, args, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tab complete suggestions from a given command. This will automatically find anything
|
||||
* which is valid for the specified command through the command's implementation.
|
||||
*
|
||||
* @param issuer
|
||||
* The issuer of the command.
|
||||
* @param commandLabel
|
||||
* The command name as entered by the user instead of the ACF registered name.
|
||||
* @param args
|
||||
* All arguments entered by the user.
|
||||
* @param isAsync
|
||||
* Whether this is run off of the main thread.
|
||||
*
|
||||
* @return The possibilities to tab complete in no particular order.
|
||||
*/
|
||||
public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync)
|
||||
throws IllegalArgumentException {
|
||||
|
||||
@@ -506,6 +689,15 @@ public abstract class BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all subcommands which are possible to tabcomplete.
|
||||
*
|
||||
* @param issuer
|
||||
* The command issuer.
|
||||
* @param args
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) {
|
||||
final Set<String> cmds = new HashSet<>();
|
||||
final int cmdIndex = Math.max(0, args.length - 1);
|
||||
@@ -525,6 +717,22 @@ public abstract class BaseCommand {
|
||||
return new ArrayList<>(cmds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete a command properly per issuer and input.
|
||||
*
|
||||
* @param issuer
|
||||
* The user who executed this.
|
||||
* @param cmd
|
||||
* The command to be completed.
|
||||
* @param args
|
||||
* All arguments given by the user.
|
||||
* @param commandLabel
|
||||
* The command name the user used.
|
||||
* @param isAsync
|
||||
* Whether the command was executed async.
|
||||
*
|
||||
* @return All results to complete the command.
|
||||
*/
|
||||
private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) {
|
||||
if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0 || cmd.complete == null) {
|
||||
return ImmutableList.of();
|
||||
@@ -534,6 +742,16 @@ public abstract class BaseCommand {
|
||||
return filterTabComplete(args[args.length-1], cmds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the actual args in string form the user typed
|
||||
* This returns a list of all tab complete options which are possible with the given argument and commands.
|
||||
* @param arg
|
||||
* Argument which was pressed tab on.
|
||||
* @param cmds
|
||||
* The possibilities to return.
|
||||
*
|
||||
* @return All possible options. This may be empty.
|
||||
*/
|
||||
private static List<String> filterTabComplete(String arg, List<String> cmds) {
|
||||
return cmds.stream()
|
||||
.distinct()
|
||||
@@ -541,11 +759,30 @@ public abstract class BaseCommand {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
RegisteredCommand getSubcommand(String subcommand) {
|
||||
return getSubcommand(subcommand, false);
|
||||
/**
|
||||
* Gets a registered command under the given subcommand name.
|
||||
*
|
||||
* @param subcommand
|
||||
* The name of the subcommand requested.
|
||||
*
|
||||
* @return The subcommand found or null if none.
|
||||
*/
|
||||
private RegisteredCommand getCommandBySubcommand(String subcommand) {
|
||||
return getCommandBySubcommand(subcommand, false);
|
||||
}
|
||||
|
||||
RegisteredCommand getSubcommand(String subcommand, boolean requireOne) {
|
||||
/**
|
||||
* Gets a registered command under the given name.
|
||||
* If requireOne is true, it won't accept more than a single matching subcommand.
|
||||
*
|
||||
* @param subcommand
|
||||
* Name of the subcommand wanted.
|
||||
* @param requireOne
|
||||
* Whether to only accept 1 result.
|
||||
*
|
||||
* @return The subcommand found, or null if none/too many.
|
||||
*/
|
||||
private RegisteredCommand getCommandBySubcommand(String subcommand, boolean requireOne) {
|
||||
final Set<RegisteredCommand> commands = subCommands.get(subcommand);
|
||||
if (!commands.isEmpty() && (!requireOne || commands.size() == 1)) {
|
||||
return commands.iterator().next();
|
||||
@@ -553,8 +790,27 @@ public abstract class BaseCommand {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean executeSubcommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) {
|
||||
final RegisteredCommand cmd = this.getSubcommand(subcommand);
|
||||
/**
|
||||
* Internally calls {@link #executeCommand(CommandOperationContext, CommandIssuer, String[], RegisteredCommand)}
|
||||
* and gets through {@link #getCommandBySubcommand(String)}.
|
||||
*
|
||||
* @param commandContext
|
||||
* The command context to use.
|
||||
* @param subcommand
|
||||
* The subcommand to find the executor of.
|
||||
* @param issuer
|
||||
* The issuer who executed the subcommand.
|
||||
* @param args
|
||||
* All arguments given by the issuer.
|
||||
*
|
||||
* @return Whether it found a command or not.
|
||||
*
|
||||
* @see #executeCommand(CommandOperationContext, CommandIssuer, String[], RegisteredCommand)
|
||||
* @see #getCommandBySubcommand(String)
|
||||
* @see RegisteredCommand#invoke(CommandIssuer, List, CommandOperationContext)
|
||||
*/
|
||||
private boolean findAndExecuteCommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) {
|
||||
final RegisteredCommand cmd = this.getCommandBySubcommand(subcommand);
|
||||
if (cmd != null) {
|
||||
executeCommand(commandContext, issuer, args, cmd);
|
||||
return true;
|
||||
@@ -563,6 +819,20 @@ public abstract class BaseCommand {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the precommand and sees whether something is wrong. Ideally, you get false from this.
|
||||
*
|
||||
* @param commandOperationContext
|
||||
* The context to use.
|
||||
* @param cmd
|
||||
* The command executed.
|
||||
* @param issuer
|
||||
* The issuer who executed the command.
|
||||
* @param args
|
||||
* The arguments the issuer provided.
|
||||
*
|
||||
* @return Whether something went wrong.
|
||||
*/
|
||||
private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) {
|
||||
Method pre = this.preCommandHandler;
|
||||
if (pre != null) {
|
||||
@@ -657,7 +927,7 @@ public abstract class BaseCommand {
|
||||
}
|
||||
|
||||
public RegisteredCommand getDefaultRegisteredCommand() {
|
||||
return this.getSubcommand(DEFAULT);
|
||||
return this.getCommandBySubcommand(DEFAULT);
|
||||
}
|
||||
|
||||
public String setContextFlags(Class<?> cls, String flags) {
|
||||
|
||||
@@ -28,6 +28,14 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Allows to add a single or several command alias(es).
|
||||
* In order to add more than one in a single go, use the syntax "alias|otheralias".
|
||||
* You can register as many aliases as wanted in a single value.
|
||||
*
|
||||
* Used on a Class, defines the root command for all subcommands in the base command.
|
||||
* Used on a method, defines a root command alias to that specific command
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface CommandAlias {
|
||||
|
||||
@@ -28,6 +28,11 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Sets the permission required to perform this command.
|
||||
*
|
||||
* Permission format will vary based on implementation platform
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface CommandPermission {
|
||||
|
||||
@@ -28,6 +28,10 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* If used on a method, sets default command handler for the root command of this group
|
||||
* If used on a parameter, sets the value to be used for context resolution
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.PARAMETER})
|
||||
public @interface Default {
|
||||
|
||||
@@ -28,6 +28,10 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Injects a dependency into the field this is attached to.
|
||||
* Any time a new dependency is registered, this will be overwritten.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Dependency {
|
||||
|
||||
@@ -28,6 +28,10 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Sets a description to the parameter or method this is attached to.
|
||||
* This is used in the help menus.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.PARAMETER})
|
||||
public @interface Description {
|
||||
|
||||
@@ -28,6 +28,13 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks the parameter this is attached to as optional.
|
||||
* This will set the parameter as null if it was not provided.
|
||||
* <p>
|
||||
* In the case the language used is Kotlin, Ceylon or any other null-enforcing JVM language,
|
||||
* you will need to allow for a nullable value.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
public @interface Optional {
|
||||
|
||||
@@ -29,6 +29,9 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This runs before any other command method each time it is invoked.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface PreCommand {}
|
||||
|
||||
@@ -29,6 +29,10 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Joins arguments into a single piece of text with the specified separator.
|
||||
* For array based parameters, defines the regex pattern to split on
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
public @interface Split {
|
||||
|
||||
@@ -28,6 +28,15 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies the syntax to be used when executing this command.
|
||||
* It should not include any descriptions of the arguments nor when some are allowed and when they are not.
|
||||
*
|
||||
* Use of this annotation is not necessary. Syntax will be automatically generated for you.
|
||||
* Use this annotation to override automatic syntax
|
||||
*
|
||||
* Use {@link Description} together with the help menu for that purpose.
|
||||
**/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.PARAMETER})
|
||||
public @interface Syntax {
|
||||
|
||||
@@ -27,7 +27,26 @@ import co.aikar.commands.CommandExecutionContext;
|
||||
import co.aikar.commands.CommandIssuer;
|
||||
import co.aikar.commands.InvalidCommandArgument;
|
||||
|
||||
/**
|
||||
* This defines a context resolver, which parses {@link T} from {@link C}.
|
||||
*
|
||||
* @param <T>
|
||||
* The type to be parsed.
|
||||
* @param <C>
|
||||
* The type of the context which the resolver would get its data from.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ContextResolver <T, C extends CommandExecutionContext<?, ? extends CommandIssuer>> {
|
||||
/**
|
||||
* Parses the context of type {@link C} into {@link T}, or throws an exception.
|
||||
*
|
||||
* @param c
|
||||
* The context to parse from.
|
||||
*
|
||||
* @return The parsed instance of the wanted type.
|
||||
*
|
||||
* @throws InvalidCommandArgument
|
||||
* In case the context contains any discrepancies, it will throw this exception.
|
||||
*/
|
||||
T getContext(C c) throws InvalidCommandArgument;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ import co.aikar.commands.CommandExecutionContext;
|
||||
import co.aikar.commands.CommandIssuer;
|
||||
|
||||
/**
|
||||
* Context Resolver that can accept null input
|
||||
* The same as {@link ContextResolver}, however it can accept a null context.
|
||||
*
|
||||
* If the parameter was marked optional, will still be called with an empty args list
|
||||
*
|
||||
* @param <T>
|
||||
* @param <C>
|
||||
*/
|
||||
public interface OptionalContextResolver <T, C extends CommandExecutionContext<?, ? extends CommandIssuer>> extends ContextResolver <T, C> {}
|
||||
|
||||
Reference in New Issue
Block a user