Add default arguments
This commit is contained in:
parent
11d40bdd87
commit
e623d72be7
42 changed files with 205 additions and 35 deletions
41
cloud-core/pom.xml
Normal file
41
cloud-core/pom.xml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ MIT License
|
||||
~
|
||||
~ Copyright (c) 2020 IntellectualSites
|
||||
~
|
||||
~ 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.
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>cloud</artifactId>
|
||||
<groupId>com.intellectualsites</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cloud-core</artifactId>
|
||||
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.intellectualsites.commands.components.CommandComponent;
|
||||
import com.intellectualsites.commands.components.StaticComponent;
|
||||
import com.intellectualsites.commands.execution.CommandExecutionHandler;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A command consists out of a chain of {@link com.intellectualsites.commands.components.CommandComponent command components}.
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class Command<C extends CommandSender> {
|
||||
|
||||
@Nonnull private final List<CommandComponent<C, ?>> components;
|
||||
@Nonnull private final CommandExecutionHandler<C> commandExecutionHandler;
|
||||
@Nullable private final Class<? extends C> senderType;
|
||||
@Nonnull private final String commandPermission;
|
||||
|
||||
public Command(@Nonnull final List<CommandComponent<C, ?>> commandComponents,
|
||||
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
||||
@Nullable final Class<? extends C> senderType,
|
||||
@Nonnull final String commandPermission) {
|
||||
this.components = Objects.requireNonNull(commandComponents, "Command components may not be null");
|
||||
if (this.components.size() == 0) {
|
||||
throw new IllegalArgumentException("At least one command component is required");
|
||||
}
|
||||
// Enforce ordering of command components
|
||||
boolean foundOptional = false;
|
||||
for (final CommandComponent<C, ?> component : this.components) {
|
||||
if (component.getName().isEmpty()) {
|
||||
throw new IllegalArgumentException("Component names may not be empty");
|
||||
}
|
||||
if (foundOptional && component.isRequired()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Command component '%s' cannot be placed after an optional component",
|
||||
component.getName()));
|
||||
} else if (!component.isRequired()) {
|
||||
foundOptional = true;
|
||||
}
|
||||
}
|
||||
this.commandExecutionHandler = commandExecutionHandler;
|
||||
this.senderType = senderType;
|
||||
this.commandPermission = commandPermission;
|
||||
}
|
||||
|
||||
public Command(@Nonnull final List<CommandComponent<C, ?>> commandComponents,
|
||||
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
||||
@Nullable final Class<? extends C> senderType) {
|
||||
this(commandComponents, commandExecutionHandler, senderType, "");
|
||||
}
|
||||
|
||||
public Command(@Nonnull final List<CommandComponent<C, ?>> commandComponents,
|
||||
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
||||
@Nonnull final String commandPermission) {
|
||||
this(commandComponents, commandExecutionHandler, null, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new command builder
|
||||
*
|
||||
* @param commandName Base command component
|
||||
* @return Command builder
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C extends CommandSender> Builder<C> newBuilder(@Nonnull final String commandName) {
|
||||
return new Builder<>(null, Collections.singletonList(StaticComponent.required(commandName)),
|
||||
new CommandExecutionHandler.NullCommandExecutionHandler<>(), "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of the command component array
|
||||
*
|
||||
* @return Copy of the command component array
|
||||
*/
|
||||
@Nonnull
|
||||
public List<CommandComponent<C, ?>> getComponents() {
|
||||
return Collections.unmodifiableList(this.components);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command execution handler
|
||||
*
|
||||
* @return Command execution handler
|
||||
*/
|
||||
@Nonnull
|
||||
public CommandExecutionHandler<C> getCommandExecutionHandler() {
|
||||
return this.commandExecutionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required sender type, if one has been specified
|
||||
*
|
||||
* @return Required sender type
|
||||
*/
|
||||
@Nonnull
|
||||
public Optional<Class<? extends C>> getSenderType() {
|
||||
return Optional.ofNullable(this.senderType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command permission
|
||||
*
|
||||
* @return Command permission
|
||||
*/
|
||||
@Nonnull
|
||||
public String getCommandPermission() {
|
||||
return this.commandPermission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the longest chain of similar components for
|
||||
* two commands
|
||||
*
|
||||
* @return List containing the longest shared component chain
|
||||
*/
|
||||
public List<CommandComponent<C, ?>> getSharedComponentChain(@Nonnull final Command<C> other) {
|
||||
final List<CommandComponent<C, ?>> commandComponents = new LinkedList<>();
|
||||
for (int i = 0; i < this.components.size() && i < other.components.size(); i++) {
|
||||
if (this.components.get(i).equals(other.components.get(i))) {
|
||||
commandComponents.add(this.components.get(i));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return commandComponents;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for {@link Command} instances. The builder is immutable, and each
|
||||
* setter method will return a new builder instance.
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
public static final class Builder<C extends CommandSender> {
|
||||
|
||||
@Nonnull private final List<CommandComponent<C, ?>> commandComponents;
|
||||
@Nonnull private final CommandExecutionHandler<C> commandExecutionHandler;
|
||||
@Nullable private final Class<? extends C> senderType;
|
||||
@Nonnull private final String commandPermission;
|
||||
|
||||
private Builder(@Nullable final Class<? extends C> senderType,
|
||||
@Nonnull final List<CommandComponent<C, ?>> commandComponents,
|
||||
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
||||
@Nonnull final String commandPermission) {
|
||||
this.senderType = senderType;
|
||||
this.commandComponents = Objects.requireNonNull(commandComponents, "Components 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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new command component to the command
|
||||
*
|
||||
* @param component Component to add
|
||||
* @param <T> Component type
|
||||
* @return New builder instance with the command component inserted into the component list
|
||||
*/
|
||||
@Nonnull
|
||||
public <T> Builder<C> withComponent(@Nonnull final CommandComponent<C, T> component) {
|
||||
final List<CommandComponent<C, ?>> commandComponents = new LinkedList<>(this.commandComponents);
|
||||
commandComponents.add(component);
|
||||
return new Builder<>(this.senderType, commandComponents, this.commandExecutionHandler, this.commandPermission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the command execution handler
|
||||
*
|
||||
* @param commandExecutionHandler New execution handler
|
||||
* @return New builder instance using the command execution handler
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder<C> withHandler(@Nonnull final CommandExecutionHandler<C> commandExecutionHandler) {
|
||||
return new Builder<>(this.senderType, this.commandComponents, commandExecutionHandler, this.commandPermission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a required sender type
|
||||
*
|
||||
* @param senderType Required sender type
|
||||
* @return New builder instance using the command execution handler
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder<C> withSenderType(@Nonnull final Class<? extends C> senderType) {
|
||||
return new Builder<>(senderType, this.commandComponents, this.commandExecutionHandler, this.commandPermission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a command permission
|
||||
*
|
||||
* @param permission Command permission
|
||||
* @return New builder instance using the command permission
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder<C> withPermission(@Nonnull final String permission) {
|
||||
return new Builder<>(this.senderType, this.commandComponents, this.commandExecutionHandler, permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a command using the builder instance
|
||||
*
|
||||
* @return Built command
|
||||
*/
|
||||
@Nonnull
|
||||
public Command<C> build() {
|
||||
return new Command<>(Collections.unmodifiableList(this.commandComponents), this.commandExecutionHandler,
|
||||
this.senderType, this.commandPermission);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.intellectualsites.commands.components.CommandSyntaxFormatter;
|
||||
import com.intellectualsites.commands.components.StandardCommandSyntaxFormatter;
|
||||
import com.intellectualsites.commands.context.CommandContextFactory;
|
||||
import com.intellectualsites.commands.context.StandardCommandContextFactory;
|
||||
import com.intellectualsites.commands.execution.CommandExecutionCoordinator;
|
||||
import com.intellectualsites.commands.execution.CommandResult;
|
||||
import com.intellectualsites.commands.internal.CommandRegistrationHandler;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* The manager is responsible for command registration, parsing delegation, etc.
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
public abstract class CommandManager<C extends CommandSender> {
|
||||
|
||||
private final CommandContextFactory<C> commandContextFactory = new StandardCommandContextFactory<>();
|
||||
|
||||
private final CommandExecutionCoordinator<C> commandExecutionCoordinator;
|
||||
private final CommandRegistrationHandler commandRegistrationHandler;
|
||||
private final CommandTree<C> commandTree;
|
||||
|
||||
private CommandSyntaxFormatter<C> commandSyntaxFormatter = new StandardCommandSyntaxFormatter<>();
|
||||
|
||||
public CommandManager(@Nonnull final Function<CommandTree<C>, CommandExecutionCoordinator<C>> commandExecutionCoordinator,
|
||||
@Nonnull final CommandRegistrationHandler commandRegistrationHandler) {
|
||||
this.commandTree = CommandTree.newTree(this, commandRegistrationHandler);
|
||||
this.commandExecutionCoordinator = commandExecutionCoordinator.apply(commandTree);
|
||||
this.commandRegistrationHandler = commandRegistrationHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command and get a future that completes with the result
|
||||
*
|
||||
* @param commandSender Sender of the command
|
||||
* @param input Input provided by the sender
|
||||
* @return Command result
|
||||
*/
|
||||
@Nonnull
|
||||
public CompletableFuture<CommandResult> executeCommand(@Nonnull final C commandSender, @Nonnull final String input) {
|
||||
return this.commandExecutionCoordinator.coordinateExecution(this.commandContextFactory.create(commandSender), tokenize(input));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get command suggestions for the "next" argument that would yield a correctly
|
||||
* parsing command input
|
||||
*
|
||||
* @param commandSender Sender of the command
|
||||
* @param input Input provided by the sender
|
||||
* @return List of suggestions
|
||||
*/
|
||||
@Nonnull
|
||||
public List<String> suggest(@Nonnull final C commandSender, @Nonnull final String input) {
|
||||
return this.commandTree.getSuggestions(this.commandContextFactory.create(commandSender), tokenize(input));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Queue<String> tokenize(@Nonnull final String input) {
|
||||
final StringTokenizer stringTokenizer = new StringTokenizer(input, " ");
|
||||
final Queue<String> tokens = new LinkedList<>();
|
||||
while (stringTokenizer.hasMoreElements()) {
|
||||
tokens.add(stringTokenizer.nextToken());
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new command
|
||||
*
|
||||
* @param command Command to register
|
||||
* @return The command manager instance
|
||||
*/
|
||||
public CommandManager<C> registerCommand(@Nonnull final Command<C> command) {
|
||||
this.commandTree.insertCommand(command);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command syntax formatter
|
||||
*
|
||||
* @return Command syntax formatter
|
||||
*/
|
||||
@Nonnull
|
||||
public CommandSyntaxFormatter<C> getCommandSyntaxFormatter() {
|
||||
return this.commandSyntaxFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the command syntax formatter
|
||||
*
|
||||
* @param commandSyntaxFormatter New formatter
|
||||
*/
|
||||
public void setCommandSyntaxFormatter(@Nonnull final CommandSyntaxFormatter<C> commandSyntaxFormatter) {
|
||||
this.commandSyntaxFormatter = commandSyntaxFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command registration handler
|
||||
*
|
||||
* @return Command registration handler
|
||||
*/
|
||||
@Nonnull
|
||||
protected CommandRegistrationHandler getCommandRegistrationHandler() {
|
||||
return this.commandRegistrationHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new command builder
|
||||
*
|
||||
* @param name Command name
|
||||
* @return Builder instance
|
||||
*/
|
||||
@Nonnull
|
||||
public Command.Builder<C> commandBuilder(@Nonnull final String name) {
|
||||
return Command.newBuilder(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internal command tree. This should not be accessed unless you know what you
|
||||
* are doing
|
||||
*
|
||||
* @return Command tree
|
||||
*/
|
||||
@Nonnull CommandTree<C> getCommandTree() {
|
||||
return this.commandTree;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,513 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.intellectualsites.commands.components.CommandComponent;
|
||||
import com.intellectualsites.commands.components.StaticComponent;
|
||||
import com.intellectualsites.commands.context.CommandContext;
|
||||
import com.intellectualsites.commands.exceptions.ComponentParseException;
|
||||
import com.intellectualsites.commands.exceptions.InvalidSyntaxException;
|
||||
import com.intellectualsites.commands.exceptions.NoPermissionException;
|
||||
import com.intellectualsites.commands.exceptions.NoSuchCommandException;
|
||||
import com.intellectualsites.commands.internal.CommandRegistrationHandler;
|
||||
import com.intellectualsites.commands.components.parser.ComponentParseResult;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Tree containing all commands and command paths
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
public class CommandTree<C extends CommandSender> {
|
||||
|
||||
private final Node<CommandComponent<C, ?>> internalTree = new Node<>(null);
|
||||
private final CommandManager<C> commandManager;
|
||||
private final CommandRegistrationHandler commandRegistrationHandler;
|
||||
|
||||
private CommandTree(@Nonnull final CommandManager<C> commandManager,
|
||||
@Nonnull final CommandRegistrationHandler commandRegistrationHandler) {
|
||||
this.commandManager = commandManager;
|
||||
this.commandRegistrationHandler = commandRegistrationHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new command tree instance
|
||||
*
|
||||
* @param commandManager Command manager
|
||||
* @param commandRegistrationHandler Command registration handler
|
||||
* @param <C> Command sender type
|
||||
* @return New command tree
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C extends CommandSender> CommandTree<C> newTree(@Nonnull final CommandManager<C> commandManager,
|
||||
@Nonnull
|
||||
final CommandRegistrationHandler commandRegistrationHandler) {
|
||||
return new CommandTree<>(commandManager, commandRegistrationHandler);
|
||||
}
|
||||
|
||||
public Optional<Command<C>> parse(@Nonnull final CommandContext<C> commandContext, @Nonnull final Queue<String> args) throws
|
||||
NoSuchCommandException {
|
||||
return parseCommand(commandContext, args, this.internalTree);
|
||||
}
|
||||
|
||||
private Optional<Command<C>> parseCommand(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Queue<String> commandQueue,
|
||||
@Nonnull final Node<CommandComponent<C, ?>> root) {
|
||||
String permission;
|
||||
if ((permission = this.isPermitted(commandContext.getCommandSender(), root)) != null) {
|
||||
throw new NoPermissionException(permission, commandContext.getCommandSender(), this.getChain(root)
|
||||
.stream()
|
||||
.map(Node::getValue)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
final List<Node<CommandComponent<C, ?>>> children = root.getChildren();
|
||||
if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) {
|
||||
// The value has to be a variable
|
||||
final Node<CommandComponent<C, ?>> child = children.get(0);
|
||||
if ((permission = this.isPermitted(commandContext.getCommandSender(), child)) != null) {
|
||||
throw new NoPermissionException(permission, commandContext.getCommandSender(), this.getChain(child)
|
||||
.stream()
|
||||
.map(Node::getValue)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
if (child.getValue() != null) {
|
||||
if (commandQueue.isEmpty()) {
|
||||
if (child.getValue().hasDefaultValue()) {
|
||||
commandQueue.add(child.getValue().getDefaultValue());
|
||||
} else if (!child.getValue().isRequired()) {
|
||||
return Optional.ofNullable(child.getValue().getOwningCommand());
|
||||
} else if (child.isLeaf()) {
|
||||
/* Not enough arguments */
|
||||
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
|
||||
.apply(Objects.requireNonNull(
|
||||
child.getValue().getOwningCommand())
|
||||
.getComponents()),
|
||||
commandContext.getCommandSender(), this.getChain(root)
|
||||
.stream()
|
||||
.map(Node::getValue)
|
||||
.collect(Collectors.toList()));
|
||||
} else {
|
||||
throw new NoSuchCommandException(commandContext.getCommandSender(),
|
||||
this.getChain(root)
|
||||
.stream()
|
||||
.map(Node::getValue)
|
||||
.collect(Collectors.toList()),
|
||||
"");
|
||||
}
|
||||
}
|
||||
final ComponentParseResult<?> result = child.getValue().getParser().parse(commandContext, commandQueue);
|
||||
if (result.getParsedValue().isPresent()) {
|
||||
commandContext.store(child.getValue().getName(), result.getParsedValue().get());
|
||||
if (child.isLeaf()) {
|
||||
if (commandQueue.isEmpty()) {
|
||||
return Optional.ofNullable(child.getValue().getOwningCommand());
|
||||
} else {
|
||||
/* Too many arguments. We have a unique path, so we can send the entire context */
|
||||
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
|
||||
.apply(Objects.requireNonNull(child.getValue()
|
||||
.getOwningCommand())
|
||||
.getComponents()),
|
||||
commandContext.getCommandSender(), this.getChain(root)
|
||||
.stream()
|
||||
.map(Node::getValue)
|
||||
.collect(
|
||||
Collectors.toList()));
|
||||
}
|
||||
} else {
|
||||
return this.parseCommand(commandContext, commandQueue, child);
|
||||
}
|
||||
} else if (result.getFailure().isPresent()) {
|
||||
throw new ComponentParseException(result.getFailure().get(), commandContext.getCommandSender(), this.getChain(child)
|
||||
.stream()
|
||||
.map(Node::getValue)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
/* There are 0 or more static components as children. No variable child components are present */
|
||||
if (children.isEmpty()) {
|
||||
/* We are at the bottom. Check if there's a command attached, in which case we're done */
|
||||
if (root.getValue() != null && root.getValue().getOwningCommand() != null) {
|
||||
if (commandQueue.isEmpty()) {
|
||||
return Optional.of(root.getValue().getOwningCommand());
|
||||
} else {
|
||||
/* Too many arguments. We have a unique path, so we can send the entire context */
|
||||
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
|
||||
.apply(root.getValue()
|
||||
.getOwningCommand()
|
||||
.getComponents()),
|
||||
commandContext.getCommandSender(), this.getChain(root)
|
||||
.stream()
|
||||
.map(Node::getValue)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
} else {
|
||||
/* Too many arguments. We have a unique path, so we can send the entire context */
|
||||
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
|
||||
.apply(Objects.requireNonNull(
|
||||
Objects.requireNonNull(root.getValue())
|
||||
.getOwningCommand())
|
||||
.getComponents()),
|
||||
commandContext.getCommandSender(), this.getChain(root)
|
||||
.stream()
|
||||
.map(Node::getValue)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
} else {
|
||||
final Iterator<Node<CommandComponent<C, ?>>> childIterator = root.getChildren().iterator();
|
||||
if (childIterator.hasNext()) {
|
||||
while (childIterator.hasNext()) {
|
||||
final Node<CommandComponent<C, ?>> child = childIterator.next();
|
||||
if (child.getValue() != null) {
|
||||
final ComponentParseResult<?> result = child.getValue().getParser().parse(commandContext, commandQueue);
|
||||
if (result.getParsedValue().isPresent()) {
|
||||
return this.parseCommand(commandContext, commandQueue, child);
|
||||
} else if (result.getFailure().isPresent() && root.children.size() == 1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* We could not find a match */
|
||||
throw new NoSuchCommandException(commandContext.getCommandSender(),
|
||||
getChain(root).stream().map(Node::getValue).collect(Collectors.toList()),
|
||||
stringOrEmpty(commandQueue.peek()));
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getSuggestions(@Nonnull final CommandContext<C> context, @Nonnull final Queue<String> commandQueue) {
|
||||
return getSuggestions(context, commandQueue, this.internalTree);
|
||||
}
|
||||
|
||||
public List<String> getSuggestions(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Queue<String> commandQueue,
|
||||
@Nonnull final Node<CommandComponent<C, ?>> root) {
|
||||
|
||||
/* If the sender isn't allowed to access the root node, no suggestions are needed */
|
||||
if (this.isPermitted(commandContext.getCommandSender(), root) != null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<Node<CommandComponent<C, ?>>> children = root.getChildren();
|
||||
if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) {
|
||||
// The value has to be a variable
|
||||
final Node<CommandComponent<C, ?>> child = children.get(0);
|
||||
if (child.getValue() != null) {
|
||||
if (commandQueue.isEmpty()) {
|
||||
if (child.isLeaf()) {
|
||||
/* Child is leaf, and so no suggestions should be sent */
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
/* Send all suggestions */
|
||||
return child.getValue().getParser().suggestions(commandContext, "");
|
||||
}
|
||||
}
|
||||
final ComponentParseResult<?> result = child.getValue().getParser().parse(commandContext, commandQueue);
|
||||
if (result.getParsedValue().isPresent()) {
|
||||
if (child.isLeaf()) {
|
||||
/* Child is leaf, and so no suggestions should be sent */
|
||||
return Collections.emptyList();
|
||||
}
|
||||
commandContext.store(child.getValue().getName(), result.getParsedValue().get());
|
||||
return this.getSuggestions(commandContext, commandQueue, child);
|
||||
} else if (result.getFailure().isPresent()) {
|
||||
/* TODO: Return error */
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
/* There are 0 or more static components as children. No variable child components are present */
|
||||
if (children.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
final Iterator<Node<CommandComponent<C, ?>>> childIterator = root.getChildren().iterator();
|
||||
if (childIterator.hasNext()) {
|
||||
while (childIterator.hasNext()) {
|
||||
final Node<CommandComponent<C, ?>> child = childIterator.next();
|
||||
if (child.getValue() != null) {
|
||||
final ComponentParseResult<?> result = child.getValue().getParser().parse(commandContext, commandQueue);
|
||||
if (result.getParsedValue().isPresent()) {
|
||||
return this.getSuggestions(commandContext, commandQueue, child);
|
||||
} else if (result.getFailure().isPresent() && root.children.size() == 1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final List<String> suggestions = new LinkedList<>();
|
||||
for (final Node<CommandComponent<C, ?>> component : root.getChildren()) {
|
||||
if (component.getValue() == null || this.isPermitted(commandContext.getCommandSender(), component) != null) {
|
||||
continue;
|
||||
}
|
||||
suggestions.addAll(
|
||||
component.getValue().getParser().suggestions(commandContext, stringOrEmpty(commandQueue.peek())));
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String stringOrEmpty(@Nullable final String string) {
|
||||
if (string == null) {
|
||||
return "";
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new command into the command tree
|
||||
*
|
||||
* @param command Command to insert
|
||||
*/
|
||||
public void insertCommand(@Nonnull final Command<C> command) {
|
||||
Node<CommandComponent<C, ?>> node = this.internalTree;
|
||||
for (final CommandComponent<C, ?> component : command.getComponents()) {
|
||||
Node<CommandComponent<C, ?>> tempNode = node.getChild(component);
|
||||
if (tempNode == null) {
|
||||
tempNode = node.addChild(component);
|
||||
}
|
||||
if (node.children.size() > 0) {
|
||||
node.children.sort(Comparator.comparing(Node::getValue));
|
||||
}
|
||||
tempNode.setParent(node);
|
||||
node = tempNode;
|
||||
}
|
||||
if (node.getValue() != null) {
|
||||
node.getValue().setOwningCommand(command);
|
||||
}
|
||||
// Verify the command structure every time we add a new command
|
||||
this.verifyAndRegister();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String isPermitted(@Nonnull final C sender, @Nonnull final Node<CommandComponent<C, ?>> node) {
|
||||
final String permission = node.nodeMeta.get("permission");
|
||||
if (permission != null) {
|
||||
return sender.hasPermission(permission) ? null : permission;
|
||||
}
|
||||
if (node.isLeaf()) {
|
||||
return sender.hasPermission(Objects.requireNonNull(Objects.requireNonNull(node.value, "node.value").getOwningCommand(),
|
||||
"owning command").getCommandPermission())
|
||||
? null : node.value.getOwningCommand().getCommandPermission();
|
||||
}
|
||||
/*
|
||||
if any of the children would permit the execution, then the sender has a valid
|
||||
chain to execute, and so we allow them to execute the root
|
||||
*/
|
||||
final List<String> missingPermissions = new LinkedList<>();
|
||||
for (final Node<CommandComponent<C, ?>> child : node.getChildren()) {
|
||||
final String check = this.isPermitted(sender, child);
|
||||
if (check == null) {
|
||||
return null;
|
||||
} else {
|
||||
missingPermissions.add(check);
|
||||
}
|
||||
}
|
||||
return String.join(", ", missingPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through all commands and register them, and verify the
|
||||
* command tree contracts
|
||||
*/
|
||||
public void verifyAndRegister() {
|
||||
// All top level commands are supposed to be registered in the command manager
|
||||
this.internalTree.children.stream().map(Node::getValue).forEach(commandComponent -> {
|
||||
if (!(commandComponent instanceof StaticComponent)) {
|
||||
throw new IllegalStateException("Top level command component cannot be a variable");
|
||||
}
|
||||
});
|
||||
this.checkAmbiguity(this.internalTree);
|
||||
// Verify that all leaf nodes have command registered
|
||||
this.getLeaves(this.internalTree).forEach(leaf -> {
|
||||
if (leaf.getOwningCommand() == null) {
|
||||
// TODO: Custom exception type
|
||||
throw new IllegalStateException("Leaf node does not have associated owning command");
|
||||
} else {
|
||||
this.commandRegistrationHandler.registerCommand(leaf.getOwningCommand());
|
||||
}
|
||||
});
|
||||
|
||||
// Register command permissions
|
||||
this.getLeavesRaw(this.internalTree).forEach(node -> {
|
||||
/* All leaves must necessarily have an owning command */
|
||||
// noinspection all
|
||||
node.nodeMeta.put("permission", node.getValue().getOwningCommand().getCommandPermission());
|
||||
// Get chain and order it tail->head then skip the tail (leaf node)
|
||||
List<Node<CommandComponent<C, ?>>> chain = this.getChain(node);
|
||||
Collections.reverse(chain);
|
||||
chain = chain.subList(1, chain.size());
|
||||
// Go through all nodes from the tail upwards until a collision occurs
|
||||
for (final Node<CommandComponent<C, ?>> commandComponentNode : chain) {
|
||||
if (commandComponentNode.nodeMeta.containsKey("permission") && !commandComponentNode.nodeMeta.get("permission")
|
||||
.equalsIgnoreCase(node.nodeMeta.get("permission"))) {
|
||||
commandComponentNode.nodeMeta.put("permission", "");
|
||||
} else {
|
||||
commandComponentNode.nodeMeta.put("permission", node.nodeMeta.get("permission"));
|
||||
}
|
||||
}
|
||||
});
|
||||
/* TODO: Figure out a way to register all combinations along a command component path */
|
||||
}
|
||||
|
||||
private void checkAmbiguity(@Nonnull final Node<CommandComponent<C, ?>> node) {
|
||||
if (node.isLeaf()) {
|
||||
return;
|
||||
}
|
||||
final int size = node.children.size();
|
||||
for (final Node<CommandComponent<C, ?>> child : node.children) {
|
||||
if (child.getValue() != null && !child.getValue().isRequired() && size > 1) {
|
||||
// TODO: Use a custom exception type here
|
||||
throw new IllegalStateException("Ambiguous command node found: " + node.getValue());
|
||||
}
|
||||
}
|
||||
node.children.forEach(this::checkAmbiguity);
|
||||
}
|
||||
|
||||
private List<Node<CommandComponent<C, ?>>> getLeavesRaw(@Nonnull final Node<CommandComponent<C, ?>> node) {
|
||||
final List<Node<CommandComponent<C, ?>>> leaves = new LinkedList<>();
|
||||
if (node.isLeaf()) {
|
||||
if (node.getValue() != null) {
|
||||
leaves.add(node);
|
||||
}
|
||||
} else {
|
||||
node.children.forEach(child -> leaves.addAll(getLeavesRaw(child)));
|
||||
}
|
||||
return leaves;
|
||||
}
|
||||
|
||||
private List<CommandComponent<C, ?>> getLeaves(@Nonnull final Node<CommandComponent<C, ?>> node) {
|
||||
final List<CommandComponent<C, ?>> leaves = new LinkedList<>();
|
||||
if (node.isLeaf()) {
|
||||
if (node.getValue() != null) {
|
||||
leaves.add(node.getValue());
|
||||
}
|
||||
} else {
|
||||
node.children.forEach(child -> leaves.addAll(getLeaves(child)));
|
||||
}
|
||||
return leaves;
|
||||
}
|
||||
|
||||
private List<Node<CommandComponent<C, ?>>> getChain(@Nullable final Node<CommandComponent<C, ?>> end) {
|
||||
final List<Node<CommandComponent<C, ?>>> chain = new LinkedList<>();
|
||||
Node<CommandComponent<C, ?>> tail = end;
|
||||
while (tail != null) {
|
||||
chain.add(tail);
|
||||
tail = tail.getParent();
|
||||
}
|
||||
Collections.reverse(chain);
|
||||
return chain;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Very simple tree structure
|
||||
*
|
||||
* @param <T> Node value type
|
||||
*/
|
||||
private static final class Node<T> {
|
||||
|
||||
private final Map<String, String> nodeMeta = new HashMap<>();
|
||||
private final List<Node<T>> children = new LinkedList<>();
|
||||
private final T value;
|
||||
private Node<T> parent;
|
||||
|
||||
private Node(@Nullable final T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private List<Node<T>> getChildren() {
|
||||
return Collections.unmodifiableList(this.children);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Node<T> addChild(@Nonnull final T child) {
|
||||
final Node<T> node = new Node<>(child);
|
||||
this.children.add(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Node<T> getChild(@Nonnull final T type) {
|
||||
for (final Node<T> child : this.children) {
|
||||
if (type.equals(child.getValue())) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isLeaf() {
|
||||
return this.children.isEmpty();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Node<?> node = (Node<?>) o;
|
||||
return Objects.equals(getValue(), node.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getValue());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Node<T> getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
public void setParent(@Nullable final Node<T> parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Node{value=" + value + '}';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.components;
|
||||
|
||||
import com.intellectualsites.commands.Command;
|
||||
import com.intellectualsites.commands.components.parser.ComponentParser;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A component that belongs to a command
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
* @param <T> The type that the component parses into
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class CommandComponent<C extends CommandSender, T> implements Comparable<CommandComponent<?, ?>> {
|
||||
|
||||
private static final Pattern NAME_PATTERN = Pattern.compile("[A-Za-z0-9]+");
|
||||
|
||||
/**
|
||||
* Indicates whether or not the component is required
|
||||
* or not. All c$ git ls-files | xargs wc -lomponents prior to any other required
|
||||
* component must also be required, such that the predicate
|
||||
* (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true,
|
||||
* where {c_0, ..., c_n-1} is the set of command components.
|
||||
*/
|
||||
private final boolean required;
|
||||
/**
|
||||
* The command component name. This might be exposed
|
||||
* to command senders and so should be chosen carefully.
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* The parser that is used to parse the command input
|
||||
* into the corresponding command type
|
||||
*/
|
||||
private final ComponentParser<C, T> parser;
|
||||
/**
|
||||
* Default value, will be empty if none was supplied
|
||||
*/
|
||||
private final String defaultValue;
|
||||
|
||||
private Command<C> owningCommand;
|
||||
|
||||
public CommandComponent(final boolean required, @Nonnull final String name,
|
||||
@Nonnull final ComponentParser<C, T> parser, @Nonnull final String defaultValue) {
|
||||
this.required = required;
|
||||
this.name = Objects.requireNonNull(name, "Name may not be null");
|
||||
if (!NAME_PATTERN.asPredicate().test(name)) {
|
||||
throw new IllegalArgumentException("Name must be alphanumeric");
|
||||
}
|
||||
this.parser = Objects.requireNonNull(parser, "Parser may not be null");
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public CommandComponent(final boolean required, @Nonnull final String name,
|
||||
@Nonnull final ComponentParser<C, T> parser) {
|
||||
this(required, name, parser, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new command component
|
||||
*
|
||||
* @param clazz Argument class
|
||||
* @param <C> Command sender type
|
||||
* @param <T> Argument Type. Used to make the compiler happy.
|
||||
* @return Component builder
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C extends CommandSender, T> CommandComponent.Builder<C, T> ofType(@Nonnull final Class<T> clazz) {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether or not the command component is required
|
||||
*
|
||||
* @return {@code true} if the component is required, {@code false} if not
|
||||
*/
|
||||
public boolean isRequired() {
|
||||
return this.required;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command component name;
|
||||
*
|
||||
* @return Component name
|
||||
*/
|
||||
@Nonnull
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parser that is used to parse the command input
|
||||
* into the corresponding command type
|
||||
*
|
||||
* @return Command parser
|
||||
*/
|
||||
@Nonnull
|
||||
public ComponentParser<C, T> getParser() {
|
||||
return this.parser;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("CommandComponent{name=%s}", this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the owning command
|
||||
*
|
||||
* @return Owning command
|
||||
*/
|
||||
@Nullable
|
||||
public Command<C> getOwningCommand() {
|
||||
return this.owningCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the owning command
|
||||
*
|
||||
* @param owningCommand Owning command
|
||||
*/
|
||||
public void setOwningCommand(@Nonnull final Command<C> owningCommand) {
|
||||
if (this.owningCommand != null) {
|
||||
throw new IllegalStateException("Cannot replace owning command");
|
||||
}
|
||||
this.owningCommand = owningCommand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final CommandComponent<?, ?> that = (CommandComponent<?, ?>) o;
|
||||
return isRequired() == that.isRequired() && Objects.equals(getName(), that.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(isRequired(), getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@Nonnull final CommandComponent<?, ?> o) {
|
||||
if (this instanceof StaticComponent) {
|
||||
if (o instanceof StaticComponent) {
|
||||
return (this.getName().compareTo(o.getName()));
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (o instanceof StaticComponent) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default value
|
||||
*
|
||||
* @return Default value
|
||||
*/
|
||||
@Nonnull public String getDefaultValue() {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the component has a default value
|
||||
*
|
||||
* @return {@code true} if the component has a default value, {@code false} if not
|
||||
*/
|
||||
public boolean hasDefaultValue() {
|
||||
return !this.isRequired() &&
|
||||
!this.getDefaultValue().isEmpty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mutable builder for {@link CommandComponent} instances
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
* @param <T> Component value type
|
||||
*/
|
||||
public static class Builder<C extends CommandSender, T> {
|
||||
|
||||
protected String name;
|
||||
protected boolean required = true;
|
||||
protected ComponentParser<C, T> parser;
|
||||
protected String defaultValue = "";
|
||||
|
||||
protected Builder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the component name. Must be alphanumeric
|
||||
*
|
||||
* @param name Alphanumeric component name
|
||||
* @return Builder instance
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder<C, T> named(@Nonnull final String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the component is required.
|
||||
* All components prior to any other required
|
||||
* component must also be required, such that the predicate
|
||||
* (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true,
|
||||
* where {c_0, ..., c_n-1} is the set of command components.
|
||||
*
|
||||
* @return Builder instance
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder<C, T> asRequired() {
|
||||
this.required = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the component is optional.
|
||||
* All components prior to any other required
|
||||
* component must also be required, such that the predicate
|
||||
* (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true,
|
||||
* where {c_0, ..., c_n-1} is the set of command components.
|
||||
*
|
||||
* @return Builder instance
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder<C, T> asOptional() {
|
||||
this.required = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the component is optional.
|
||||
* All components prior to any other required
|
||||
* component must also be required, such that the predicate
|
||||
* (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true,
|
||||
* where {c_0, ..., c_n-1} is the set of command components.
|
||||
*
|
||||
* @param defaultValue Default value that will be used if none was supplied
|
||||
* @return Builder instance
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder<C, T> asOptionalWithDefault(@Nonnull final String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
this.required = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the component parser
|
||||
*
|
||||
* @param parser Component parser
|
||||
* @return Builder instance
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder<C, T> withParser(@Nonnull final ComponentParser<C, T> parser) {
|
||||
this.parser = Objects.requireNonNull(parser, "Parser may not be null");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a command component from the builder settings
|
||||
*
|
||||
* @return Constructed component
|
||||
*/
|
||||
@Nonnull
|
||||
public CommandComponent<C, T> build() {
|
||||
return new CommandComponent<>(this.required, this.name, this.parser, this.defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.components;
|
||||
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Utility that formats chains of {@link CommandComponent command components} into syntax strings
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CommandSyntaxFormatter<C extends CommandSender> extends Function<List<CommandComponent<C, ?>>, String> {
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
String apply(@Nonnull List<CommandComponent<C, ?>> commandComponents);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.components;
|
||||
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link CommandSyntaxFormatter} implementation that uses the following rules:
|
||||
* <ul>
|
||||
* <li>static components are serialized as their name, without a bracket</li>
|
||||
* <li>required components are serialized as their name, surrounded by angle brackets</li>
|
||||
* <li>optional components are serialized as their name, surrounded by square brackets</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
public class StandardCommandSyntaxFormatter<C extends CommandSender> implements CommandSyntaxFormatter<C> {
|
||||
|
||||
public StandardCommandSyntaxFormatter() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String apply(@Nonnull final List<CommandComponent<C, ?>> commandComponents) {
|
||||
final StringBuilder stringBuilder = new StringBuilder();
|
||||
final Iterator<CommandComponent<C, ?>> iterator = commandComponents.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final CommandComponent<?, ?> commandComponent = iterator.next();
|
||||
if (commandComponent instanceof StaticComponent) {
|
||||
stringBuilder.append(commandComponent.getName());
|
||||
} else {
|
||||
if (commandComponent.isRequired()) {
|
||||
stringBuilder.append("<").append(commandComponent.getName()).append(">");
|
||||
} else {
|
||||
stringBuilder.append("[").append(commandComponent.getName()).append("]");
|
||||
}
|
||||
}
|
||||
if (iterator.hasNext()) {
|
||||
stringBuilder.append(" ");
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.components;
|
||||
|
||||
import com.intellectualsites.commands.context.CommandContext;
|
||||
import com.intellectualsites.commands.components.parser.ComponentParseResult;
|
||||
import com.intellectualsites.commands.components.parser.ComponentParser;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link CommandComponent} type that recognizes fixed strings. This type does not parse variables.
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
public final class StaticComponent<C extends CommandSender> extends CommandComponent<C, String> {
|
||||
|
||||
private StaticComponent(final boolean required, @Nonnull final String name, @Nonnull final String... aliases) {
|
||||
super(required, name, new StaticComponentParser<>(name, aliases));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new static component instance for a required command component
|
||||
*
|
||||
* @param name Component name
|
||||
* @param aliases Component aliases
|
||||
* @param <C> Command sender type
|
||||
* @return Constructed component
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C extends CommandSender> StaticComponent<C> required(@Nonnull final String name,
|
||||
@Nonnull final String... aliases) {
|
||||
return new StaticComponent<>(true, name, aliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new static component instance for an optional command component
|
||||
*
|
||||
* @param name Component name
|
||||
* @param aliases Component aliases
|
||||
* @param <C> Command sender type
|
||||
* @return Constructed component
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C extends CommandSender> StaticComponent<C> optional(@Nonnull final String name,
|
||||
@Nonnull final String... aliases) {
|
||||
return new StaticComponent<>(false, name, aliases);
|
||||
}
|
||||
|
||||
|
||||
private static final class StaticComponentParser<C extends CommandSender> implements ComponentParser<C, String> {
|
||||
|
||||
private final String name;
|
||||
private final Set<String> acceptedStrings = new HashSet<>();
|
||||
|
||||
private StaticComponentParser(@Nonnull final String name, @Nonnull final String... aliases) {
|
||||
this.acceptedStrings.add(this.name = name);
|
||||
this.acceptedStrings.addAll(Arrays.asList(aliases));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ComponentParseResult<String> parse(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Queue<String> inputQueue) {
|
||||
final String string = inputQueue.peek();
|
||||
if (string == null) {
|
||||
return ComponentParseResult.failure(new NullPointerException("No input provided"));
|
||||
}
|
||||
for (final String acceptedString : this.acceptedStrings) {
|
||||
if (string.equalsIgnoreCase(acceptedString)) {
|
||||
// Remove the head of the queue
|
||||
inputQueue.remove();
|
||||
return ComponentParseResult.success(this.name);
|
||||
}
|
||||
}
|
||||
return ComponentParseResult.failure(new IllegalArgumentException(string));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> suggestions(@Nonnull final CommandContext<C> commandContext, @Nonnull final String input) {
|
||||
if (this.name.toLowerCase(Locale.ENGLISH).startsWith(input)) {
|
||||
return Collections.singletonList(this.name);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.components.parser;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Result of the parsing done by a {@link ComponentParser}
|
||||
*
|
||||
* @param <T> Parser return type
|
||||
*/
|
||||
public abstract class ComponentParseResult<T> {
|
||||
|
||||
private ComponentParseResult() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the parsing failed
|
||||
*
|
||||
* @param failure Failure reason
|
||||
* @param <T> Parser return type
|
||||
* @return Failed parse result
|
||||
*/
|
||||
@Nonnull
|
||||
public static <T> ComponentParseResult<T> failure(@Nonnull final Throwable failure) {
|
||||
return new ParseFailure<>(failure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the parsing succeeded
|
||||
*
|
||||
* @param value Value produced by the parser
|
||||
* @param <T> Parser return type
|
||||
* @return Succeeded parse result
|
||||
*/
|
||||
@Nonnull
|
||||
public static <T> ComponentParseResult<T> success(@Nonnull final T value) {
|
||||
return new ParseSuccess<>(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parsed value, if it exists
|
||||
*
|
||||
* @return Optional containing the parsed value
|
||||
*/
|
||||
@Nonnull
|
||||
public abstract Optional<T> getParsedValue();
|
||||
|
||||
/**
|
||||
* Get the failure reason, if it exists
|
||||
*
|
||||
* @return Optional containing the failure reason
|
||||
*/
|
||||
@Nonnull
|
||||
public abstract Optional<Throwable> getFailure();
|
||||
|
||||
|
||||
private static final class ParseSuccess<T> extends ComponentParseResult<T> {
|
||||
|
||||
private final T value;
|
||||
|
||||
private ParseSuccess(@Nonnull final T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Optional<T> getParsedValue() {
|
||||
return Optional.of(this.value);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Optional<Throwable> getFailure() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static final class ParseFailure<T> extends ComponentParseResult<T> {
|
||||
|
||||
private final Throwable failure;
|
||||
|
||||
private ParseFailure(@Nonnull final Throwable failure) {
|
||||
this.failure = failure;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Optional<T> getParsedValue() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Optional<Throwable> getFailure() {
|
||||
return Optional.of(this.failure);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.components.parser;
|
||||
|
||||
import com.intellectualsites.commands.context.CommandContext;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* Parser that parses strings into values of a specific type
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
* @param <T> Value type
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ComponentParser<C extends CommandSender, T> {
|
||||
|
||||
/**
|
||||
* Parse command input into a command result
|
||||
*
|
||||
* @param commandContext Command context
|
||||
* @param inputQueue The queue of arguments
|
||||
* @return Parsed command result
|
||||
*/
|
||||
@Nonnull
|
||||
ComponentParseResult<T> parse(@Nonnull CommandContext<C> commandContext, @Nonnull Queue<String> inputQueue);
|
||||
|
||||
/**
|
||||
* Get a list of suggested arguments that would be correctly parsed by this parser
|
||||
*
|
||||
* @param commandContext Command context
|
||||
* @param input Input string
|
||||
* @return List of suggestions
|
||||
*/
|
||||
@Nonnull
|
||||
default List<String> suggestions(@Nonnull final CommandContext<C> commandContext, @Nonnull final String input) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.components.standard;
|
||||
|
||||
import com.intellectualsites.commands.components.CommandComponent;
|
||||
import com.intellectualsites.commands.components.parser.ComponentParseResult;
|
||||
import com.intellectualsites.commands.components.parser.ComponentParser;
|
||||
import com.intellectualsites.commands.context.CommandContext;
|
||||
import com.intellectualsites.commands.exceptions.parsing.NumberParseException;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Queue;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class ByteComponent<C extends CommandSender> extends CommandComponent<C, Byte> {
|
||||
|
||||
private final byte min;
|
||||
private final byte max;
|
||||
|
||||
private ByteComponent(final boolean required, @Nonnull final String name, final byte min, final byte max, final String defaultValue) {
|
||||
super(required, name, new ByteParser<>(min, max), defaultValue);
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <C extends CommandSender> Builder<C> newBuilder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <C extends CommandSender> CommandComponent<C, Byte> required(@Nonnull final String name) {
|
||||
return ByteComponent.<C>newBuilder().named(name).asRequired().build();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <C extends CommandSender> CommandComponent<C, Byte> optional(@Nonnull final String name) {
|
||||
return ByteComponent.<C>newBuilder().named(name).asOptional().build();
|
||||
}
|
||||
|
||||
|
||||
public static final class Builder<C extends CommandSender> extends CommandComponent.Builder<C, Byte> {
|
||||
|
||||
private byte min = Byte.MIN_VALUE;
|
||||
private byte max = Byte.MAX_VALUE;
|
||||
|
||||
@Nonnull
|
||||
public Builder<C> withMin(final byte min) {
|
||||
this.min = min;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Builder<C> withMax(final byte max) {
|
||||
this.max = max;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ByteComponent<C> build() {
|
||||
return new ByteComponent<>(this.required, this.name, this.min, this.max, this.defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the minimum accepted byteeger that could have been parsed
|
||||
*
|
||||
* @return Minimum byteeger
|
||||
*/
|
||||
public byte getMin() {
|
||||
return this.min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum accepted byteeger that could have been parsed
|
||||
*
|
||||
* @return Maximum byteeger
|
||||
*/
|
||||
public byte getMax() {
|
||||
return this.max;
|
||||
}
|
||||
|
||||
|
||||
private static final class ByteParser<C extends CommandSender> implements ComponentParser<C, Byte> {
|
||||
|
||||
private final byte min;
|
||||
private final byte max;
|
||||
|
||||
public ByteParser(final byte min, final byte max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ComponentParseResult<Byte> parse(
|
||||
@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Queue<String> inputQueue) {
|
||||
final String input = inputQueue.peek();
|
||||
if (input == null) {
|
||||
return ComponentParseResult.failure(new NullPointerException("No input was provided"));
|
||||
}
|
||||
try {
|
||||
final byte value = Byte.parseByte(input);
|
||||
if (value < this.min || value > this.max) {
|
||||
return ComponentParseResult.failure(
|
||||
new ByteParseException(input,
|
||||
this.min,
|
||||
this.max));
|
||||
}
|
||||
inputQueue.remove();
|
||||
return ComponentParseResult.success(value);
|
||||
} catch (final Exception e) {
|
||||
return ComponentParseResult.failure(
|
||||
new ByteParseException(input, this.min,
|
||||
this.max));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static final class ByteParseException extends NumberParseException {
|
||||
|
||||
public ByteParseException(@Nonnull final String input, final byte min, final byte max) {
|
||||
super(input, min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMin() {
|
||||
return this.getMin().byteValue() != Byte.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMax() {
|
||||
return this.getMax().byteValue() != Byte.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNumberType() {
|
||||
return "byte";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.components.standard;
|
||||
|
||||
import com.intellectualsites.commands.components.CommandComponent;
|
||||
import com.intellectualsites.commands.components.parser.ComponentParseResult;
|
||||
import com.intellectualsites.commands.components.parser.ComponentParser;
|
||||
import com.intellectualsites.commands.context.CommandContext;
|
||||
import com.intellectualsites.commands.exceptions.parsing.NumberParseException;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Queue;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class IntegerComponent<C extends CommandSender> extends CommandComponent<C, Integer> {
|
||||
|
||||
private final int min;
|
||||
private final int max;
|
||||
|
||||
private IntegerComponent(final boolean required, @Nonnull final String name, final int min, final int max, final String defaultValue) {
|
||||
super(required, name, new IntegerParser<>(min, max), defaultValue);
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Nonnull public static <C extends CommandSender> Builder<C> newBuilder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
@Nonnull public static <C extends CommandSender> CommandComponent<C, Integer> required(@Nonnull final String name) {
|
||||
return IntegerComponent.<C>newBuilder().named(name).asRequired().build();
|
||||
}
|
||||
|
||||
@Nonnull public static <C extends CommandSender> CommandComponent<C, Integer> optional(@Nonnull final String name) {
|
||||
return IntegerComponent.<C>newBuilder().named(name).asOptional().build();
|
||||
}
|
||||
|
||||
@Nonnull public static <C extends CommandSender> CommandComponent<C, Integer> optional(@Nonnull final String name, final int defaultNum) {
|
||||
return IntegerComponent.<C>newBuilder().named(name).asOptionalWithDefault(Integer.toString(defaultNum)).build();
|
||||
}
|
||||
|
||||
|
||||
public static final class Builder<C extends CommandSender> extends CommandComponent.Builder<C, Integer> {
|
||||
|
||||
private int min = Integer.MIN_VALUE;
|
||||
private int max = Integer.MAX_VALUE;
|
||||
|
||||
@Nonnull public Builder<C> withMin(final int min) {
|
||||
this.min = min;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull public Builder<C> withMax(final int max) {
|
||||
this.max = max;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public IntegerComponent<C> build() {
|
||||
return new IntegerComponent<>(this.required, this.name, this.min, this.max, this.defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the minimum accepted integer that could have been parsed
|
||||
*
|
||||
* @return Minimum integer
|
||||
*/
|
||||
public int getMin() {
|
||||
return this.min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum accepted integer that could have been parsed
|
||||
*
|
||||
* @return Maximum integer
|
||||
*/
|
||||
public int getMax() {
|
||||
return this.max;
|
||||
}
|
||||
|
||||
|
||||
private static final class IntegerParser<C extends CommandSender> implements ComponentParser<C, Integer> {
|
||||
|
||||
private final int min;
|
||||
private final int max;
|
||||
|
||||
public IntegerParser(final int min, final int max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ComponentParseResult<Integer> parse(
|
||||
@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Queue<String> inputQueue) {
|
||||
final String input = inputQueue.peek();
|
||||
if (input == null) {
|
||||
return ComponentParseResult.failure(new NullPointerException("No input was provided"));
|
||||
}
|
||||
try {
|
||||
final int value = Integer.parseInt(input);
|
||||
if (value < this.min || value > this.max) {
|
||||
return ComponentParseResult.failure(new IntegerParseException(input, this.min, this.max));
|
||||
}
|
||||
inputQueue.remove();
|
||||
return ComponentParseResult.success(value);
|
||||
} catch (final Exception e) {
|
||||
return ComponentParseResult.failure(new IntegerParseException(input, this.min, this.max));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static final class IntegerParseException extends NumberParseException {
|
||||
|
||||
public IntegerParseException(@Nonnull final String input, final int min, final int max) {
|
||||
super(input, min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMin() {
|
||||
return this.getMin().intValue() != Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMax() {
|
||||
return this.getMax().intValue() != Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNumberType() {
|
||||
return "integer";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.context;
|
||||
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Command context used to assist in the parsing of commands
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
public final class CommandContext<C extends CommandSender> {
|
||||
|
||||
private final Map<String, Object> internalStorage = new HashMap<>();
|
||||
private final C commandSender;
|
||||
|
||||
/**
|
||||
* Create a new command context instance
|
||||
*
|
||||
* @param commandSender Sender of the command
|
||||
*/
|
||||
public CommandContext(@Nonnull final C commandSender) {
|
||||
this.commandSender = commandSender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sender that executed the command
|
||||
*
|
||||
* @return Command sender
|
||||
*/
|
||||
@Nonnull
|
||||
public C getCommandSender() {
|
||||
return this.commandSender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a value in the context map
|
||||
*
|
||||
* @param key Key
|
||||
* @param value Value
|
||||
* @param <T> Value type
|
||||
*/
|
||||
public <T> void store(@Nonnull final String key, @Nonnull final T value) {
|
||||
this.internalStorage.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value from its key
|
||||
*
|
||||
* @param key Key
|
||||
* @param <T> Value type
|
||||
* @return Value
|
||||
*/
|
||||
public <T> Optional<T> get(@Nonnull final String key) {
|
||||
final Object value = this.internalStorage.get(key);
|
||||
if (value != null) {
|
||||
@SuppressWarnings("ALL") final T castedValue = (T) value;
|
||||
return Optional.of(castedValue);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.context;
|
||||
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Factory for {@link CommandContext} instances
|
||||
*/
|
||||
public interface CommandContextFactory<C extends CommandSender> {
|
||||
|
||||
/**
|
||||
* Create a new command context
|
||||
*
|
||||
* @param sender Command sender
|
||||
* @return Command context
|
||||
*/
|
||||
@Nonnull
|
||||
CommandContext<C> create(@Nonnull C sender);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.context;
|
||||
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public final class StandardCommandContextFactory<C extends CommandSender> implements CommandContextFactory<C> {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public CommandContext<C> create(@Nonnull final C sender) {
|
||||
return new CommandContext<>(sender);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.exceptions;
|
||||
|
||||
import com.intellectualsites.commands.components.CommandComponent;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exception thrown when parsing user input into a command
|
||||
*/
|
||||
public class CommandParseException extends IllegalArgumentException {
|
||||
|
||||
private final CommandSender commandSender;
|
||||
private final List<CommandComponent<?, ?>> currentChain;
|
||||
|
||||
/**
|
||||
* Construct a new command parse exception
|
||||
*
|
||||
* @param commandSender Sender who executed the command
|
||||
* @param currentChain Chain leading up to the exception
|
||||
*/
|
||||
protected CommandParseException(@Nonnull final CommandSender commandSender,
|
||||
@Nonnull final List<CommandComponent<?, ?>> currentChain) {
|
||||
this.commandSender = commandSender;
|
||||
this.currentChain = currentChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command sender
|
||||
*
|
||||
* @return Command sender
|
||||
*/
|
||||
@Nonnull
|
||||
public CommandSender getCommandSender() {
|
||||
return this.commandSender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command chain leading up to the exception
|
||||
*
|
||||
* @return Unmodifiable list of command components
|
||||
*/
|
||||
@Nonnull
|
||||
public List<CommandComponent<?, ?>> getCurrentChain() {
|
||||
return Collections.unmodifiableList(this.currentChain);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.exceptions;
|
||||
|
||||
import com.intellectualsites.commands.components.CommandComponent;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
public class ComponentParseException extends CommandParseException {
|
||||
|
||||
private final Throwable cause;
|
||||
|
||||
public ComponentParseException(@Nonnull final Throwable throwable, @Nonnull final CommandSender commandSender, @Nonnull final List<CommandComponent<?, ?>> currentChain) {
|
||||
super(commandSender, currentChain);
|
||||
this.cause = throwable;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Throwable getCause() {
|
||||
return this.cause;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.exceptions;
|
||||
|
||||
import com.intellectualsites.commands.components.CommandComponent;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exception sent when a {@link CommandSender} inputs invalid command syntax
|
||||
*/
|
||||
public class InvalidSyntaxException extends CommandParseException {
|
||||
|
||||
private final String correctSyntax;
|
||||
|
||||
public InvalidSyntaxException(@Nonnull final String correctSyntax,
|
||||
@Nonnull final CommandSender commandSender,
|
||||
@Nonnull final List<CommandComponent<?, ?>> currentChain) {
|
||||
super(commandSender, currentChain);
|
||||
this.correctSyntax = correctSyntax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the correct syntax of the command
|
||||
*
|
||||
* @return Correct command syntax
|
||||
*/
|
||||
@Nonnull
|
||||
public String getCorrectSyntax() {
|
||||
return this.correctSyntax;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return String.format("Invalid command syntax. Correct syntax is: %s", this.correctSyntax);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.exceptions;
|
||||
|
||||
import com.intellectualsites.commands.components.CommandComponent;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exception thrown when a {@link CommandSender} misses a permission required
|
||||
* to execute a {@link com.intellectualsites.commands.Command}
|
||||
*/
|
||||
public class NoPermissionException extends CommandParseException {
|
||||
|
||||
private final String missingPermission;
|
||||
|
||||
public NoPermissionException(@Nonnull final String missingPermission,
|
||||
@Nonnull final CommandSender commandSender,
|
||||
@Nonnull final List<CommandComponent<?, ?>> currentChain) {
|
||||
super(commandSender, currentChain);
|
||||
this.missingPermission = missingPermission;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return String.format("Missing permission '%s'", this.missingPermission);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String getMissingPermission() {
|
||||
return this.missingPermission;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.exceptions;
|
||||
|
||||
import com.intellectualsites.commands.components.CommandComponent;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exception thrown when a command sender tries to execute
|
||||
* a command that doesn't exist
|
||||
*/
|
||||
public class NoSuchCommandException extends CommandParseException {
|
||||
|
||||
private final String suppliedCommand;
|
||||
|
||||
/**
|
||||
* Construct a no such command exception
|
||||
*
|
||||
* @param commandSender Sender who executed the command
|
||||
* @param currentChain Chain leading up to the exception
|
||||
* @param command Entered command (following the command chain)
|
||||
*/
|
||||
public NoSuchCommandException(@Nonnull final CommandSender commandSender,
|
||||
@Nonnull final List<CommandComponent<?, ?>> currentChain,
|
||||
@Nonnull final String command) {
|
||||
super(commandSender, currentChain);
|
||||
this.suppliedCommand = command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the supplied command
|
||||
*
|
||||
* @return Supplied command
|
||||
*/
|
||||
@Nonnull
|
||||
public String getSuppliedCommand() {
|
||||
return this.suppliedCommand;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.exceptions.parsing;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class NumberParseException extends IllegalArgumentException {
|
||||
|
||||
private final String input;
|
||||
private final Number min;
|
||||
private final Number max;
|
||||
|
||||
public NumberParseException(@Nonnull final String input, final int min, final int max) {
|
||||
this.input = input;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
if (this.hasMin() && this.hasMax()) {
|
||||
return "'" + this.input + "' is not a valid " + this.getNumberType() + " in the range [" + this.min + ", " + this.max + "]";
|
||||
} else if (this.hasMin()) {
|
||||
return "'" + this.input + "' is not a valid " + this.getNumberType() + " above " + this.min;
|
||||
} else if (this.hasMax()) {
|
||||
return "'" + this.input + "' is not a valid " + this.getNumberType() + " below " + this.max;
|
||||
} else {
|
||||
return String.format("'%s' is not a valid %s", this.input, this.getNumberType());
|
||||
}
|
||||
}
|
||||
|
||||
public abstract String getNumberType();
|
||||
|
||||
public abstract boolean hasMax();
|
||||
|
||||
public abstract boolean hasMin();
|
||||
|
||||
/**
|
||||
* Get the input that failed to parse
|
||||
*
|
||||
* @return Input
|
||||
*/
|
||||
@Nonnull public String getInput() {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum accepted integer that could have been parsed
|
||||
*
|
||||
* @return Minimum integer
|
||||
*/
|
||||
public Number getMin() {
|
||||
return this.min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum accepted integer that could have been parsed
|
||||
*
|
||||
* @return Maximum integer
|
||||
*/
|
||||
public Number getMax() {
|
||||
return this.max;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.execution;
|
||||
|
||||
import com.intellectualsites.commands.CommandTree;
|
||||
import com.intellectualsites.commands.context.CommandContext;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* The command execution coordinator is responsible for
|
||||
* coordinating command execution. This includes determining
|
||||
* what thread the command should be executed on, whether or
|
||||
* not command may be executed in parallel, etc.
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
public abstract class CommandExecutionCoordinator<C extends CommandSender> {
|
||||
|
||||
private final CommandTree<C> commandTree;
|
||||
|
||||
/**
|
||||
* Construct a new command execution coordinator
|
||||
*
|
||||
* @param commandTree Command tree
|
||||
*/
|
||||
public CommandExecutionCoordinator(@Nonnull final CommandTree<C> commandTree) {
|
||||
this.commandTree = commandTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a simple command execution coordinator that executes all commands immediately, on the calling thread
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
* @return New coordinator instance
|
||||
*/
|
||||
public static <C extends CommandSender> Function<CommandTree<C>, CommandExecutionCoordinator<C>> simpleCoordinator() {
|
||||
return SimpleCoordinator::new;
|
||||
}
|
||||
|
||||
public abstract CompletableFuture<CommandResult> coordinateExecution(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Queue<String> input);
|
||||
|
||||
/**
|
||||
* Get the command tree
|
||||
*
|
||||
* @return Command tree
|
||||
*/
|
||||
@Nonnull
|
||||
protected CommandTree<C> getCommandTree() {
|
||||
return this.commandTree;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A simple command execution coordinator that executes all commands immediately, on the calling thread
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
public static final class SimpleCoordinator<C extends CommandSender> extends CommandExecutionCoordinator<C> {
|
||||
|
||||
private SimpleCoordinator(@Nonnull final CommandTree<C> commandTree) {
|
||||
super(commandTree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> coordinateExecution(@Nonnull CommandContext<C> commandContext,
|
||||
@Nonnull Queue<String> input) {
|
||||
final CompletableFuture<CommandResult> completableFuture = new CompletableFuture<>();
|
||||
try {
|
||||
this.getCommandTree().parse(commandContext, input).ifPresent(
|
||||
command -> command.getCommandExecutionHandler().execute(commandContext));
|
||||
completableFuture.complete(new CommandResult());
|
||||
} catch (final Exception e) {
|
||||
completableFuture.completeExceptionally(e);
|
||||
}
|
||||
return completableFuture;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.execution;
|
||||
|
||||
import com.intellectualsites.commands.context.CommandContext;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Handler that is invoked whenever a {@link com.intellectualsites.commands.Command} is executed
|
||||
* by a {@link com.intellectualsites.commands.sender.CommandSender}
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CommandExecutionHandler<C extends CommandSender> {
|
||||
|
||||
/**
|
||||
* Handle command execution
|
||||
*
|
||||
* @param commandContext Command context
|
||||
*/
|
||||
void execute(@Nonnull final CommandContext<C> commandContext);
|
||||
|
||||
|
||||
/**
|
||||
* Command execution handler that does nothing
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
class NullCommandExecutionHandler<C extends CommandSender> implements CommandExecutionHandler<C> {
|
||||
|
||||
@Override
|
||||
public void execute(@Nonnull final CommandContext<C> commandContext) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.execution;
|
||||
|
||||
/**
|
||||
* The result of a command execution
|
||||
*/
|
||||
public class CommandResult {
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.internal;
|
||||
|
||||
import com.intellectualsites.commands.Command;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Utility that registers commands natively for whatever
|
||||
* platform the library is used in. This can do nothing, if
|
||||
* the target platform does not have its own concept of commands
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CommandRegistrationHandler {
|
||||
|
||||
/**
|
||||
* Command registration handler that does nothing
|
||||
*/
|
||||
NullCommandRegistrationHandler NULL_COMMAND_REGISTRATION_HANDLER = new NullCommandRegistrationHandler();
|
||||
|
||||
/**
|
||||
* Attempt to register the command
|
||||
*
|
||||
* @param command Command to register
|
||||
* @return {@code true} if the command was registered successfully,
|
||||
* else {@code false}
|
||||
*/
|
||||
boolean registerCommand(@Nonnull final Command<?> command);
|
||||
|
||||
|
||||
final class NullCommandRegistrationHandler implements CommandRegistrationHandler {
|
||||
|
||||
private NullCommandRegistrationHandler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerCommand(@Nonnull final Command<?> command) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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.sender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public interface CommandSender {
|
||||
|
||||
/**
|
||||
* Check if the command sender has a given permission node
|
||||
*
|
||||
* @param permission Permission node
|
||||
*/
|
||||
boolean hasPermission(@Nonnull String permission);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.intellectualsites.commands.components.StaticComponent;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class CommandTest {
|
||||
|
||||
@Test()
|
||||
void noComponents() {
|
||||
Assertions.assertEquals(1, Command.newBuilder("test").build().getComponents().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ensureOrdering() {
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () ->
|
||||
Command.newBuilder("test").withComponent(StaticComponent.optional("something"))
|
||||
.withComponent(StaticComponent.required("somethingelse")).build());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.intellectualsites.commands.components.StaticComponent;
|
||||
import com.intellectualsites.commands.components.standard.IntegerComponent;
|
||||
import com.intellectualsites.commands.context.CommandContext;
|
||||
import com.intellectualsites.commands.exceptions.NoPermissionException;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Optional;
|
||||
|
||||
class CommandTreeTest {
|
||||
|
||||
private static CommandManager<CommandSender> commandManager;
|
||||
|
||||
@BeforeAll
|
||||
static void newTree() {
|
||||
commandManager = new TestCommandManager();
|
||||
commandManager.registerCommand(commandManager.commandBuilder("test")
|
||||
.withComponent(StaticComponent.required("one")).build())
|
||||
.registerCommand(commandManager.commandBuilder("test")
|
||||
.withComponent(StaticComponent.required("two")).withPermission("no").build())
|
||||
.registerCommand(commandManager.commandBuilder("test")
|
||||
.withComponent(StaticComponent.required("opt"))
|
||||
.withComponent(IntegerComponent.optional("num", 15)).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse() {
|
||||
final Optional<Command<CommandSender>> command = commandManager.getCommandTree().parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(
|
||||
Arrays.asList("test", "one")));
|
||||
Assertions.assertTrue(command.isPresent());
|
||||
Assertions.assertThrows(NoPermissionException.class, () -> commandManager.getCommandTree().parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(
|
||||
Arrays.asList("test", "two"))));
|
||||
commandManager.getCommandTree().parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("test", "opt")))
|
||||
.ifPresent(c -> c.getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender())));
|
||||
commandManager.getCommandTree().parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("test", "opt", "12")))
|
||||
.ifPresent(c -> c.getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSuggestions() {
|
||||
Assertions.assertFalse(commandManager.getCommandTree().getSuggestions(new CommandContext<>(new TestCommandSender()), new LinkedList<>(
|
||||
Collections.singletonList("test"))).isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.intellectualsites.commands.execution.CommandExecutionCoordinator;
|
||||
import com.intellectualsites.commands.internal.CommandRegistrationHandler;
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
public class TestCommandManager extends CommandManager<CommandSender> {
|
||||
|
||||
protected TestCommandManager() {
|
||||
super(CommandExecutionCoordinator.simpleCoordinator(), CommandRegistrationHandler.NULL_COMMAND_REGISTRATION_HANDLER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.intellectualsites.commands.sender.CommandSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class TestCommandSender implements CommandSender {
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@Nonnull final String permission) {
|
||||
if (permission.equalsIgnoreCase("no")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue