diff --git a/commands-jline/README.md b/commands-jline/README.md
new file mode 100644
index 00000000..6ceed81a
--- /dev/null
+++ b/commands-jline/README.md
@@ -0,0 +1 @@
+Command implementation for [JLine](https://github.com/jline/jline3)
diff --git a/commands-jline/pom.xml b/commands-jline/pom.xml
new file mode 100644
index 00000000..4870c29c
--- /dev/null
+++ b/commands-jline/pom.xml
@@ -0,0 +1,24 @@
+
+
+
+ Commands
+ com.intellectualsites
+ 1.0-SNAPSHOT
+
+ 4.0.0
+ commands-jline
+
+
+ org.jline
+ jline
+ 3.16.0
+
+
+ com.intellectualsites
+ Commands
+ 1.0-SNAPSHOT
+
+
+
diff --git a/commands-jline/src/main/java/com/intellectualsites/commands/jline/JLineCommandManager.java b/commands-jline/src/main/java/com/intellectualsites/commands/jline/JLineCommandManager.java
new file mode 100644
index 00000000..006aaa7f
--- /dev/null
+++ b/commands-jline/src/main/java/com/intellectualsites/commands/jline/JLineCommandManager.java
@@ -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 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, CommandExecutionCoordinator> executionCoordinatorFunction) {
+ super(executionCoordinatorFunction, CommandRegistrationHandler.NULL_COMMAND_REGISTRATION_HANDLER);
+ }
+
+ @Override
+ public void complete(@Nonnull final LineReader lineReader, @Nonnull final ParsedLine parsedLine, @Nonnull final List list) {
+ // TODO: Implement
+ }
+
+}
diff --git a/commands-jline/src/main/java/com/intellectualsites/commands/jline/JLineCommandSender.java b/commands-jline/src/main/java/com/intellectualsites/commands/jline/JLineCommandSender.java
new file mode 100644
index 00000000..e4f7aaf5
--- /dev/null
+++ b/commands-jline/src/main/java/com/intellectualsites/commands/jline/JLineCommandSender.java
@@ -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 {
+}
diff --git a/pom.xml b/pom.xml
index 5211bac2..ad5a1636 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,10 @@
com.intellectualsites
Commands
1.0-SNAPSHOT
- jar
+
+ commands-jline
+
+ pom
2020
diff --git a/src/main/java/com/intellectualsites/commands/Command.java b/src/main/java/com/intellectualsites/commands/Command.java
index d5879b9c..1fe8ed7a 100644
--- a/src/main/java/com/intellectualsites/commands/Command.java
+++ b/src/main/java/com/intellectualsites/commands/Command.java
@@ -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 Command sender type
*/
-public class Command {
+public class Command {
- private final CommandComponent>[] components;
+ private final CommandComponent[] components;
+ private final CommandExecutionHandler commandExecutionHandler;
- private Command(@Nonnull final CommandComponent>[] commandComponents) {
+ protected Command(@Nonnull final CommandComponent[] commandComponents, @Nonnull final CommandExecutionHandler 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 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 Builder 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[] getComponents() {
+ return (CommandComponent[]) Arrays.asList(this.components).toArray();
+ }
+
+ /**
+ * Get the command execution handler
+ *
+ * @return Command execution handler
+ */
+ @Nonnull public CommandExecutionHandler getCommandExecutionHandler() {
+ return this.commandExecutionHandler;
}
/**
@@ -70,8 +95,8 @@ public class Command {
*
* @return List containing the longest shared component chain
*/
- public List> getSharedComponentChain(@Nonnull final Command other) {
- final List> commandComponents = new LinkedList<>();
+ public List> getSharedComponentChain(@Nonnull final Command other) {
+ final List> 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 {
+
+ private final List> commandComponents;
+ private final CommandExecutionHandler commandExecutionHandler;
+
+ private Builder(@Nonnull final List> commandComponents, @Nonnull final CommandExecutionHandler commandExecutionHandler) {
+ this.commandComponents = commandComponents;
+ this.commandExecutionHandler = commandExecutionHandler;
+ }
+
+ /**
+ * Add a new command component to the command
+ *
+ * @param component Component to add
+ * @param Component type
+ * @return New builder instance with the command component inserted into the component list
+ */
+ @Nonnull
+ public Builder withComponent(@Nonnull final CommandComponent component) {
+ final List> 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 withHandler(@Nonnull final CommandExecutionHandler commandExecutionHandler) {
+ return new Builder<>(this.commandComponents, commandExecutionHandler);
+ }
+
+ /**
+ * Build a command using the builder instance
+ *
+ * @return Built command
+ */
+ @Nonnull
+ public Command build() {
+ return new Command<>(this.commandComponents.toArray(new CommandComponent[0]), this.commandExecutionHandler);
+ }
+
+ }
+
}
diff --git a/src/main/java/com/intellectualsites/commands/CommandManager.java b/src/main/java/com/intellectualsites/commands/CommandManager.java
new file mode 100644
index 00000000..b008be1a
--- /dev/null
+++ b/src/main/java/com/intellectualsites/commands/CommandManager.java
@@ -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 Command sender type
+ */
+public abstract class CommandManager {
+
+ private final CommandExecutionCoordinator commandExecutionCoordinator;
+ private final CommandTree commandTree;
+
+ protected CommandManager(@Nonnull final Function, CommandExecutionCoordinator> commandExecutionCoordinator,
+ @Nonnull final CommandRegistrationHandler commandRegistrationHandler) {
+ this.commandTree = CommandTree.newTree(commandRegistrationHandler);
+ this.commandExecutionCoordinator = commandExecutionCoordinator.apply(commandTree);
+ }
+
+ public CompletableFuture executeCommand(@Nonnull final C commandSender, @Nonnull final String input) {
+ final StringTokenizer stringTokenizer = new StringTokenizer(input, " ");
+ final Queue tokens = new LinkedList<>();
+ while (stringTokenizer.hasMoreElements()) {
+ tokens.add(stringTokenizer.nextToken());
+ }
+ final CommandContext 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);
+ }
+
+}
diff --git a/src/main/java/com/intellectualsites/commands/CommandTree.java b/src/main/java/com/intellectualsites/commands/CommandTree.java
index f0bd1588..36e43158 100644
--- a/src/main/java/com/intellectualsites/commands/CommandTree.java
+++ b/src/main/java/com/intellectualsites/commands/CommandTree.java
@@ -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 {
- private final Node> internalTree = new Node<>(null);
+ private final Node> internalTree = new Node<>(null);
private final CommandRegistrationHandler commandRegistrationHandler;
private CommandTree(@Nonnull final CommandRegistrationHandler commandRegistrationHandler) {
@@ -63,26 +64,25 @@ public class CommandTree {
return new CommandTree<>(commandRegistrationHandler);
}
- public Optional parse(@Nonnull final C commandSender, @Nonnull final String[] args) throws NoSuchCommandException {
- final Queue commandQueue = new LinkedList<>(Arrays.asList(args));
- return parseCommand(commandSender, commandQueue, this.internalTree);
+ public Optional> parse(@Nonnull final CommandContext commandContext, @Nonnull final Queue args) throws NoSuchCommandException {
+ return parseCommand(commandContext, args, this.internalTree);
}
- private Optional parseCommand(@Nonnull final C commandSender, @Nonnull final Queue commandQueue,
- @Nonnull final Node> root) throws NoSuchCommandException {
+ private Optional> parseCommand(@Nonnull final CommandContext commandContext, @Nonnull final Queue commandQueue,
+ @Nonnull final Node> root) throws NoSuchCommandException {
- final List>> children = root.getChildren();
+ final List>> children = root.getChildren();
if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) {
// The value has to be a variable
- final Node> child = children.get(0);
+ final Node> 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 {
return Optional.of(root.getValue().getOwningCommand());
} else {
/* TODO: Indicate that we could not resolve the command here */
- final List> components = this.getChain(root).stream().map(Node::getValue).collect(Collectors.toList());
+ final List> 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 {
} 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 {
}
}
}
+ */
- /* We could not find a match */
- throw new NoSuchCommandException(commandSender, getChain(root).stream().map(Node::getValue).collect(Collectors.toList()), popped);
- }
-
- /*
- final Iterator>> childIterator = root.getChildren().iterator();
- if (childIterator.hasNext()) {
- while (childIterator.hasNext()) {
- final Node> 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>> childIterator = root.getChildren().iterator();
+ if (childIterator.hasNext()) {
+ while (childIterator.hasNext()) {
+ final Node> 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 {
*
* @param command Command to insert
*/
- public void insertCommand(@Nonnull final Command command) {
- Node> node = this.internalTree;
- for (final CommandComponent> component : command.getComponents()) {
- Node> tempNode = node.getChild(component);
+ public void insertCommand(@Nonnull final Command command) {
+ Node> node = this.internalTree;
+ for (final CommandComponent component : command.getComponents()) {
+ Node> tempNode = node.getChild(component);
if (tempNode == null) {
tempNode = node.addChild(component);
}
@@ -202,12 +201,12 @@ public class CommandTree {
/* TODO: Figure out a way to register all combinations along a command component path */
}
- private void checkAmbiguity(@Nonnull final Node> node) {
+ private void checkAmbiguity(@Nonnull final Node> node) {
if (node.isLeaf()) {
return;
}
final int size = node.children.size();
- for (final Node> child : node.children) {
+ for (final Node> 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 {
node.children.forEach(this::checkAmbiguity);
}
- private List> getLeaves(@Nonnull final Node> node) {
- final List> leaves = new LinkedList<>();
+ private List> getLeaves(@Nonnull final Node> node) {
+ final List> leaves = new LinkedList<>();
if (node.isLeaf()) {
if (node.getValue() != null) {
leaves.add(node.getValue());
@@ -228,9 +227,9 @@ public class CommandTree {
return leaves;
}
- private List>> getChain(@Nullable final Node> end) {
- final List>> chain = new LinkedList<>();
- Node> tail = end;
+ private List>> getChain(@Nullable final Node> end) {
+ final List>> chain = new LinkedList<>();
+ Node> tail = end;
while (tail != null) {
chain.add(tail);
tail = end.getParent();
diff --git a/src/main/java/com/intellectualsites/commands/components/CommandComponent.java b/src/main/java/com/intellectualsites/commands/components/CommandComponent.java
index e5b2ebfe..1d4575af 100644
--- a/src/main/java/com/intellectualsites/commands/components/CommandComponent.java
+++ b/src/main/java/com/intellectualsites/commands/components/CommandComponent.java
@@ -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 Command sender type
* @param The type that the component parses into
*/
-public class CommandComponent implements Comparable> {
+public class CommandComponent implements Comparable> {
private static final Pattern NAME_PATTERN = Pattern.compile("[A-Za-z0-9]+");
@@ -57,12 +59,12 @@ public class CommandComponent implements Comparable> {
* The parser that is used to parse the command input
* into the corresponding command type
*/
- private final ComponentParser parser;
+ private final ComponentParser parser;
- private Command owningCommand;
+ private Command owningCommand;
CommandComponent(final boolean required, @Nonnull final String name,
- @Nonnull final ComponentParser parser) {
+ @Nonnull final ComponentParser 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 implements Comparable> {
* Create a new command component
*
* @param clazz Argument class
+ * @param Command sender type
* @param Argument Type
* @return Component builder
*/
- @Nonnull public static CommandComponent.Builder ofType(@Nonnull final Class clazz) {
+ @Nonnull public static CommandComponent.Builder ofType(@Nonnull final Class clazz) {
return new Builder<>();
}
@@ -106,7 +109,7 @@ public class CommandComponent implements Comparable> {
*
* @return Command parser
*/
- @Nonnull public ComponentParser getParser() {
+ @Nonnull public ComponentParser getParser() {
return this.parser;
}
@@ -119,7 +122,7 @@ public class CommandComponent implements Comparable> {
*
* @return Owning command
*/
- @Nullable public Command getOwningCommand() {
+ @Nullable public Command getOwningCommand() {
return this.owningCommand;
}
@@ -128,7 +131,7 @@ public class CommandComponent implements Comparable> {
*
* @param owningCommand Owning command
*/
- public void setOwningCommand(@Nonnull final Command owningCommand) {
+ public void setOwningCommand(@Nonnull final Command owningCommand) {
if (this.owningCommand != null) {
throw new IllegalStateException("Cannot replace owning command");
}
@@ -143,7 +146,7 @@ public class CommandComponent implements Comparable> {
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 implements Comparable> {
}
@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 implements Comparable> {
}
- public static class Builder {
+ public static class Builder {
private String name;
private boolean required = true;
- private ComponentParser parser;
+ private ComponentParser parser;
private Builder() {
}
@@ -185,7 +188,7 @@ public class CommandComponent implements Comparable> {
* @param name Alphanumeric component name
* @return Builder instance
*/
- @Nonnull public Builder named(@Nonnull final String name) {
+ @Nonnull public Builder named(@Nonnull final String name) {
this.name = name;
return this;
}
@@ -199,7 +202,7 @@ public class CommandComponent implements Comparable> {
*
* @return Builder instance
*/
- @Nonnull public Builder asRequired() {
+ @Nonnull public Builder asRequired() {
this.required = true;
return this;
}
@@ -213,7 +216,7 @@ public class CommandComponent implements Comparable> {
*
* @return Builder instance
*/
- @Nonnull public Builder asOptional() {
+ @Nonnull public Builder asOptional() {
this.required = false;
return this;
}
@@ -224,7 +227,7 @@ public class CommandComponent implements Comparable> {
* @param parser Component parser
* @return Builder instance
*/
- @Nonnull public Builder withParser(@Nonnull final ComponentParser parser) {
+ @Nonnull public Builder withParser(@Nonnull final ComponentParser parser) {
this.parser = Objects.requireNonNull(parser, "Parser may not be null");
return this;
}
@@ -234,7 +237,7 @@ public class CommandComponent implements Comparable> {
*
* @return Constructed component
*/
- @Nonnull public CommandComponent build() {
+ @Nonnull public CommandComponent build() {
return new CommandComponent<>(this.required, this.name, this.parser);
}
diff --git a/src/main/java/com/intellectualsites/commands/components/StaticComponent.java b/src/main/java/com/intellectualsites/commands/components/StaticComponent.java
index a7c22f94..029891ab 100644
--- a/src/main/java/com/intellectualsites/commands/components/StaticComponent.java
+++ b/src/main/java/com/intellectualsites/commands/components/StaticComponent.java
@@ -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 {
+public final class StaticComponent extends CommandComponent {
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 StaticComponent required(@Nonnull final String name, @Nonnull final String ... aliases) {
+ return new StaticComponent<>(true, name, aliases);
+ }
+
+ @Nonnull public static StaticComponent optional(@Nonnull final String name, @Nonnull final String ... aliases) {
+ return new StaticComponent<>(false, name, aliases);
}
- private static final class StaticComponentParser implements ComponentParser {
+ private static final class StaticComponentParser implements ComponentParser {
private final String name;
private final Set acceptedStrings = new HashSet<>();
@@ -50,7 +59,7 @@ public final class StaticComponent extends CommandComponent {
this.acceptedStrings.addAll(Arrays.asList(aliases));
}
- @Nonnull @Override public ComponentParseResult parse(@Nonnull final CommandSender sender, @Nonnull final Queue inputQueue) {
+ @Nonnull @Override public ComponentParseResult parse(@Nonnull final CommandContext commandContext, @Nonnull final Queue inputQueue) {
final String string = inputQueue.peek();
if (string == null) {
return ComponentParseResult.failure(this.name);
diff --git a/src/main/java/com/intellectualsites/commands/context/CommandContext.java b/src/main/java/com/intellectualsites/commands/context/CommandContext.java
new file mode 100644
index 00000000..02c2ca00
--- /dev/null
+++ b/src/main/java/com/intellectualsites/commands/context/CommandContext.java
@@ -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 {
+
+ 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;
+ }
+
+}
diff --git a/src/main/java/com/intellectualsites/commands/exceptions/CommandParseException.java b/src/main/java/com/intellectualsites/commands/exceptions/CommandParseException.java
index 36042c35..3584c188 100644
--- a/src/main/java/com/intellectualsites/commands/exceptions/CommandParseException.java
+++ b/src/main/java/com/intellectualsites/commands/exceptions/CommandParseException.java
@@ -36,7 +36,7 @@ import java.util.List;
public class CommandParseException extends IllegalArgumentException {
private final CommandSender commandSender;
- private final List> currentChain;
+ private final List> 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> currentChain) {
+ protected CommandParseException(@Nonnull final CommandSender commandSender, @Nonnull final List> 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> getCurrentChain() {
+ public List> getCurrentChain() {
return Collections.unmodifiableList(this.currentChain);
}
diff --git a/src/main/java/com/intellectualsites/commands/exceptions/NoSuchCommandException.java b/src/main/java/com/intellectualsites/commands/exceptions/NoSuchCommandException.java
index 07bc3b68..92b025b3 100644
--- a/src/main/java/com/intellectualsites/commands/exceptions/NoSuchCommandException.java
+++ b/src/main/java/com/intellectualsites/commands/exceptions/NoSuchCommandException.java
@@ -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> currentChain,
+ public NoSuchCommandException(@Nonnull final CommandSender commandSender, @Nonnull final List> currentChain,
@Nonnull final String command) {
super(commandSender, currentChain);
this.suppliedCommand = command;
diff --git a/src/main/java/com/intellectualsites/commands/execution/CommandExecutionCoordinator.java b/src/main/java/com/intellectualsites/commands/execution/CommandExecutionCoordinator.java
new file mode 100644
index 00000000..c6e6fa17
--- /dev/null
+++ b/src/main/java/com/intellectualsites/commands/execution/CommandExecutionCoordinator.java
@@ -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 Command sender type
+ */
+public abstract class CommandExecutionCoordinator {
+
+ private final CommandTree commandTree;
+
+ /**
+ * Construct a new command execution coordinator
+ *
+ * @param commandTree Command tree
+ */
+ public CommandExecutionCoordinator(@Nonnull final CommandTree commandTree) {
+ this.commandTree = commandTree;
+ }
+
+ /**
+ * Returns a simple command execution coordinator that executes all commands immediately, on the calling thread
+ *
+ * @param Command sender type
+ * @return New coordinator instance
+ */
+ public static Function, CommandExecutionCoordinator> simpleCoordinator() {
+ return SimpleCoordinator::new;
+ }
+
+ public abstract CompletableFuture coordinateExecution(@Nonnull final CommandContext commandContext,
+ @Nonnull final Queue input);
+
+ /**
+ * Get the command tree
+ *
+ * @return Command tree
+ */
+ @Nonnull protected CommandTree getCommandTree() {
+ return this.commandTree;
+ }
+
+
+ public static class SimpleCoordinator extends CommandExecutionCoordinator {
+
+ private SimpleCoordinator(@Nonnull final CommandTree commandTree) {
+ super(commandTree);
+ }
+
+ @Override
+ public CompletableFuture coordinateExecution(@Nonnull CommandContext commandContext, @Nonnull Queue input) {
+ final CompletableFuture 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;
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/intellectualsites/commands/execution/CommandExecutionHandler.java b/src/main/java/com/intellectualsites/commands/execution/CommandExecutionHandler.java
new file mode 100644
index 00000000..7b805ac2
--- /dev/null
+++ b/src/main/java/com/intellectualsites/commands/execution/CommandExecutionHandler.java
@@ -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 Command sender type
+ */
+@FunctionalInterface public interface CommandExecutionHandler {
+
+ /**
+ * Handle command execution
+ *
+ * @param commandContext Command context
+ */
+ void execute(@Nonnull final CommandContext commandContext);
+
+
+ class NullCommandExecutionHandler implements CommandExecutionHandler {
+
+ @Override
+ public void execute(@Nonnull final CommandContext commandContext) {
+
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/intellectualsites/commands/execution/CommandResult.java b/src/main/java/com/intellectualsites/commands/execution/CommandResult.java
new file mode 100644
index 00000000..7a09cd68
--- /dev/null
+++ b/src/main/java/com/intellectualsites/commands/execution/CommandResult.java
@@ -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 {
+}
diff --git a/src/main/java/com/intellectualsites/commands/parser/ComponentParser.java b/src/main/java/com/intellectualsites/commands/parser/ComponentParser.java
index 276da6f5..64d600cf 100644
--- a/src/main/java/com/intellectualsites/commands/parser/ComponentParser.java
+++ b/src/main/java/com/intellectualsites/commands/parser/ComponentParser.java
@@ -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 {
+@FunctionalInterface
+public interface ComponentParser {
/**
* 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 parse(@Nonnull CommandSender sender, @Nonnull Queue inputQueue);
+ @Nonnull
+ ComponentParseResult parse(@Nonnull CommandContext commandContext, @Nonnull Queue inputQueue);
}