Merge pull request #3
* Start implementing command preprocessors * Call the preprocessing step and also add a command suggestions processor * Merge branch 'master' into preprocessing * Improve command pre-processing
This commit is contained in:
parent
75f65c2da2
commit
e01fd6af37
13 changed files with 534 additions and 20 deletions
|
|
@ -32,6 +32,7 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<module name="Checker">
|
<module name="Checker">
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
If you set the basedir property below, then all reported file
|
If you set the basedir property below, then all reported file
|
||||||
names will be relative to the specified directory. See
|
names will be relative to the specified directory. See
|
||||||
|
|
@ -41,7 +42,7 @@
|
||||||
-->
|
-->
|
||||||
<property name="severity" value="error"/>
|
<property name="severity" value="error"/>
|
||||||
|
|
||||||
<property name="fileExtensions" value="java, properties, xml"/>
|
<property name="fileExtensions" value="java, properties"/>
|
||||||
|
|
||||||
<!-- Excludes all 'module-info.java' files -->
|
<!-- Excludes all 'module-info.java' files -->
|
||||||
<!-- See https://checkstyle.org/config_filefilters.html -->
|
<!-- See https://checkstyle.org/config_filefilters.html -->
|
||||||
|
|
|
||||||
|
|
@ -23,20 +23,29 @@
|
||||||
//
|
//
|
||||||
package com.intellectualsites.commands;
|
package com.intellectualsites.commands;
|
||||||
|
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
import com.intellectualsites.commands.components.CommandSyntaxFormatter;
|
import com.intellectualsites.commands.components.CommandSyntaxFormatter;
|
||||||
import com.intellectualsites.commands.components.StandardCommandSyntaxFormatter;
|
import com.intellectualsites.commands.components.StandardCommandSyntaxFormatter;
|
||||||
|
import com.intellectualsites.commands.context.CommandContext;
|
||||||
import com.intellectualsites.commands.context.CommandContextFactory;
|
import com.intellectualsites.commands.context.CommandContextFactory;
|
||||||
import com.intellectualsites.commands.context.StandardCommandContextFactory;
|
import com.intellectualsites.commands.context.StandardCommandContextFactory;
|
||||||
import com.intellectualsites.commands.execution.CommandExecutionCoordinator;
|
import com.intellectualsites.commands.execution.CommandExecutionCoordinator;
|
||||||
import com.intellectualsites.commands.execution.CommandResult;
|
import com.intellectualsites.commands.execution.CommandResult;
|
||||||
|
import com.intellectualsites.commands.execution.CommandSuggestionProcessor;
|
||||||
|
import com.intellectualsites.commands.execution.FilteringCommandSuggestionProcessor;
|
||||||
|
import com.intellectualsites.commands.execution.preprocessor.CommandPreprocessingContext;
|
||||||
|
import com.intellectualsites.commands.execution.preprocessor.CommandPreprocessor;
|
||||||
|
import com.intellectualsites.commands.execution.preprocessor.AcceptingCommandPreprocessor;
|
||||||
import com.intellectualsites.commands.internal.CommandRegistrationHandler;
|
import com.intellectualsites.commands.internal.CommandRegistrationHandler;
|
||||||
import com.intellectualsites.commands.meta.CommandMeta;
|
import com.intellectualsites.commands.meta.CommandMeta;
|
||||||
import com.intellectualsites.commands.sender.CommandSender;
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
|
import com.intellectualsites.services.ServicePipeline;
|
||||||
|
import com.intellectualsites.services.State;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
@ -51,12 +60,14 @@ import java.util.function.Function;
|
||||||
public abstract class CommandManager<C extends CommandSender, M extends CommandMeta> {
|
public abstract class CommandManager<C extends CommandSender, M extends CommandMeta> {
|
||||||
|
|
||||||
private final CommandContextFactory<C> commandContextFactory = new StandardCommandContextFactory<>();
|
private final CommandContextFactory<C> commandContextFactory = new StandardCommandContextFactory<>();
|
||||||
|
private final ServicePipeline servicePipeline = ServicePipeline.builder().build();
|
||||||
|
|
||||||
private final CommandExecutionCoordinator<C, M> commandExecutionCoordinator;
|
private final CommandExecutionCoordinator<C, M> commandExecutionCoordinator;
|
||||||
private final CommandRegistrationHandler<M> commandRegistrationHandler;
|
private final CommandRegistrationHandler<M> commandRegistrationHandler;
|
||||||
private final CommandTree<C, M> commandTree;
|
private final CommandTree<C, M> commandTree;
|
||||||
|
|
||||||
private CommandSyntaxFormatter<C> commandSyntaxFormatter = new StandardCommandSyntaxFormatter<>();
|
private CommandSyntaxFormatter<C> commandSyntaxFormatter = new StandardCommandSyntaxFormatter<>();
|
||||||
|
private CommandSuggestionProcessor<C> commandSuggestionProcessor = new FilteringCommandSuggestionProcessor<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command manager instance
|
* Create a new command manager instance
|
||||||
|
|
@ -70,6 +81,8 @@ public abstract class CommandManager<C extends CommandSender, M extends CommandM
|
||||||
this.commandTree = CommandTree.newTree(this, commandRegistrationHandler);
|
this.commandTree = CommandTree.newTree(this, commandRegistrationHandler);
|
||||||
this.commandExecutionCoordinator = commandExecutionCoordinator.apply(commandTree);
|
this.commandExecutionCoordinator = commandExecutionCoordinator.apply(commandTree);
|
||||||
this.commandRegistrationHandler = commandRegistrationHandler;
|
this.commandRegistrationHandler = commandRegistrationHandler;
|
||||||
|
this.servicePipeline.registerServiceType(new TypeToken<CommandPreprocessor<C>>() {
|
||||||
|
}, new AcceptingCommandPreprocessor<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -80,9 +93,20 @@ public abstract class CommandManager<C extends CommandSender, M extends CommandM
|
||||||
* @return Command result
|
* @return Command result
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public CompletableFuture<CommandResult> executeCommand(@Nonnull final C commandSender, @Nonnull final String input) {
|
public CompletableFuture<CommandResult<C>> executeCommand(@Nonnull final C commandSender, @Nonnull final String input) {
|
||||||
return this.commandExecutionCoordinator.coordinateExecution(this.commandContextFactory.create(commandSender),
|
final CommandContext<C> context = this.commandContextFactory.create(commandSender);
|
||||||
tokenize(input));
|
final LinkedList<String> inputQueue = this.tokenize(input);
|
||||||
|
try {
|
||||||
|
if (this.preprocessContext(context, inputQueue) == State.ACCEPTED) {
|
||||||
|
return this.commandExecutionCoordinator.coordinateExecution(context, inputQueue);
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
final CompletableFuture<CommandResult<C>> future = new CompletableFuture<>();
|
||||||
|
future.completeExceptionally(e);
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
/* Wasn't allowed to execute the command */
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -95,13 +119,21 @@ public abstract class CommandManager<C extends CommandSender, M extends CommandM
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public List<String> suggest(@Nonnull final C commandSender, @Nonnull final String input) {
|
public List<String> suggest(@Nonnull final C commandSender, @Nonnull final String input) {
|
||||||
return this.commandTree.getSuggestions(this.commandContextFactory.create(commandSender), tokenize(input));
|
final CommandContext<C> context = this.commandContextFactory.create(commandSender);
|
||||||
|
final LinkedList<String> inputQueue = this.tokenize(input);
|
||||||
|
if (this.preprocessContext(context, inputQueue) == State.ACCEPTED) {
|
||||||
|
return this.commandSuggestionProcessor.apply(new CommandPreprocessingContext<>(context, inputQueue),
|
||||||
|
this.commandTree.getSuggestions(
|
||||||
|
context, inputQueue));
|
||||||
|
} else {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private Queue<String> tokenize(@Nonnull final String input) {
|
private LinkedList<String> tokenize(@Nonnull final String input) {
|
||||||
final StringTokenizer stringTokenizer = new StringTokenizer(input, " ");
|
final StringTokenizer stringTokenizer = new StringTokenizer(input, " ");
|
||||||
final Queue<String> tokens = new LinkedList<>();
|
final LinkedList<String> tokens = new LinkedList<>();
|
||||||
while (stringTokenizer.hasMoreElements()) {
|
while (stringTokenizer.hasMoreElements()) {
|
||||||
tokens.add(stringTokenizer.nextToken());
|
tokens.add(stringTokenizer.nextToken());
|
||||||
}
|
}
|
||||||
|
|
@ -194,4 +226,52 @@ public abstract class CommandManager<C extends CommandSender, M extends CommandM
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public abstract M createDefaultCommandMeta();
|
public abstract M createDefaultCommandMeta();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new command preprocessor. The order they are registered in is respected, and they
|
||||||
|
* are called in LIFO order
|
||||||
|
*
|
||||||
|
* @param processor Processor to register
|
||||||
|
*/
|
||||||
|
public void registerCommandPreProcessor(@Nonnull final CommandPreprocessor<C> processor) {
|
||||||
|
this.servicePipeline.registerServiceImplementation(new TypeToken<CommandPreprocessor<C>>() {
|
||||||
|
}, processor,
|
||||||
|
Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preprocess a command context instance
|
||||||
|
*
|
||||||
|
* @param context Command context
|
||||||
|
* @param inputQueue Command input as supplied by sender
|
||||||
|
* @return {@link State#ACCEPTED} if the command should be parsed and executed, else {@link State#REJECTED}
|
||||||
|
*/
|
||||||
|
public State preprocessContext(@Nonnull final CommandContext<C> context, @Nonnull final LinkedList<String> inputQueue) {
|
||||||
|
this.servicePipeline.pump(new CommandPreprocessingContext<>(context, inputQueue))
|
||||||
|
.through(new TypeToken<CommandPreprocessor<C>>() {
|
||||||
|
})
|
||||||
|
.getResult();
|
||||||
|
return context.<String>get(AcceptingCommandPreprocessor.PROCESSED_INDICATOR_KEY).orElse("").isEmpty()
|
||||||
|
? State.REJECTED
|
||||||
|
: State.ACCEPTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the command suggestions processor instance currently used in this command manager
|
||||||
|
*
|
||||||
|
* @return Command suggestions processor
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public CommandSuggestionProcessor<C> getCommandSuggestionProcessor() {
|
||||||
|
return this.commandSuggestionProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the command suggestions processor for this command manager
|
||||||
|
*
|
||||||
|
* @param commandSuggestionProcessor New command suggestions processor
|
||||||
|
*/
|
||||||
|
public void setCommandSuggestionProcessor(@Nonnull final CommandSuggestionProcessor<C> commandSuggestionProcessor) {
|
||||||
|
this.commandSuggestionProcessor = commandSuggestionProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@
|
||||||
//
|
//
|
||||||
package com.intellectualsites.commands.components;
|
package com.intellectualsites.commands.components;
|
||||||
|
|
||||||
import com.intellectualsites.commands.context.CommandContext;
|
|
||||||
import com.intellectualsites.commands.components.parser.ComponentParseResult;
|
import com.intellectualsites.commands.components.parser.ComponentParseResult;
|
||||||
import com.intellectualsites.commands.components.parser.ComponentParser;
|
import com.intellectualsites.commands.components.parser.ComponentParser;
|
||||||
|
import com.intellectualsites.commands.context.CommandContext;
|
||||||
import com.intellectualsites.commands.sender.CommandSender;
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
@ -33,7 +33,6 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -109,11 +108,8 @@ public final class StaticComponent<C extends CommandSender> extends CommandCompo
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public List<String> suggestions(@Nonnull final CommandContext<C> commandContext, @Nonnull final String input) {
|
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.singletonList(this.name);
|
||||||
}
|
}
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import java.util.List;
|
||||||
* a command that doesn't exist
|
* a command that doesn't exist
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class NoSuchCommandException extends CommandParseException {
|
public final class NoSuchCommandException extends CommandParseException {
|
||||||
|
|
||||||
private final String suppliedCommand;
|
private final String suppliedCommand;
|
||||||
|
|
||||||
|
|
@ -52,6 +52,19 @@ public class NoSuchCommandException extends CommandParseException {
|
||||||
this.suppliedCommand = command;
|
this.suppliedCommand = command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
for (final CommandComponent<?, ?> commandComponent : this.getCurrentChain()) {
|
||||||
|
if (commandComponent == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
builder.append(" ").append(commandComponent.getName());
|
||||||
|
}
|
||||||
|
return String.format("Unrecognized command input '%s' following chain%s", this.suppliedCommand, builder.toString());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the supplied command
|
* Get the supplied command
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ public abstract class CommandExecutionCoordinator<C extends CommandSender, M ext
|
||||||
* @param input Command input
|
* @param input Command input
|
||||||
* @return Future that completes with the result
|
* @return Future that completes with the result
|
||||||
*/
|
*/
|
||||||
public abstract CompletableFuture<CommandResult> coordinateExecution(@Nonnull CommandContext<C> commandContext,
|
public abstract CompletableFuture<CommandResult<C>> coordinateExecution(@Nonnull CommandContext<C> commandContext,
|
||||||
@Nonnull Queue<String> input);
|
@Nonnull Queue<String> input);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,13 +102,13 @@ public abstract class CommandExecutionCoordinator<C extends CommandSender, M ext
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<CommandResult> coordinateExecution(@Nonnull final CommandContext<C> commandContext,
|
public CompletableFuture<CommandResult<C>> coordinateExecution(@Nonnull final CommandContext<C> commandContext,
|
||||||
@Nonnull final Queue<String> input) {
|
@Nonnull final Queue<String> input) {
|
||||||
final CompletableFuture<CommandResult> completableFuture = new CompletableFuture<>();
|
final CompletableFuture<CommandResult<C>> completableFuture = new CompletableFuture<>();
|
||||||
try {
|
try {
|
||||||
this.getCommandTree().parse(commandContext, input).ifPresent(
|
this.getCommandTree().parse(commandContext, input).ifPresent(
|
||||||
command -> command.getCommandExecutionHandler().execute(commandContext));
|
command -> command.getCommandExecutionHandler().execute(commandContext));
|
||||||
completableFuture.complete(new CommandResult());
|
completableFuture.complete(new CommandResult<>(commandContext));
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
completableFuture.completeExceptionally(e);
|
completableFuture.completeExceptionally(e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,36 @@
|
||||||
//
|
//
|
||||||
package com.intellectualsites.commands.execution;
|
package com.intellectualsites.commands.execution;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.context.CommandContext;
|
||||||
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The result of a command execution
|
* The result of a command execution
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
*/
|
*/
|
||||||
public class CommandResult {
|
public class CommandResult<C extends CommandSender> {
|
||||||
|
|
||||||
|
private final CommandContext<C> commandContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new command result instance
|
||||||
|
*
|
||||||
|
* @param context Command context
|
||||||
|
*/
|
||||||
|
public CommandResult(@Nonnull final CommandContext<C> context) {
|
||||||
|
this.commandContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the command context
|
||||||
|
*
|
||||||
|
* @return Command context
|
||||||
|
*/
|
||||||
|
@Nonnull public CommandContext<C> getCommandContext() {
|
||||||
|
return this.commandContext;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package com.intellectualsites.commands.execution;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.execution.preprocessor.CommandPreprocessingContext;
|
||||||
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processor that formats command suggestions
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public interface CommandSuggestionProcessor<C extends CommandSender> extends
|
||||||
|
BiFunction<CommandPreprocessingContext<C>, List<String>, List<String>> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package com.intellectualsites.commands.execution;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.execution.preprocessor.CommandPreprocessingContext;
|
||||||
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command suggestions processor that checks the input queue head and filters based on that
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public final class FilteringCommandSuggestionProcessor<C extends CommandSender> implements CommandSuggestionProcessor<C> {
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public List<String> apply(@Nonnull final CommandPreprocessingContext<C> context, @Nonnull final List<String> strings) {
|
||||||
|
final String input;
|
||||||
|
if (context.getInputQueue().isEmpty()) {
|
||||||
|
input = "";
|
||||||
|
} else {
|
||||||
|
input = context.getInputQueue().peek();
|
||||||
|
}
|
||||||
|
final List<String> suggestions = new LinkedList<>();
|
||||||
|
for (final String suggestion : strings) {
|
||||||
|
if (suggestion.startsWith(input)) {
|
||||||
|
suggestions.add(suggestion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package com.intellectualsites.commands.execution.preprocessor;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link CommandPreprocessor} that does nothing besides indicating that the context
|
||||||
|
* has been properly processed
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public final class AcceptingCommandPreprocessor<C extends CommandSender> implements CommandPreprocessor<C> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key used to access the context meta that indicates that the context has been fully processed
|
||||||
|
*/
|
||||||
|
public static final String PROCESSED_INDICATOR_KEY = "__COMMAND_PRE_PROCESSED__";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(@Nonnull final CommandPreprocessingContext<C> context) {
|
||||||
|
context.getCommandContext().store(PROCESSED_INDICATOR_KEY, "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package com.intellectualsites.commands.execution.preprocessor;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.context.CommandContext;
|
||||||
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context for {@link CommandPreprocessor command preprocessors}
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public final class CommandPreprocessingContext<C extends CommandSender> {
|
||||||
|
|
||||||
|
private final CommandContext<C> commandContext;
|
||||||
|
private final LinkedList<String> inputQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new command preprocessing context
|
||||||
|
*
|
||||||
|
* @param commandContext Command context
|
||||||
|
* @param inputQueue Command input as supplied by sender
|
||||||
|
*/
|
||||||
|
public CommandPreprocessingContext(@Nonnull final CommandContext<C> commandContext,
|
||||||
|
@Nonnull final LinkedList<String> inputQueue) {
|
||||||
|
this.commandContext = commandContext;
|
||||||
|
this.inputQueue = inputQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the command context
|
||||||
|
*
|
||||||
|
* @return Command context
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public CommandContext<C> getCommandContext() {
|
||||||
|
return this.commandContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the original input queue. All changes will persist and will be
|
||||||
|
* used during parsing
|
||||||
|
*
|
||||||
|
* @return Input queue
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public LinkedList<String> getInputQueue() {
|
||||||
|
return this.inputQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final CommandPreprocessingContext<?> that = (CommandPreprocessingContext<?>) o;
|
||||||
|
return Objects.equals(getCommandContext(), that.getCommandContext())
|
||||||
|
&& Objects.equals(getInputQueue(), that.getInputQueue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(getCommandContext(), getInputQueue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package com.intellectualsites.commands.execution.preprocessor;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
|
import com.intellectualsites.services.types.ConsumerService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command preprocessor that gets to act on command input
|
||||||
|
* before it's sent to the command parser.
|
||||||
|
* <p>
|
||||||
|
* Command preprocessors may filter out invalid commands by using
|
||||||
|
* {@link ConsumerService#interrupt()}
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public interface CommandPreprocessor<C extends CommandSender> extends ConsumerService<CommandPreprocessingContext<C>> {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command preprocessor system
|
||||||
|
*/
|
||||||
|
package com.intellectualsites.commands.execution.preprocessor;
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package com.intellectualsites.commands;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.components.standard.EnumComponent;
|
||||||
|
import com.intellectualsites.commands.execution.preprocessor.CommandPreprocessingContext;
|
||||||
|
import com.intellectualsites.commands.execution.preprocessor.CommandPreprocessor;
|
||||||
|
import com.intellectualsites.commands.meta.SimpleCommandMeta;
|
||||||
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
|
import com.intellectualsites.services.types.ConsumerService;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
public class CommandPreProcessorTest {
|
||||||
|
|
||||||
|
private static CommandManager<CommandSender, SimpleCommandMeta> manager;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void newTree() {
|
||||||
|
manager = new TestCommandManager();
|
||||||
|
manager.registerCommand(manager.commandBuilder("test", SimpleCommandMeta.empty())
|
||||||
|
.withComponent(EnumComponent.required(SampleEnum.class, "enum"))
|
||||||
|
.withHandler(
|
||||||
|
commandContext -> System.out.printf("enum = %s | integer = %d\n",
|
||||||
|
commandContext.<SampleEnum>get(
|
||||||
|
"enum").orElse(
|
||||||
|
SampleEnum.VALUE1),
|
||||||
|
commandContext.<Integer>get(
|
||||||
|
"int").orElseThrow(
|
||||||
|
() -> new NullPointerException(
|
||||||
|
"int"))))
|
||||||
|
.build());
|
||||||
|
manager.registerCommandPreProcessor(new SamplePreprocessor());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPreprocessing() {
|
||||||
|
Assertions.assertEquals(10, manager.executeCommand(new TestCommandSender(), "10 test value1")
|
||||||
|
.join().getCommandContext().<Integer>get("int").orElse(0));
|
||||||
|
manager.executeCommand(new TestCommandSender(), "aa test value1").join();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum SampleEnum {
|
||||||
|
VALUE1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static final class SamplePreprocessor implements CommandPreprocessor<CommandSender> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(@Nonnull final CommandPreprocessingContext<CommandSender> context) {
|
||||||
|
try {
|
||||||
|
final int num = Integer.parseInt(context.getInputQueue().removeFirst());
|
||||||
|
context.getCommandContext().store("int", num);
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
/* Will prevent execution */
|
||||||
|
ConsumerService.interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue