diff --git a/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java b/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java index a4e44b6a..b32d3716 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java +++ b/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java @@ -7,6 +7,20 @@ package com.onarandombox.MultiverseCore; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + import buscript.Buscript; import com.dumptruckman.minecraft.util.Logging; import com.onarandombox.MultiverseCore.MVWorld.NullLocation; @@ -18,6 +32,7 @@ import com.onarandombox.MultiverseCore.api.MVWorldManager; import com.onarandombox.MultiverseCore.api.MultiverseCoreConfig; import com.onarandombox.MultiverseCore.api.MultiverseMessaging; import com.onarandombox.MultiverseCore.api.SafeTTeleporter; +import com.onarandombox.MultiverseCore.commands.CreateCommand; import com.onarandombox.MultiverseCore.commands.DebugCommand; import com.onarandombox.MultiverseCore.commandsold.AnchorCommand; import com.onarandombox.MultiverseCore.commandsold.CheckCommand; @@ -25,7 +40,6 @@ import com.onarandombox.MultiverseCore.commandsold.CloneCommand; import com.onarandombox.MultiverseCore.commandsold.ConfigCommand; import com.onarandombox.MultiverseCore.commandsold.ConfirmCommand; import com.onarandombox.MultiverseCore.commandsold.CoordCommand; -import com.onarandombox.MultiverseCore.commandsold.CreateCommand; import com.onarandombox.MultiverseCore.commandsold.DeleteCommand; import com.onarandombox.MultiverseCore.commandsold.EnvironmentCommand; import com.onarandombox.MultiverseCore.commandsold.GameruleCommand; @@ -105,20 +119,6 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPluginLoader; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - /** * The implementation of the Multiverse-{@link Core}. */ @@ -743,7 +743,6 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core { this.commandHandler.registerCommand(new VersionCommand(this)); this.commandHandler.registerCommand(new ListCommand(this)); this.commandHandler.registerCommand(new InfoCommand(this)); - this.commandHandler.registerCommand(new CreateCommand(this)); this.commandHandler.registerCommand(new CloneCommand(this)); this.commandHandler.registerCommand(new ImportCommand(this)); this.commandHandler.registerCommand(new ReloadCommand(this)); @@ -770,7 +769,6 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core { this.commandHandler.registerCommand(new AnchorCommand(this)); // Misc Commands this.commandHandler.registerCommand(new EnvironmentCommand(this)); - // this.commandHandler.registerCommand(new DebugCommand(this)); this.commandHandler.registerCommand(new SilentCommand(this)); this.commandHandler.registerCommand(new GeneratorCommand(this)); this.commandHandler.registerCommand(new CheckCommand(this)); @@ -780,6 +778,7 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core { //**NEW ACF COMMAND HANDLER** this.commandManager.registerCommand(new DebugCommand(this)); + this.commandManager.registerCommand(new CreateCommand(this)); } /** diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java new file mode 100644 index 00000000..a6b28fed --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java @@ -0,0 +1,108 @@ +package com.onarandombox.MultiverseCore.commands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +import co.aikar.commands.CommandIssuer; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.onarandombox.MultiverseCore.MultiverseCore; +import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlag; +import com.onarandombox.MultiverseCore.commandtools.flags.CommandValueFlag; +import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagGroup; +import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.WorldType; +import org.bukkit.command.CommandException; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +@CommandAlias("mv") +public class CreateCommand extends MultiverseCommand { + + public CreateCommand(@NotNull MultiverseCore plugin) { + super(plugin); + + registerFlagGroup(CommandFlagGroup.builder("mvcreate") + .add(CommandValueFlag.builder("--seed", String.class) + .addAlias("-s") + .completion(() -> Collections.singleton(String.valueOf(new Random().nextLong()))) + .build()) + .add(CommandValueFlag.builder("--generator", String.class) + .addAlias("-g") + .completion(() -> Arrays.stream(Bukkit.getServer().getPluginManager().getPlugins()) + .filter(Plugin::isEnabled) + .filter(genplugin -> this.plugin.getUnsafeCallWrapper().wrap( + () -> genplugin.getDefaultWorldGenerator("world", ""), + genplugin.getName(), + "Get generator" + ) != null) + .map(genplugin -> genplugin.getDescription().getName()) + .collect(Collectors.toList())) + .build()) + .add(CommandValueFlag.builder("--world-type", WorldType.class) + .addAlias("-t") + .context((value) -> { + try { + return WorldType.valueOf(value.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new CommandException("Invalid world type: " + value); + } + }) + .completion(() -> { + List types = new ArrayList<>(); + for (WorldType type : WorldType.values()) { + types.add(type.name().toLowerCase()); + } + return types; + }) + .build()) + .add(CommandFlag.builder("--adjust-spawn") + .addAlias("-n") + .build()) + .add(CommandFlag.builder("--no-structures") + .addAlias("-a") + .build()) + .build()); + } + + @Subcommand("create") + @CommandPermission("multiverse.core.create") + @CommandCompletion("WORLDNAME @flags:groupName=mvcreate") + @Syntax(" -s [seed] -g [generator[:id]] -t [worldtype] [-n] -a [true|false]") + @Description("") //TODO + public void onCreateCommand(CommandIssuer issuer, + + @Syntax("") + @Description("") //TODO + String worldName, + + @Syntax("") + @Description("") //TODO + World.Environment environment, + + @Optional + @Syntax("[world-flags]") + @Description("") //TODO + String[] flags + ) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + issuer.sendMessage(worldName + " " + environment.toString()); + issuer.sendMessage("--seed: " + parsedFlags.hasFlag("--seed") + " " + String.valueOf(parsedFlags.flagValue("--seed", String.class))); + issuer.sendMessage("--generator: " + parsedFlags.hasFlag("--generator") + " " + String.valueOf(parsedFlags.flagValue("--generator", String.class))); + issuer.sendMessage("--world-type: " + parsedFlags.hasFlag("--world-type") + " " + String.valueOf(parsedFlags.flagValue("--world-type", WorldType.class))); + issuer.sendMessage("--adjust-spawn: " + parsedFlags.hasFlag("--adjust-spawn") + " " + String.valueOf(parsedFlags.flagValue("--adjust-spawn", String.class))); + issuer.sendMessage("--no-structures: " + parsedFlags.hasFlag("--no-structures") + " " + String.valueOf(parsedFlags.flagValue("--no-structures", String.class))); + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/DebugCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/DebugCommand.java index e4cabe2a..9e702267 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/DebugCommand.java +++ b/src/main/java/com/onarandombox/MultiverseCore/commands/DebugCommand.java @@ -16,7 +16,7 @@ import org.jetbrains.annotations.NotNull; @CommandAlias("mv") public class DebugCommand extends MultiverseCommand { - public DebugCommand(MultiverseCore plugin) { + public DebugCommand(@NotNull MultiverseCore plugin) { super(plugin); } @@ -27,7 +27,6 @@ public class DebugCommand extends MultiverseCommand { this.displayDebugMode(issuer); } - @Subcommand("debug") @CommandPermission("multiverse.core.debug") @Syntax("<{@@mv-core.debug_change_syntax}>") diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/MultiverseCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/MultiverseCommand.java index 42a65604..f9385144 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/MultiverseCommand.java +++ b/src/main/java/com/onarandombox/MultiverseCore/commands/MultiverseCommand.java @@ -2,12 +2,38 @@ package com.onarandombox.MultiverseCore.commands; import co.aikar.commands.BaseCommand; import com.onarandombox.MultiverseCore.MultiverseCore; +import com.onarandombox.MultiverseCore.api.MVWorldManager; +import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagGroup; +import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagsManager; +import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags; +import org.jetbrains.annotations.NotNull; +/** + * A base command for Multiverse. + */ public class MultiverseCommand extends BaseCommand { protected final MultiverseCore plugin; + protected final MVWorldManager worldManager; + protected final CommandFlagsManager flagsManager; - protected MultiverseCommand(MultiverseCore plugin) { + private String flagGroupName; + + protected MultiverseCommand(@NotNull MultiverseCore plugin) { this.plugin = plugin; + this.worldManager = plugin.getMVWorldManager(); + this.flagsManager = plugin.getCommandManager().getFlagsManager(); + } + + protected void registerFlagGroup(@NotNull CommandFlagGroup flagGroup) { + if (flagGroupName != null) { + throw new IllegalStateException("Flag group already registered! (name: " + flagGroupName + ")"); + } + flagsManager.registerFlagGroup(flagGroup); + flagGroupName = flagGroup.getName(); + } + + protected @NotNull ParsedCommandFlags parseFlags(@NotNull String[] flags) { + return flagsManager.parse(flagGroupName, flags); } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandCompletions.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandCompletions.java index 7b5cbd9f..72faa7ad 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandCompletions.java +++ b/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandCompletions.java @@ -1,9 +1,24 @@ package com.onarandombox.MultiverseCore.commandtools; +import java.util.Collection; + +import co.aikar.commands.BukkitCommandCompletionContext; import co.aikar.commands.PaperCommandCompletions; +import org.jetbrains.annotations.NotNull; public class MVCommandCompletions extends PaperCommandCompletions { + protected final MVCommandManager commandManager; + public MVCommandCompletions(MVCommandManager mvCommandManager) { super(mvCommandManager); + this.commandManager = mvCommandManager; + + registerAsyncCompletion("flags", this::suggestFlags); + } + + @NotNull + private Collection suggestFlags(@NotNull BukkitCommandCompletionContext context) { + return this.commandManager.getFlagsManager().suggest( + context.getConfig("groupName", ""), context.getContextValue(String[].class)); } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandManager.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandManager.java index d84689f2..0522c6c3 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandManager.java +++ b/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandManager.java @@ -8,6 +8,8 @@ import co.aikar.commands.CommandCompletions; import co.aikar.commands.CommandContexts; import co.aikar.commands.PaperCommandManager; import com.onarandombox.MultiverseCore.MultiverseCore; +import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagsManager; +import org.jetbrains.annotations.NotNull; /** * Main class to manage permissions. @@ -15,8 +17,9 @@ import com.onarandombox.MultiverseCore.MultiverseCore; public class MVCommandManager extends PaperCommandManager { private final MultiverseCore plugin; + private CommandFlagsManager flagsManager; - public MVCommandManager(MultiverseCore plugin) { + public MVCommandManager(@NotNull MultiverseCore plugin) { super(plugin); this.plugin = plugin; @@ -26,13 +29,25 @@ public class MVCommandManager extends PaperCommandManager { this.locales.loadLanguages(); } + /** + * Gets class responsible for flag handling. + * + * @return A not-null {@link CommandFlagsManager}. + */ + public synchronized @NotNull CommandFlagsManager getFlagsManager() { + if (this.flagsManager == null) { + this.flagsManager = new CommandFlagsManager(); + } + return flagsManager; + } + /** * Gets class responsible for parsing string args into objects. * * @return A not-null {@link CommandContexts}. */ @Override - public synchronized CommandContexts getCommandContexts() { + public synchronized @NotNull CommandContexts getCommandContexts() { if (this.contexts == null) { this.contexts = new MVCommandContexts(this); } @@ -45,7 +60,7 @@ public class MVCommandManager extends PaperCommandManager { * @return A not-null {@link CommandCompletions}. */ @Override - public synchronized CommandCompletions getCommandCompletions() { + public synchronized @NotNull CommandCompletions getCommandCompletions() { if (this.completions == null) { this.completions = new MVCommandCompletions(this); } diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlag.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlag.java new file mode 100644 index 00000000..6323244e --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlag.java @@ -0,0 +1,93 @@ +package com.onarandombox.MultiverseCore.commandtools.flags; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +/** + * Represents a flag. + */ +public class CommandFlag { + /** + * A builder for a flag. + * + * @param key The key for the new flag. + * @return The builder. + */ + public static @NotNull Builder builder(@NotNull String key){ + return new Builder<>(key); + } + + private final String key; + private final List aliases; + + /** + * Creates a new flag. + * + * @param builder The builder. + */ + protected CommandFlag(@NotNull Builder builder) { + key = builder.key; + aliases = builder.aliases; + } + + /** + * Get the key of this flag. + * + * @return The key of this flag. + */ + public @NotNull String getKey() { + return key; + } + + /** + * Get the aliases of this flag. + * + * @return The aliases of this flag. + */ + public @NotNull List getAliases() { + return aliases; + } + + /** + * A builder for a flag. + * + * @param The type of the builder. + */ + public static class Builder> { + private final String key; + private final List aliases; + + /** + * Create a new builder. + * + * @param key The key for the new flag. + */ + public Builder(@NotNull String key) { + this.key = key; + aliases = new ArrayList<>(); + } + + /** + * Add one or more alias to the flag. + * + * @param alias The alias to add. + * @return The builder. + */ + public @NotNull S addAlias(@NotNull String...alias){ + Collections.addAll(this.aliases, alias); + return (S) this; + } + + /** + * Build the flag. + * + * @return The flag. + */ + public @NotNull CommandFlag build(){ + return new CommandFlag(this); + } + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlagGroup.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlagGroup.java new file mode 100644 index 00000000..a36d23d7 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlagGroup.java @@ -0,0 +1,129 @@ +package com.onarandombox.MultiverseCore.commandtools.flags; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A group of flags. + */ +public class CommandFlagGroup { + /** + * A builder for a flag group. + * + * @param name The name of the group. + * @return The builder. + */ + public static @NotNull Builder builder(@NotNull String name) { + return new Builder(name); + } + + private final String name; + private final List keys; + private final Map keysFlagMap; + + /** + * Creates a new flag group. + * + * @param builder The builder. + */ + protected CommandFlagGroup(@NotNull Builder builder) { + name = builder.name; + keys = builder.keys; + keysFlagMap = builder.keysFlagMap; + } + + /** + * Get the name of this group. + * + * @return The name of this group. + */ + public @NotNull String getName() { + return name; + } + + /** + * Check if this group contains a flag with the given key. Works with alias keys. + * + * @param key The key to check. + * @return True if the group contains a flag with the given key, false otherwise. + */ + public boolean hasKey(@Nullable String key) { + return keysFlagMap.containsKey(key); + } + + /** + * Get the remaining keys after the given flags have been removed. Works with alias keys. + * + * @param flags The flags to remove. + * @return The remaining keys. + */ + public @NotNull Set getRemainingKeys(@NotNull String[] flags) { + Set keysRemaining = new HashSet<>(this.keys); + for (String flag : flags) { + CommandFlag mvFlag = this.getFlagByKey(flag); + if (mvFlag != null) { + keysRemaining.remove(mvFlag.getKey()); + } + } + return keysRemaining; + } + + /** + * Get a flag by its key. Alias keys are supported as well. + * + * @param key The key of the flag. + * @return The flag if found, null otherwise. + */ + public @Nullable CommandFlag getFlagByKey(String key) { + return keysFlagMap.get(key); + } + + /** + * A builder for {@link CommandFlagGroup}. + */ + public static class Builder { + private final String name; + private final List keys; + private final Map keysFlagMap; + + /** + * Creates a new builder. + * + * @param name The name of the flag group. + */ + public Builder(@NotNull String name) { + this.name = name; + this.keys = new ArrayList<>(); + this.keysFlagMap = new HashMap<>(); + } + + /** + * Adds a flag to the group. + * + * @param flag The flag to add. + * @return The builder. + */ + public @NotNull Builder add(CommandFlag flag) { + keys.add(flag.getKey()); + keysFlagMap.put(flag.getKey(), flag); + flag.getAliases().forEach((alias) -> keysFlagMap.put(alias, flag)); + return this; + } + + /** + * Builds the flag group. + * + * @return The flag group. + */ + public @NotNull CommandFlagGroup build() { + return new CommandFlagGroup(this); + } + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlagsManager.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlagsManager.java new file mode 100644 index 00000000..6c66e462 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlagsManager.java @@ -0,0 +1,93 @@ +package com.onarandombox.MultiverseCore.commandtools.flags; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import co.aikar.commands.InvalidCommandArgument; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Manages all the flag groups and parsing. + */ +public class CommandFlagsManager { + private final Map flagGroupMap; + + /** + * Creates a new FlagsManager. + */ + public CommandFlagsManager() { + flagGroupMap = new HashMap<>(); + } + + /** + * Registers a flag group. + * + * @param flagGroup The target flag group to register. + */ + public void registerFlagGroup(@NotNull CommandFlagGroup flagGroup) { + flagGroupMap.put(flagGroup.getName(), flagGroup); + } + + /** + * Get a flag group by name. + * + * @param groupName The target flag group name. + * @return The flag group if found, null otherwise. + */ + public @Nullable CommandFlagGroup getFlagGroup(@Nullable String groupName) { + return this.flagGroupMap.get(groupName); + } + + /** + * Autocompletes suggestions for flags. + * + * @param groupName The target flag group name. + * @param flags The current flags so far. + * @return The list of suggestions. + */ + public @NotNull Collection suggest(@Nullable String groupName, @NotNull String[] flags) { + CommandFlagGroup flagGroup = this.getFlagGroup(groupName); + if (flagGroup == null) { + return Collections.emptyList(); + } + + Collection suggestions = new ArrayList<>(); + CommandFlag currentFlag = (flags.length <= 1) ? null : flagGroup.getFlagByKey(flags[flags.length - 2]); + + if (currentFlag instanceof CommandValueFlag) { + CommandValueFlag valueFlag = (CommandValueFlag) currentFlag; + if (valueFlag.getCompletion() != null) { + suggestions.addAll(valueFlag.getCompletion().get()); + } + if (valueFlag.isOptional()) { + suggestions.addAll(flagGroup.getRemainingKeys(flags)); + } + } else { + suggestions.addAll(flagGroup.getRemainingKeys(flags)); + } + + return suggestions; + } + + /** + * Parses the flags. + * + * @param groupName The target flag group name. + * @param flags The flags to parse. + * @return The parsed flags. + * + * @throws InvalidCommandArgument If the flags are invalid. + */ + public @NotNull ParsedCommandFlags parse(@Nullable String groupName, @NotNull String[] flags) { + CommandFlagGroup flagGroup = this.getFlagGroup(groupName); + if (flagGroup == null) { + return ParsedCommandFlags.EMPTY; + } + + return new CommandFlagsParser(this.getFlagGroup(groupName), flags).parse(); + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlagsParser.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlagsParser.java new file mode 100644 index 00000000..4c5bde8c --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandFlagsParser.java @@ -0,0 +1,118 @@ +package com.onarandombox.MultiverseCore.commandtools.flags; + +import co.aikar.commands.InvalidCommandArgument; + +/** + * Parses flags from a string array. Each parser should only be used once. + */ +public class CommandFlagsParser { + private final CommandFlagGroup flagGroup; + private final String[] flags; + + private ParsedCommandFlags parsedFlags; + private boolean nextArgMayBeKey; + private boolean nextArgMayBeValue; + private CommandFlag currentFlag; + + /** + * Creates a new CommandFlagsParser. + * + * @param flagGroup The flag group to parse flags for. + * @param flags The flags to parse. + */ + public CommandFlagsParser(CommandFlagGroup flagGroup, String[] flags) { + this.flagGroup = flagGroup; + this.flags = flags; + } + + /** + * Parses the flags. + * + * @return The parsed flags. + */ + public ParsedCommandFlags parse() { + parsedFlags = new ParsedCommandFlags(); + + // First argument is always a key + this.nextArgMayBeKey = true; + this.nextArgMayBeValue = false; + + for (String flag : flags) { + if (this.nextArgMayBeKey) { + if (parseKey(flag)) continue; + } + if (this.nextArgMayBeValue) { + if (parseValue(flag)) continue; + } + throw new InvalidCommandArgument(flag + " is not a valid flag."); + } + + if (!this.nextArgMayBeKey && this.nextArgMayBeValue) { + throw new InvalidCommandArgument(currentFlag.getKey() + " requires a value!"); + } + + return parsedFlags; + } + + /** + * Parses a key. + * + * @param flag The flag to parse. + * @return True if the flag was parsed as a key, false otherwise. + */ + private boolean parseKey(String flag) { + CommandFlag potentialFlag = flagGroup.getFlagByKey(flag); + if (potentialFlag == null) { + return false; + } + + this.currentFlag = potentialFlag; + + if (this.currentFlag instanceof CommandValueFlag) { + CommandValueFlag valueFlag = (CommandValueFlag) this.currentFlag; + + if (valueFlag.isOptional()) { + parsedFlags.addFlagResult(valueFlag.getKey(), valueFlag.getDefaultValue()); + this.nextArgMayBeKey = true; + this.nextArgMayBeValue = true; + return true; + } + + this.nextArgMayBeKey = false; + this.nextArgMayBeValue = true; + return true; + } + + parsedFlags.addFlagResult(this.currentFlag.getKey(), null); + this.nextArgMayBeKey = true; + this.nextArgMayBeValue = false; + + return true; + } + + /** + * Parses a value. + * + * @param flag The flag to parse. + * @return True if the flag was parsed as a value, false otherwise. + */ + private boolean parseValue(String flag) { + if (this.currentFlag == null) { + throw new InvalidCommandArgument("Some flag logic error occurred at " + flag + ""); + } + if (flagGroup.hasKey(flag)) { + throw new InvalidCommandArgument(currentFlag.getKey() + " requires a value!"); + } + + Object flagValue; + CommandValueFlag valueFlag = (CommandValueFlag) this.currentFlag; + flagValue = valueFlag.getContext() != null ? valueFlag.getContext().apply(flag) : flag; + parsedFlags.addFlagResult(valueFlag.getKey(), flagValue); + + // After a value, the next argument must be a key + this.nextArgMayBeKey = true; + this.nextArgMayBeValue = false; + this.currentFlag = null; + return true; + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandValueFlag.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandValueFlag.java new file mode 100644 index 00000000..bf1a7034 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/CommandValueFlag.java @@ -0,0 +1,172 @@ +package com.onarandombox.MultiverseCore.commandtools.flags; + +import java.util.Collection; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a flag with a value. + * + * @param The type of the value. + */ +public class CommandValueFlag extends CommandFlag { + /** + * A builder for a flag. + * + * @param key The key for the new flag. + * @param type The type of the value. + * @return The builder. + */ + public static @NotNull Builder builder(@NotNull String key, @NotNull Class type) { + return new Builder<>(key, type); + } + + private final Class type; + private final boolean optional; + private final T defaultValue; + private final Function context; + private final Supplier> completion; + + /** + * Creates a new flag. + * + * @param builder The builder. + */ + protected CommandValueFlag(@NotNull Builder builder) { + super(builder); + type = builder.type; + optional = builder.optional; + defaultValue = builder.defaultValue; + context = builder.context; + completion = builder.completion; + } + + /** + * Get the type of the value. + * + * @return The type of the value. + */ + public @NotNull Class getType() { + return type; + } + + /** + * Check if it is optional for users to specify a value. + * + * @return True if the value is optional, false otherwise. + */ + public boolean isOptional() { + return optional; + } + + /** + * Get the default value. May be null. + * + * @return The default value. + */ + public @Nullable T getDefaultValue() { + return defaultValue; + } + + /** + * Get the context. May be null for {@link String} value type. + * + * @return The context. + */ + public @Nullable Function getContext() { + return context; + } + + /** + * Get the completion. May be null. + * + * @return The completion. + */ + public @Nullable Supplier> getCompletion() { + return completion; + } + + /** + * A builder for a flag. + * + * @param The type of the value. + * @param The type of the builder. + */ + public static class Builder> extends CommandFlag.Builder { + private final Class type; + private boolean optional = false; + private T defaultValue = null; + private Function context = null; + private Supplier> completion = null; + + /** + * Create a new builder. + * + * @param key The key for the new flag. + * @param type The type of the value. + */ + public Builder(@NotNull String key, @NotNull Class type) { + super(key); + this.type = type; + } + + /** + * Set the flag as optional for users to specify a value. + * + * @return The builder. + */ + public @NotNull S optional() { + this.optional = true; + return (S) this; + } + + /** + * Set the default value. Used if optional is true and user does not specify a value. + * + * @param defaultValue The default value. + * @return The builder. + */ + public @NotNull S defaultValue(@NotNull T defaultValue) { + this.defaultValue = defaultValue; + return (S) this; + } + + /** + * Set the context callback for parsing string into value type. + * + * @param context The context. + * @return The builder. + */ + public @NotNull S context(@NotNull Function context) { + this.context = context; + return (S) this; + } + + /** + * Set the completion callback for autocomplete. + * + * @param completion The completion. + * @return The builder. + */ + public @NotNull S completion(@NotNull Supplier> completion) { + this.completion = completion; + return (S) this; + } + + /** + * Build the flag. + * + * @return The flag. + */ + @Override + public @NotNull CommandFlag build() { + if (context == null && !String.class.equals(type)) { + throw new IllegalStateException("Context is required for none-string value flags"); + } + return new CommandValueFlag<>(this); + } + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/ParsedCommandFlags.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/ParsedCommandFlags.java new file mode 100644 index 00000000..469e917e --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/commandtools/flags/ParsedCommandFlags.java @@ -0,0 +1,52 @@ +package com.onarandombox.MultiverseCore.commandtools.flags; + +import java.util.HashMap; +import java.util.Map; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Object to contain the results of the flags present and its values. + */ +public class ParsedCommandFlags +{ + public static final ParsedCommandFlags EMPTY = new ParsedCommandFlags(); + + private final Map flagValues; + + public ParsedCommandFlags() { + flagValues = new HashMap<>(); + } + + /** + * Add a flag result to the parsed flags. + * + * @param key The key of the flag. + * @param value The value of the flag. + */ + void addFlagResult(@NotNull String key, @Nullable Object value) { + flagValues.put(key, value); + } + + /** + * Check if a flag is present. + * + * @param key The key of the flag. + * @return True if the flag is present, false otherwise. + */ + public boolean hasFlag(@Nullable String key) { + return this.flagValues.containsKey(key); + } + + /** + * Get the value of a flag. + * + * @param key The key of the flag. + * @return The value of the flag, null if flag does not exist or no value. + */ + public @Nullable T flagValue(@Nullable String key, @NotNull Class type) { + Object value = this.flagValues.get(key); + return (T) value; + } +}