From 02f02dd077ee294d598623da4b7946621619f7d2 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Thu, 17 Dec 2020 16:32:26 +0100 Subject: [PATCH] Added MissingSubcommandException, plus some more internal changes --- .../components/commands/CommandGroup.java | 10 ++- .../components/commands/CommandMethod.java | 6 +- .../commands/CommandMethodArgument.java | 11 +++- .../components/commands/CommandNode.java | 2 +- .../components/commands/ExecutionContext.java | 13 ++++ .../commands/QuartzCommandExecutor.java | 3 +- .../components/commands/TypeCollection.java | 2 + .../exceptions/ArgumentParseException.java | 7 +++ .../commands/exceptions/CommandException.java | 4 ++ .../exceptions/InvalidSenderException.java | 7 +++ .../MissingSubcommandException.java | 63 +++++++++++++++++++ .../UnknownArgumentTypeException.java | 15 +++++ .../quartzlib/tools/text/RawMessage.java | 2 +- ...nTests.java => CommandExecutionTests.java} | 2 +- .../fr/zcraft/ztoaster/ToastCommands.java | 0 .../main/java/fr/zcraft/ztoaster/Toaster.java | 9 ++- 16 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 src/main/java/fr/zcraft/quartzlib/components/commands/ExecutionContext.java create mode 100644 src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/MissingSubcommandException.java create mode 100644 src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/UnknownArgumentTypeException.java rename src/test/java/fr/zcraft/quartzlib/components/commands/{CommandRegistrationTests.java => CommandExecutionTests.java} (93%) rename {src/test => ztoaster/src/main}/java/fr/zcraft/ztoaster/ToastCommands.java (100%) diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/CommandGroup.java b/src/main/java/fr/zcraft/quartzlib/components/commands/CommandGroup.java index 77db87b5..a805d904 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/CommandGroup.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/CommandGroup.java @@ -1,15 +1,17 @@ package fr.zcraft.quartzlib.components.commands; import fr.zcraft.quartzlib.components.commands.exceptions.CommandException; +import fr.zcraft.quartzlib.components.commands.exceptions.MissingSubcommandException; import java.lang.reflect.Field; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.Nullable; -class CommandGroup extends CommandNode { +public class CommandGroup extends CommandNode { private final Class commandGroupClass; @Nullable @@ -52,7 +54,7 @@ private CommandGroup( DiscoveryUtils.getSubCommands(this, typeCollection).forEach(this::addSubCommand); } - public Iterable getSubCommands() { + public Collection getSubCommands() { return this.subCommands.values(); } @@ -91,6 +93,10 @@ void run(Object parentInstance, CommandSender sender, String[] args) throws Comm } private void runSelf(Object instance, CommandSender sender, String[] args) throws CommandException { + if (args.length == 0) { + throw new MissingSubcommandException(this); + } + String commandName = args[0]; CommandNode subCommand = subCommands.get(commandName); // TODO: handle null diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/CommandMethod.java b/src/main/java/fr/zcraft/quartzlib/components/commands/CommandMethod.java index d0edd920..6f0e52f9 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/CommandMethod.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/CommandMethod.java @@ -29,7 +29,7 @@ class CommandMethod { if (parameter.isAnnotationPresent(Sender.class)) { // TODO: check for multiple sender arguments senderArgument = new CommandMethodSenderArgument(parameter, i, typeCollection); } else { - arguments.add(new CommandMethodArgument(parameter, i, typeCollection)); + arguments.add(new CommandMethodArgument(this, parameter, i, typeCollection)); } } @@ -69,4 +69,8 @@ private Object[] parseArguments(CommandSender sender, String[] args) public CommandMethodArgument[] getArguments() { return arguments; } + + public Method getMethod() { + return method; + } } diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/CommandMethodArgument.java b/src/main/java/fr/zcraft/quartzlib/components/commands/CommandMethodArgument.java index 1a151e10..d9c35483 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/CommandMethodArgument.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/CommandMethodArgument.java @@ -1,6 +1,7 @@ package fr.zcraft.quartzlib.components.commands; import fr.zcraft.quartzlib.components.commands.exceptions.ArgumentParseException; +import fr.zcraft.quartzlib.components.commands.exceptions.UnknownArgumentTypeException; import java.lang.reflect.Parameter; public class CommandMethodArgument { @@ -8,10 +9,16 @@ public class CommandMethodArgument { private final int position; private final ArgumentTypeWrapper typeHandler; - public CommandMethodArgument(Parameter parameter, int position, TypeCollection typeCollection) { + public CommandMethodArgument( + CommandMethod parent, + Parameter parameter, + int position, + TypeCollection typeCollection + ) { this.parameter = parameter; this.position = position; - this.typeHandler = typeCollection.findArgumentType(parameter.getType()).get(); // FIXME: handle unknown types + this.typeHandler = typeCollection.findArgumentType(parameter.getType()) + .orElseThrow(() -> new UnknownArgumentTypeException(parent.getMethod(), parameter.getType())); } public Object parse(String raw) throws ArgumentParseException { diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/CommandNode.java b/src/main/java/fr/zcraft/quartzlib/components/commands/CommandNode.java index 365da497..d5371bd0 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/CommandNode.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/CommandNode.java @@ -4,7 +4,7 @@ import org.bukkit.command.CommandSender; import org.jetbrains.annotations.Nullable; -abstract class CommandNode { +public abstract class CommandNode { private final String name; @Nullable private final CommandGroup parent; diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/ExecutionContext.java b/src/main/java/fr/zcraft/quartzlib/components/commands/ExecutionContext.java new file mode 100644 index 00000000..c350bc21 --- /dev/null +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/ExecutionContext.java @@ -0,0 +1,13 @@ +package fr.zcraft.quartzlib.components.commands; + +import org.bukkit.command.CommandSender; + +public class ExecutionContext { + private final CommandSender sender; + private final String[] fullArgs; + + public ExecutionContext(CommandSender sender, String[] fullArgs) { + this.sender = sender; + this.fullArgs = fullArgs; + } +} diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/QuartzCommandExecutor.java b/src/main/java/fr/zcraft/quartzlib/components/commands/QuartzCommandExecutor.java index 9c2fa779..b51c52af 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/QuartzCommandExecutor.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/QuartzCommandExecutor.java @@ -1,6 +1,7 @@ package fr.zcraft.quartzlib.components.commands; import fr.zcraft.quartzlib.components.commands.exceptions.CommandException; +import fr.zcraft.quartzlib.tools.text.RawMessage; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -19,7 +20,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command try { group.run(sender, args); } catch (CommandException e) { - throw new RuntimeException(e); // TODO + RawMessage.send(sender, e.display(sender).build()); } return true; } diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/TypeCollection.java b/src/main/java/fr/zcraft/quartzlib/components/commands/TypeCollection.java index 19600349..c914798b 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/TypeCollection.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/TypeCollection.java @@ -62,6 +62,8 @@ private void registerNativeTypes() { register(new ArgumentTypeWrapper<>(Integer.class, new IntegerArgumentType())); register(new ArgumentTypeWrapper<>(String.class, s -> s)); + register(new ArgumentTypeWrapper<>(int.class, new IntegerArgumentType())); + // Generic types register(new EnumArgumentType()); diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/ArgumentParseException.java b/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/ArgumentParseException.java index b6ff6416..c2e20753 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/ArgumentParseException.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/ArgumentParseException.java @@ -1,4 +1,11 @@ package fr.zcraft.quartzlib.components.commands.exceptions; +import fr.zcraft.quartzlib.components.rawtext.RawText; +import org.bukkit.command.CommandSender; + public class ArgumentParseException extends CommandException { + @Override + public RawText display(CommandSender sender) { + return null; + } } diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/CommandException.java b/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/CommandException.java index ea6c97d8..baa218af 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/CommandException.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/CommandException.java @@ -1,4 +1,8 @@ package fr.zcraft.quartzlib.components.commands.exceptions; +import fr.zcraft.quartzlib.components.rawtext.RawText; +import org.bukkit.command.CommandSender; + public abstract class CommandException extends Exception { + public abstract RawText display(CommandSender sender); } diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/InvalidSenderException.java b/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/InvalidSenderException.java index 68787e5e..ca07d99d 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/InvalidSenderException.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/InvalidSenderException.java @@ -1,4 +1,11 @@ package fr.zcraft.quartzlib.components.commands.exceptions; +import fr.zcraft.quartzlib.components.rawtext.RawText; +import org.bukkit.command.CommandSender; + public class InvalidSenderException extends CommandException { + @Override + public RawText display(CommandSender sender) { + return null; + } } diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/MissingSubcommandException.java b/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/MissingSubcommandException.java new file mode 100644 index 00000000..61d32686 --- /dev/null +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/MissingSubcommandException.java @@ -0,0 +1,63 @@ +package fr.zcraft.quartzlib.components.commands.exceptions; + +import fr.zcraft.quartzlib.components.commands.CommandGroup; +import fr.zcraft.quartzlib.components.commands.CommandNode; +import fr.zcraft.quartzlib.components.i18n.I; +import fr.zcraft.quartzlib.components.rawtext.RawText; +import fr.zcraft.quartzlib.components.rawtext.RawTextPart; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +public class MissingSubcommandException extends CommandException { + private final CommandGroup commandGroup; + + public MissingSubcommandException(CommandGroup commandGroup) { + this.commandGroup = commandGroup; + } + + @Override + public RawText display(CommandSender sender) { + RawTextPart text = new RawText(I.t("Missing subcommand: ")) + .color(ChatColor.RED) + .then("/").color(ChatColor.WHITE) + .then(getParents()).color(ChatColor.AQUA) + .then(" <").style(ChatColor.GRAY) + .then(I.t("sub-command")) + .style(ChatColor.GRAY, ChatColor.UNDERLINE) + .hover(appendSubCommandList(new RawText())) + .then(">").style(ChatColor.GRAY); + + return text.build(); + } + + private String getParents() { + StringBuilder builder = new StringBuilder(); + + CommandGroup group = commandGroup; + + do { + if (builder.length() > 0) { + builder.append(' '); + } + builder.append(group.getName()); + group = group.getParent(); + } while (group != null); + + return builder.toString(); + } + + private RawTextPart appendSubCommandList(RawTextPart text) { + boolean first = true; + text = text.then(I.t("One of the following:\n ")); + for (CommandNode subCommand : commandGroup.getSubCommands()) { + if (!first) { + text = text.then(", ").color(ChatColor.GRAY); + } + first = false; + + text = text.then(subCommand.getName()).color(ChatColor.AQUA); + } + + return text; + } +} diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/UnknownArgumentTypeException.java b/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/UnknownArgumentTypeException.java new file mode 100644 index 00000000..bb0fc275 --- /dev/null +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/exceptions/UnknownArgumentTypeException.java @@ -0,0 +1,15 @@ +package fr.zcraft.quartzlib.components.commands.exceptions; + +import java.lang.reflect.Method; + +public class UnknownArgumentTypeException extends RuntimeException { + public UnknownArgumentTypeException(Method method, Class foundType) { + super(getErrorMessage(method, foundType)); + } + + private static String getErrorMessage(Method method, Class foundType) { + return "Found unknown command argument type: '" + foundType + + "' (found in '" + method.toString() + "'). " + + "Did you forget to register it to the CommandManager?"; + } +} diff --git a/src/main/java/fr/zcraft/quartzlib/tools/text/RawMessage.java b/src/main/java/fr/zcraft/quartzlib/tools/text/RawMessage.java index 302338aa..73ba86f8 100644 --- a/src/main/java/fr/zcraft/quartzlib/tools/text/RawMessage.java +++ b/src/main/java/fr/zcraft/quartzlib/tools/text/RawMessage.java @@ -41,7 +41,7 @@ * Utility to send JSON messages. * *

This tool uses the /tellraw command to send the messages. If the JSON is not correctly - * formatted, the message will not be sent and a Runtime exception containing the exception throw by + * formatted, the message will not be sent and a Runtime exception containing the exception thrown by * the vanilla /tellraw command will be thrown.

*/ public final class RawMessage { diff --git a/src/test/java/fr/zcraft/quartzlib/components/commands/CommandRegistrationTests.java b/src/test/java/fr/zcraft/quartzlib/components/commands/CommandExecutionTests.java similarity index 93% rename from src/test/java/fr/zcraft/quartzlib/components/commands/CommandRegistrationTests.java rename to src/test/java/fr/zcraft/quartzlib/components/commands/CommandExecutionTests.java index 04adafde..c1dc1569 100644 --- a/src/test/java/fr/zcraft/quartzlib/components/commands/CommandRegistrationTests.java +++ b/src/test/java/fr/zcraft/quartzlib/components/commands/CommandExecutionTests.java @@ -6,7 +6,7 @@ import org.junit.Before; import org.junit.Test; -public class CommandRegistrationTests extends MockedToasterTest { +public class CommandExecutionTests extends MockedToasterTest { private CommandManager commands; @Before diff --git a/src/test/java/fr/zcraft/ztoaster/ToastCommands.java b/ztoaster/src/main/java/fr/zcraft/ztoaster/ToastCommands.java similarity index 100% rename from src/test/java/fr/zcraft/ztoaster/ToastCommands.java rename to ztoaster/src/main/java/fr/zcraft/ztoaster/ToastCommands.java diff --git a/ztoaster/src/main/java/fr/zcraft/ztoaster/Toaster.java b/ztoaster/src/main/java/fr/zcraft/ztoaster/Toaster.java index 3c1d8df0..7b221da3 100644 --- a/ztoaster/src/main/java/fr/zcraft/ztoaster/Toaster.java +++ b/ztoaster/src/main/java/fr/zcraft/ztoaster/Toaster.java @@ -30,6 +30,7 @@ package fr.zcraft.ztoaster; +import fr.zcraft.quartzlib.components.commands.CommandManager; import fr.zcraft.quartzlib.components.gui.Gui; import fr.zcraft.quartzlib.components.i18n.I18n; import fr.zcraft.quartzlib.components.scoreboard.Sidebar; @@ -64,7 +65,8 @@ public class Toaster extends QuartzPlugin implements Listener { */ private Sidebar toasterSidebar; - public Toaster () {} + public Toaster() { + } protected Toaster(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) { super(loader, description, dataFolder, file); @@ -72,6 +74,7 @@ protected Toaster(JavaPluginLoader loader, PluginDescriptionFile description, Fi /** * . + * * @return The id for a new toast. */ public static int newToastId() { @@ -80,6 +83,7 @@ public static int newToastId() { /** * . + * * @return an array of all the toasts ever created (until toaster restart). */ public static Toast[] getToasts() { @@ -106,7 +110,8 @@ public void onEnable() { loadComponents(Gui.class, ToasterWorker.class, SidebarScoreboard.class, I18n.class); - // Commands.register("toaster", AddCommand.class, OpenCommand.class, ListCommand.class); + new CommandManager() + .registerCommand("toaster", ToastCommands.class, ToastCommands::new); I18n.useDefaultPrimaryLocale(); I18n.setFallbackLocale(Locale.US);