🧹 Clean up CommandManager
This commit is contained in:
parent
4368305bc9
commit
16623969ad
6 changed files with 403 additions and 56 deletions
|
|
@ -24,7 +24,9 @@
|
||||||
package cloud.commandframework;
|
package cloud.commandframework;
|
||||||
|
|
||||||
import cloud.commandframework.arguments.CommandArgument;
|
import cloud.commandframework.arguments.CommandArgument;
|
||||||
|
import cloud.commandframework.arguments.CommandSuggestionEngine;
|
||||||
import cloud.commandframework.arguments.CommandSyntaxFormatter;
|
import cloud.commandframework.arguments.CommandSyntaxFormatter;
|
||||||
|
import cloud.commandframework.arguments.DelegatingCommandSuggestionEngineFactory;
|
||||||
import cloud.commandframework.arguments.StandardCommandSyntaxFormatter;
|
import cloud.commandframework.arguments.StandardCommandSyntaxFormatter;
|
||||||
import cloud.commandframework.arguments.flags.CommandFlag;
|
import cloud.commandframework.arguments.flags.CommandFlag;
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
import cloud.commandframework.arguments.parser.ArgumentParser;
|
||||||
|
|
@ -44,6 +46,7 @@ import cloud.commandframework.execution.postprocessor.CommandPostprocessor;
|
||||||
import cloud.commandframework.execution.preprocessor.AcceptingCommandPreprocessor;
|
import cloud.commandframework.execution.preprocessor.AcceptingCommandPreprocessor;
|
||||||
import cloud.commandframework.execution.preprocessor.CommandPreprocessingContext;
|
import cloud.commandframework.execution.preprocessor.CommandPreprocessingContext;
|
||||||
import cloud.commandframework.execution.preprocessor.CommandPreprocessor;
|
import cloud.commandframework.execution.preprocessor.CommandPreprocessor;
|
||||||
|
import cloud.commandframework.internal.CommandInputTokenizer;
|
||||||
import cloud.commandframework.internal.CommandRegistrationHandler;
|
import cloud.commandframework.internal.CommandRegistrationHandler;
|
||||||
import cloud.commandframework.meta.CommandMeta;
|
import cloud.commandframework.meta.CommandMeta;
|
||||||
import cloud.commandframework.permission.CommandPermission;
|
import cloud.commandframework.permission.CommandPermission;
|
||||||
|
|
@ -63,7 +66,6 @@ import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.StringTokenizer;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
@ -75,8 +77,6 @@ import java.util.function.Function;
|
||||||
*/
|
*/
|
||||||
public abstract class CommandManager<C> {
|
public abstract class CommandManager<C> {
|
||||||
|
|
||||||
private static final List<String> SINGLE_EMPTY_SUGGESTION = Collections.unmodifiableList(Collections.singletonList(""));
|
|
||||||
|
|
||||||
private final Map<Class<? extends Exception>, BiConsumer<C, ? extends Exception>> exceptionHandlers = new HashMap<>();
|
private final Map<Class<? extends Exception>, BiConsumer<C, ? extends Exception>> exceptionHandlers = new HashMap<>();
|
||||||
private final EnumSet<ManagerSettings> managerSettings = EnumSet.of(
|
private final EnumSet<ManagerSettings> managerSettings = EnumSet.of(
|
||||||
ManagerSettings.ENFORCE_INTERMEDIARY_PERMISSIONS);
|
ManagerSettings.ENFORCE_INTERMEDIARY_PERMISSIONS);
|
||||||
|
|
@ -87,6 +87,7 @@ public abstract class CommandManager<C> {
|
||||||
private final Collection<Command<C>> commands = new LinkedList<>();
|
private final Collection<Command<C>> commands = new LinkedList<>();
|
||||||
private final CommandExecutionCoordinator<C> commandExecutionCoordinator;
|
private final CommandExecutionCoordinator<C> commandExecutionCoordinator;
|
||||||
private final CommandTree<C> commandTree;
|
private final CommandTree<C> commandTree;
|
||||||
|
private final CommandSuggestionEngine<C> commandSuggestionEngine;
|
||||||
|
|
||||||
private CommandSyntaxFormatter<C> commandSyntaxFormatter = new StandardCommandSyntaxFormatter<>();
|
private CommandSyntaxFormatter<C> commandSyntaxFormatter = new StandardCommandSyntaxFormatter<>();
|
||||||
private CommandSuggestionProcessor<C> commandSuggestionProcessor = new FilteringCommandSuggestionProcessor<>();
|
private CommandSuggestionProcessor<C> commandSuggestionProcessor = new FilteringCommandSuggestionProcessor<>();
|
||||||
|
|
@ -105,30 +106,13 @@ public abstract class CommandManager<C> {
|
||||||
this.commandTree = CommandTree.newTree(this);
|
this.commandTree = CommandTree.newTree(this);
|
||||||
this.commandExecutionCoordinator = commandExecutionCoordinator.apply(commandTree);
|
this.commandExecutionCoordinator = commandExecutionCoordinator.apply(commandTree);
|
||||||
this.commandRegistrationHandler = commandRegistrationHandler;
|
this.commandRegistrationHandler = commandRegistrationHandler;
|
||||||
|
this.commandSuggestionEngine = new DelegatingCommandSuggestionEngineFactory<>(this).create();
|
||||||
this.servicePipeline.registerServiceType(new TypeToken<CommandPreprocessor<C>>() {
|
this.servicePipeline.registerServiceType(new TypeToken<CommandPreprocessor<C>>() {
|
||||||
}, new AcceptingCommandPreprocessor<>());
|
}, new AcceptingCommandPreprocessor<>());
|
||||||
this.servicePipeline.registerServiceType(new TypeToken<CommandPostprocessor<C>>() {
|
this.servicePipeline.registerServiceType(new TypeToken<CommandPostprocessor<C>>() {
|
||||||
}, new AcceptingCommandPostprocessor<>());
|
}, new AcceptingCommandPostprocessor<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tokenize an input string
|
|
||||||
*
|
|
||||||
* @param input Input string
|
|
||||||
* @return List of tokens
|
|
||||||
*/
|
|
||||||
public static @NonNull LinkedList<@NonNull String> tokenize(final @NonNull String input) {
|
|
||||||
final StringTokenizer stringTokenizer = new StringTokenizer(input, " ");
|
|
||||||
final LinkedList<String> tokens = new LinkedList<>();
|
|
||||||
while (stringTokenizer.hasMoreElements()) {
|
|
||||||
tokens.add(stringTokenizer.nextToken());
|
|
||||||
}
|
|
||||||
if (input.endsWith(" ")) {
|
|
||||||
tokens.add("");
|
|
||||||
}
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a command and get a future that completes with the result
|
* Execute a command and get a future that completes with the result
|
||||||
*
|
*
|
||||||
|
|
@ -141,7 +125,7 @@ public abstract class CommandManager<C> {
|
||||||
final @NonNull String input
|
final @NonNull String input
|
||||||
) {
|
) {
|
||||||
final CommandContext<C> context = this.commandContextFactory.create(false, commandSender);
|
final CommandContext<C> context = this.commandContextFactory.create(false, commandSender);
|
||||||
final LinkedList<String> inputQueue = tokenize(input);
|
final LinkedList<String> inputQueue = new CommandInputTokenizer(input).tokenize();
|
||||||
try {
|
try {
|
||||||
if (this.preprocessContext(context, inputQueue) == State.ACCEPTED) {
|
if (this.preprocessContext(context, inputQueue) == State.ACCEPTED) {
|
||||||
return this.commandExecutionCoordinator.coordinateExecution(context, inputQueue);
|
return this.commandExecutionCoordinator.coordinateExecution(context, inputQueue);
|
||||||
|
|
@ -167,25 +151,11 @@ public abstract class CommandManager<C> {
|
||||||
final @NonNull C commandSender,
|
final @NonNull C commandSender,
|
||||||
final @NonNull String input
|
final @NonNull String input
|
||||||
) {
|
) {
|
||||||
final CommandContext<C> context = this.commandContextFactory.create(true, commandSender);
|
final CommandContext<C> context = this.commandContextFactory.create(
|
||||||
final LinkedList<String> inputQueue = tokenize(input);
|
true,
|
||||||
|
commandSender
|
||||||
final List<String> suggestions;
|
);
|
||||||
if (this.preprocessContext(context, inputQueue) == State.ACCEPTED) {
|
return this.commandSuggestionEngine.getSuggestions(context, input);
|
||||||
suggestions = this.commandSuggestionProcessor.apply(
|
|
||||||
new CommandPreprocessingContext<>(context, inputQueue),
|
|
||||||
this.commandTree.getSuggestions(
|
|
||||||
context, inputQueue)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
suggestions = Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.getSetting(ManagerSettings.FORCE_SUGGESTION) && suggestions.isEmpty()) {
|
|
||||||
return SINGLE_EMPTY_SUGGESTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
return suggestions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -281,7 +251,14 @@ public abstract class CommandManager<C> {
|
||||||
public abstract boolean hasPermission(@NonNull C sender, @NonNull String permission);
|
public abstract boolean hasPermission(@NonNull C sender, @NonNull String permission);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command builder
|
* Create a new command builder. This will also register the creating manager in the command
|
||||||
|
* builder using {@link Command.Builder#manager(CommandManager)}, so that the command
|
||||||
|
* builder is associated with the creating manager. This allows for parser inference based on
|
||||||
|
* the type, with the help of the {@link ParserRegistry parser registry}
|
||||||
|
* <p>
|
||||||
|
* This method will not register the command in the manager. To do that, {@link #command(Command.Builder)}
|
||||||
|
* or {@link #command(Command)} has to be invoked with either the {@link Command.Builder} instance, or the constructed
|
||||||
|
* {@link Command command} instance
|
||||||
*
|
*
|
||||||
* @param name Command name
|
* @param name Command name
|
||||||
* @param aliases Command aliases
|
* @param aliases Command aliases
|
||||||
|
|
@ -295,11 +272,25 @@ public abstract class CommandManager<C> {
|
||||||
final @NonNull Description description,
|
final @NonNull Description description,
|
||||||
final @NonNull CommandMeta meta
|
final @NonNull CommandMeta meta
|
||||||
) {
|
) {
|
||||||
return Command.<C>newBuilder(name, meta, description, aliases.toArray(new String[0])).manager(this);
|
return Command.<C>newBuilder(
|
||||||
|
name,
|
||||||
|
meta,
|
||||||
|
description,
|
||||||
|
aliases.toArray(new String[0])
|
||||||
|
).manager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command builder
|
* Create a new command builder with an empty description.
|
||||||
|
* <p>
|
||||||
|
* This will also register the creating manager in the command
|
||||||
|
* builder using {@link Command.Builder#manager(CommandManager)}, so that the command
|
||||||
|
* builder is associated with the creating manager. This allows for parser inference based on
|
||||||
|
* the type, with the help of the {@link ParserRegistry parser registry}
|
||||||
|
* <p>
|
||||||
|
* This method will not register the command in the manager. To do that, {@link #command(Command.Builder)}
|
||||||
|
* or {@link #command(Command)} has to be invoked with either the {@link Command.Builder} instance, or the constructed
|
||||||
|
* {@link Command command} instance
|
||||||
*
|
*
|
||||||
* @param name Command name
|
* @param name Command name
|
||||||
* @param aliases Command aliases
|
* @param aliases Command aliases
|
||||||
|
|
@ -311,11 +302,23 @@ public abstract class CommandManager<C> {
|
||||||
final @NonNull Collection<String> aliases,
|
final @NonNull Collection<String> aliases,
|
||||||
final @NonNull CommandMeta meta
|
final @NonNull CommandMeta meta
|
||||||
) {
|
) {
|
||||||
return Command.<C>newBuilder(name, meta, Description.empty(), aliases.toArray(new String[0])).manager(this);
|
return Command.<C>newBuilder(
|
||||||
|
name,
|
||||||
|
meta,
|
||||||
|
Description.empty(),
|
||||||
|
aliases.toArray(new String[0])
|
||||||
|
).manager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command builder
|
* Create a new command builder. This will also register the creating manager in the command
|
||||||
|
* builder using {@link Command.Builder#manager(CommandManager)}, so that the command
|
||||||
|
* builder is associated with the creating manager. This allows for parser inference based on
|
||||||
|
* the type, with the help of the {@link ParserRegistry parser registry}
|
||||||
|
* <p>
|
||||||
|
* This method will not register the command in the manager. To do that, {@link #command(Command.Builder)}
|
||||||
|
* or {@link #command(Command)} has to be invoked with either the {@link Command.Builder} instance, or the constructed
|
||||||
|
* {@link Command command} instance
|
||||||
*
|
*
|
||||||
* @param name Command name
|
* @param name Command name
|
||||||
* @param meta Command meta
|
* @param meta Command meta
|
||||||
|
|
@ -329,11 +332,25 @@ public abstract class CommandManager<C> {
|
||||||
final @NonNull Description description,
|
final @NonNull Description description,
|
||||||
final @NonNull String... aliases
|
final @NonNull String... aliases
|
||||||
) {
|
) {
|
||||||
return Command.<C>newBuilder(name, meta, description, aliases).manager(this);
|
return Command.<C>newBuilder(
|
||||||
|
name,
|
||||||
|
meta,
|
||||||
|
description,
|
||||||
|
aliases
|
||||||
|
).manager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command builder
|
* Create a new command builder with an empty description.
|
||||||
|
* <p>
|
||||||
|
* This will also register the creating manager in the command
|
||||||
|
* builder using {@link Command.Builder#manager(CommandManager)}, so that the command
|
||||||
|
* builder is associated with the creating manager. This allows for parser inference based on
|
||||||
|
* the type, with the help of the {@link ParserRegistry parser registry}
|
||||||
|
* <p>
|
||||||
|
* This method will not register the command in the manager. To do that, {@link #command(Command.Builder)}
|
||||||
|
* or {@link #command(Command)} has to be invoked with either the {@link Command.Builder} instance, or the constructed
|
||||||
|
* {@link Command command} instance
|
||||||
*
|
*
|
||||||
* @param name Command name
|
* @param name Command name
|
||||||
* @param meta Command meta
|
* @param meta Command meta
|
||||||
|
|
@ -345,11 +362,25 @@ public abstract class CommandManager<C> {
|
||||||
final @NonNull CommandMeta meta,
|
final @NonNull CommandMeta meta,
|
||||||
final @NonNull String... aliases
|
final @NonNull String... aliases
|
||||||
) {
|
) {
|
||||||
return Command.<C>newBuilder(name, meta, Description.empty(), aliases).manager(this);
|
return Command.<C>newBuilder(
|
||||||
|
name,
|
||||||
|
meta,
|
||||||
|
Description.empty(),
|
||||||
|
aliases
|
||||||
|
).manager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command builder using a default command meta instance.
|
* Create a new command builder using default command meta created by {@link #createDefaultCommandMeta()}.
|
||||||
|
* <p>
|
||||||
|
* This will also register the creating manager in the command
|
||||||
|
* builder using {@link Command.Builder#manager(CommandManager)}, so that the command
|
||||||
|
* builder is associated with the creating manager. This allows for parser inference based on
|
||||||
|
* the type, with the help of the {@link ParserRegistry parser registry}
|
||||||
|
* <p>
|
||||||
|
* This method will not register the command in the manager. To do that, {@link #command(Command.Builder)}
|
||||||
|
* or {@link #command(Command)} has to be invoked with either the {@link Command.Builder} instance, or the constructed
|
||||||
|
* {@link Command command} instance
|
||||||
*
|
*
|
||||||
* @param name Command name
|
* @param name Command name
|
||||||
* @param description Command description
|
* @param description Command description
|
||||||
|
|
@ -363,11 +394,26 @@ public abstract class CommandManager<C> {
|
||||||
final @NonNull Description description,
|
final @NonNull Description description,
|
||||||
final @NonNull String... aliases
|
final @NonNull String... aliases
|
||||||
) {
|
) {
|
||||||
return Command.<C>newBuilder(name, this.createDefaultCommandMeta(), description, aliases).manager(this);
|
return Command.<C>newBuilder(
|
||||||
|
name,
|
||||||
|
this.createDefaultCommandMeta(),
|
||||||
|
description,
|
||||||
|
aliases
|
||||||
|
).manager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command builder using a default command meta instance.
|
* Create a new command builder using default command meta created by {@link #createDefaultCommandMeta()}, and
|
||||||
|
* an empty description.
|
||||||
|
* <p>
|
||||||
|
* This will also register the creating manager in the command
|
||||||
|
* builder using {@link Command.Builder#manager(CommandManager)}, so that the command
|
||||||
|
* builder is associated with the creating manager. This allows for parser inference based on
|
||||||
|
* the type, with the help of the {@link ParserRegistry parser registry}
|
||||||
|
* <p>
|
||||||
|
* This method will not register the command in the manager. To do that, {@link #command(Command.Builder)}
|
||||||
|
* or {@link #command(Command)} has to be invoked with either the {@link Command.Builder} instance, or the constructed
|
||||||
|
* {@link Command command} instance
|
||||||
*
|
*
|
||||||
* @param name Command name
|
* @param name Command name
|
||||||
* @param aliases Command aliases
|
* @param aliases Command aliases
|
||||||
|
|
@ -379,11 +425,20 @@ public abstract class CommandManager<C> {
|
||||||
final @NonNull String name,
|
final @NonNull String name,
|
||||||
final @NonNull String... aliases
|
final @NonNull String... aliases
|
||||||
) {
|
) {
|
||||||
return Command.<C>newBuilder(name, this.createDefaultCommandMeta(), Description.empty(), aliases).manager(this);
|
return Command.<C>newBuilder(
|
||||||
|
name,
|
||||||
|
this.createDefaultCommandMeta(),
|
||||||
|
Description.empty(),
|
||||||
|
aliases
|
||||||
|
).manager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command argument builder
|
* Create a new command argument builder.
|
||||||
|
* <p>
|
||||||
|
* This will also invoke {@link CommandArgument.Builder#manager(CommandManager)}
|
||||||
|
* so that the argument is associated with the calling command manager. This allows for parser inference based on
|
||||||
|
* the type, with the help of the {@link ParserRegistry parser registry}.
|
||||||
*
|
*
|
||||||
* @param type Argument type
|
* @param type Argument type
|
||||||
* @param name Argument name
|
* @param name Argument name
|
||||||
|
|
@ -433,8 +488,10 @@ public abstract class CommandManager<C> {
|
||||||
* @see #preprocessContext(CommandContext, LinkedList) Preprocess a context
|
* @see #preprocessContext(CommandContext, LinkedList) Preprocess a context
|
||||||
*/
|
*/
|
||||||
public void registerCommandPreProcessor(final @NonNull CommandPreprocessor<C> processor) {
|
public void registerCommandPreProcessor(final @NonNull CommandPreprocessor<C> processor) {
|
||||||
this.servicePipeline.registerServiceImplementation(new TypeToken<CommandPreprocessor<C>>() {
|
this.servicePipeline.registerServiceImplementation(
|
||||||
}, processor,
|
new TypeToken<CommandPreprocessor<C>>() {
|
||||||
|
},
|
||||||
|
processor,
|
||||||
Collections.emptyList()
|
Collections.emptyList()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -612,6 +669,7 @@ public abstract class CommandManager<C> {
|
||||||
*
|
*
|
||||||
* @param setting Setting
|
* @param setting Setting
|
||||||
* @return {@code true} if the setting is activated or {@code false} if it's not
|
* @return {@code true} if the setting is activated or {@code false} if it's not
|
||||||
|
* @see #setSetting(ManagerSettings, boolean) Update a manager setting
|
||||||
*/
|
*/
|
||||||
public boolean getSetting(final @NonNull ManagerSettings setting) {
|
public boolean getSetting(final @NonNull ManagerSettings setting) {
|
||||||
return this.managerSettings.contains(setting);
|
return this.managerSettings.contains(setting);
|
||||||
|
|
@ -622,6 +680,7 @@ public abstract class CommandManager<C> {
|
||||||
*
|
*
|
||||||
* @param setting Setting to set
|
* @param setting Setting to set
|
||||||
* @param value Value
|
* @param value Value
|
||||||
|
* @see #getSetting(ManagerSettings) Get a manager setting
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public void setSetting(
|
public void setSetting(
|
||||||
|
|
@ -638,6 +697,9 @@ public abstract class CommandManager<C> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configurable command related settings
|
* Configurable command related settings
|
||||||
|
*
|
||||||
|
* @see CommandManager#setSetting(ManagerSettings, boolean) Set a manager setting
|
||||||
|
* @see CommandManager#getSetting(ManagerSettings) Get a manager setting
|
||||||
*/
|
*/
|
||||||
public enum ManagerSettings {
|
public enum ManagerSettings {
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||||
|
//
|
||||||
|
// 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 cloud.commandframework.arguments;
|
||||||
|
|
||||||
|
import cloud.commandframework.context.CommandContext;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler that produces command suggestions depending on input
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public interface CommandSuggestionEngine<C> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get command suggestions for the "next" argument that would yield a correctly
|
||||||
|
* parsing command input
|
||||||
|
*
|
||||||
|
* @param context Request context
|
||||||
|
* @param input Input provided by the sender
|
||||||
|
* @return List of suggestions
|
||||||
|
*/
|
||||||
|
@NonNull List<@NonNull String> getSuggestions(
|
||||||
|
@NonNull CommandContext<C> context,
|
||||||
|
@NonNull String input
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||||
|
//
|
||||||
|
// 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 cloud.commandframework.arguments;
|
||||||
|
|
||||||
|
import cloud.commandframework.CommandManager;
|
||||||
|
import cloud.commandframework.CommandTree;
|
||||||
|
import cloud.commandframework.context.CommandContext;
|
||||||
|
import cloud.commandframework.execution.preprocessor.CommandPreprocessingContext;
|
||||||
|
import cloud.commandframework.internal.CommandInputTokenizer;
|
||||||
|
import cloud.commandframework.services.State;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command suggestion engine that delegates to a {@link cloud.commandframework.CommandTree}
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public final class DelegatingCommandSuggestionEngine<C> implements CommandSuggestionEngine<C> {
|
||||||
|
|
||||||
|
private static final List<String> SINGLE_EMPTY_SUGGESTION = Collections.unmodifiableList(Collections.singletonList(""));
|
||||||
|
|
||||||
|
private final CommandManager<C> commandManager;
|
||||||
|
private final CommandTree<C> commandTree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new delegating command suggestion engine
|
||||||
|
*
|
||||||
|
* @param commandManager Command manager
|
||||||
|
* @param commandTree Command tree
|
||||||
|
*/
|
||||||
|
DelegatingCommandSuggestionEngine(
|
||||||
|
final @NonNull CommandManager<C> commandManager,
|
||||||
|
final @NonNull CommandTree<C> commandTree
|
||||||
|
) {
|
||||||
|
this.commandManager = commandManager;
|
||||||
|
this.commandTree = commandTree;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull List<@NonNull String> getSuggestions(
|
||||||
|
@NonNull final CommandContext<C> context,
|
||||||
|
@NonNull final String input
|
||||||
|
) {
|
||||||
|
final @NonNull LinkedList<@NonNull String> inputQueue = new CommandInputTokenizer(input).tokenize();
|
||||||
|
final List<String> suggestions;
|
||||||
|
if (this.commandManager.preprocessContext(context, inputQueue) == State.ACCEPTED) {
|
||||||
|
suggestions = this.commandManager.getCommandSuggestionProcessor().apply(
|
||||||
|
new CommandPreprocessingContext<>(context, inputQueue),
|
||||||
|
this.commandTree.getSuggestions(
|
||||||
|
context,
|
||||||
|
inputQueue
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
suggestions = Collections.emptyList();
|
||||||
|
}
|
||||||
|
if (this.commandManager.getSetting(CommandManager.ManagerSettings.FORCE_SUGGESTION) && suggestions.isEmpty()) {
|
||||||
|
return SINGLE_EMPTY_SUGGESTION;
|
||||||
|
}
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||||
|
//
|
||||||
|
// 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 cloud.commandframework.arguments;
|
||||||
|
|
||||||
|
import cloud.commandframework.CommandManager;
|
||||||
|
import cloud.commandframework.CommandTree;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory that produces {@link DelegatingCommandSuggestionEngine} instances
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public final class DelegatingCommandSuggestionEngineFactory<C> {
|
||||||
|
|
||||||
|
private final CommandManager<C> commandManager;
|
||||||
|
private final CommandTree<C> commandTree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new delegating command suggestion engine factory
|
||||||
|
*
|
||||||
|
* @param commandManager Command manager
|
||||||
|
*/
|
||||||
|
public DelegatingCommandSuggestionEngineFactory(final @NonNull CommandManager<C> commandManager) {
|
||||||
|
this.commandManager = commandManager;
|
||||||
|
this.commandTree = commandManager.getCommandTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the engine instance
|
||||||
|
*
|
||||||
|
* @return New engine instance
|
||||||
|
*/
|
||||||
|
public @NonNull DelegatingCommandSuggestionEngine<C> create() {
|
||||||
|
return new DelegatingCommandSuggestionEngine<>(
|
||||||
|
this.commandManager,
|
||||||
|
this.commandTree
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||||
|
//
|
||||||
|
// 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 cloud.commandframework.internal;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tokenizer that splits command inputs into tokens. This will split the string
|
||||||
|
* at every blank space. If the input string ends with a blank space, a trailing
|
||||||
|
* empty string will be added to the token list
|
||||||
|
*/
|
||||||
|
public final class CommandInputTokenizer {
|
||||||
|
|
||||||
|
private static final String DELIMITER = " ";
|
||||||
|
private static final String EMPTY = "";
|
||||||
|
|
||||||
|
private final StringTokenizerFactory stringTokenizerFactory = new StringTokenizerFactory();
|
||||||
|
private final String input;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new input tokenizer
|
||||||
|
*
|
||||||
|
* @param input Input that is to be turned into tokens
|
||||||
|
*/
|
||||||
|
public CommandInputTokenizer(final @NonNull String input) {
|
||||||
|
this.input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn the input into tokens
|
||||||
|
*
|
||||||
|
* @return Linked list containing the tokenized input
|
||||||
|
*/
|
||||||
|
public @NonNull LinkedList<@NonNull String> tokenize() {
|
||||||
|
final StringTokenizer stringTokenizer = stringTokenizerFactory.createStringTokenizer();
|
||||||
|
final LinkedList<String> tokens = new LinkedList<>();
|
||||||
|
while (stringTokenizer.hasMoreElements()) {
|
||||||
|
tokens.add(stringTokenizer.nextToken());
|
||||||
|
}
|
||||||
|
if (input.endsWith(DELIMITER)) {
|
||||||
|
tokens.add(EMPTY);
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory class that creates {@link StringTokenizer} instances
|
||||||
|
*/
|
||||||
|
private final class StringTokenizerFactory {
|
||||||
|
|
||||||
|
private @NonNull StringTokenizer createStringTokenizer() {
|
||||||
|
return new StringTokenizer(input, DELIMITER);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ include(':cloud-javacord')
|
||||||
include(':cloud-jda')
|
include(':cloud-jda')
|
||||||
include(':example-bukkit')
|
include(':example-bukkit')
|
||||||
include(':cloud-tasks')
|
include(':cloud-tasks')
|
||||||
|
include(':cloud-sponge')
|
||||||
project(':cloud-bukkit').projectDir = file('cloud-minecraft/cloud-bukkit')
|
project(':cloud-bukkit').projectDir = file('cloud-minecraft/cloud-bukkit')
|
||||||
project(':cloud-paper').projectDir = file('cloud-minecraft/cloud-paper')
|
project(':cloud-paper').projectDir = file('cloud-minecraft/cloud-paper')
|
||||||
project(':cloud-brigadier').projectDir = file('cloud-minecraft/cloud-brigadier')
|
project(':cloud-brigadier').projectDir = file('cloud-minecraft/cloud-brigadier')
|
||||||
|
|
@ -24,3 +25,4 @@ project(':cloud-cloudburst').projectDir = file('cloud-minecraft/cloud-cloudburst
|
||||||
project(':cloud-javacord').projectDir = file('cloud-discord/cloud-javacord')
|
project(':cloud-javacord').projectDir = file('cloud-discord/cloud-javacord')
|
||||||
project(':cloud-jda').projectDir = file('cloud-discord/cloud-jda')
|
project(':cloud-jda').projectDir = file('cloud-discord/cloud-jda')
|
||||||
project(':example-bukkit').projectDir = file('examples/example-bukkit')
|
project(':example-bukkit').projectDir = file('examples/example-bukkit')
|
||||||
|
project(':cloud-sponge').projectDir = file('cloud-minecraft/cloud-sponge')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue