Lots of progress

This commit is contained in:
Alexander Söderberg 2020-09-05 21:38:12 +02:00
parent a9b2524238
commit d4143246b7
No known key found for this signature in database
GPG key ID: C0207FF7EA146678
17 changed files with 606 additions and 84 deletions

1
commands-jline/README.md Normal file
View file

@ -0,0 +1 @@
Command implementation for [JLine](https://github.com/jline/jline3)

24
commands-jline/pom.xml Normal file
View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Commands</artifactId>
<groupId>com.intellectualsites</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>commands-jline</artifactId>
<dependencies>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline</artifactId>
<version>3.16.0</version>
</dependency>
<dependency>
<groupId>com.intellectualsites</groupId>
<artifactId>Commands</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,74 @@
//
// 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.jline;
import com.intellectualsites.commands.Command;
import com.intellectualsites.commands.CommandManager;
import com.intellectualsites.commands.CommandTree;
import com.intellectualsites.commands.execution.CommandExecutionCoordinator;
import com.intellectualsites.commands.internal.CommandRegistrationHandler;
import org.jline.reader.*;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.function.Function;
/**
* Command manager for use with JLine
*/
public class JLineCommandManager extends CommandManager<JLineCommandSender> implements Completer {
public static void main(String[] args) throws Exception {
// TODO: REMOVE THIS!!!!
final JLineCommandManager jLineCommandManager = new JLineCommandManager(CommandExecutionCoordinator.simpleCoordinator());
final Terminal terminal = TerminalBuilder.builder().dumb(true).build();
LineReader lineReader = LineReaderBuilder.builder()
.completer(jLineCommandManager).terminal(terminal).appName("Test").build();
boolean[] shouldStop = new boolean[] { false };
jLineCommandManager.registerCommand(Command.newBuilder("stop").withHandler(commandContext ->
shouldStop[0] = true).build());
while (!shouldStop[0]) {
final String line = lineReader.readLine();
if (line == null || line.isEmpty() || !line.startsWith("/")) {
continue;
}
jLineCommandManager.executeCommand(new JLineCommandSender(), line.substring(1)).join();
if (shouldStop[0]) {
System.out.println("Stopping.");
}
}
}
public JLineCommandManager(@Nonnull final Function<CommandTree<JLineCommandSender>, CommandExecutionCoordinator<JLineCommandSender>> executionCoordinatorFunction) {
super(executionCoordinatorFunction, CommandRegistrationHandler.NULL_COMMAND_REGISTRATION_HANDLER);
}
@Override
public void complete(@Nonnull final LineReader lineReader, @Nonnull final ParsedLine parsedLine, @Nonnull final List<Candidate> list) {
// TODO: Implement
}
}

View file

@ -0,0 +1,29 @@
//
// 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.jline;
import com.intellectualsites.commands.sender.CommandSender;
public class JLineCommandSender implements CommandSender {
}

View file

@ -7,7 +7,10 @@
<groupId>com.intellectualsites</groupId>
<artifactId>Commands</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<modules>
<module>commands-jline</module>
</modules>
<packaging>pom</packaging>
<inceptionYear>2020</inceptionYear>
<licenses>
<license>

View file

@ -24,33 +24,50 @@
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 java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.*;
/**
* A command consists out of a chain of {@link com.intellectualsites.commands.components.CommandComponent command components}.
*
* @param <C> Command sender type
*/
public class Command {
public class Command<C extends CommandSender> {
private final CommandComponent<?>[] components;
private final CommandComponent<C, ?>[] components;
private final CommandExecutionHandler<C> commandExecutionHandler;
private Command(@Nonnull final CommandComponent<?>[] commandComponents) {
protected Command(@Nonnull final CommandComponent<C, ?>[] commandComponents, @Nonnull final CommandExecutionHandler<C> commandExecutionHandler) {
this.components = Objects.requireNonNull(commandComponents, "Command components may not be null");
if (this.components.length == 0){
if (this.components.length == 0) {
throw new IllegalArgumentException("At least one command component is required");
}
// Enforce ordering of command components
boolean foundOptional = false;
for (final CommandComponent<?> component : this.components) {
for (final CommandComponent<C, ?> component : this.components) {
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;
}
/**
* 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<>(Collections.singletonList(StaticComponent.required(commandName)),
new CommandExecutionHandler.NullCommandExecutionHandler<>());
}
/**
@ -58,10 +75,18 @@ public class Command {
*
* @return Copy of the command component array
*/
@Nonnull public CommandComponent<?>[] getComponents() {
final CommandComponent<?>[] commandComponents = new CommandComponent<?>[this.components.length];
System.arraycopy(this.components, 0, commandComponents, 0, this.components.length);
return commandComponents;
@Nonnull @SuppressWarnings("ALL")
public CommandComponent<C, ?>[] getComponents() {
return (CommandComponent<C, ?>[]) Arrays.asList(this.components).toArray();
}
/**
* Get the command execution handler
*
* @return Command execution handler
*/
@Nonnull public CommandExecutionHandler<C> getCommandExecutionHandler() {
return this.commandExecutionHandler;
}
/**
@ -70,8 +95,8 @@ public class Command {
*
* @return List containing the longest shared component chain
*/
public List<CommandComponent<?>> getSharedComponentChain(@Nonnull final Command other) {
final List<CommandComponent<?>> commandComponents = new LinkedList<>();
public List<CommandComponent<C, ?>> getSharedComponentChain(@Nonnull final Command<C> other) {
final List<CommandComponent<C, ?>> commandComponents = new LinkedList<>();
for (int i = 0; i < this.components.length && i < other.components.length; i++) {
if (this.components[i].equals(other.components[i])) {
commandComponents.add(this.components[i]);
@ -82,4 +107,52 @@ public class Command {
return commandComponents;
}
public static class Builder<C extends CommandSender> {
private final List<CommandComponent<C, ?>> commandComponents;
private final CommandExecutionHandler<C> commandExecutionHandler;
private Builder(@Nonnull final List<CommandComponent<C, ?>> commandComponents, @Nonnull final CommandExecutionHandler<C> commandExecutionHandler) {
this.commandComponents = commandComponents;
this.commandExecutionHandler = commandExecutionHandler;
}
/**
* 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<>(commandComponents, this.commandExecutionHandler);
}
/**
* 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.commandComponents, commandExecutionHandler);
}
/**
* Build a command using the builder instance
*
* @return Built command
*/
@Nonnull
public Command<C> build() {
return new Command<>(this.commandComponents.toArray(new CommandComponent[0]), this.commandExecutionHandler);
}
}
}

View file

@ -0,0 +1,74 @@
//
// 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.context.CommandContext;
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.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 CommandExecutionCoordinator<C> commandExecutionCoordinator;
private final CommandTree<C> commandTree;
protected CommandManager(@Nonnull final Function<CommandTree<C>, CommandExecutionCoordinator<C>> commandExecutionCoordinator,
@Nonnull final CommandRegistrationHandler commandRegistrationHandler) {
this.commandTree = CommandTree.newTree(commandRegistrationHandler);
this.commandExecutionCoordinator = commandExecutionCoordinator.apply(commandTree);
}
public CompletableFuture<CommandResult> executeCommand(@Nonnull final C commandSender, @Nonnull final String input) {
final StringTokenizer stringTokenizer = new StringTokenizer(input, " ");
final Queue<String> tokens = new LinkedList<>();
while (stringTokenizer.hasMoreElements()) {
tokens.add(stringTokenizer.nextToken());
}
final CommandContext<C> context = new CommandContext<>(commandSender);
return this.commandExecutionCoordinator.coordinateExecution(context, tokens);
}
/**
* Register a new command
*
* @param command Command to register
*/
public void registerCommand(@Nonnull final Command command) {
this.commandTree.insertCommand(command);
}
}

View file

@ -27,6 +27,7 @@ import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.intellectualsites.commands.components.CommandComponent;
import com.intellectualsites.commands.components.StaticComponent;
import com.intellectualsites.commands.context.CommandContext;
import com.intellectualsites.commands.exceptions.NoSuchCommandException;
import com.intellectualsites.commands.internal.CommandRegistrationHandler;
import com.intellectualsites.commands.parser.ComponentParseResult;
@ -44,7 +45,7 @@ import java.util.stream.Collectors;
*/
public class CommandTree<C extends CommandSender> {
private final Node<CommandComponent<?>> internalTree = new Node<>(null);
private final Node<CommandComponent<C, ?>> internalTree = new Node<>(null);
private final CommandRegistrationHandler commandRegistrationHandler;
private CommandTree(@Nonnull final CommandRegistrationHandler commandRegistrationHandler) {
@ -63,26 +64,25 @@ public class CommandTree<C extends CommandSender> {
return new CommandTree<>(commandRegistrationHandler);
}
public Optional<Command> parse(@Nonnull final C commandSender, @Nonnull final String[] args) throws NoSuchCommandException {
final Queue<String> commandQueue = new LinkedList<>(Arrays.asList(args));
return parseCommand(commandSender, commandQueue, this.internalTree);
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> parseCommand(@Nonnull final C commandSender, @Nonnull final Queue<String> commandQueue,
@Nonnull final Node<CommandComponent<?>> root) throws NoSuchCommandException {
private Optional<Command<C>> parseCommand(@Nonnull final CommandContext<C> commandContext, @Nonnull final Queue<String> commandQueue,
@Nonnull final Node<CommandComponent<C, ?>> root) throws NoSuchCommandException {
final List<Node<CommandComponent<?>>> children = root.getChildren();
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<?>> child = children.get(0);
final Node<CommandComponent<C, ?>> child = children.get(0);
if (child.getValue() != null) {
final ComponentParseResult<?> result = child.getValue().getParser().parse(commandSender, commandQueue);
final ComponentParseResult<?> result = child.getValue().getParser().parse(commandContext, commandQueue);
if (result.getParsedValue().isPresent()) {
/* TODO: Add context */
if (child.isLeaf()) {
return Optional.ofNullable(child.getValue().getOwningCommand());
} else {
return this.parseCommand(commandSender, commandQueue, child);
return this.parseCommand(commandContext, commandQueue, child);
}
} else if (result.getFailure().isPresent()) {
/* TODO: Return error */
@ -97,13 +97,12 @@ public class CommandTree<C extends CommandSender> {
return Optional.of(root.getValue().getOwningCommand());
} else {
/* TODO: Indicate that we could not resolve the command here */
final List<CommandComponent<?>> components = this.getChain(root).stream().map(Node::getValue).collect(Collectors.toList());
final List<CommandComponent<C, ?>> components = this.getChain(root).stream().map(Node::getValue).collect(Collectors.toList());
}
} else {
/*
final String popped = commandQueue.poll();
if (popped == null) {
/* Not enough arguments */
/* TODO: Send correct usage */
return Optional.empty();
}
@ -122,7 +121,6 @@ public class CommandTree<C extends CommandSender> {
} else if (comparison > 0) {
high = mid - 1;
} else {
/* We found a match */
if (node.isLeaf()) {
return Optional.ofNullable(node.getValue().getOwningCommand());
} else {
@ -130,26 +128,27 @@ public class CommandTree<C extends CommandSender> {
}
}
}
*/
/* We could not find a match */
throw new NoSuchCommandException(commandSender, getChain(root).stream().map(Node::getValue).collect(Collectors.toList()), popped);
}
/*
final Iterator<Node<CommandComponent<?>>> childIterator = root.getChildren().iterator();
if (childIterator.hasNext()) {
while (childIterator.hasNext()) {
final Node<CommandComponent<?>> child = childIterator.next();
if (child.getValue() != null) {
final ComponentParseResult<?> result = child.getValue().getParser().parse(commandSender, commandQueue);
if (result.getParsedValue().isPresent()) {
return this.parseCommand(commandSender, commandQueue, child);
} else if (result.getFailure().isPresent() && root.children.size() == 1) {
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()),
java.util.Objects.requireNonNull(commandQueue.peek()));
}
*/
return Optional.empty();
}
@ -159,10 +158,10 @@ public class CommandTree<C extends CommandSender> {
*
* @param command Command to insert
*/
public void insertCommand(@Nonnull final Command command) {
Node<CommandComponent<?>> node = this.internalTree;
for (final CommandComponent<?> component : command.getComponents()) {
Node<CommandComponent<?>> tempNode = node.getChild(component);
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);
}
@ -202,12 +201,12 @@ public class CommandTree<C extends CommandSender> {
/* TODO: Figure out a way to register all combinations along a command component path */
}
private void checkAmbiguity(@Nonnull final Node<CommandComponent<?>> node) {
private void checkAmbiguity(@Nonnull final Node<CommandComponent<C, ?>> node) {
if (node.isLeaf()) {
return;
}
final int size = node.children.size();
for (final Node<CommandComponent<?>> child : node.children) {
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());
@ -216,8 +215,8 @@ public class CommandTree<C extends CommandSender> {
node.children.forEach(this::checkAmbiguity);
}
private List<CommandComponent<?>> getLeaves(@Nonnull final Node<CommandComponent<?>> node) {
final List<CommandComponent<?>> leaves = new LinkedList<>();
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());
@ -228,9 +227,9 @@ public class CommandTree<C extends CommandSender> {
return leaves;
}
private List<Node<CommandComponent<?>>> getChain(@Nullable final Node<CommandComponent<?>> end) {
final List<Node<CommandComponent<?>>> chain = new LinkedList<>();
Node<CommandComponent<?>> tail = end;
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 = end.getParent();

View file

@ -25,6 +25,7 @@ package com.intellectualsites.commands.components;
import com.intellectualsites.commands.Command;
import com.intellectualsites.commands.parser.ComponentParser;
import com.intellectualsites.commands.sender.CommandSender;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -34,9 +35,10 @@ 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
*/
public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
public class CommandComponent<C extends CommandSender, T> implements Comparable<CommandComponent<?, ?>> {
private static final Pattern NAME_PATTERN = Pattern.compile("[A-Za-z0-9]+");
@ -57,12 +59,12 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
* The parser that is used to parse the command input
* into the corresponding command type
*/
private final ComponentParser<T> parser;
private final ComponentParser<C, T> parser;
private Command owningCommand;
private Command<C> owningCommand;
CommandComponent(final boolean required, @Nonnull final String name,
@Nonnull final ComponentParser<T> parser) {
@Nonnull final ComponentParser<C, T> parser) {
this.required = required;
this.name = Objects.requireNonNull(name, "Name may not be null");
if (!NAME_PATTERN.asPredicate().test(name)) {
@ -75,10 +77,11 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
* Create a new command component
*
* @param clazz Argument class
* @param <C> Command sender type
* @param <T> Argument Type
* @return Component builder
*/
@Nonnull public static <T> CommandComponent.Builder<T> ofType(@Nonnull final Class<T> clazz) {
@Nonnull public static <C extends CommandSender, T> CommandComponent.Builder<C, T> ofType(@Nonnull final Class<T> clazz) {
return new Builder<>();
}
@ -106,7 +109,7 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
*
* @return Command parser
*/
@Nonnull public ComponentParser<T> getParser() {
@Nonnull public ComponentParser<C, T> getParser() {
return this.parser;
}
@ -119,7 +122,7 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
*
* @return Owning command
*/
@Nullable public Command getOwningCommand() {
@Nullable public Command<C> getOwningCommand() {
return this.owningCommand;
}
@ -128,7 +131,7 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
*
* @param owningCommand Owning command
*/
public void setOwningCommand(@Nonnull final Command owningCommand) {
public void setOwningCommand(@Nonnull final Command<C> owningCommand) {
if (this.owningCommand != null) {
throw new IllegalStateException("Cannot replace owning command");
}
@ -143,7 +146,7 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
if (o == null || getClass() != o.getClass()) {
return false;
}
final CommandComponent<?> that = (CommandComponent<?>) o;
final CommandComponent<?, ?> that = (CommandComponent<?, ?>) o;
return isRequired() == that.isRequired() && com.google.common.base.Objects.equal(getName(), that.getName());
}
@ -153,7 +156,7 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
}
@Override
public int compareTo(@Nonnull final CommandComponent<?> o) {
public int compareTo(@Nonnull final CommandComponent<?, ?> o) {
if (this instanceof StaticComponent) {
if (o instanceof StaticComponent) {
return (this.getName().compareTo(o.getName()));
@ -170,11 +173,11 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
}
public static class Builder<T> {
public static class Builder<C extends CommandSender, T> {
private String name;
private boolean required = true;
private ComponentParser<T> parser;
private ComponentParser<C, T> parser;
private Builder() {
}
@ -185,7 +188,7 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
* @param name Alphanumeric component name
* @return Builder instance
*/
@Nonnull public Builder<T> named(@Nonnull final String name) {
@Nonnull public Builder<C, T> named(@Nonnull final String name) {
this.name = name;
return this;
}
@ -199,7 +202,7 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
*
* @return Builder instance
*/
@Nonnull public Builder<T> asRequired() {
@Nonnull public Builder<C, T> asRequired() {
this.required = true;
return this;
}
@ -213,7 +216,7 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
*
* @return Builder instance
*/
@Nonnull public Builder<T> asOptional() {
@Nonnull public Builder<C, T> asOptional() {
this.required = false;
return this;
}
@ -224,7 +227,7 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
* @param parser Component parser
* @return Builder instance
*/
@Nonnull public Builder<T> withParser(@Nonnull final ComponentParser<T> parser) {
@Nonnull public Builder<C, T> withParser(@Nonnull final ComponentParser<C, T> parser) {
this.parser = Objects.requireNonNull(parser, "Parser may not be null");
return this;
}
@ -234,7 +237,7 @@ public class CommandComponent<T> implements Comparable<CommandComponent<?>> {
*
* @return Constructed component
*/
@Nonnull public CommandComponent<T> build() {
@Nonnull public CommandComponent<C, T> build() {
return new CommandComponent<>(this.required, this.name, this.parser);
}

View file

@ -23,6 +23,7 @@
//
package com.intellectualsites.commands.components;
import com.intellectualsites.commands.context.CommandContext;
import com.intellectualsites.commands.parser.ComponentParseResult;
import com.intellectualsites.commands.parser.ComponentParser;
import com.intellectualsites.commands.sender.CommandSender;
@ -33,14 +34,22 @@ import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
public final class StaticComponent extends CommandComponent<String> {
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));
super(required, name, new StaticComponentParser<>(name, aliases));
}
@Nonnull public static <C extends CommandSender> StaticComponent<C> required(@Nonnull final String name, @Nonnull final String ... aliases) {
return new StaticComponent<>(true, name, aliases);
}
@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 implements ComponentParser<String> {
private static final class StaticComponentParser<C extends CommandSender> implements ComponentParser<C, String> {
private final String name;
private final Set<String> acceptedStrings = new HashSet<>();
@ -50,7 +59,7 @@ public final class StaticComponent extends CommandComponent<String> {
this.acceptedStrings.addAll(Arrays.asList(aliases));
}
@Nonnull @Override public ComponentParseResult<String> parse(@Nonnull final CommandSender sender, @Nonnull final Queue<String> inputQueue) {
@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(this.name);

View file

@ -0,0 +1,47 @@
//
// 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 class CommandContext<C extends CommandSender> {
private final C commandSender;
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;
}
}

View file

@ -36,7 +36,7 @@ import java.util.List;
public class CommandParseException extends IllegalArgumentException {
private final CommandSender commandSender;
private final List<CommandComponent<?>> currentChain;
private final List<CommandComponent<?, ?>> currentChain;
/**
* Construct a new command parse exception
@ -44,7 +44,7 @@ public class CommandParseException extends IllegalArgumentException {
* @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) {
protected CommandParseException(@Nonnull final CommandSender commandSender, @Nonnull final List<CommandComponent<?, ?>> currentChain) {
this.commandSender = commandSender;
this.currentChain = currentChain;
}
@ -65,7 +65,7 @@ public class CommandParseException extends IllegalArgumentException {
* @return Unmodifiable list of command components
*/
@Nonnull
public List<CommandComponent<?>> getCurrentChain() {
public List<CommandComponent<?, ?>> getCurrentChain() {
return Collections.unmodifiableList(this.currentChain);
}

View file

@ -44,7 +44,7 @@ public class NoSuchCommandException extends CommandParseException {
* @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,
public NoSuchCommandException(@Nonnull final CommandSender commandSender, @Nonnull final List<CommandComponent<?, ?>> currentChain,
@Nonnull final String command) {
super(commandSender, currentChain);
this.suppliedCommand = command;

View file

@ -0,0 +1,100 @@
//
// 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;
}
public static 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;
}
}
}

View file

@ -0,0 +1,56 @@
//
// 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);
class NullCommandExecutionHandler<C extends CommandSender> implements CommandExecutionHandler<C> {
@Override
public void execute(@Nonnull final CommandContext<C> commandContext) {
}
}
}

View file

@ -0,0 +1,27 @@
//
// 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;
public class CommandResult {
}

View file

@ -23,20 +23,23 @@
//
package com.intellectualsites.commands.parser;
import com.intellectualsites.commands.context.CommandContext;
import com.intellectualsites.commands.sender.CommandSender;
import javax.annotation.Nonnull;
import java.util.Queue;
@FunctionalInterface public interface ComponentParser<T> {
@FunctionalInterface
public interface ComponentParser<C extends CommandSender, T> {
/**
* Parse command input into a command result
*
* @param sender Sender who sent the command
* @param inputQueue The queue of arguments
* @param commandContext Command context
* @param inputQueue The queue of arguments
* @return Parsed command result
*/
@Nonnull ComponentParseResult<T> parse(@Nonnull CommandSender sender, @Nonnull Queue<String> inputQueue);
@Nonnull
ComponentParseResult<T> parse(@Nonnull CommandContext<C> commandContext, @Nonnull Queue<String> inputQueue);
}