From fcd269b6e7e491863a272e2ae971a711ff4ae487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Fri, 9 Oct 2020 20:44:17 +0200 Subject: [PATCH] :sparkles: Add String[] args --- .../cloud/commandframework/CommandTree.java | 9 ++ .../DelegatingCommandSuggestionEngine.java | 2 + .../parser/StandardParserRegistry.java | 2 + .../standard/StringArrayArgument.java | 122 ++++++++++++++++++ .../context/CommandContext.java | 10 ++ .../brigadier/CloudBrigadierManager.java | 4 + .../examples/bukkit/ExamplePlugin.java | 27 ++++ 7 files changed, 176 insertions(+) create mode 100644 cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArrayArgument.java diff --git a/cloud-core/src/main/java/cloud/commandframework/CommandTree.java b/cloud-core/src/main/java/cloud/commandframework/CommandTree.java index fe4632d3..7c81a9a6 100644 --- a/cloud-core/src/main/java/cloud/commandframework/CommandTree.java +++ b/cloud-core/src/main/java/cloud/commandframework/CommandTree.java @@ -39,6 +39,7 @@ import cloud.commandframework.exceptions.NoSuchCommandException; import cloud.commandframework.permission.CommandPermission; import cloud.commandframework.permission.OrPermission; import cloud.commandframework.types.tuples.Pair; +import io.leangen.geantyref.GenericTypeReflector; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -452,6 +453,14 @@ public final class CommandTree { } // END: Flags + // START: Array arguments + if (child.getValue() != null && GenericTypeReflector.erase(child.getValue().getValueType().getType()).isArray()) { + while (commandQueue.size() > 1) { + commandQueue.remove(); + } + } + // END: Array arguments + if (child.getValue() != null) { if (commandQueue.isEmpty()) { return Collections.emptyList(); diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/DelegatingCommandSuggestionEngine.java b/cloud-core/src/main/java/cloud/commandframework/arguments/DelegatingCommandSuggestionEngine.java index 1041dced..bfe592bc 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/DelegatingCommandSuggestionEngine.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/DelegatingCommandSuggestionEngine.java @@ -67,6 +67,8 @@ public final class DelegatingCommandSuggestionEngine implements CommandSugges @NonNull final String input ) { final @NonNull LinkedList<@NonNull String> inputQueue = new CommandInputTokenizer(input).tokenize(); + /* Store a copy of the input queue in the context */ + context.store("__raw_input__", new LinkedList<>(inputQueue)); final List suggestions; if (this.commandManager.preprocessContext(context, inputQueue) == State.ACCEPTED) { suggestions = this.commandManager.getCommandSuggestionProcessor().apply( diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParserRegistry.java b/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParserRegistry.java index 3fc4b0a5..d8c2fe27 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParserRegistry.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParserRegistry.java @@ -35,6 +35,7 @@ import cloud.commandframework.arguments.standard.FloatArgument; import cloud.commandframework.arguments.standard.IntegerArgument; import cloud.commandframework.arguments.standard.ShortArgument; import cloud.commandframework.arguments.standard.StringArgument; +import cloud.commandframework.arguments.standard.StringArrayArgument; import cloud.commandframework.arguments.standard.UUIDArgument; import io.leangen.geantyref.GenericTypeReflector; import io.leangen.geantyref.TypeToken; @@ -112,6 +113,7 @@ public final class StandardParserRegistry implements ParserRegistry { (double) options.get(StandardParameters.RANGE_MAX, Double.MAX_VALUE) )); this.registerParserSupplier(TypeToken.get(Character.class), options -> new CharArgument.CharacterParser()); + this.registerParserSupplier(TypeToken.get(String[].class), options -> new StringArrayArgument.StringArrayParser<>()); /* Make this one less awful */ this.registerParserSupplier(TypeToken.get(String.class), options -> { final boolean greedy = options.get(StandardParameters.GREEDY, false); diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArrayArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArrayArgument.java new file mode 100644 index 00000000..43851bc0 --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArrayArgument.java @@ -0,0 +1,122 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package cloud.commandframework.arguments.standard; + +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.arguments.parser.ArgumentParseResult; +import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.context.CommandContext; +import io.leangen.geantyref.TypeToken; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; +import java.util.Queue; +import java.util.function.BiFunction; + +/** + * This is a command argument type that essentially mimics {@link StringArgument#greedy(String)}, + * but then splits the input string into a string array. The input string will be split at + * every blank space. + * + * @param Command sender type + */ +public final class StringArrayArgument extends CommandArgument { + + private StringArrayArgument( + final boolean required, + final @NonNull String name, + final @Nullable BiFunction, String, List> suggestionsProvider + ) { + super( + required, + name, + new StringArrayParser<>(), + "", + TypeToken.get(String[].class), + suggestionsProvider + ); + } + + /** + * Create a new required string array argument + * + * @param name Argument name + * @param suggestionsProvider Suggestions provider + * @param Command sender type + * @return Created argument + */ + public static @NonNull StringArrayArgument of( + final @NonNull String name, + final @NonNull BiFunction, String, List> suggestionsProvider + ) { + return new StringArrayArgument<>( + true, + name, + suggestionsProvider + ); + } + + /** + * Create a new optional string array argument + * + * @param name Argument name + * @param suggestionsProvider Suggestions provider + * @param Command sender type + * @return Created argument + */ + public static @NonNull StringArrayArgument optional( + final @NonNull String name, + final @NonNull BiFunction, String, List> suggestionsProvider + ) { + return new StringArrayArgument<>( + false, + name, + suggestionsProvider + ); + } + + + /** + * Parser that parses input into a string array + * + * @param Command sender type + */ + public static final class StringArrayParser implements ArgumentParser { + + @Override + public @NonNull ArgumentParseResult parse( + @NonNull final CommandContext<@NonNull C> commandContext, + @NonNull final Queue<@NonNull String> inputQueue + ) { + final String[] result = new String[inputQueue.size()]; + for (int i = 0; i < result.length; i++) { + result[i] = inputQueue.remove(); + } + return ArgumentParseResult.success(result); + } + + } + +} diff --git a/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java b/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java index 5af60e73..098d8bc4 100644 --- a/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java +++ b/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java @@ -30,6 +30,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; import java.util.Optional; @@ -205,6 +206,15 @@ public final class CommandContext { return this.getOptional(key).orElse(defaultValue); } + /** + * Get the raw input. This should only be used when {@link #isSuggestions()} is {@code true} + * + * @return Raw input in token form + */ + public @NonNull LinkedList<@NonNull String> getRawInput() { + return this.getOrDefault("__raw_input__", new LinkedList<>()); + } + /** * Create an argument timing for a specific argument * diff --git a/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/CloudBrigadierManager.java b/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/CloudBrigadierManager.java index fd164023..900b06ef 100644 --- a/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/CloudBrigadierManager.java +++ b/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/CloudBrigadierManager.java @@ -38,6 +38,7 @@ import cloud.commandframework.arguments.standard.FloatArgument; import cloud.commandframework.arguments.standard.IntegerArgument; import cloud.commandframework.arguments.standard.ShortArgument; import cloud.commandframework.arguments.standard.StringArgument; +import cloud.commandframework.arguments.standard.StringArrayArgument; import cloud.commandframework.context.CommandContext; import cloud.commandframework.permission.CommandPermission; import cloud.commandframework.permission.Permission; @@ -185,6 +186,9 @@ public final class CloudBrigadierManager { /* Map flags to a greedy string */ this.registerMapping(new TypeToken>() { }, false, argument -> StringArgumentType.greedyString()); + /* Map String[] to a greedy string */ + this.registerMapping(new TypeToken>() { + }, false, argument -> StringArgumentType.greedyString()); } /** diff --git a/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java index c916d06c..ef870c0a 100644 --- a/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java +++ b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java @@ -43,6 +43,7 @@ import cloud.commandframework.arguments.parser.ParserParameters; import cloud.commandframework.arguments.parser.StandardParameters; import cloud.commandframework.arguments.standard.EnumArgument; import cloud.commandframework.arguments.standard.IntegerArgument; +import cloud.commandframework.arguments.standard.StringArrayArgument; import cloud.commandframework.bukkit.BukkitCommandManager; import cloud.commandframework.bukkit.BukkitCommandMetaBuilder; import cloud.commandframework.bukkit.CloudBukkitCapabilities; @@ -62,6 +63,7 @@ import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextColor; +import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; @@ -76,6 +78,7 @@ import org.bukkit.util.Vector; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; @@ -364,6 +367,30 @@ public final class ExamplePlugin extends JavaPlugin { c.get("accent") ))) ); + // + // Create a Bukkit-like command + // + manager.command( + manager.commandBuilder( + "arraycommand", + Description.of("Bukkit-esque cmmand") + ).argument( + StringArrayArgument.optional( + "args", + (context, lastString) -> { + final List allArgs = context.getRawInput(); + if (allArgs.size() > 1 && allArgs.get(1).equals("curry")) { + return Collections.singletonList("hot"); + } + return Collections.emptyList(); + } + ), + Description.of("Arguments") + ).handler(context -> { + final String[] args = context.getOrDefault("args", new String[0]); + context.getSender().sendMessage("You wrote: " + StringUtils.join(args, " ")); + }) + ); } @CommandMethod("example help [query]")