From 8e9859e4712f06ad287f8c0a18725309151778ec Mon Sep 17 00:00:00 2001 From: Nathan Adams Date: Tue, 25 Sep 2018 17:38:04 +0200 Subject: [PATCH] Added javadoc to CommandDispatcher --- .../mojang/brigadier/CommandDispatcher.java | 284 ++++++++++++++++++ 1 file changed, 284 insertions(+) diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java index 7214fcbd..78d8b5b3 100644 --- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java +++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java @@ -29,9 +29,27 @@ import java.util.function.Predicate; import java.util.stream.Collectors; + +/** + * The core command dispatcher, for registering, parsing, and executing commands. + * + * @param a custom "source" type, such as a user or originator of a command + */ public class CommandDispatcher { + /** + * The string required to separate individual arguments in an input string + * + * @see #ARGUMENT_SEPARATOR_CHAR + */ public static final String ARGUMENT_SEPARATOR = " "; + + /** + * The char required to separate individual arguments in an input string + * + * @see #ARGUMENT_SEPARATOR + */ public static final char ARGUMENT_SEPARATOR_CHAR = ' '; + private static final String USAGE_OPTIONAL_OPEN = "["; private static final String USAGE_OPTIONAL_CLOSE = "]"; private static final String USAGE_REQUIRED_OPEN = "("; @@ -48,33 +66,144 @@ public boolean test(final CommandNode input) { private ResultConsumer consumer = (c, s, r) -> { }; + /** + * Create a new {@link CommandDispatcher} with the specified root node. + * + *

This is often useful to copy existing or pre-defined command trees.

+ * + * @param root the existing {@link RootCommandNode} to use as the basis for this tree + */ public CommandDispatcher(final RootCommandNode root) { this.root = root; } + /** + * Creates a new {@link CommandDispatcher} with an empty command tree. + */ public CommandDispatcher() { this(new RootCommandNode<>()); } + /** + * Utility method for registering new commands. + * + *

This is a shortcut for calling {@link RootCommandNode#addChild(CommandNode)} after building the provided {@code command}.

+ * + *

As {@link RootCommandNode} can only hold literals, this method will only allow literal arguments.

+ * + * @param command a literal argument builder to add to this command tree + * @return the node added to this tree + */ public LiteralCommandNode register(final LiteralArgumentBuilder command) { final LiteralCommandNode build = command.build(); root.addChild(build); return build; } + /** + * Sets a callback to be informed of the result of every command. + * + * @param consumer the new result consumer to be called + */ public void setConsumer(final ResultConsumer consumer) { this.consumer = consumer; } + /** + * Parses and executes a given command. + * + *

This is a shortcut to first {@link #parse(StringReader, Object)} and then {@link #execute(ParseResults)}.

+ * + *

It is recommended to parse and execute as separate steps, as parsing is often the most expensive step, and easiest to cache.

+ * + *

If this command returns a value, then it successfully executed something. If it could not parse the command, or the execution was a failure, + * then an exception will be thrown. Most exceptions will be of type {@link CommandSyntaxException}, but it is possible that a {@link RuntimeException} + * may bubble up from the result of a command. The meaning behind the returned result is arbitrary, and will depend + * entirely on what command was performed.

+ * + *

If the command passes through a node that is {@link CommandNode#isFork()} then it will be 'forked'. + * A forked command will not bubble up any {@link CommandSyntaxException}s, and the 'result' returned will turn into + * 'amount of successful commands executes'.

+ * + *

After each and any command is ran, a registered callback given to {@link #setConsumer(ResultConsumer)} + * will be notified of the result and success of the command. You can use that method to gather more meaningful + * results than this method will return, especially when a command forks.

+ * + * @param input a command string to parse & execute + * @param source a custom "source" object, usually representing the originator of this command + * @return a numeric result from a "command" that was performed + * @throws CommandSyntaxException if the command failed to parse or execute + * @throws RuntimeException if the command failed to execute and was not handled gracefully + * @see #parse(String, Object) + * @see #parse(StringReader, Object) + * @see #execute(ParseResults) + * @see #execute(StringReader, Object) + */ public int execute(final String input, final S source) throws CommandSyntaxException { return execute(new StringReader(input), source); } + /** + * Parses and executes a given command. + * + *

This is a shortcut to first {@link #parse(StringReader, Object)} and then {@link #execute(ParseResults)}.

+ * + *

It is recommended to parse and execute as separate steps, as parsing is often the most expensive step, and easiest to cache.

+ * + *

If this command returns a value, then it successfully executed something. If it could not parse the command, or the execution was a failure, + * then an exception will be thrown. Most exceptions will be of type {@link CommandSyntaxException}, but it is possible that a {@link RuntimeException} + * may bubble up from the result of a command. The meaning behind the returned result is arbitrary, and will depend + * entirely on what command was performed.

+ * + *

If the command passes through a node that is {@link CommandNode#isFork()} then it will be 'forked'. + * A forked command will not bubble up any {@link CommandSyntaxException}s, and the 'result' returned will turn into + * 'amount of successful commands executes'.

+ * + *

After each and any command is ran, a registered callback given to {@link #setConsumer(ResultConsumer)} + * will be notified of the result and success of the command. You can use that method to gather more meaningful + * results than this method will return, especially when a command forks.

+ * + * @param input a command string to parse & execute + * @param source a custom "source" object, usually representing the originator of this command + * @return a numeric result from a "command" that was performed + * @throws CommandSyntaxException if the command failed to parse or execute + * @throws RuntimeException if the command failed to execute and was not handled gracefully + * @see #parse(String, Object) + * @see #parse(StringReader, Object) + * @see #execute(ParseResults) + * @see #execute(String, Object) + */ public int execute(final StringReader input, final S source) throws CommandSyntaxException { final ParseResults parse = parse(input, source); return execute(parse); } + /** + * Executes a given pre-parsed command. + * + *

If this command returns a value, then it successfully executed something. If the execution was a failure, + * then an exception will be thrown. + * Most exceptions will be of type {@link CommandSyntaxException}, but it is possible that a {@link RuntimeException} + * may bubble up from the result of a command. The meaning behind the returned result is arbitrary, and will depend + * entirely on what command was performed.

+ * + *

If the command passes through a node that is {@link CommandNode#isFork()} then it will be 'forked'. + * A forked command will not bubble up any {@link CommandSyntaxException}s, and the 'result' returned will turn into + * 'amount of successful commands executes'.

+ * + *

After each and any command is ran, a registered callback given to {@link #setConsumer(ResultConsumer)} + * will be notified of the result and success of the command. You can use that method to gather more meaningful + * results than this method will return, especially when a command forks.

+ * + * @param parse the result of a successful {@link #parse(StringReader, Object)} + * @return a numeric result from a "command" that was performed. + * @throws CommandSyntaxException if the command failed to parse or execute + * @throws RuntimeException if the command failed to execute and was not handled gracefully + * @see #parse(String, Object) + * @see #parse(StringReader, Object) + * @see #execute(String, Object) + * @see #execute(StringReader, Object) + */ public int execute(final ParseResults parse) throws CommandSyntaxException { if (parse.getReader().canRead()) { if (parse.getExceptions().size() == 1) { @@ -157,10 +286,64 @@ public int execute(final ParseResults parse) throws CommandSyntaxException { return forked ? successfulForks : result; } + /** + * Parses a given command. + * + *

The result of this method can be cached, and it is advised to do so where appropriate. Parsing is often the + * most expensive step, and this allows you to essentially "precompile" a command if it will be ran often.

+ * + *

If the command passes through a node that is {@link CommandNode#isFork()} then the resulting context will be marked as 'forked'. + * Forked contexts may contain child contexts, which may be modified by the {@link RedirectModifier} attached to the fork.

+ * + *

Parsing a command can never fail, you will always be provided with a new {@link ParseResults}. + * However, that does not mean that it will always parse into a valid command. You should inspect the returned results + * to check for validity. If its {@link ParseResults#getReader()} {@link StringReader#canRead()} then it did not finish + * parsing successfully. You can use that position as an indicator to the user where the command stopped being valid. + * You may inspect {@link ParseResults#getExceptions()} if you know the parse failed, as it will explain why it could + * not find any valid commands. It may contain multiple exceptions, one for each "potential node" that it could have visited, + * explaining why it did not go down that node.

+ * + *

When you eventually call {@link #execute(ParseResults)} with the result of this method, the above error checking + * will occur. You only need to inspect it yourself if you wish to handle that yourself.

+ * + * @param command a command string to parse + * @param source a custom "source" object, usually representing the originator of this command + * @return the result of parsing this command + * @see #parse(StringReader, Object) + * @see #execute(ParseResults) + * @see #execute(String, Object) + */ public ParseResults parse(final String command, final S source) { return parse(new StringReader(command), source); } + /** + * Parses a given command. + * + *

The result of this method can be cached, and it is advised to do so where appropriate. Parsing is often the + * most expensive step, and this allows you to essentially "precompile" a command if it will be ran often.

+ * + *

If the command passes through a node that is {@link CommandNode#isFork()} then the resulting context will be marked as 'forked'. + * Forked contexts may contain child contexts, which may be modified by the {@link RedirectModifier} attached to the fork.

+ * + *

Parsing a command can never fail, you will always be provided with a new {@link ParseResults}. + * However, that does not mean that it will always parse into a valid command. You should inspect the returned results + * to check for validity. If its {@link ParseResults#getReader()} {@link StringReader#canRead()} then it did not finish + * parsing successfully. You can use that position as an indicator to the user where the command stopped being valid. + * You may inspect {@link ParseResults#getExceptions()} if you know the parse failed, as it will explain why it could + * not find any valid commands. It may contain multiple exceptions, one for each "potential node" that it could have visited, + * explaining why it did not go down that node.

+ * + *

When you eventually call {@link #execute(ParseResults)} with the result of this method, the above error checking + * will occur. You only need to inspect it yourself if you wish to handle that yourself.

+ * + * @param command a command string to parse + * @param source a custom "source" object, usually representing the originator of this command + * @return the result of parsing this command + * @see #parse(String, Object) + * @see #execute(ParseResults) + * @see #execute(String, Object) + */ public ParseResults parse(final StringReader command, final S source) { final CommandContextBuilder context = new CommandContextBuilder<>(this, source, 0); return parseNodes(root, command, context); @@ -256,6 +439,27 @@ private ParseResults parseNodes(final CommandNode node, final StringReader return new ParseResults<>(contextSoFar, originalReader.getCursor(), originalReader, errors == null ? Collections.emptyMap() : errors); } + /** + * Gets all possible executable commands following the given node. + * + *

You may use {@link #getRoot()} as a target to get all usage data for the entire command tree.

+ * + *

The returned syntax will be in "simple" form: {@code } and {@code literal}. "Optional" nodes will be + * listed as multiple entries: the parent node, and the child nodes. + * For example, a required literal "foo" followed by an optional param "int" will be two nodes:

+ *
    + *
  • {@code foo}
  • + *
  • {@code foo }
  • + *
+ * + *

The path to the specified node will not be prepended to the output, as there can theoretically be many + * ways to reach a given node. It will only give you paths relative to the specified node, not absolute from root.

+ * + * @param node target node to get child usage strings for + * @param source a custom "source" object, usually representing the originator of this command + * @param restricted if true, commands that the {@code source} cannot access will not be mentioned + * @return array of full usage strings under the target node + */ public String[] getAllUsage(final CommandNode node, final S source, final boolean restricted) { final ArrayList result = Lists.newArrayList(); getAllUsage(node, source, result, "", restricted); @@ -281,6 +485,27 @@ private void getAllUsage(final CommandNode node, final S source, final ArrayL } } + /** + * Gets the possible executable commands from a specified node. + * + *

You may use {@link #getRoot()} as a target to get usage data for the entire command tree.

+ * + *

The returned syntax will be in "smart" form: {@code }, {@code literal}, {@code [optional]} and {@code (either|or)}. + * These forms may be mixed and matched to provide as much information about the child nodes as it can, without being too verbose. + * For example, a required literal "foo" followed by an optional param "int" can be compressed into one string:

+ *
    + *
  • {@code foo []}
  • + *
+ * + *

The path to the specified node will not be prepended to the output, as there can theoretically be many + * ways to reach a given node. It will only give you paths relative to the specified node, not absolute from root.

+ * + *

The returned usage will be restricted to only commands that the provided {@code source} can use.

+ * + * @param node target node to get child usage strings for + * @param source a custom "source" object, usually representing the originator of this command + * @return array of full usage strings under the target node + */ public Map, String> getSmartUsage(final CommandNode node, final S source) { final Map, String> result = Maps.newLinkedHashMap(); @@ -348,6 +573,21 @@ private String getSmartUsage(final CommandNode node, final S source, final bo return self; } + /** + * Gets suggestions for a parsed input string on what comes next. + * + *

As it is ultimately up to custom argument types to provide suggestions, it may be an asynchronous operation, + * for example getting in-game data or player names etc. As such, this method returns a future and no guarantees + * are made to when or how the future completes.

+ * + *

The suggestions provided will be in the context of the end of the parsed input string, but may suggest + * new or replacement strings for earlier in the input string. For example, if the end of the string was + * {@code foobar} but an argument preferred it to be {@code minecraft:foobar}, it will suggest a replacement for that + * whole segment of the input.

+ * + * @param parse the result of a {@link #parse(StringReader, Object)} + * @return a future that will eventually resolve into a {@link Suggestions} object + */ public CompletableFuture getCompletionSuggestions(final ParseResults parse) { final CommandContextBuilder rootContext = parse.getContext(); final CommandContextBuilder context = rootContext.getLastChild(); @@ -397,10 +637,33 @@ public CompletableFuture getCompletionSuggestions(final ParseResult return result; } + /** + * Gets the root of this command tree. + * + *

This is often useful as a target of a {@link com.mojang.brigadier.builder.ArgumentBuilder#redirect(CommandNode)}, + * {@link #getAllUsage(CommandNode, Object, boolean)} or {@link #getSmartUsage(CommandNode, Object)}. + * You may also use it to clone the command tree via {@link #CommandDispatcher(RootCommandNode)}.

+ * + * @return root of the command tree + */ public RootCommandNode getRoot() { return root; } + /** + * Finds a valid path to a given node on the command tree. + * + *

There may theoretically be multiple paths to a node on the tree, especially with the use of forking or redirecting. + * As such, this method makes no guarantees about which path it finds. It will not look at forks or redirects, + * and find the first instance of the target node on the tree.

+ * + *

The only guarantee made is that for the same command tree and the same version of this library, the result of + * this method will always be a valid input for {@link #findNode(Collection)}, which should return the same node + * as provided to this method.

+ * + * @param target the target node you are finding a path for + * @return a path to the resulting node, or an empty list if it was not found + */ public Collection getPath(final CommandNode target) { final List>> nodes = new ArrayList<>(); addPaths(root, nodes, new ArrayList<>()); @@ -420,6 +683,17 @@ public Collection getPath(final CommandNode target) { return Collections.emptyList(); } + /** + * Finds a node by its path + * + *

Paths may be generated with {@link #getPath(CommandNode)}, and are guaranteed (for the same tree, and the + * same version of this library) to always produce the same valid node by this method.

+ * + *

If a node could not be found at the specified path, then {@code null} will be returned.

+ * + * @param path a generated path to a node + * @return the node at the given path, or null if not found + */ public CommandNode findNode(final Collection path) { CommandNode node = root; for (final String name : path) { @@ -431,6 +705,16 @@ public CommandNode findNode(final Collection path) { return node; } + /** + * Scans the command tree for potential ambiguous commands. + * + *

This is a shortcut for {@link CommandNode#findAmbiguities(AmbiguityConsumer)} on {@link #getRoot()}.

+ * + *

Ambiguities are detected by testing every {@link CommandNode#getExamples()} on one node verses every sibling + * node. This is not fool proof, and relies a lot on the providers of the used argument types to give good examples.

+ * + * @param consumer a callback to be notified of potential ambiguities + */ public void findAmbiguities(final AmbiguityConsumer consumer) { root.findAmbiguities(consumer); }