diff --git a/src/main/java/net/pingex/dcf/commands/AnnotatedCommand.java b/src/main/java/net/pingex/dcf/commands/AnnotatedCommand.java new file mode 100644 index 0000000..e05c2bd --- /dev/null +++ b/src/main/java/net/pingex/dcf/commands/AnnotatedCommand.java @@ -0,0 +1,39 @@ +package net.pingex.dcf.commands; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicated a method which is in fact a command + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface AnnotatedCommand +{ + /** + * Main name of the command + */ + String name(); + + /** + * Command can also be called using the following list of aliases + */ + String[] aliases() default {}; + + /** + * Description of the command. + */ + String description() default Command.Defaults.DESCRIPTION; + + /** + * Is the command enabled ? Can it be invoked ? + */ + boolean isEnabled() default Command.Defaults.IS_ENABLED; + + /** + * Command usage help + */ + String usage() default Command.Defaults.USAGE; +} diff --git a/src/main/java/net/pingex/dcf/commands/Command.java b/src/main/java/net/pingex/dcf/commands/Command.java index dc740a2..564b82a 100644 --- a/src/main/java/net/pingex/dcf/commands/Command.java +++ b/src/main/java/net/pingex/dcf/commands/Command.java @@ -1,5 +1,6 @@ package net.pingex.dcf.commands; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -141,12 +142,23 @@ public abstract class Command implements ICommandExecutor return new Command(name, aliases, description, isEnabled, usage) { @Override - public void execute(List arguments) + public void execute(List arguments) throws Throwable { toExecute.execute(arguments); } }; } + + /** + * Build a new Command using a method and a target object (for use with annotated command) + * @param target The method which needs to be invoked + * @param invokable The matching object + * @return Built command. + */ + public Command build(Method target, Object invokable) + { + return build(arguments -> target.invoke(invokable)); + } } /** @@ -159,6 +171,21 @@ public abstract class Command implements ICommandExecutor return new Builder(name); } + /** + * Create a new command object from an annotated method + * @param target Target method + * @return A new created command built from the method, or `null` if the method is not a valid command. + */ + public static Command buildFromAnnotatedCommand(AnnotatedCommand meta, Method target, Object invokable) + { + return builder(meta.name()) + .aliases(Arrays.asList(meta.aliases())) + .description(meta.description()) + .enabled(meta.isEnabled()) + .usage(meta.usage()) + .build(target, invokable); + } + public String getName() { return name; diff --git a/src/main/java/net/pingex/dcf/commands/CommandHandler.java b/src/main/java/net/pingex/dcf/commands/CommandHandler.java index 1cf0099..6d2d0c4 100644 --- a/src/main/java/net/pingex/dcf/commands/CommandHandler.java +++ b/src/main/java/net/pingex/dcf/commands/CommandHandler.java @@ -3,9 +3,11 @@ package net.pingex.dcf.commands; import net.pingex.dcf.commands.parser.BasicParser; import net.pingex.dcf.commands.parser.ICommandParser; import net.pingex.dcf.commands.parser.ParserException; +import net.pingex.dcf.modularity.PluginWrapper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import sx.blah.discord.handle.impl.events.MessageReceivedEvent; +import java.lang.reflect.Method; import java.util.List; /** @@ -41,7 +43,42 @@ public class CommandHandler return; // Not a command } LOGGER.debug("Attempting to run command {} by user #{}.", command, event.getMessage().getAuthor().getID()); + } + + /** + * Registers a plugin's commands, if any + * @param pluginWrapper Target plugin + */ + public static void registerPlugin(PluginWrapper pluginWrapper) + { + LOGGER.debug("Registering commands for plugin {}.", pluginWrapper.getId()); + + // Raw commands + if(IWithCommands.class.isAssignableFrom(pluginWrapper.getInstance().getClass())) + { + LOGGER.debug("Plugin has commands (raw commands)."); + ((IWithCommands) pluginWrapper.getInstance()).getCommands().forEach(CommandRegistry::registerCommand); + } + // Annotated commands + if(IWithAnnotatedCommands.class.isAssignableFrom(pluginWrapper.getInstance().getClass())) + { + LOGGER.debug("Plugin has commands (annotated commands)."); + ((IWithAnnotatedCommands) pluginWrapper.getInstance()).getAnnotatedObjects().forEach(e -> + { + for(Method i : e.getClass().getMethods()) + if(i.isAnnotationPresent(AnnotatedCommand.class)) + CommandRegistry.registerCommand(Command.buildFromAnnotatedCommand(i.getAnnotation(AnnotatedCommand.class), i, e)); + }); + } + } + /** + * Unregisters a plugin's commands, if any + * @param pluginWrapper Target plugin + */ + public static void unregisterPlugin(PluginWrapper pluginWrapper) + { + LOGGER.debug("Removing commands for plugin {}.", pluginWrapper.getId()); } } diff --git a/src/main/java/net/pingex/dcf/commands/ICommandExecutor.java b/src/main/java/net/pingex/dcf/commands/ICommandExecutor.java index 6a8f433..667c6e2 100644 --- a/src/main/java/net/pingex/dcf/commands/ICommandExecutor.java +++ b/src/main/java/net/pingex/dcf/commands/ICommandExecutor.java @@ -8,5 +8,5 @@ import java.util.List; @FunctionalInterface public interface ICommandExecutor { - void execute(List arguments); + void execute(List arguments) throws Throwable; } diff --git a/src/main/java/net/pingex/dcf/commands/IWithAnnotatedCommands.java b/src/main/java/net/pingex/dcf/commands/IWithAnnotatedCommands.java new file mode 100644 index 0000000..e81e9e4 --- /dev/null +++ b/src/main/java/net/pingex/dcf/commands/IWithAnnotatedCommands.java @@ -0,0 +1,16 @@ +package net.pingex.dcf.commands; + +import java.util.Set; + +/** + * Indicates a plugin which can run commands (using annotated commands) + */ +@FunctionalInterface +public interface IWithAnnotatedCommands +{ + /** + * Gives all annotated commands for this plugin. + * @return A set of objects which contains annotated commands. + */ + Set getAnnotatedObjects(); +} diff --git a/src/main/java/net/pingex/dcf/commands/IWithCommands.java b/src/main/java/net/pingex/dcf/commands/IWithCommands.java new file mode 100644 index 0000000..48c9a4e --- /dev/null +++ b/src/main/java/net/pingex/dcf/commands/IWithCommands.java @@ -0,0 +1,16 @@ +package net.pingex.dcf.commands; + +import java.util.Set; + +/** + * Indicates a plugin which can run commands (using commands object) + */ +@FunctionalInterface +public interface IWithCommands +{ + /** + * Give all commands + * @return ALL THE COMMANDS \o/ + */ + Set getCommands(); +}