✨ Support repeating literals or argument names (#168)
Signed-off-by: Irmo van den Berge <irmo.vandenberge@ziggo.nl>
This commit is contained in:
parent
1e91273e0e
commit
5224050c99
5 changed files with 330 additions and 53 deletions
|
|
@ -43,13 +43,13 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A command consists out of a chain of {@link CommandArgument command arguments}.
|
* A command consists out of a chain of {@link CommandArgument command arguments}.
|
||||||
|
|
@ -58,7 +58,8 @@ import java.util.function.Consumer;
|
||||||
*/
|
*/
|
||||||
public class Command<C> {
|
public class Command<C> {
|
||||||
|
|
||||||
private final Map<@NonNull CommandArgument<C, ?>, @NonNull Description> arguments;
|
private final List<@NonNull CommandComponent<C>> components;
|
||||||
|
private final List<@NonNull CommandArgument<C, ?>> arguments;
|
||||||
private final CommandExecutionHandler<C> commandExecutionHandler;
|
private final CommandExecutionHandler<C> commandExecutionHandler;
|
||||||
private final Class<? extends C> senderType;
|
private final Class<? extends C> senderType;
|
||||||
private final CommandPermission commandPermission;
|
private final CommandPermission commandPermission;
|
||||||
|
|
@ -67,26 +68,27 @@ public class Command<C> {
|
||||||
/**
|
/**
|
||||||
* Construct a new command
|
* Construct a new command
|
||||||
*
|
*
|
||||||
* @param commandArguments Command argument and description pairs
|
* @param commandComponents Command component argument and description
|
||||||
* @param commandExecutionHandler Execution handler
|
* @param commandExecutionHandler Execution handler
|
||||||
* @param senderType Required sender type. May be {@code null}
|
* @param senderType Required sender type. May be {@code null}
|
||||||
* @param commandPermission Command permission
|
* @param commandPermission Command permission
|
||||||
* @param commandMeta Command meta instance
|
* @param commandMeta Command meta instance
|
||||||
*/
|
*/
|
||||||
public Command(
|
public Command(
|
||||||
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
|
final @NonNull List<@NonNull CommandComponent<C>> commandComponents,
|
||||||
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
|
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
|
||||||
final @Nullable Class<? extends C> senderType,
|
final @Nullable Class<? extends C> senderType,
|
||||||
final @NonNull CommandPermission commandPermission,
|
final @NonNull CommandPermission commandPermission,
|
||||||
final @NonNull CommandMeta commandMeta
|
final @NonNull CommandMeta commandMeta
|
||||||
) {
|
) {
|
||||||
this.arguments = Objects.requireNonNull(commandArguments, "Command arguments may not be null");
|
this.components = Objects.requireNonNull(commandComponents, "Command components may not be null");
|
||||||
if (this.arguments.size() == 0) {
|
this.arguments = this.components.stream().map(CommandComponent::getArgument).collect(Collectors.toList());
|
||||||
throw new IllegalArgumentException("At least one command argument is required");
|
if (this.components.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("At least one command component is required");
|
||||||
}
|
}
|
||||||
// Enforce ordering of command arguments
|
// Enforce ordering of command arguments
|
||||||
boolean foundOptional = false;
|
boolean foundOptional = false;
|
||||||
for (final CommandArgument<C, ?> argument : this.arguments.keySet()) {
|
for (final CommandArgument<C, ?> argument : this.arguments) {
|
||||||
if (argument.getName().isEmpty()) {
|
if (argument.getName().isEmpty()) {
|
||||||
throw new IllegalArgumentException("Argument names may not be empty");
|
throw new IllegalArgumentException("Argument names may not be empty");
|
||||||
}
|
}
|
||||||
|
|
@ -109,18 +111,77 @@ public class Command<C> {
|
||||||
/**
|
/**
|
||||||
* Construct a new command
|
* Construct a new command
|
||||||
*
|
*
|
||||||
* @param commandArguments Command arguments
|
* @param commandComponents Command components
|
||||||
* @param commandExecutionHandler Execution handler
|
* @param commandExecutionHandler Execution handler
|
||||||
* @param senderType Required sender type. May be {@code null}
|
* @param senderType Required sender type. May be {@code null}
|
||||||
* @param commandMeta Command meta instance
|
* @param commandMeta Command meta instance
|
||||||
*/
|
*/
|
||||||
|
public Command(
|
||||||
|
final @NonNull List<@NonNull CommandComponent<C>> commandComponents,
|
||||||
|
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
|
||||||
|
final @Nullable Class<? extends C> senderType,
|
||||||
|
final @NonNull CommandMeta commandMeta
|
||||||
|
) {
|
||||||
|
this(commandComponents, commandExecutionHandler, senderType, Permission.empty(), commandMeta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new command
|
||||||
|
*
|
||||||
|
* @param commandComponents Command components
|
||||||
|
* @param commandExecutionHandler Execution handler
|
||||||
|
* @param commandPermission Command permission
|
||||||
|
* @param commandMeta Command meta instance
|
||||||
|
*/
|
||||||
|
public Command(
|
||||||
|
final @NonNull List<@NonNull CommandComponent<C>> commandComponents,
|
||||||
|
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
|
||||||
|
final @NonNull CommandPermission commandPermission,
|
||||||
|
final @NonNull CommandMeta commandMeta
|
||||||
|
) {
|
||||||
|
this(commandComponents, commandExecutionHandler, null, commandPermission, commandMeta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new command
|
||||||
|
*
|
||||||
|
* @param commandArguments Command argument and description pairs
|
||||||
|
* @param commandExecutionHandler Execution handler
|
||||||
|
* @param senderType Required sender type. May be {@code null}
|
||||||
|
* @param commandPermission Command permission
|
||||||
|
* @param commandMeta Command meta instance
|
||||||
|
* @deprecated Map does not allow for the same literal or variable argument name to repeat
|
||||||
|
* @see #Command(List, CommandExecutionHandler, Class, CommandPermission, CommandMeta)
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public Command(
|
||||||
|
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
|
||||||
|
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
|
||||||
|
final @Nullable Class<? extends C> senderType,
|
||||||
|
final @NonNull CommandPermission commandPermission,
|
||||||
|
final @NonNull CommandMeta commandMeta
|
||||||
|
) {
|
||||||
|
this(mapToComponents(commandArguments), commandExecutionHandler, senderType, commandPermission, commandMeta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new command
|
||||||
|
*
|
||||||
|
* @param commandArguments Command arguments
|
||||||
|
* @param commandExecutionHandler Execution handler
|
||||||
|
* @param senderType Required sender type. May be {@code null}
|
||||||
|
* @param commandMeta Command meta instance
|
||||||
|
* @deprecated Map does not allow for the same literal or variable argument name to repeat
|
||||||
|
* @see #Command(List, CommandExecutionHandler, Class, CommandMeta)
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public Command(
|
public Command(
|
||||||
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
|
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
|
||||||
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
|
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
|
||||||
final @Nullable Class<? extends C> senderType,
|
final @Nullable Class<? extends C> senderType,
|
||||||
final @NonNull CommandMeta commandMeta
|
final @NonNull CommandMeta commandMeta
|
||||||
) {
|
) {
|
||||||
this(commandArguments, commandExecutionHandler, senderType, Permission.empty(), commandMeta);
|
this(mapToComponents(commandArguments), commandExecutionHandler, senderType, commandMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -130,14 +191,27 @@ public class Command<C> {
|
||||||
* @param commandExecutionHandler Execution handler
|
* @param commandExecutionHandler Execution handler
|
||||||
* @param commandPermission Command permission
|
* @param commandPermission Command permission
|
||||||
* @param commandMeta Command meta instance
|
* @param commandMeta Command meta instance
|
||||||
|
* @deprecated Map does not allow for the same literal or variable argument name to repeat
|
||||||
|
* @see #Command(List, CommandExecutionHandler, CommandPermission, CommandMeta)
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public Command(
|
public Command(
|
||||||
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
|
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
|
||||||
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
|
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
|
||||||
final @NonNull CommandPermission commandPermission,
|
final @NonNull CommandPermission commandPermission,
|
||||||
final @NonNull CommandMeta commandMeta
|
final @NonNull CommandMeta commandMeta
|
||||||
) {
|
) {
|
||||||
this(commandArguments, commandExecutionHandler, null, commandPermission, commandMeta);
|
this(mapToComponents(commandArguments), commandExecutionHandler, commandPermission, commandMeta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a map of CommandArgument and Description pairs to a List of CommandComponent
|
||||||
|
// Used for backwards-compatibility
|
||||||
|
private static <C> @NonNull List<@NonNull CommandComponent<C>> mapToComponents(
|
||||||
|
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments
|
||||||
|
) {
|
||||||
|
return commandArguments.entrySet().stream()
|
||||||
|
.map(e -> CommandComponent.of(e.getKey(), e.getValue()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -157,13 +231,13 @@ public class Command<C> {
|
||||||
final @NonNull Description description,
|
final @NonNull Description description,
|
||||||
final @NonNull String... aliases
|
final @NonNull String... aliases
|
||||||
) {
|
) {
|
||||||
final Map<@NonNull CommandArgument<C, ?>, @NonNull Description> map = new LinkedHashMap<>();
|
final List<CommandComponent<C>> commands = new ArrayList<>();
|
||||||
map.put(StaticArgument.of(commandName, aliases), description);
|
commands.add(CommandComponent.of(StaticArgument.of(commandName, aliases), description));
|
||||||
return new Builder<>(
|
return new Builder<>(
|
||||||
null,
|
null,
|
||||||
commandMeta,
|
commandMeta,
|
||||||
null,
|
null,
|
||||||
map,
|
commands,
|
||||||
new CommandExecutionHandler.NullCommandExecutionHandler<>(),
|
new CommandExecutionHandler.NullCommandExecutionHandler<>(),
|
||||||
Permission.empty(),
|
Permission.empty(),
|
||||||
Collections.emptyList()
|
Collections.emptyList()
|
||||||
|
|
@ -185,13 +259,13 @@ public class Command<C> {
|
||||||
final @NonNull CommandMeta commandMeta,
|
final @NonNull CommandMeta commandMeta,
|
||||||
final @NonNull String... aliases
|
final @NonNull String... aliases
|
||||||
) {
|
) {
|
||||||
final Map<CommandArgument<C, ?>, Description> map = new LinkedHashMap<>();
|
final List<CommandComponent<C>> commands = new ArrayList<>();
|
||||||
map.put(StaticArgument.of(commandName, aliases), Description.empty());
|
commands.add(CommandComponent.of(StaticArgument.of(commandName, aliases), Description.empty()));
|
||||||
return new Builder<>(
|
return new Builder<>(
|
||||||
null,
|
null,
|
||||||
commandMeta,
|
commandMeta,
|
||||||
null,
|
null,
|
||||||
map,
|
commands,
|
||||||
new CommandExecutionHandler.NullCommandExecutionHandler<>(),
|
new CommandExecutionHandler.NullCommandExecutionHandler<>(),
|
||||||
Permission.empty(),
|
Permission.empty(),
|
||||||
Collections.emptyList()
|
Collections.emptyList()
|
||||||
|
|
@ -201,10 +275,19 @@ public class Command<C> {
|
||||||
/**
|
/**
|
||||||
* Return a copy of the command argument array
|
* Return a copy of the command argument array
|
||||||
*
|
*
|
||||||
* @return Copy of the command argument array
|
* @return Copy of the command argument array. This List is mutable.
|
||||||
*/
|
*/
|
||||||
public @NonNull List<CommandArgument<@NonNull C, @NonNull ?>> getArguments() {
|
public @NonNull List<CommandArgument<@NonNull C, @NonNull ?>> getArguments() {
|
||||||
return new ArrayList<>(this.arguments.keySet());
|
return new ArrayList<>(this.arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of the command component array
|
||||||
|
*
|
||||||
|
* @return Copy of the command component array. This List is mutable.
|
||||||
|
*/
|
||||||
|
public @NonNull List<CommandComponent<@NonNull C>> getComponents() {
|
||||||
|
return new ArrayList<>(this.components);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -248,9 +331,18 @@ public class Command<C> {
|
||||||
*
|
*
|
||||||
* @param argument Argument
|
* @param argument Argument
|
||||||
* @return Argument description
|
* @return Argument description
|
||||||
|
* @throws IllegalArgumentException If the command argument does not exist
|
||||||
|
* @deprecated More than one matching command argument may exist per command.
|
||||||
|
* Use {@link #getArguments()} and search in that, instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public @NonNull String getArgumentDescription(final @NonNull CommandArgument<C, ?> argument) {
|
public @NonNull String getArgumentDescription(final @NonNull CommandArgument<C, ?> argument) {
|
||||||
return this.arguments.get(argument).getDescription();
|
for (final CommandComponent<C> component : this.components) {
|
||||||
|
if (component.getArgument().equals(argument)) {
|
||||||
|
return component.getDescription().getDescription();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Command argument not found: " + argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -282,7 +374,7 @@ public class Command<C> {
|
||||||
public static final class Builder<C> {
|
public static final class Builder<C> {
|
||||||
|
|
||||||
private final CommandMeta commandMeta;
|
private final CommandMeta commandMeta;
|
||||||
private final Map<CommandArgument<C, ?>, Description> commandArguments;
|
private final List<CommandComponent<C>> commandComponents;
|
||||||
private final CommandExecutionHandler<C> commandExecutionHandler;
|
private final CommandExecutionHandler<C> commandExecutionHandler;
|
||||||
private final Class<? extends C> senderType;
|
private final Class<? extends C> senderType;
|
||||||
private final CommandPermission commandPermission;
|
private final CommandPermission commandPermission;
|
||||||
|
|
@ -293,14 +385,14 @@ public class Command<C> {
|
||||||
final @Nullable CommandManager<C> commandManager,
|
final @Nullable CommandManager<C> commandManager,
|
||||||
final @NonNull CommandMeta commandMeta,
|
final @NonNull CommandMeta commandMeta,
|
||||||
final @Nullable Class<? extends C> senderType,
|
final @Nullable Class<? extends C> senderType,
|
||||||
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
|
final @NonNull List<@NonNull CommandComponent<C>> commandComponents,
|
||||||
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
|
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
|
||||||
final @NonNull CommandPermission commandPermission,
|
final @NonNull CommandPermission commandPermission,
|
||||||
final @NonNull Collection<CommandFlag<?>> flags
|
final @NonNull Collection<CommandFlag<?>> flags
|
||||||
) {
|
) {
|
||||||
this.commandManager = commandManager;
|
this.commandManager = commandManager;
|
||||||
this.senderType = senderType;
|
this.senderType = senderType;
|
||||||
this.commandArguments = Objects.requireNonNull(commandArguments, "Arguments may not be null");
|
this.commandComponents = Objects.requireNonNull(commandComponents, "Components may not be null");
|
||||||
this.commandExecutionHandler = Objects.requireNonNull(commandExecutionHandler, "Execution handler may not be null");
|
this.commandExecutionHandler = Objects.requireNonNull(commandExecutionHandler, "Execution handler may not be null");
|
||||||
this.commandPermission = Objects.requireNonNull(commandPermission, "Permission may not be null");
|
this.commandPermission = Objects.requireNonNull(commandPermission, "Permission may not be null");
|
||||||
this.commandMeta = Objects.requireNonNull(commandMeta, "Meta may not be null");
|
this.commandMeta = Objects.requireNonNull(commandMeta, "Meta may not be null");
|
||||||
|
|
@ -346,7 +438,7 @@ public class Command<C> {
|
||||||
this.commandManager,
|
this.commandManager,
|
||||||
commandMeta,
|
commandMeta,
|
||||||
this.senderType,
|
this.senderType,
|
||||||
this.commandArguments,
|
this.commandComponents,
|
||||||
this.commandExecutionHandler,
|
this.commandExecutionHandler,
|
||||||
this.commandPermission,
|
this.commandPermission,
|
||||||
this.flags
|
this.flags
|
||||||
|
|
@ -388,7 +480,7 @@ public class Command<C> {
|
||||||
commandManager,
|
commandManager,
|
||||||
this.commandMeta,
|
this.commandMeta,
|
||||||
this.senderType,
|
this.senderType,
|
||||||
this.commandArguments,
|
this.commandComponents,
|
||||||
this.commandExecutionHandler,
|
this.commandExecutionHandler,
|
||||||
this.commandPermission,
|
this.commandPermission,
|
||||||
this.flags
|
this.flags
|
||||||
|
|
@ -465,13 +557,13 @@ public class Command<C> {
|
||||||
+ " Use CommandArgument#copy to create a copy of the argument.");
|
+ " Use CommandArgument#copy to create a copy of the argument.");
|
||||||
}
|
}
|
||||||
argument.setArgumentRegistered();
|
argument.setArgumentRegistered();
|
||||||
final Map<CommandArgument<C, ?>, Description> commandArgumentMap = new LinkedHashMap<>(this.commandArguments);
|
final List<CommandComponent<C>> commandComponents = new ArrayList<>(this.commandComponents);
|
||||||
commandArgumentMap.put(argument, description);
|
commandComponents.add(CommandComponent.of(argument, description));
|
||||||
return new Builder<>(
|
return new Builder<>(
|
||||||
this.commandManager,
|
this.commandManager,
|
||||||
this.commandMeta,
|
this.commandMeta,
|
||||||
this.senderType,
|
this.senderType,
|
||||||
commandArgumentMap,
|
commandComponents,
|
||||||
this.commandExecutionHandler,
|
this.commandExecutionHandler,
|
||||||
this.commandPermission,
|
this.commandPermission,
|
||||||
this.flags
|
this.flags
|
||||||
|
|
@ -491,13 +583,13 @@ public class Command<C> {
|
||||||
final CommandArgument.@NonNull Builder<C, T> builder,
|
final CommandArgument.@NonNull Builder<C, T> builder,
|
||||||
final @NonNull Description description
|
final @NonNull Description description
|
||||||
) {
|
) {
|
||||||
final Map<CommandArgument<C, ?>, Description> commandArgumentMap = new LinkedHashMap<>(this.commandArguments);
|
final List<CommandComponent<C>> commandComponents = new ArrayList<>(this.commandComponents);
|
||||||
commandArgumentMap.put(builder.build(), description);
|
commandComponents.add(CommandComponent.of(builder.build(), description));
|
||||||
return new Builder<>(
|
return new Builder<>(
|
||||||
this.commandManager,
|
this.commandManager,
|
||||||
this.commandMeta,
|
this.commandMeta,
|
||||||
this.senderType,
|
this.senderType,
|
||||||
commandArgumentMap,
|
commandComponents,
|
||||||
this.commandExecutionHandler,
|
this.commandExecutionHandler,
|
||||||
this.commandPermission,
|
this.commandPermission,
|
||||||
this.flags
|
this.flags
|
||||||
|
|
@ -676,7 +768,7 @@ public class Command<C> {
|
||||||
this.commandManager,
|
this.commandManager,
|
||||||
this.commandMeta,
|
this.commandMeta,
|
||||||
this.senderType,
|
this.senderType,
|
||||||
this.commandArguments,
|
this.commandComponents,
|
||||||
commandExecutionHandler,
|
commandExecutionHandler,
|
||||||
this.commandPermission,
|
this.commandPermission,
|
||||||
this.flags
|
this.flags
|
||||||
|
|
@ -694,7 +786,7 @@ public class Command<C> {
|
||||||
this.commandManager,
|
this.commandManager,
|
||||||
this.commandMeta,
|
this.commandMeta,
|
||||||
senderType,
|
senderType,
|
||||||
this.commandArguments,
|
this.commandComponents,
|
||||||
this.commandExecutionHandler,
|
this.commandExecutionHandler,
|
||||||
this.commandPermission,
|
this.commandPermission,
|
||||||
this.flags
|
this.flags
|
||||||
|
|
@ -712,7 +804,7 @@ public class Command<C> {
|
||||||
this.commandManager,
|
this.commandManager,
|
||||||
this.commandMeta,
|
this.commandMeta,
|
||||||
this.senderType,
|
this.senderType,
|
||||||
this.commandArguments,
|
this.commandComponents,
|
||||||
this.commandExecutionHandler,
|
this.commandExecutionHandler,
|
||||||
permission,
|
permission,
|
||||||
this.flags
|
this.flags
|
||||||
|
|
@ -730,7 +822,7 @@ public class Command<C> {
|
||||||
this.commandManager,
|
this.commandManager,
|
||||||
this.commandMeta,
|
this.commandMeta,
|
||||||
this.senderType,
|
this.senderType,
|
||||||
this.commandArguments,
|
this.commandComponents,
|
||||||
this.commandExecutionHandler,
|
this.commandExecutionHandler,
|
||||||
Permission.of(permission),
|
Permission.of(permission),
|
||||||
this.flags
|
this.flags
|
||||||
|
|
@ -750,12 +842,13 @@ public class Command<C> {
|
||||||
*/
|
*/
|
||||||
public @NonNull Builder<C> proxies(final @NonNull Command<C> command) {
|
public @NonNull Builder<C> proxies(final @NonNull Command<C> command) {
|
||||||
Builder<C> builder = this;
|
Builder<C> builder = this;
|
||||||
for (final CommandArgument<C, ?> argument : command.getArguments()) {
|
for (final CommandComponent<C> component : command.getComponents()) {
|
||||||
|
final CommandArgument<C, ?> argument = component.getArgument();
|
||||||
if (argument instanceof StaticArgument) {
|
if (argument instanceof StaticArgument) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final CommandArgument<C, ?> builtArgument = argument.copy();
|
final CommandArgument<C, ?> builtArgument = argument.copy();
|
||||||
builder = builder.argument(builtArgument, Description.of(command.getArgumentDescription(argument)));
|
builder = builder.argument(builtArgument, component.getDescription());
|
||||||
}
|
}
|
||||||
if (this.commandPermission.toString().isEmpty()) {
|
if (this.commandPermission.toString().isEmpty()) {
|
||||||
builder = builder.permission(command.getCommandPermission());
|
builder = builder.permission(command.getCommandPermission());
|
||||||
|
|
@ -787,7 +880,7 @@ public class Command<C> {
|
||||||
this.commandManager,
|
this.commandManager,
|
||||||
this.commandMeta,
|
this.commandMeta,
|
||||||
this.senderType,
|
this.senderType,
|
||||||
this.commandArguments,
|
this.commandComponents,
|
||||||
this.commandExecutionHandler,
|
this.commandExecutionHandler,
|
||||||
this.commandPermission,
|
this.commandPermission,
|
||||||
Collections.unmodifiableList(flags)
|
Collections.unmodifiableList(flags)
|
||||||
|
|
@ -811,14 +904,14 @@ public class Command<C> {
|
||||||
* @return Built command
|
* @return Built command
|
||||||
*/
|
*/
|
||||||
public @NonNull Command<C> build() {
|
public @NonNull Command<C> build() {
|
||||||
final LinkedHashMap<CommandArgument<C, ?>, Description> commandArguments = new LinkedHashMap<>(this.commandArguments);
|
final List<CommandComponent<C>> commandComponents = new ArrayList<>(this.commandComponents);
|
||||||
/* Construct flag node */
|
/* Construct flag node */
|
||||||
if (!flags.isEmpty()) {
|
if (!flags.isEmpty()) {
|
||||||
final FlagArgument<C> flagArgument = new FlagArgument<>(this.flags);
|
final FlagArgument<C> flagArgument = new FlagArgument<>(this.flags);
|
||||||
commandArguments.put(flagArgument, Description.of("Command flags"));
|
commandComponents.add(CommandComponent.of(flagArgument, Description.of("Command flags")));
|
||||||
}
|
}
|
||||||
return new Command<>(
|
return new Command<>(
|
||||||
Collections.unmodifiableMap(commandArguments),
|
Collections.unmodifiableList(commandComponents),
|
||||||
this.commandExecutionHandler,
|
this.commandExecutionHandler,
|
||||||
this.senderType,
|
this.senderType,
|
||||||
this.commandPermission,
|
this.commandPermission,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package cloud.commandframework;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import cloud.commandframework.arguments.CommandArgument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single literal or argument component of a command
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public final class CommandComponent<C> {
|
||||||
|
private final CommandArgument<C, ?> argument;
|
||||||
|
private final Description description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a new CommandComponent
|
||||||
|
*
|
||||||
|
* @param commandArgument Command Component Argument
|
||||||
|
* @param commandDescription Command Component Description
|
||||||
|
*/
|
||||||
|
private CommandComponent(
|
||||||
|
final @NonNull CommandArgument<C, ?> commandArgument,
|
||||||
|
final @NonNull Description commandDescription
|
||||||
|
) {
|
||||||
|
this.argument = commandArgument;
|
||||||
|
this.description = commandDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the command component argument details
|
||||||
|
*
|
||||||
|
* @return command component argument details
|
||||||
|
*/
|
||||||
|
public @NonNull CommandArgument<C, ?> getArgument() {
|
||||||
|
return this.argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the command component description
|
||||||
|
*
|
||||||
|
* @return command component description
|
||||||
|
*/
|
||||||
|
public @NonNull Description getDescription() {
|
||||||
|
return this.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(getArgument(), getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
} else if (o instanceof CommandComponent) {
|
||||||
|
CommandComponent<?> that = (CommandComponent<?>) o;
|
||||||
|
return getArgument().equals(that.getArgument())
|
||||||
|
&& getDescription().equals(that.getDescription());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String toString() {
|
||||||
|
return String.format("%s{argument=%s,description=%s}", this.getClass().getSimpleName(),
|
||||||
|
this.argument, this.description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new CommandComponent with the provided argument and description
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
* @param commandArgument Command Component Argument
|
||||||
|
* @param commandDescription Command Component Description
|
||||||
|
* @return new CommandComponent
|
||||||
|
*/
|
||||||
|
public static <C> @NonNull CommandComponent<C> of(
|
||||||
|
final @NonNull CommandArgument<C, ?> commandArgument,
|
||||||
|
final @NonNull Description commandDescription
|
||||||
|
) {
|
||||||
|
return new CommandComponent<C>(commandArgument, commandDescription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
//
|
//
|
||||||
package cloud.commandframework;
|
package cloud.commandframework;
|
||||||
|
|
||||||
import cloud.commandframework.arguments.CommandArgument;
|
|
||||||
import cloud.commandframework.arguments.standard.IntegerArgument;
|
import cloud.commandframework.arguments.standard.IntegerArgument;
|
||||||
import cloud.commandframework.meta.CommandMeta;
|
import cloud.commandframework.meta.CommandMeta;
|
||||||
import cloud.commandframework.meta.SimpleCommandMeta;
|
import cloud.commandframework.meta.SimpleCommandMeta;
|
||||||
|
|
@ -142,17 +141,17 @@ class CommandHelpHandlerTest {
|
||||||
.apply(helpTopic.getCommand().getArguments(), null));
|
.apply(helpTopic.getCommand().getArguments(), null));
|
||||||
System.out.printf(" ├── Description: %s\n", helpTopic.getDescription());
|
System.out.printf(" ├── Description: %s\n", helpTopic.getDescription());
|
||||||
System.out.println(" └── Args: ");
|
System.out.println(" └── Args: ");
|
||||||
final Iterator<CommandArgument<TestCommandSender, ?>> iterator = helpTopic.getCommand().getArguments().iterator();
|
final Iterator<CommandComponent<TestCommandSender>> iterator = helpTopic.getCommand().getComponents().iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
final CommandArgument<TestCommandSender, ?> argument = iterator.next();
|
final CommandComponent<TestCommandSender> component = iterator.next();
|
||||||
|
|
||||||
String description = helpTopic.getCommand().getArgumentDescription(argument);
|
String description = component.getDescription().getDescription();
|
||||||
if (!description.isEmpty()) {
|
if (!description.isEmpty()) {
|
||||||
description = ": " + description;
|
description = ": " + description;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.printf(" %s %s%s\n", iterator.hasNext() ? "├──" : "└──", manager.getCommandSyntaxFormatter().apply(
|
System.out.printf(" %s %s%s\n", iterator.hasNext() ? "├──" : "└──", manager.getCommandSyntaxFormatter().apply(
|
||||||
Collections.singletonList(argument), null), description);
|
Collections.singletonList(component.getArgument()), null), description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ import cloud.commandframework.exceptions.AmbiguousNodeException;
|
||||||
import cloud.commandframework.exceptions.NoPermissionException;
|
import cloud.commandframework.exceptions.NoPermissionException;
|
||||||
import cloud.commandframework.meta.SimpleCommandMeta;
|
import cloud.commandframework.meta.SimpleCommandMeta;
|
||||||
import cloud.commandframework.types.tuples.Pair;
|
import cloud.commandframework.types.tuples.Pair;
|
||||||
|
import io.leangen.geantyref.TypeToken;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
@ -42,6 +44,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
|
|
||||||
class CommandTreeTest {
|
class CommandTreeTest {
|
||||||
|
|
@ -220,6 +223,45 @@ class CommandTreeTest {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getArgumentsAndComponents() {
|
||||||
|
// Create and register a command
|
||||||
|
Command<TestCommandSender> command = manager.commandBuilder("component")
|
||||||
|
.literal("literal", "literalalias")
|
||||||
|
.literal("detail", Description.of("detaildescription"))
|
||||||
|
.argument(CommandArgument.ofType(int.class, "argument"),
|
||||||
|
Description.of("argumentdescription"))
|
||||||
|
.build();
|
||||||
|
manager.command(command);
|
||||||
|
|
||||||
|
// Verify all the details we have configured are present
|
||||||
|
List<CommandArgument<TestCommandSender, ?>> arguments = command.getArguments();
|
||||||
|
List<CommandComponent<TestCommandSender>> components = command.getComponents();
|
||||||
|
Assertions.assertEquals(arguments.size(), components.size());
|
||||||
|
Assertions.assertEquals(4, components.size());
|
||||||
|
|
||||||
|
// Arguments should exactly match the component getArgument() result, in same order
|
||||||
|
for (int i = 0; i < components.size(); i++) {
|
||||||
|
Assertions.assertEquals(components.get(i).getArgument(), arguments.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Argument configuration, we know component has the same argument so no need to test those
|
||||||
|
// TODO: Aliases
|
||||||
|
Assertions.assertEquals("component", arguments.get(0).getName());
|
||||||
|
Assertions.assertEquals("literal", arguments.get(1).getName());
|
||||||
|
Assertions.assertEquals("detail", arguments.get(2).getName());
|
||||||
|
Assertions.assertEquals("argument", arguments.get(3).getName());
|
||||||
|
|
||||||
|
// Check argument is indeed a command argument
|
||||||
|
Assertions.assertEquals(TypeToken.get(int.class), arguments.get(3).getValueType());
|
||||||
|
|
||||||
|
// Check description is set for all components, is empty when not specified
|
||||||
|
Assertions.assertEquals("", components.get(0).getDescription().getDescription());
|
||||||
|
Assertions.assertEquals("", components.get(1).getDescription().getDescription());
|
||||||
|
Assertions.assertEquals("detaildescription", components.get(2).getDescription().getDescription());
|
||||||
|
Assertions.assertEquals("argumentdescription", components.get(3).getDescription().getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getSuggestions() {
|
void getSuggestions() {
|
||||||
Assertions.assertFalse(
|
Assertions.assertFalse(
|
||||||
|
|
@ -319,6 +361,36 @@ class CommandTreeTest {
|
||||||
newTree();
|
newTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLiteralRepeatingArgument() {
|
||||||
|
// Build a command with a literal repeating
|
||||||
|
Command<TestCommandSender> command = manager.commandBuilder("repeatingargscommand")
|
||||||
|
.literal("repeat")
|
||||||
|
.literal("middle")
|
||||||
|
.literal("repeat")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Verify built command has the repeat argument twice
|
||||||
|
List<CommandArgument<TestCommandSender, ?>> args = command.getArguments();
|
||||||
|
Assertions.assertEquals(4, args.size());
|
||||||
|
Assertions.assertEquals("repeatingargscommand", args.get(0).getName());
|
||||||
|
Assertions.assertEquals("repeat", args.get(1).getName());
|
||||||
|
Assertions.assertEquals("middle", args.get(2).getName());
|
||||||
|
Assertions.assertEquals("repeat", args.get(3).getName());
|
||||||
|
|
||||||
|
// Register
|
||||||
|
manager.command(command);
|
||||||
|
|
||||||
|
// If internally it drops repeating arguments, then it would register:
|
||||||
|
// > /repeatingargscommand repeat middle
|
||||||
|
// So check that we can register that exact command without an ambiguity exception
|
||||||
|
manager.command(
|
||||||
|
manager.commandBuilder("repeatingargscommand")
|
||||||
|
.literal("repeat")
|
||||||
|
.literal("middle")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testDuplicateArgument() {
|
void testDuplicateArgument() {
|
||||||
final CommandArgument<TestCommandSender, String> argument = StringArgument.of("test");
|
final CommandArgument<TestCommandSender, String> argument = StringArgument.of("test");
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
//
|
//
|
||||||
package cloud.commandframework.minecraft.extras;
|
package cloud.commandframework.minecraft.extras;
|
||||||
|
|
||||||
|
import cloud.commandframework.CommandComponent;
|
||||||
import cloud.commandframework.CommandHelpHandler;
|
import cloud.commandframework.CommandHelpHandler;
|
||||||
import cloud.commandframework.CommandManager;
|
import cloud.commandframework.CommandManager;
|
||||||
import cloud.commandframework.arguments.CommandArgument;
|
import cloud.commandframework.arguments.CommandArgument;
|
||||||
|
|
@ -391,34 +392,35 @@ public final class MinecraftHelp<C> {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
final Iterator<CommandArgument<C, ?>> iterator = helpTopic.getCommand().getArguments().iterator();
|
final Iterator<CommandComponent<C>> iterator = helpTopic.getCommand().getComponents().iterator();
|
||||||
/* Skip the first one because it's the command literal */
|
/* Skip the first one because it's the command literal */
|
||||||
iterator.next();
|
iterator.next();
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
final CommandArgument<C, ?> argument = iterator.next();
|
final CommandComponent<C> component = iterator.next();
|
||||||
|
final CommandArgument<C, ?> argument = component.getArgument();
|
||||||
|
|
||||||
String syntax = this.commandManager.getCommandSyntaxFormatter()
|
String syntax = this.commandManager.getCommandSyntaxFormatter()
|
||||||
.apply(Collections.singletonList(argument), null);
|
.apply(Collections.singletonList(argument), null);
|
||||||
|
|
||||||
final TextComponent.Builder component = Component.text()
|
final TextComponent.Builder textComponent = Component.text()
|
||||||
.append(Component.text(" "))
|
.append(Component.text(" "))
|
||||||
.append(iterator.hasNext() ? this.branch() : this.lastBranch())
|
.append(iterator.hasNext() ? this.branch() : this.lastBranch())
|
||||||
.append(this.highlight(Component.text(" " + syntax, this.colors.highlight)));
|
.append(this.highlight(Component.text(" " + syntax, this.colors.highlight)));
|
||||||
if (!argument.isRequired()) {
|
if (!argument.isRequired()) {
|
||||||
component.append(Component.text(
|
textComponent.append(Component.text(
|
||||||
" (" + this.messageProvider.apply(sender, MESSAGE_OPTIONAL) + ")",
|
" (" + this.messageProvider.apply(sender, MESSAGE_OPTIONAL) + ")",
|
||||||
this.colors.alternateHighlight
|
this.colors.alternateHighlight
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
final String description = helpTopic.getCommand().getArgumentDescription(argument);
|
final String description = component.getDescription().getDescription();
|
||||||
if (!description.isEmpty()) {
|
if (!description.isEmpty()) {
|
||||||
component
|
textComponent
|
||||||
.append(Component.text(" - ", this.colors.accent))
|
.append(Component.text(" - ", this.colors.accent))
|
||||||
.append(Component.text(description, this.colors.text));
|
.append(Component.text(description, this.colors.text));
|
||||||
}
|
}
|
||||||
|
|
||||||
audience.sendMessage(Identity.nil(), component);
|
audience.sendMessage(Identity.nil(), textComponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
audience.sendMessage(Identity.nil(), this.footer(sender));
|
audience.sendMessage(Identity.nil(), this.footer(sender));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue