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:
Alexander Söderberg 2020-09-12 20:29:58 +02:00 committed by GitHub
parent 75f65c2da2
commit e01fd6af37
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 534 additions and 20 deletions

View file

@ -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 -->

View file

@ -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;
}
} }

View file

@ -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();
}
} }

View file

@ -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
* *

View file

@ -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);
} }

View file

@ -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;
}
} }

View file

@ -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>> {
}

View file

@ -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;
}
}

View file

@ -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");
}
}

View file

@ -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());
}
}

View file

@ -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>> {
}

View file

@ -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;

View file

@ -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();
}
}
}
}