Add command postprocessing
This commit is contained in:
parent
7501bd4743
commit
77cbf15faa
11 changed files with 331 additions and 8 deletions
|
|
@ -38,6 +38,9 @@ import com.intellectualsites.commands.execution.CommandExecutionCoordinator;
|
|||
import com.intellectualsites.commands.execution.CommandResult;
|
||||
import com.intellectualsites.commands.execution.CommandSuggestionProcessor;
|
||||
import com.intellectualsites.commands.execution.FilteringCommandSuggestionProcessor;
|
||||
import com.intellectualsites.commands.execution.postprocessor.AcceptingCommandPostprocessor;
|
||||
import com.intellectualsites.commands.execution.postprocessor.CommandPostprocessingContext;
|
||||
import com.intellectualsites.commands.execution.postprocessor.CommandPostprocessor;
|
||||
import com.intellectualsites.commands.execution.preprocessor.AcceptingCommandPreprocessor;
|
||||
import com.intellectualsites.commands.execution.preprocessor.CommandPreprocessingContext;
|
||||
import com.intellectualsites.commands.execution.preprocessor.CommandPreprocessor;
|
||||
|
|
@ -94,6 +97,8 @@ public abstract class CommandManager<C> {
|
|||
this.commandRegistrationHandler = commandRegistrationHandler;
|
||||
this.servicePipeline.registerServiceType(new TypeToken<CommandPreprocessor<C>>() {
|
||||
}, new AcceptingCommandPreprocessor<>());
|
||||
this.servicePipeline.registerServiceType(new TypeToken<CommandPostprocessor<C>>() {
|
||||
}, new AcceptingCommandPostprocessor<>());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -305,13 +310,26 @@ public abstract class CommandManager<C> {
|
|||
Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new command postprocessor. The order they are registered in is respected, and they
|
||||
* are called in LIFO order
|
||||
*
|
||||
* @param processor Processor to register
|
||||
* @see #preprocessContext(CommandContext, LinkedList) Preprocess a context
|
||||
*/
|
||||
public void registerCommandPostProcessor(@Nonnull final CommandPostprocessor<C> processor) {
|
||||
this.servicePipeline.registerServiceImplementation(new TypeToken<CommandPostprocessor<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}
|
||||
* @see #registerExceptionHandler(Class, BiConsumer) Register a command preprocessor
|
||||
* @see #registerCommandPreProcessor(CommandPreprocessor) Register a command preprocessor
|
||||
*/
|
||||
public State preprocessContext(@Nonnull final CommandContext<C> context, @Nonnull final LinkedList<String> inputQueue) {
|
||||
this.servicePipeline.pump(new CommandPreprocessingContext<>(context, inputQueue))
|
||||
|
|
@ -323,6 +341,24 @@ public abstract class CommandManager<C> {
|
|||
: State.ACCEPTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Postprocess a command context instance
|
||||
*
|
||||
* @param context Command context
|
||||
* @param command Command instance
|
||||
* @return {@link State#ACCEPTED} if the command should be parsed and executed, else {@link State#REJECTED}
|
||||
* @see #registerCommandPostProcessor(CommandPostprocessor) Register a command postprocessor
|
||||
*/
|
||||
public State postprocessContext(@Nonnull final CommandContext<C> context, @Nonnull final Command<C> command) {
|
||||
this.servicePipeline.pump(new CommandPostprocessingContext<>(context, command))
|
||||
.through(new TypeToken<CommandPostprocessor<C>>() {
|
||||
})
|
||||
.getResult();
|
||||
return context.<String>get(AcceptingCommandPostprocessor.PROCESSED_INDICATOR_KEY).orElse("").isEmpty()
|
||||
? State.REJECTED
|
||||
: State.ACCEPTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command suggestions processor instance currently used in this command manager
|
||||
*
|
||||
|
|
|
|||
|
|
@ -559,6 +559,16 @@ public final class CommandTree<C> {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command manager
|
||||
*
|
||||
* @return Command manager
|
||||
*/
|
||||
@Nonnull
|
||||
public CommandManager<C> getCommandManager() {
|
||||
return this.commandManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Very simple tree structure
|
||||
*
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@
|
|||
package com.intellectualsites.commands.execution;
|
||||
|
||||
import com.intellectualsites.commands.Command;
|
||||
import com.intellectualsites.commands.CommandManager;
|
||||
import com.intellectualsites.commands.CommandTree;
|
||||
import com.intellectualsites.commands.context.CommandContext;
|
||||
import com.intellectualsites.services.State;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
|
@ -33,6 +35,7 @@ import java.util.Optional;
|
|||
import java.util.Queue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
|
@ -43,6 +46,7 @@ import java.util.function.Supplier;
|
|||
*/
|
||||
public final class AsynchronousCommandExecutionCoordinator<C> extends CommandExecutionCoordinator<C> {
|
||||
|
||||
private final CommandManager<C> commandManager;
|
||||
private final Executor executor;
|
||||
private final boolean synchronizeParsing;
|
||||
|
||||
|
|
@ -52,6 +56,7 @@ public final class AsynchronousCommandExecutionCoordinator<C> extends CommandExe
|
|||
super(commandTree);
|
||||
this.executor = executor;
|
||||
this.synchronizeParsing = synchronizeParsing;
|
||||
this.commandManager = commandTree.getCommandManager();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,17 +74,21 @@ public final class AsynchronousCommandExecutionCoordinator<C> extends CommandExe
|
|||
public CompletableFuture<CommandResult<C>> coordinateExecution(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Queue<String> input) {
|
||||
|
||||
final Consumer<Command<C>> commandConsumer = command -> {
|
||||
if (this.commandManager.postprocessContext(commandContext, command) == State.ACCEPTED) {
|
||||
command.getCommandExecutionHandler().execute(commandContext);
|
||||
}
|
||||
};
|
||||
final Supplier<CommandResult<C>> supplier;
|
||||
if (this.synchronizeParsing) {
|
||||
final Optional<Command<C>> commandOptional = this.getCommandTree().parse(commandContext, input);
|
||||
supplier = () -> {
|
||||
commandOptional.ifPresent(command -> command.getCommandExecutionHandler().execute(commandContext));
|
||||
commandOptional.ifPresent(commandConsumer);
|
||||
return new CommandResult<>(commandContext);
|
||||
};
|
||||
} else {
|
||||
supplier = () -> {
|
||||
this.getCommandTree().parse(commandContext, input).ifPresent(
|
||||
command -> command.getCommandExecutionHandler().execute(commandContext));
|
||||
this.getCommandTree().parse(commandContext, input).ifPresent(commandConsumer);
|
||||
return new CommandResult<>(commandContext);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ package com.intellectualsites.commands.execution;
|
|||
|
||||
import com.intellectualsites.commands.CommandTree;
|
||||
import com.intellectualsites.commands.context.CommandContext;
|
||||
import com.intellectualsites.services.State;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Queue;
|
||||
|
|
@ -101,8 +102,11 @@ public abstract class CommandExecutionCoordinator<C> {
|
|||
@Nonnull final Queue<String> input) {
|
||||
final CompletableFuture<CommandResult<C>> completableFuture = new CompletableFuture<>();
|
||||
try {
|
||||
this.getCommandTree().parse(commandContext, input).ifPresent(
|
||||
command -> command.getCommandExecutionHandler().execute(commandContext));
|
||||
this.getCommandTree().parse(commandContext, input).ifPresent(command -> {
|
||||
if (this.getCommandTree().getCommandManager().postprocessContext(commandContext, command) == State.ACCEPTED) {
|
||||
command.getCommandExecutionHandler().execute(commandContext);
|
||||
}
|
||||
});
|
||||
completableFuture.complete(new CommandResult<>(commandContext));
|
||||
} catch (final Exception e) {
|
||||
completableFuture.completeExceptionally(e);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// 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.postprocessor;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* {@link CommandPostprocessor} that does nothing besides indicating that the context
|
||||
* has been properly processed
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
public final class AcceptingCommandPostprocessor<C> implements CommandPostprocessor<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_POST_PROCESSED__";
|
||||
|
||||
@Override
|
||||
public void accept(@Nonnull final CommandPostprocessingContext<C> context) {
|
||||
context.getCommandContext().store(PROCESSED_INDICATOR_KEY, "true");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// 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.postprocessor;
|
||||
|
||||
import com.intellectualsites.commands.Command;
|
||||
import com.intellectualsites.commands.context.CommandContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Context for {@link CommandPostprocessor command postprocessors}
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
public final class CommandPostprocessingContext<C> {
|
||||
|
||||
private final CommandContext<C> commandContext;
|
||||
private final Command<C> command;
|
||||
|
||||
/**
|
||||
* Construct a new command postprocessing context
|
||||
*
|
||||
* @param commandContext Command context
|
||||
* @param command Command instance
|
||||
*/
|
||||
public CommandPostprocessingContext(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Command<C> command) {
|
||||
this.commandContext = commandContext;
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command context
|
||||
*
|
||||
* @return Command context
|
||||
*/
|
||||
@Nonnull
|
||||
public CommandContext<C> getCommandContext() {
|
||||
return this.commandContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command instance
|
||||
*
|
||||
* @return Command instance
|
||||
*/
|
||||
@Nonnull
|
||||
public Command<C> getCommand() {
|
||||
return this.command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final CommandPostprocessingContext<?> that = (CommandPostprocessingContext<?>) o;
|
||||
return Objects.equals(getCommandContext(), that.getCommandContext())
|
||||
&& Objects.equals(this.getCommand(), that.getCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.getCommandContext(), this.getCommand());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// 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.postprocessor;
|
||||
|
||||
import com.intellectualsites.services.types.ConsumerService;
|
||||
|
||||
/**
|
||||
* Command postprocessor that acts on commands before execution
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
public interface CommandPostprocessor<C> extends ConsumerService<CommandPostprocessingContext<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 postprocessing system
|
||||
*/
|
||||
package com.intellectualsites.commands.execution.postprocessor;
|
||||
|
|
@ -33,7 +33,6 @@ import com.intellectualsites.services.types.ConsumerService;
|
|||
* {@link ConsumerService#interrupt()}
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public interface CommandPreprocessor<C> extends ConsumerService<CommandPreprocessingContext<C>> {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,6 @@
|
|||
//
|
||||
|
||||
/**
|
||||
* Command preprocessor system
|
||||
* Command preprocessing system
|
||||
*/
|
||||
package com.intellectualsites.commands.execution.preprocessor;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// 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.execution.postprocessor.CommandPostprocessingContext;
|
||||
import com.intellectualsites.commands.execution.postprocessor.CommandPostprocessor;
|
||||
import com.intellectualsites.commands.meta.SimpleCommandMeta;
|
||||
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 CommandPostProcessorTest {
|
||||
|
||||
private static CommandManager<TestCommandSender> manager;
|
||||
private static final boolean[] state = new boolean[] {false};
|
||||
|
||||
@BeforeAll
|
||||
static void newTree() {
|
||||
manager = new TestCommandManager();
|
||||
manager.command(manager.commandBuilder("test", SimpleCommandMeta.empty())
|
||||
.handler(c -> state[0] = true)
|
||||
.build());
|
||||
manager.registerCommandPostProcessor(new SamplePostprocessor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPreprocessing() {
|
||||
manager.executeCommand(new TestCommandSender(),"test").join();
|
||||
Assertions.assertEquals(false, state[0]);
|
||||
}
|
||||
|
||||
static final class SamplePostprocessor implements CommandPostprocessor<TestCommandSender> {
|
||||
|
||||
@Override
|
||||
public void accept(@Nonnull final CommandPostprocessingContext<TestCommandSender> context) {
|
||||
ConsumerService.interrupt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue