✨ Initial support for compound arguments
This allows for grouping and mappings of multiple command arguments by using product types.
This commit is contained in:
parent
e033ee88db
commit
94710c5174
22 changed files with 1032 additions and 28 deletions
|
|
@ -133,7 +133,6 @@
|
||||||
<!-- Checks for Size Violations. -->
|
<!-- Checks for Size Violations. -->
|
||||||
<!-- See https://checkstyle.org/config_sizes.html -->
|
<!-- See https://checkstyle.org/config_sizes.html -->
|
||||||
<module name="MethodLength"/>
|
<module name="MethodLength"/>
|
||||||
<module name="ParameterNumber"/>
|
|
||||||
|
|
||||||
<!-- Checks for whitespace -->
|
<!-- Checks for whitespace -->
|
||||||
<!-- See https://checkstyle.org/config_whitespace.html -->
|
<!-- See https://checkstyle.org/config_whitespace.html -->
|
||||||
|
|
@ -166,7 +165,6 @@
|
||||||
<module name="EqualsHashCode"/>
|
<module name="EqualsHashCode"/>
|
||||||
<module name="IllegalInstantiation"/>
|
<module name="IllegalInstantiation"/>
|
||||||
<module name="InnerAssignment"/>
|
<module name="InnerAssignment"/>
|
||||||
<module name="MagicNumber"/>
|
|
||||||
<module name="MissingSwitchDefault"/>
|
<module name="MissingSwitchDefault"/>
|
||||||
<module name="MultipleVariableDeclarations"/>
|
<module name="MultipleVariableDeclarations"/>
|
||||||
<module name="SimplifyBooleanExpression"/>
|
<module name="SimplifyBooleanExpression"/>
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A command consists out of a chain of {@link CommandArgument command arguments}.
|
* A command consists out of a chain of {@link CommandArgument command arguments}.
|
||||||
|
|
@ -359,7 +360,6 @@ public class Command<C> {
|
||||||
this.commandExecutionHandler, this.commandPermission);
|
this.commandExecutionHandler, this.commandPermission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new command argument by interacting with a constructed command argument builder
|
* Add a new command argument by interacting with a constructed command argument builder
|
||||||
*
|
*
|
||||||
|
|
@ -381,6 +381,138 @@ public class Command<C> {
|
||||||
return this.argument(builder.build());
|
return this.argument(builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compound helper methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new argument pair that maps to {@link Pair}
|
||||||
|
* <p>
|
||||||
|
* For this to work, there must be a {@link CommandManager}
|
||||||
|
* attached to the command builder. To guarantee this, it is recommended to get the command builder instance
|
||||||
|
* using {@link CommandManager#commandBuilder(String, String...)}
|
||||||
|
*
|
||||||
|
* @param name Name of the argument
|
||||||
|
* @param names Pair containing the names of the sub-arguments
|
||||||
|
* @param parserPair Pair containing the types of the sub-arguments. There must be parsers for these types registered
|
||||||
|
* in the {@link com.intellectualsites.commands.arguments.parser.ParserRegistry} used by the
|
||||||
|
* {@link CommandManager} attached to this command
|
||||||
|
* @param description Description of the argument
|
||||||
|
* @param <U> First type
|
||||||
|
* @param <V> Second type
|
||||||
|
* @return Builder instance with the argument inserted
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public <U, V> Builder<C> argumentPair(@Nonnull final String name,
|
||||||
|
@Nonnull final Pair<String, String> names,
|
||||||
|
@Nonnull final Pair<Class<U>, Class<V>> parserPair,
|
||||||
|
@Nonnull final Description description) {
|
||||||
|
if (this.commandManager == null) {
|
||||||
|
throw new IllegalStateException("This cannot be called from a command that has no command manager attached");
|
||||||
|
}
|
||||||
|
return this.argument(ArgumentPair.required(this.commandManager, name, names, parserPair).simple(), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new argument pair that maps to a custom type.
|
||||||
|
* <p>
|
||||||
|
* For this to work, there must be a {@link CommandManager}
|
||||||
|
* attached to the command builder. To guarantee this, it is recommended to get the command builder instance
|
||||||
|
* using {@link CommandManager#commandBuilder(String, String...)}
|
||||||
|
*
|
||||||
|
* @param name Name of the argument
|
||||||
|
* @param outputType The output type
|
||||||
|
* @param names Pair containing the names of the sub-arguments
|
||||||
|
* @param parserPair Pair containing the types of the sub-arguments. There must be parsers for these types registered
|
||||||
|
* in the {@link com.intellectualsites.commands.arguments.parser.ParserRegistry} used by the
|
||||||
|
* {@link CommandManager} attached to this command
|
||||||
|
* @param mapper Mapper that maps from {@link Pair} to the custom type
|
||||||
|
* @param description Description of the argument
|
||||||
|
* @param <U> First type
|
||||||
|
* @param <V> Second type
|
||||||
|
* @param <O> Output type
|
||||||
|
* @return Builder instance with the argument inserted
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public <U, V, O> Builder<C> argumentPair(@Nonnull final String name,
|
||||||
|
@Nonnull final TypeToken<O> outputType,
|
||||||
|
@Nonnull final Pair<String, String> names,
|
||||||
|
@Nonnull final Pair<Class<U>, Class<V>> parserPair,
|
||||||
|
@Nonnull final Function<Pair<U, V>, O> mapper,
|
||||||
|
@Nonnull final Description description) {
|
||||||
|
if (this.commandManager == null) {
|
||||||
|
throw new IllegalStateException("This cannot be called from a command that has no command manager attached");
|
||||||
|
}
|
||||||
|
return this.argument(
|
||||||
|
ArgumentPair.required(this.commandManager, name, names, parserPair).withMapper(outputType, mapper),
|
||||||
|
description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new argument pair that maps to {@link com.intellectualsites.commands.types.tuples.Triplet}
|
||||||
|
* <p>
|
||||||
|
* For this to work, there must be a {@link CommandManager}
|
||||||
|
* attached to the command builder. To guarantee this, it is recommended to get the command builder instance
|
||||||
|
* using {@link CommandManager#commandBuilder(String, String...)}
|
||||||
|
*
|
||||||
|
* @param name Name of the argument
|
||||||
|
* @param names Triplet containing the names of the sub-arguments
|
||||||
|
* @param parserTriplet Triplet containing the types of the sub-arguments. There must be parsers for these types
|
||||||
|
* registered in the {@link com.intellectualsites.commands.arguments.parser.ParserRegistry}
|
||||||
|
* used by the {@link CommandManager} attached to this command
|
||||||
|
* @param description Description of the argument
|
||||||
|
* @param <U> First type
|
||||||
|
* @param <V> Second type
|
||||||
|
* @param <W> Third type
|
||||||
|
* @return Builder instance with the argument inserted
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public <U, V, W> Builder<C> argumentTriplet(@Nonnull final String name,
|
||||||
|
@Nonnull final Triplet<String, String, String> names,
|
||||||
|
@Nonnull final Triplet<Class<U>, Class<V>, Class<W>> parserTriplet,
|
||||||
|
@Nonnull final Description description) {
|
||||||
|
if (this.commandManager == null) {
|
||||||
|
throw new IllegalStateException("This cannot be called from a command that has no command manager attached");
|
||||||
|
}
|
||||||
|
return this.argument(ArgumentTriplet.required(this.commandManager, name, names, parserTriplet).simple(), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new argument triplet that maps to a custom type.
|
||||||
|
* <p>
|
||||||
|
* For this to work, there must be a {@link CommandManager}
|
||||||
|
* attached to the command builder. To guarantee this, it is recommended to get the command builder instance
|
||||||
|
* using {@link CommandManager#commandBuilder(String, String...)}
|
||||||
|
*
|
||||||
|
* @param name Name of the argument
|
||||||
|
* @param outputType The output type
|
||||||
|
* @param names Triplet containing the names of the sub-arguments
|
||||||
|
* @param parserTriplet Triplet containing the types of the sub-arguments. There must be parsers for these types
|
||||||
|
* registered in the {@link com.intellectualsites.commands.arguments.parser.ParserRegistry} used by
|
||||||
|
* the {@link CommandManager} attached to this command
|
||||||
|
* @param mapper Mapper that maps from {@link Triplet} to the custom type
|
||||||
|
* @param description Description of the argument
|
||||||
|
* @param <U> First type
|
||||||
|
* @param <V> Second type
|
||||||
|
* @param <W> Third type
|
||||||
|
* @param <O> Output type
|
||||||
|
* @return Builder instance with the argument inserted
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public <U, V, W, O> Builder<C> argumentTriplet(@Nonnull final String name,
|
||||||
|
@Nonnull final TypeToken<O> outputType,
|
||||||
|
@Nonnull final Triplet<String, String, String> names,
|
||||||
|
@Nonnull final Triplet<Class<U>, Class<V>, Class<W>> parserTriplet,
|
||||||
|
@Nonnull final Function<Triplet<U, V, W>, O> mapper,
|
||||||
|
@Nonnull final Description description) {
|
||||||
|
if (this.commandManager == null) {
|
||||||
|
throw new IllegalStateException("This cannot be called from a command that has no command manager attached");
|
||||||
|
}
|
||||||
|
return this.argument(
|
||||||
|
ArgumentTriplet.required(this.commandManager, name, names, parserTriplet).withMapper(outputType, mapper),
|
||||||
|
description);
|
||||||
|
}
|
||||||
|
|
||||||
|
// End of compound helper methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify the command execution handler
|
* Specify the command execution handler
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -360,6 +360,25 @@ public final class CommandTree<C> {
|
||||||
if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticArgument)) {
|
if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticArgument)) {
|
||||||
// The value has to be a variable
|
// The value has to be a variable
|
||||||
final Node<CommandArgument<C, ?>> child = children.get(0);
|
final Node<CommandArgument<C, ?>> child = children.get(0);
|
||||||
|
|
||||||
|
// START: Compound arguments
|
||||||
|
/* When we get in here, we need to treat compound arguments a little differently */
|
||||||
|
if (child.getValue() instanceof CompoundArgument) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final CompoundArgument<?, C, ?> compoundArgument = (CompoundArgument<?, C, ?>) child.getValue();
|
||||||
|
/* See how many arguments it requires */
|
||||||
|
final int requiredArguments = compoundArgument.getParserTuple().getSize();
|
||||||
|
/* Figure out whether we even need to care about this */
|
||||||
|
if (commandQueue.size() <= requiredArguments) {
|
||||||
|
/* Attempt to pop as many arguments from the stack as possible */
|
||||||
|
for (int i = 0; i < requiredArguments - 1 && commandQueue.size() > 1; i++) {
|
||||||
|
commandQueue.remove();
|
||||||
|
commandContext.store("__parsing_argument__", i + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// END: Compound arguments
|
||||||
|
|
||||||
if (child.getValue() != null) {
|
if (child.getValue() != null) {
|
||||||
if (commandQueue.isEmpty()) {
|
if (commandQueue.isEmpty()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|
@ -367,6 +386,10 @@ public final class CommandTree<C> {
|
||||||
} else if (child.isLeaf() && commandQueue.size() < 2) {
|
} else if (child.isLeaf() && commandQueue.size() < 2) {
|
||||||
return child.getValue().getSuggestionsProvider().apply(commandContext, commandQueue.peek());
|
return child.getValue().getSuggestionsProvider().apply(commandContext, commandQueue.peek());
|
||||||
} else if (child.isLeaf()) {
|
} else if (child.isLeaf()) {
|
||||||
|
if (child.getValue() instanceof CompoundArgument) {
|
||||||
|
final String last = ((LinkedList<String>) commandQueue).getLast();
|
||||||
|
return child.getValue().getSuggestionsProvider().apply(commandContext, last);
|
||||||
|
}
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
} else if (commandQueue.peek().isEmpty()) {
|
} else if (commandQueue.peek().isEmpty()) {
|
||||||
return child.getValue().getSuggestionsProvider().apply(commandContext, commandQueue.remove());
|
return child.getValue().getSuggestionsProvider().apply(commandContext, commandQueue.remove());
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ public class CommandArgument<C, T> implements Comparable<CommandArgument<?, ?>>
|
||||||
/**
|
/**
|
||||||
* The type that is produces by the argument's parser
|
* The type that is produces by the argument's parser
|
||||||
*/
|
*/
|
||||||
private final Class<T> valueType;
|
private final TypeToken<T> valueType;
|
||||||
/**
|
/**
|
||||||
* Suggestion provider
|
* Suggestion provider
|
||||||
*/
|
*/
|
||||||
|
|
@ -99,7 +99,7 @@ public class CommandArgument<C, T> implements Comparable<CommandArgument<?, ?>>
|
||||||
@Nonnull final String name,
|
@Nonnull final String name,
|
||||||
@Nonnull final ArgumentParser<C, T> parser,
|
@Nonnull final ArgumentParser<C, T> parser,
|
||||||
@Nonnull final String defaultValue,
|
@Nonnull final String defaultValue,
|
||||||
@Nonnull final Class<T> valueType,
|
@Nonnull final TypeToken<T> valueType,
|
||||||
@Nullable final BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider) {
|
@Nullable final BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider) {
|
||||||
this.required = required;
|
this.required = required;
|
||||||
this.name = Objects.requireNonNull(name, "Name may not be null");
|
this.name = Objects.requireNonNull(name, "Name may not be null");
|
||||||
|
|
@ -114,6 +114,25 @@ public class CommandArgument<C, T> implements Comparable<CommandArgument<?, ?>>
|
||||||
: suggestionsProvider;
|
: suggestionsProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new command argument
|
||||||
|
*
|
||||||
|
* @param required Whether or not the argument is required
|
||||||
|
* @param name The argument name
|
||||||
|
* @param parser The argument parser
|
||||||
|
* @param defaultValue Default value used when no value is provided by the command sender
|
||||||
|
* @param valueType Type produced by the parser
|
||||||
|
* @param suggestionsProvider Suggestions provider
|
||||||
|
*/
|
||||||
|
public CommandArgument(final boolean required,
|
||||||
|
@Nonnull final String name,
|
||||||
|
@Nonnull final ArgumentParser<C, T> parser,
|
||||||
|
@Nonnull final String defaultValue,
|
||||||
|
@Nonnull final Class<T> valueType,
|
||||||
|
@Nullable final BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider) {
|
||||||
|
this(required, name, parser, defaultValue, TypeToken.of(valueType), suggestionsProvider);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new command argument
|
* Construct a new command argument
|
||||||
*
|
*
|
||||||
|
|
@ -144,11 +163,26 @@ public class CommandArgument<C, T> implements Comparable<CommandArgument<?, ?>>
|
||||||
* @return Argument builder
|
* @return Argument builder
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static <C, T> CommandArgument.Builder<C, T> ofType(@Nonnull final Class<T> clazz,
|
public static <C, T> CommandArgument.Builder<C, T> ofType(@Nonnull final TypeToken<T> clazz,
|
||||||
@Nonnull final String name) {
|
@Nonnull final String name) {
|
||||||
return new Builder<>(clazz, name);
|
return new Builder<>(clazz, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command argument
|
||||||
|
*
|
||||||
|
* @param clazz Argument class
|
||||||
|
* @param name Argument name
|
||||||
|
* @param <C> Command sender type
|
||||||
|
* @param <T> Argument Type. Used to make the compiler happy.
|
||||||
|
* @return Argument builder
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static <C, T> CommandArgument.Builder<C, T> ofType(@Nonnull final Class<T> clazz,
|
||||||
|
@Nonnull final String name) {
|
||||||
|
return new Builder<>(TypeToken.of(clazz), name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether or not the command argument is required
|
* Check whether or not the command argument is required
|
||||||
*
|
*
|
||||||
|
|
@ -277,7 +311,7 @@ public class CommandArgument<C, T> implements Comparable<CommandArgument<?, ?>>
|
||||||
* @return Value type
|
* @return Value type
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public Class<T> getValueType() {
|
public TypeToken<T> getValueType() {
|
||||||
return this.valueType;
|
return this.valueType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,7 +344,7 @@ public class CommandArgument<C, T> implements Comparable<CommandArgument<?, ?>>
|
||||||
*/
|
*/
|
||||||
public static class Builder<C, T> {
|
public static class Builder<C, T> {
|
||||||
|
|
||||||
private final Class<T> valueType;
|
private final TypeToken<T> valueType;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
private CommandManager<C> manager;
|
private CommandManager<C> manager;
|
||||||
|
|
@ -319,12 +353,17 @@ public class CommandArgument<C, T> implements Comparable<CommandArgument<?, ?>>
|
||||||
private String defaultValue = "";
|
private String defaultValue = "";
|
||||||
private BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider;
|
private BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider;
|
||||||
|
|
||||||
protected Builder(@Nonnull final Class<T> valueType,
|
protected Builder(@Nonnull final TypeToken<T> valueType,
|
||||||
@Nonnull final String name) {
|
@Nonnull final String name) {
|
||||||
this.valueType = valueType;
|
this.valueType = valueType;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Builder(@Nonnull final Class<T> valueType,
|
||||||
|
@Nonnull final String name) {
|
||||||
|
this(TypeToken.of(valueType), name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the command manager. Will be used to create a default parser
|
* Set the command manager. Will be used to create a default parser
|
||||||
* if none was provided
|
* if none was provided
|
||||||
|
|
@ -418,7 +457,7 @@ public class CommandArgument<C, T> implements Comparable<CommandArgument<?, ?>>
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public CommandArgument<C, T> build() {
|
public CommandArgument<C, T> build() {
|
||||||
if (this.parser == null && this.manager != null) {
|
if (this.parser == null && this.manager != null) {
|
||||||
this.parser = this.manager.getParserRegistry().createParser(TypeToken.of(valueType), ParserParameters.empty())
|
this.parser = this.manager.getParserRegistry().createParser(valueType, ParserParameters.empty())
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
if (this.parser == null) {
|
if (this.parser == null) {
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ final class DelegatingSuggestionsProvider<C> implements BiFunction<CommandContex
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> apply(final CommandContext<C> context, final String s) {
|
public List<String> apply(final CommandContext<C> context, final String string) {
|
||||||
return this.parser.suggestions(context, s);
|
return this.parser.suggestions(context, string);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,20 @@ public class StandardCommandSyntaxFormatter<C> implements CommandSyntaxFormatter
|
||||||
if (commandArgument instanceof StaticArgument) {
|
if (commandArgument instanceof StaticArgument) {
|
||||||
stringBuilder.append(commandArgument.getName());
|
stringBuilder.append(commandArgument.getName());
|
||||||
} else {
|
} else {
|
||||||
if (commandArgument.isRequired()) {
|
if (commandArgument instanceof CompoundArgument) {
|
||||||
|
final char prefix = commandArgument.isRequired() ? '<' : '[';
|
||||||
|
final char suffix = commandArgument.isRequired() ? '>' : ']';
|
||||||
|
stringBuilder.append(prefix);
|
||||||
|
// noinspection all
|
||||||
|
final Object[] names = ((CompoundArgument<?, C, ?>) commandArgument).getNames().toArray();
|
||||||
|
for (int i = 0; i < names.length; i++) {
|
||||||
|
stringBuilder.append(prefix).append(names[i]).append(suffix);
|
||||||
|
if ((i + 1) < names.length) {
|
||||||
|
stringBuilder.append(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stringBuilder.append(suffix);
|
||||||
|
} else if (commandArgument.isRequired()) {
|
||||||
stringBuilder.append("<").append(commandArgument.getName()).append(">");
|
stringBuilder.append("<").append(commandArgument.getName()).append(">");
|
||||||
} else {
|
} else {
|
||||||
stringBuilder.append("[").append(commandArgument.getName()).append("]");
|
stringBuilder.append("[").append(commandArgument.getName()).append("]");
|
||||||
|
|
@ -90,10 +103,24 @@ public class StandardCommandSyntaxFormatter<C> implements CommandSyntaxFormatter
|
||||||
prefix = "[";
|
prefix = "[";
|
||||||
suffix = "]";
|
suffix = "]";
|
||||||
}
|
}
|
||||||
stringBuilder.append(" ")
|
|
||||||
.append(prefix)
|
if (argument instanceof CompoundArgument) {
|
||||||
.append(argument.getName())
|
stringBuilder.append(" ").append(prefix);
|
||||||
.append(suffix);
|
// noinspection all
|
||||||
|
final Object[] names = ((CompoundArgument<?, C, ?>) argument).getNames().toArray();
|
||||||
|
for (int i = 0; i < names.length; i++) {
|
||||||
|
stringBuilder.append(prefix).append(names[i]).append(suffix);
|
||||||
|
if ((i + 1) < names.length) {
|
||||||
|
stringBuilder.append(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stringBuilder.append(suffix);
|
||||||
|
} else {
|
||||||
|
stringBuilder.append(" ")
|
||||||
|
.append(prefix)
|
||||||
|
.append(argument.getName())
|
||||||
|
.append(suffix);
|
||||||
|
}
|
||||||
tail = tail.getChildren().get(0);
|
tail = tail.getChildren().get(0);
|
||||||
}
|
}
|
||||||
return stringBuilder.toString();
|
return stringBuilder.toString();
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import javax.annotation.Nonnull;
|
||||||
* @param <U> First type
|
* @param <U> First type
|
||||||
* @param <V> Second type
|
* @param <V> Second type
|
||||||
*/
|
*/
|
||||||
public class Pair<U, V> {
|
public class Pair<U, V> implements Tuple {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final U first;
|
private final U first;
|
||||||
|
|
@ -104,4 +104,18 @@ public class Pair<U, V> {
|
||||||
return String.format("(%s, %s)", this.first, this.second);
|
return String.format("(%s, %s)", this.first, this.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getSize() {
|
||||||
|
return Tuples.SIZE_PAIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public final Object[] toArray() {
|
||||||
|
final Object[] array = new Object[2];
|
||||||
|
array[0] = this.first;
|
||||||
|
array[1] = this.second;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ import javax.annotation.Nonnull;
|
||||||
* @param <W> Third type
|
* @param <W> Third type
|
||||||
* @param <X> Fourth type
|
* @param <X> Fourth type
|
||||||
*/
|
*/
|
||||||
public class Quartet<U, V, W, X> {
|
public class Quartet<U, V, W, X> implements Tuple {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final U first;
|
private final U first;
|
||||||
|
|
@ -142,4 +142,20 @@ public class Quartet<U, V, W, X> {
|
||||||
return String.format("(%s, %s, %s, %s)", this.first, this.second, this.third, this.fourth);
|
return String.format("(%s, %s, %s, %s)", this.first, this.second, this.third, this.fourth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getSize() {
|
||||||
|
return Tuples.SIZE_QUARTET;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public final Object[] toArray() {
|
||||||
|
final Object[] array = new Object[4];
|
||||||
|
array[0] = this.first;
|
||||||
|
array[1] = this.second;
|
||||||
|
array[3] = this.third;
|
||||||
|
array[4] = this.fourth;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ import javax.annotation.Nonnull;
|
||||||
* @param <X> Fourth type
|
* @param <X> Fourth type
|
||||||
* @param <Y> Fifth type
|
* @param <Y> Fifth type
|
||||||
*/
|
*/
|
||||||
public class Quintet<U, V, W, X, Y> {
|
public class Quintet<U, V, W, X, Y> implements Tuple {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final U first;
|
private final U first;
|
||||||
|
|
@ -161,4 +161,21 @@ public class Quintet<U, V, W, X, Y> {
|
||||||
return String.format("(%s, %s, %s, %s, %s)", this.first, this.second, this.third, this.fourth, this.fifth);
|
return String.format("(%s, %s, %s, %s, %s)", this.first, this.second, this.third, this.fourth, this.fifth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getSize() {
|
||||||
|
return Tuples.SIZE_QUINTET;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public final Object[] toArray() {
|
||||||
|
final Object[] array = new Object[5];
|
||||||
|
array[0] = this.first;
|
||||||
|
array[1] = this.second;
|
||||||
|
array[3] = this.third;
|
||||||
|
array[4] = this.fourth;
|
||||||
|
array[5] = this.fifth;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ import javax.annotation.Nonnull;
|
||||||
* @param <Y> Fifth type
|
* @param <Y> Fifth type
|
||||||
* @param <Z> Sixth type
|
* @param <Z> Sixth type
|
||||||
*/
|
*/
|
||||||
public class Sextet<U, V, W, X, Y, Z> {
|
public class Sextet<U, V, W, X, Y, Z> implements Tuple {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final U first;
|
private final U first;
|
||||||
|
|
@ -181,4 +181,22 @@ public class Sextet<U, V, W, X, Y, Z> {
|
||||||
this.fourth, this.fifth, this.sixth);
|
this.fourth, this.fifth, this.sixth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getSize() {
|
||||||
|
return Tuples.SIZE_SEXTET;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public final Object[] toArray() {
|
||||||
|
final Object[] array = new Object[6];
|
||||||
|
array[0] = this.first;
|
||||||
|
array[1] = this.second;
|
||||||
|
array[3] = this.third;
|
||||||
|
array[4] = this.fourth;
|
||||||
|
array[5] = this.fifth;
|
||||||
|
array[6] = this.sixth;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import javax.annotation.Nonnull;
|
||||||
* @param <V> Second type
|
* @param <V> Second type
|
||||||
* @param <W> Third type
|
* @param <W> Third type
|
||||||
*/
|
*/
|
||||||
public class Triplet<U, V, W> {
|
public class Triplet<U, V, W> implements Tuple {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final U first;
|
private final U first;
|
||||||
|
|
@ -123,4 +123,19 @@ public class Triplet<U, V, W> {
|
||||||
return String.format("(%s, %s, %s)", this.first, this.second, this.third);
|
return String.format("(%s, %s, %s)", this.first, this.second, this.third);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getSize() {
|
||||||
|
return Tuples.SIZE_TRIPLET;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public final Object[] toArray() {
|
||||||
|
final Object[] array = new Object[3];
|
||||||
|
array[0] = this.first;
|
||||||
|
array[1] = this.second;
|
||||||
|
array[2] = this.third;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,12 @@ import javax.annotation.Nonnull;
|
||||||
*/
|
*/
|
||||||
public final class Tuples {
|
public final class Tuples {
|
||||||
|
|
||||||
|
static final int SIZE_PAIR = 2;
|
||||||
|
static final int SIZE_TRIPLET = 3;
|
||||||
|
static final int SIZE_QUARTET = 4;
|
||||||
|
static final int SIZE_QUINTET = 5;
|
||||||
|
static final int SIZE_SEXTET = 6;
|
||||||
|
|
||||||
private Tuples() {
|
private Tuples() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// 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 com.intellectualsites.commands.arguments.compound;
|
||||||
|
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
import com.intellectualsites.commands.CommandManager;
|
||||||
|
import com.intellectualsites.commands.arguments.parser.ArgumentParser;
|
||||||
|
import com.intellectualsites.commands.arguments.parser.ParserParameters;
|
||||||
|
import com.intellectualsites.commands.arguments.parser.ParserRegistry;
|
||||||
|
import com.intellectualsites.commands.types.tuples.Pair;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A compound argument consisting of two inner arguments
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
* @param <U> First argument type
|
||||||
|
* @param <V> Second argument type
|
||||||
|
* @param <O> Output type
|
||||||
|
*/
|
||||||
|
public final class ArgumentPair<C, U, V, O> extends CompoundArgument<Pair<U, V>, C, O> {
|
||||||
|
|
||||||
|
private ArgumentPair(final boolean required,
|
||||||
|
@Nonnull final String name,
|
||||||
|
@Nonnull final Pair<String, String> names,
|
||||||
|
@Nonnull final Pair<Class<U>, Class<V>> types,
|
||||||
|
@Nonnull final Pair<ArgumentParser<C, U>, ArgumentParser<C, V>> parserPair,
|
||||||
|
@Nonnull final Function<Pair<U, V>, O> mapper,
|
||||||
|
@Nonnull final TypeToken<O> valueType) {
|
||||||
|
super(required, name, names, parserPair, types, mapper, o -> Pair.of((U) o[0], (V) o[1]), valueType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a builder for an argument pair
|
||||||
|
*
|
||||||
|
* @param manager Command manager
|
||||||
|
* @param name Argument name
|
||||||
|
* @param names Sub-argument names
|
||||||
|
* @param types Pair containing the types of the sub-arguments. There must be parsers for these types registered
|
||||||
|
* in the {@link com.intellectualsites.commands.arguments.parser.ParserRegistry} used by the
|
||||||
|
* {@link CommandManager}
|
||||||
|
* @param <C> Command sender type
|
||||||
|
* @param <U> First parsed type
|
||||||
|
* @param <V> Second parsed type
|
||||||
|
* @return Intermediary builder
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static <C, U, V> ArgumentPairIntermediaryBuilder<C, U, V> required(@Nonnull final CommandManager<C> manager,
|
||||||
|
@Nonnull final String name,
|
||||||
|
@Nonnull final Pair<String, String> names,
|
||||||
|
@Nonnull final Pair<Class<U>, Class<V>> types) {
|
||||||
|
final ParserRegistry<C> parserRegistry = manager.getParserRegistry();
|
||||||
|
final ArgumentParser<C, U> firstParser = parserRegistry.createParser(TypeToken.of(types.getFirst()),
|
||||||
|
ParserParameters.empty()).orElseThrow(() ->
|
||||||
|
new IllegalArgumentException(
|
||||||
|
"Could not create parser for primary type"));
|
||||||
|
final ArgumentParser<C, V> secondaryParser = parserRegistry.createParser(TypeToken.of(types.getSecond()),
|
||||||
|
ParserParameters.empty()).orElseThrow(() ->
|
||||||
|
new IllegalArgumentException(
|
||||||
|
"Could not create parser for secondary type"));
|
||||||
|
return new ArgumentPairIntermediaryBuilder<>(true, name, names, Pair.of(firstParser, secondaryParser), types);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
|
public static final class ArgumentPairIntermediaryBuilder<C, U, V> {
|
||||||
|
|
||||||
|
private final boolean required;
|
||||||
|
private final String name;
|
||||||
|
private final Pair<ArgumentParser<C, U>, ArgumentParser<C, V>> parserPair;
|
||||||
|
private final Pair<String, String> names;
|
||||||
|
private final Pair<Class<U>, Class<V>> types;
|
||||||
|
|
||||||
|
private ArgumentPairIntermediaryBuilder(final boolean required,
|
||||||
|
@Nonnull final String name,
|
||||||
|
@Nonnull final Pair<String, String> names,
|
||||||
|
@Nonnull final Pair<ArgumentParser<C, U>, ArgumentParser<C, V>> parserPair,
|
||||||
|
@Nonnull final Pair<Class<U>, Class<V>> types) {
|
||||||
|
this.required = required;
|
||||||
|
this.name = name;
|
||||||
|
this.names = names;
|
||||||
|
this.parserPair = parserPair;
|
||||||
|
this.types = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a simple argument pair that maps to a pair
|
||||||
|
*
|
||||||
|
* @return Argument pair
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public ArgumentPair<C, U, V, Pair<U, V>> simple() {
|
||||||
|
return new ArgumentPair<C, U, V, Pair<U, V>>(this.required,
|
||||||
|
this.name,
|
||||||
|
this.names,
|
||||||
|
this.types,
|
||||||
|
this.parserPair,
|
||||||
|
Function.identity(),
|
||||||
|
new TypeToken<Pair<U, V>>() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an argument pair that maps to a specific type
|
||||||
|
*
|
||||||
|
* @param clazz Output class
|
||||||
|
* @param mapper Output mapper
|
||||||
|
* @param <O> Output type
|
||||||
|
* @return Created pair
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public <O> ArgumentPair<C, U, V, O> withMapper(@Nonnull final TypeToken<O> clazz,
|
||||||
|
@Nonnull final Function<Pair<U, V>, O> mapper) {
|
||||||
|
return new ArgumentPair<C, U, V, O>(this.required, this.name, this.names, this.types, this.parserPair, mapper, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an argument pair that maps to a specific type
|
||||||
|
*
|
||||||
|
* @param clazz Output class
|
||||||
|
* @param mapper Output mapper
|
||||||
|
* @param <O> Output type
|
||||||
|
* @return Created pair
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public <O> ArgumentPair<C, U, V, O> withMapper(@Nonnull final Class<O> clazz,
|
||||||
|
@Nonnull final Function<Pair<U, V>, O> mapper) {
|
||||||
|
return this.withMapper(TypeToken.of(clazz), mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// 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 com.intellectualsites.commands.arguments.compound;
|
||||||
|
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
import com.intellectualsites.commands.CommandManager;
|
||||||
|
import com.intellectualsites.commands.arguments.parser.ArgumentParser;
|
||||||
|
import com.intellectualsites.commands.arguments.parser.ParserParameters;
|
||||||
|
import com.intellectualsites.commands.arguments.parser.ParserRegistry;
|
||||||
|
import com.intellectualsites.commands.types.tuples.Triplet;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A compound argument consisting of three inner arguments
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
* @param <U> First argument type
|
||||||
|
* @param <V> Second argument type
|
||||||
|
* @param <W> Third argument type
|
||||||
|
* @param <O> Output type
|
||||||
|
*/
|
||||||
|
public final class ArgumentTriplet<C, U, V, W, O> extends CompoundArgument<Triplet<U, V, W>, C, O> {
|
||||||
|
|
||||||
|
private ArgumentTriplet(final boolean required,
|
||||||
|
@Nonnull final String name,
|
||||||
|
@Nonnull final Triplet<String, String, String> names,
|
||||||
|
@Nonnull final Triplet<Class<U>, Class<V>, Class<W>> types,
|
||||||
|
@Nonnull final Triplet<ArgumentParser<C, U>, ArgumentParser<C, V>,
|
||||||
|
ArgumentParser<C, W>> parserTriplet,
|
||||||
|
@Nonnull final Function<Triplet<U, V, W>, O> mapper,
|
||||||
|
@Nonnull final TypeToken<O> valueType) {
|
||||||
|
super(required, name, names, parserTriplet, types, mapper, o -> Triplet.of((U) o[0], (V) o[1], (W) o[2]), valueType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a builder for an argument triplet
|
||||||
|
*
|
||||||
|
* @param manager Command manager
|
||||||
|
* @param name Argument name
|
||||||
|
* @param names Sub-argument names
|
||||||
|
* @param types Triplet containing the types of the sub-arguments. There must be parsers for these types registered
|
||||||
|
* in the {@link com.intellectualsites.commands.arguments.parser.ParserRegistry} used by the
|
||||||
|
* {@link CommandManager}
|
||||||
|
* @param <C> Command sender type
|
||||||
|
* @param <U> First parsed type
|
||||||
|
* @param <V> Second parsed type
|
||||||
|
* @param <W> Third type
|
||||||
|
* @return Intermediary builder
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static <C, U, V, W> ArgumentTripletIntermediaryBuilder<C, U, V, W>
|
||||||
|
required(@Nonnull final CommandManager<C> manager,
|
||||||
|
@Nonnull final String name,
|
||||||
|
@Nonnull final Triplet<String, String, String> names,
|
||||||
|
@Nonnull final Triplet<Class<U>, Class<V>, Class<W>> types) {
|
||||||
|
final ParserRegistry<C> parserRegistry = manager.getParserRegistry();
|
||||||
|
final ArgumentParser<C, U> firstParser = parserRegistry.createParser(TypeToken.of(types.getFirst()),
|
||||||
|
ParserParameters.empty()).orElseThrow(() ->
|
||||||
|
new IllegalArgumentException(
|
||||||
|
"Could not create parser for primary type"));
|
||||||
|
final ArgumentParser<C, V> secondaryParser = parserRegistry.createParser(TypeToken.of(types.getSecond()),
|
||||||
|
ParserParameters.empty()).orElseThrow(() ->
|
||||||
|
new IllegalArgumentException(
|
||||||
|
"Could not create parser for secondary type"));
|
||||||
|
final ArgumentParser<C, W> tertiaryParser = parserRegistry.createParser(TypeToken.of(types.getThird()),
|
||||||
|
ParserParameters.empty()).orElseThrow(() ->
|
||||||
|
new IllegalArgumentException(
|
||||||
|
"Could not create parser for tertiary type"));
|
||||||
|
return new ArgumentTripletIntermediaryBuilder<>(true, name, names,
|
||||||
|
Triplet.of(firstParser, secondaryParser, tertiaryParser), types);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
|
public static final class ArgumentTripletIntermediaryBuilder<C, U, V, W> {
|
||||||
|
|
||||||
|
private final boolean required;
|
||||||
|
private final String name;
|
||||||
|
private final Triplet<ArgumentParser<C, U>, ArgumentParser<C, V>, ArgumentParser<C, W>> parserTriplet;
|
||||||
|
private final Triplet<String, String, String> names;
|
||||||
|
private final Triplet<Class<U>, Class<V>, Class<W>> types;
|
||||||
|
|
||||||
|
private ArgumentTripletIntermediaryBuilder(final boolean required,
|
||||||
|
@Nonnull final String name,
|
||||||
|
@Nonnull final Triplet<String, String, String> names,
|
||||||
|
@Nonnull final Triplet<ArgumentParser<C, U>,
|
||||||
|
ArgumentParser<C, V>, ArgumentParser<C, W>> parserTriplet,
|
||||||
|
@Nonnull final Triplet<Class<U>, Class<V>, Class<W>> types) {
|
||||||
|
this.required = required;
|
||||||
|
this.name = name;
|
||||||
|
this.names = names;
|
||||||
|
this.parserTriplet = parserTriplet;
|
||||||
|
this.types = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a simple argument triplet that maps to a triplet
|
||||||
|
*
|
||||||
|
* @return Argument triplet
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public ArgumentTriplet<C, U, V, W, Triplet<U, V, W>> simple() {
|
||||||
|
return new ArgumentTriplet<>(this.required,
|
||||||
|
this.name,
|
||||||
|
this.names,
|
||||||
|
this.types,
|
||||||
|
this.parserTriplet,
|
||||||
|
Function.identity(),
|
||||||
|
new TypeToken<Triplet<U, V, W>>() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an argument triplet that maps to a specific type
|
||||||
|
*
|
||||||
|
* @param clazz Output class
|
||||||
|
* @param mapper Output mapper
|
||||||
|
* @param <O> Output type
|
||||||
|
* @return Created triplet
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public <O> ArgumentTriplet<C, U, V, W, O> withMapper(@Nonnull final TypeToken<O> clazz,
|
||||||
|
@Nonnull final Function<Triplet<U, V, W>, O> mapper) {
|
||||||
|
return new ArgumentTriplet<>(this.required, this.name, this.names, this.types, this.parserTriplet, mapper, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an argument triplet that maps to a specific type
|
||||||
|
*
|
||||||
|
* @param clazz Output class
|
||||||
|
* @param mapper Output mapper
|
||||||
|
* @param <O> Output type
|
||||||
|
* @return Created triplet
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public <O> ArgumentTriplet<C, U, V, W, O> withMapper(@Nonnull final Class<O> clazz,
|
||||||
|
@Nonnull final Function<Triplet<U, V, W>, O> mapper) {
|
||||||
|
return new ArgumentTriplet<>(this.required, this.name, this.names, this.types,
|
||||||
|
this.parserTriplet, mapper, TypeToken.of(clazz));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// 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 com.intellectualsites.commands.arguments.compound;
|
||||||
|
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
import com.intellectualsites.commands.arguments.CommandArgument;
|
||||||
|
import com.intellectualsites.commands.arguments.parser.ArgumentParseResult;
|
||||||
|
import com.intellectualsites.commands.arguments.parser.ArgumentParser;
|
||||||
|
import com.intellectualsites.commands.context.CommandContext;
|
||||||
|
import com.intellectualsites.commands.types.tuples.Tuple;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compound argument
|
||||||
|
*
|
||||||
|
* @param <T> Tuple type
|
||||||
|
* @param <C> Command sender type
|
||||||
|
* @param <O> Output type
|
||||||
|
*/
|
||||||
|
public class CompoundArgument<T extends Tuple, C, O> extends CommandArgument<C, O> {
|
||||||
|
|
||||||
|
private final Tuple types;
|
||||||
|
private final Tuple names;
|
||||||
|
private final Tuple parserTuple;
|
||||||
|
|
||||||
|
CompoundArgument(final boolean required,
|
||||||
|
@Nonnull final String name,
|
||||||
|
@Nonnull final Tuple names,
|
||||||
|
@Nonnull final Tuple parserTuple,
|
||||||
|
@Nonnull final Tuple types,
|
||||||
|
@Nonnull final Function<T, O> mapper,
|
||||||
|
@Nonnull final Function<Object[], T> tupleFactory,
|
||||||
|
@Nonnull final TypeToken<O> valueType) {
|
||||||
|
super(required,
|
||||||
|
name,
|
||||||
|
new CompoundParser<>(parserTuple, mapper, tupleFactory),
|
||||||
|
"",
|
||||||
|
valueType,
|
||||||
|
null);
|
||||||
|
this.parserTuple = parserTuple;
|
||||||
|
this.names = names;
|
||||||
|
this.types = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the tuple containing the internal parsers
|
||||||
|
*
|
||||||
|
* @return Internal parsers
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public Tuple getParserTuple() {
|
||||||
|
return this.parserTuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the argument names
|
||||||
|
*
|
||||||
|
* @return Argument names
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public Tuple getNames() {
|
||||||
|
return this.names;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the parser types
|
||||||
|
*
|
||||||
|
* @return Parser types
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public Tuple getTypes() {
|
||||||
|
return this.types;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final class CompoundParser<T extends Tuple, C, O> implements ArgumentParser<C, O> {
|
||||||
|
|
||||||
|
private final Object[] parsers;
|
||||||
|
private final Function<T, O> mapper;
|
||||||
|
private final Function<Object[], T> tupleFactory;
|
||||||
|
|
||||||
|
private CompoundParser(@Nonnull final Tuple parserTuple,
|
||||||
|
@Nonnull final Function<T, O> mapper,
|
||||||
|
@Nonnull final Function<Object[], T> tupleFactory) {
|
||||||
|
this.parsers = parserTuple.toArray();
|
||||||
|
this.mapper = mapper;
|
||||||
|
this.tupleFactory = tupleFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public ArgumentParseResult<O> parse(@Nonnull final CommandContext<C> commandContext,
|
||||||
|
@Nonnull final Queue<String> inputQueue) {
|
||||||
|
final Object[] output = new Object[this.parsers.length];
|
||||||
|
for (int i = 0; i < this.parsers.length; i++) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final ArgumentParser<C, ?> parser = (ArgumentParser<C, ?>) this.parsers[i];
|
||||||
|
final ArgumentParseResult<?> result = parser.parse(commandContext, inputQueue);
|
||||||
|
if (result.getFailure().isPresent()) {
|
||||||
|
/* Return the failure */
|
||||||
|
return ArgumentParseResult.failure(result.getFailure().get());
|
||||||
|
}
|
||||||
|
/* Store the parsed value */
|
||||||
|
output[i] = result.getParsedValue().orElse(null);
|
||||||
|
}
|
||||||
|
/* We now know that we have complete output, as none of the parsers returned a failure */
|
||||||
|
return ArgumentParseResult.success(this.mapper.apply(this.tupleFactory.apply(output)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public List<String> suggestions(@Nonnull final CommandContext<C> commandContext,
|
||||||
|
@Nonnull final String input) {
|
||||||
|
/*
|
||||||
|
This method will be called n times, each time for each of the internal types.
|
||||||
|
The problem is that we need to then know which of the parsers to forward the
|
||||||
|
suggestion request to. This is done by storing the number of parsed subtypes
|
||||||
|
in the context, so we can then extract that number and forward the request
|
||||||
|
*/
|
||||||
|
final int argument = commandContext.getOrDefault("__parsing_argument__", 1) - 1;
|
||||||
|
System.out.printf("Compound argument suggestions: %d | %s\n", argument, input);
|
||||||
|
|
||||||
|
//noinspection all
|
||||||
|
return ((ArgumentParser<C, ?>) this.parsers[argument]).suggestions(commandContext, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Argument types that consists of 2 or more sub-types
|
||||||
|
*/
|
||||||
|
package com.intellectualsites.commands.arguments.compound;
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// 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 com.intellectualsites.commands.types.tuples;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tuple type
|
||||||
|
*/
|
||||||
|
public interface Tuple {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the tuple size
|
||||||
|
*
|
||||||
|
* @return Tuple size
|
||||||
|
*/
|
||||||
|
int getSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn the tuple into a type erased array
|
||||||
|
*
|
||||||
|
* @return Created array
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Object[] toArray();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -48,6 +48,12 @@ class CommandHelpHandlerTest {
|
||||||
final SimpleCommandMeta meta2 = SimpleCommandMeta.builder().with("description", "Command with variables").build();
|
final SimpleCommandMeta meta2 = SimpleCommandMeta.builder().with("description", "Command with variables").build();
|
||||||
manager.command(manager.commandBuilder("test", meta2).literal("int").
|
manager.command(manager.commandBuilder("test", meta2).literal("int").
|
||||||
argument(IntegerArgument.required("int"), Description.of("A number")).build());
|
argument(IntegerArgument.required("int"), Description.of("A number")).build());
|
||||||
|
|
||||||
|
manager.command(manager.commandBuilder("vec")
|
||||||
|
.meta("description", "Takes in a vector")
|
||||||
|
.argumentPair("vec", Pair.of("x", "y"),
|
||||||
|
Pair.of(Double.class, Double.class), Description.of("Vector"))
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -63,7 +69,7 @@ class CommandHelpHandlerTest {
|
||||||
@Test
|
@Test
|
||||||
void testLongestChains() {
|
void testLongestChains() {
|
||||||
final List<String> longestChains = manager.getCommandHelpHandler().getLongestSharedChains();
|
final List<String> longestChains = manager.getCommandHelpHandler().getLongestSharedChains();
|
||||||
Assertions.assertEquals(Arrays.asList("test int|this"), longestChains);
|
Assertions.assertEquals(Arrays.asList("test int|this", "vec <<x> <y>>"), longestChains);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -77,6 +83,9 @@ class CommandHelpHandlerTest {
|
||||||
final CommandHelpHandler.HelpTopic<TestCommandSender> query3 = manager.getCommandHelpHandler().queryHelp("test int");
|
final CommandHelpHandler.HelpTopic<TestCommandSender> query3 = manager.getCommandHelpHandler().queryHelp("test int");
|
||||||
Assertions.assertTrue(query3 instanceof CommandHelpHandler.VerboseHelpTopic);
|
Assertions.assertTrue(query3 instanceof CommandHelpHandler.VerboseHelpTopic);
|
||||||
this.printTopic("test int", query3);
|
this.printTopic("test int", query3);
|
||||||
|
final CommandHelpHandler.HelpTopic<TestCommandSender> query4 = manager.getCommandHelpHandler().queryHelp("vec");
|
||||||
|
Assertions.assertTrue(query4 instanceof CommandHelpHandler.VerboseHelpTopic);
|
||||||
|
this.printTopic("vec", query4);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printTopic(@Nonnull final String query,
|
private void printTopic(@Nonnull final String query,
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,17 @@ public class CommandSuggestionsTest {
|
||||||
.argument(IntegerArgument.<TestCommandSender>newBuilder("num")
|
.argument(IntegerArgument.<TestCommandSender>newBuilder("num")
|
||||||
.withSuggestionsProvider((c, s) -> Arrays.asList("3", "33", "333")).build())
|
.withSuggestionsProvider((c, s) -> Arrays.asList("3", "33", "333")).build())
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
|
manager.command(manager.commandBuilder("com")
|
||||||
|
.argumentPair("com", Pair.of("x", "y"), Pair.of(Integer.class, TestEnum.class),
|
||||||
|
Description.empty())
|
||||||
|
.argument(IntegerArgument.required("int"))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
manager.command(manager.commandBuilder("com2")
|
||||||
|
.argumentPair("com", Pair.of("x", "enum"),
|
||||||
|
Pair.of(Integer.class, TestEnum.class), Description.empty())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -121,6 +132,21 @@ public class CommandSuggestionsTest {
|
||||||
Assertions.assertEquals(Arrays.asList("3", "33", "333"), suggestions);
|
Assertions.assertEquals(Arrays.asList("3", "33", "333"), suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCompound() {
|
||||||
|
final String input = "com ";
|
||||||
|
final List<String> suggestions = manager.suggest(new TestCommandSender(), input);
|
||||||
|
Assertions.assertEquals(Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"), suggestions);
|
||||||
|
final String input2 = "com 1 ";
|
||||||
|
final List<String> suggestions2 = manager.suggest(new TestCommandSender(), input2);
|
||||||
|
Assertions.assertEquals(Arrays.asList("foo", "bar"), suggestions2);
|
||||||
|
final String input3 = "com 1 foo ";
|
||||||
|
final List<String> suggestions3 = manager.suggest(new TestCommandSender(), input3);
|
||||||
|
Assertions.assertEquals(Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"), suggestions3);
|
||||||
|
final String input4 = "com2 1 ";
|
||||||
|
final List<String> suggestions4 = manager.suggest(new TestCommandSender(), input4);
|
||||||
|
Assertions.assertEquals(Arrays.asList("foo", "bar"), suggestions4);
|
||||||
|
}
|
||||||
|
|
||||||
public enum TestEnum {
|
public enum TestEnum {
|
||||||
FOO,
|
FOO,
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,28 @@ class CommandTreeTest {
|
||||||
.withPermission("command.outer")
|
.withPermission("command.outer")
|
||||||
.handler(c -> System.out.println("Using outer command"))
|
.handler(c -> System.out.println("Using outer command"))
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
|
/* Build command for testing compound types */
|
||||||
|
manager.command(manager.commandBuilder("pos")
|
||||||
|
.argument(ArgumentPair.required(manager, "pos", Pair.of("x", "y"),
|
||||||
|
Pair.of(Integer.class, Integer.class))
|
||||||
|
.simple())
|
||||||
|
.handler(c -> {
|
||||||
|
final Pair<Integer, Integer> pair = c.getRequired("pos");
|
||||||
|
System.out.printf("X: %d | Y: %d\n", pair.getFirst(), pair.getSecond());
|
||||||
|
})
|
||||||
|
.build());
|
||||||
|
manager.command(manager.commandBuilder("vec")
|
||||||
|
.argument(ArgumentPair.required(manager, "vec", Pair.of("x", "y"),
|
||||||
|
Pair.of(Double.class, Double.class))
|
||||||
|
.withMapper(Vector2.class,
|
||||||
|
pair -> new Vector2(pair.getFirst(), pair.getSecond()))
|
||||||
|
)
|
||||||
|
.handler(c -> {
|
||||||
|
final Vector2 vector2 = c.getRequired("vec");
|
||||||
|
System.out.printf("X: %f | Y: %f\n", vector2.getX(), vector2.getY());
|
||||||
|
})
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -157,8 +179,35 @@ class CommandTreeTest {
|
||||||
manager.executeCommand(new TestCommandSender(), "command").join();
|
manager.executeCommand(new TestCommandSender(), "command").join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCompound() {
|
||||||
|
manager.executeCommand(new TestCommandSender(), "pos -3 2").join();
|
||||||
|
manager.executeCommand(new TestCommandSender(), "vec 1 1").join();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class SpecificCommandSender extends TestCommandSender {
|
public static final class SpecificCommandSender extends TestCommandSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final class Vector2 {
|
||||||
|
|
||||||
|
private final double x;
|
||||||
|
private final double y;
|
||||||
|
|
||||||
|
private Vector2(final double x, final double y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getX() {
|
||||||
|
return this.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getY() {
|
||||||
|
return this.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ public final class CloudBrigadierManager<C, S> {
|
||||||
@Nullable
|
@Nullable
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private <T, K extends ArgumentParser<?, ?>> Pair<ArgumentType<?>, Boolean> getArgument(
|
private <T, K extends ArgumentParser<?, ?>> Pair<ArgumentType<?>, Boolean> getArgument(
|
||||||
@Nonnull final Class<?> valueType,
|
@Nonnull final TypeToken<?> valueType,
|
||||||
@Nonnull final TypeToken<T> argumentType,
|
@Nonnull final TypeToken<T> argumentType,
|
||||||
@Nonnull final K argument) {
|
@Nonnull final K argument) {
|
||||||
final ArgumentParser<C, ?> commandArgument = (ArgumentParser<C, ?>) argument;
|
final ArgumentParser<C, ?> commandArgument = (ArgumentParser<C, ?>) argument;
|
||||||
|
|
@ -225,9 +225,9 @@ public final class CloudBrigadierManager<C, S> {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private <T, K extends ArgumentParser<C, T>> Pair<ArgumentType<?>, Boolean> createDefaultMapper(
|
private <T, K extends ArgumentParser<C, T>> Pair<ArgumentType<?>, Boolean> createDefaultMapper(
|
||||||
@Nonnull final Class<?> clazz,
|
@Nonnull final TypeToken<?> clazz,
|
||||||
@Nonnull final ArgumentParser<C, T> argument) {
|
@Nonnull final ArgumentParser<C, T> argument) {
|
||||||
final Supplier<ArgumentType<?>> argumentTypeSupplier = this.defaultArgumentTypeSuppliers.get(clazz);
|
final Supplier<ArgumentType<?>> argumentTypeSupplier = this.defaultArgumentTypeSuppliers.get(clazz.getRawType());
|
||||||
if (argumentTypeSupplier != null) {
|
if (argumentTypeSupplier != null) {
|
||||||
return new Pair<>(argumentTypeSupplier.get(), true);
|
return new Pair<>(argumentTypeSupplier.get(), true);
|
||||||
}
|
}
|
||||||
|
|
@ -298,12 +298,54 @@ public final class CloudBrigadierManager<C, S> {
|
||||||
@Nonnull final BiPredicate<S, CommandPermission> permissionChecker,
|
@Nonnull final BiPredicate<S, CommandPermission> permissionChecker,
|
||||||
@Nonnull final com.mojang.brigadier.Command<S> executor,
|
@Nonnull final com.mojang.brigadier.Command<S> executor,
|
||||||
@Nonnull final SuggestionProvider<S> suggestionProvider) {
|
@Nonnull final SuggestionProvider<S> suggestionProvider) {
|
||||||
|
if (root.getValue() instanceof CompoundArgument) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final CompoundArgument<?, C, ?> compoundArgument = (CompoundArgument<?, C, ?>) root.getValue();
|
||||||
|
final Object[] parsers = compoundArgument.getParserTuple().toArray();
|
||||||
|
final Object[] types = compoundArgument.getTypes().toArray();
|
||||||
|
final Object[] names = compoundArgument.getNames().toArray();
|
||||||
|
|
||||||
|
/* Build nodes backwards */
|
||||||
|
final ArgumentBuilder<S, ?>[] argumentBuilders = new ArgumentBuilder[parsers.length];
|
||||||
|
|
||||||
|
for (int i = parsers.length - 1; i >= 0; i--) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final ArgumentParser<C, ?> parser = (ArgumentParser<C, ?>) parsers[i];
|
||||||
|
final Pair<ArgumentType<?>, Boolean> pair = this.getArgument(TypeToken.of((Class<?>) types[i]),
|
||||||
|
TypeToken.of(parser.getClass()),
|
||||||
|
parser);
|
||||||
|
final SuggestionProvider<S> provider = pair.getRight() ? null : suggestionProvider;
|
||||||
|
final ArgumentBuilder<S, ?> fragmentBuilder = RequiredArgumentBuilder
|
||||||
|
.<S, Object>argument((String) names[i], (ArgumentType<Object>) pair.getLeft())
|
||||||
|
.suggests(provider)
|
||||||
|
.requires(sender -> permissionChecker.test(sender,
|
||||||
|
(CommandPermission) root.getNodeMeta()
|
||||||
|
.getOrDefault("permission", Permission.empty())));
|
||||||
|
argumentBuilders[i] = fragmentBuilder;
|
||||||
|
|
||||||
|
if (forceExecutor || (i == parsers.length - 1) && (root.isLeaf() || !root.getValue().isRequired())) {
|
||||||
|
fragmentBuilder.executes(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Link all previous builder to this one */
|
||||||
|
if ((i + 1) < parsers.length) {
|
||||||
|
fragmentBuilder.then(argumentBuilders[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final CommandTree.Node<CommandArgument<C, ?>> node : root.getChildren()) {
|
||||||
|
argumentBuilders[parsers.length - 1]
|
||||||
|
.then(constructCommandNode(forceExecutor, node, permissionChecker, executor, suggestionProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
return argumentBuilders[0];
|
||||||
|
}
|
||||||
ArgumentBuilder<S, ?> argumentBuilder;
|
ArgumentBuilder<S, ?> argumentBuilder;
|
||||||
if (root.getValue() instanceof StaticArgument) {
|
if (root.getValue() instanceof StaticArgument) {
|
||||||
argumentBuilder = LiteralArgumentBuilder.<S>literal(root.getValue().getName())
|
argumentBuilder = LiteralArgumentBuilder.<S>literal(root.getValue().getName())
|
||||||
.requires(sender -> permissionChecker.test(sender, (CommandPermission) root.getNodeMeta()
|
.requires(sender -> permissionChecker.test(sender, (CommandPermission) root.getNodeMeta()
|
||||||
.getOrDefault("permission", Permission.empty())))
|
.getOrDefault("permission",
|
||||||
|
Permission.empty())))
|
||||||
.executes(executor);
|
.executes(executor);
|
||||||
} else {
|
} else {
|
||||||
final Pair<ArgumentType<?>, Boolean> pair = this.getArgument(root.getValue().getValueType(),
|
final Pair<ArgumentType<?>, Boolean> pair = this.getArgument(root.getValue().getValueType(),
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -210,13 +211,35 @@ public final class BukkitTest extends JavaPlugin {
|
||||||
.build(), Description.of("Help Query"))
|
.build(), Description.of("Help Query"))
|
||||||
.handler(c -> minecraftHelp.queryCommands(c.<String>get("query").orElse(""),
|
.handler(c -> minecraftHelp.queryCommands(c.<String>get("query").orElse(""),
|
||||||
c.getSender())).build());
|
c.getSender())).build());
|
||||||
|
this.registerTeleportCommand(mgr);
|
||||||
mgr.registerExceptionHandler(InvalidSyntaxException.class, (c, e) -> e.printStackTrace());
|
mgr.registerExceptionHandler(InvalidSyntaxException.class, (c, e) -> e.printStackTrace());
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerTeleportCommand(@Nonnull final BukkitCommandManager<CommandSender> manager) {
|
||||||
|
manager.command(mgr.commandBuilder("teleport")
|
||||||
|
.meta("description", "Takes in a location and teleports the player there")
|
||||||
|
.withSenderType(Player.class)
|
||||||
|
.argument(WorldArgument.required("world"), Description.of("World name"))
|
||||||
|
.argumentTriplet("coords",
|
||||||
|
TypeToken.of(Vector.class),
|
||||||
|
Triplet.of("x", "y", "z"),
|
||||||
|
Triplet.of(Double.class, Double.class, Double.class),
|
||||||
|
triplet -> new Vector(triplet.getFirst(), triplet.getSecond(), triplet.getThird()),
|
||||||
|
Description.of("Coordinates"))
|
||||||
|
.handler(context -> {
|
||||||
|
context.getSender().sendMessage(ChatColor.GOLD + "Teleporting!");
|
||||||
|
Bukkit.getScheduler().runTask(this, () -> {
|
||||||
|
final World world = context.getRequired("world");
|
||||||
|
final Vector vector = context.getRequired("coords");
|
||||||
|
((Player) context.getSender()).teleport(vector.toLocation(world));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
@CommandDescription("Test cloud command using @CommandMethod")
|
@CommandDescription("Test cloud command using @CommandMethod")
|
||||||
@CommandMethod(value = "annotation|a <input> [number]", permission = "some.permission.node")
|
@CommandMethod(value = "annotation|a <input> [number]", permission = "some.permission.node")
|
||||||
private void annotatedCommand(@Nonnull final Player player,
|
private void annotatedCommand(@Nonnull final Player player,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue