Updated JavaDocs from PR #116

This commit is contained in:
Proximyst
2018-05-31 19:10:23 -04:00
committed by Aikar
parent 0b65989c7b
commit 0be34242db
15 changed files with 484 additions and 21 deletions
@@ -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> {}