.
This commit is contained in:
parent
254af80fde
commit
72f7625b19
53 changed files with 679 additions and 756 deletions
18
kencommandapi-core/build.gradle.kts
Normal file
18
kencommandapi-core/build.gradle.kts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
plugins {
|
||||
id("java")
|
||||
id("maven-publish")
|
||||
id("de.kentoj.scrow.scrow-repository")
|
||||
id("de.kentoj.scrow.base-dependencies")
|
||||
id("de.kentoj.scrow.java-library")
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("KenCommandAPI") {
|
||||
groupId = rootProject.group.toString()
|
||||
version = rootProject.version.toString()
|
||||
artifactId = project.name
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
package de.kentoj.kencommandapi;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandException;
|
||||
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
|
||||
import de.kentoj.kencommandapi.api.types.IntegerArgumentType;
|
||||
import de.kentoj.kencommandapi.impl.AbstractKenCommandApi;
|
||||
import de.kentoj.kencommandapi.impl.processing.CommandContextImpl;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
|
||||
private static class BasicCommandContext extends CommandContextImpl<PrintStream, BasicCommandContext> {
|
||||
public BasicCommandContext(PrintStream sender, Map<String, ParsedArgument<PrintStream, BasicCommandContext, ?>> parsedArguments) {
|
||||
super(sender, parsedArguments);
|
||||
}
|
||||
|
||||
public void respond(String msg) {
|
||||
getSender().println("|==================|");
|
||||
getSender().println(msg);
|
||||
getSender().println("|==================|");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
var api = new AbstractKenCommandApi<PrintStream, BasicCommandContext>() {
|
||||
@Override
|
||||
public BasicCommandContext createContext(PrintStream sender, Map<String, ParsedArgument<PrintStream, BasicCommandContext, ?>> parsedArguments) {
|
||||
return new BasicCommandContext(sender, parsedArguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendErrorMessage(PrintStream printStream, CommandException ex) {
|
||||
System.err.println(ex.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(PrintStream printStream, String permission) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var root = api.createNode("coins");
|
||||
|
||||
var addNode = api.createNode("add", "deposit");
|
||||
addNode.setExecutor(ctx -> {
|
||||
int amount = ctx.getArg("amount");
|
||||
|
||||
ctx.respond("adding " + amount + "$ to your balance");
|
||||
});
|
||||
|
||||
addNode.addArgument(api.createArgument("amount", new IntegerArgumentType<>()));
|
||||
root.addLiteral(addNode);
|
||||
api.invoke(root, System.out, new String[]{"add", "100"});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package de.kentoj.kencommandapi.api;
|
||||
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
|
||||
import de.kentoj.kencommandapi.internal.CommandNodeImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface CommandNode<T> {
|
||||
static <T> CommandNode<T> node(String... name) {
|
||||
return new CommandNodeImpl<>(name);
|
||||
}
|
||||
|
||||
@NotNull String[] getNames();
|
||||
|
||||
void setDescription(@Nullable String description);
|
||||
|
||||
@Nullable String getDescription();
|
||||
|
||||
/*
|
||||
* arguments
|
||||
*/
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if there is already an argument with the same id
|
||||
* @throws IllegalStateException if a greedy argument was added to the node previously
|
||||
*/
|
||||
<V> void addArgument(CommandArgument<T, V> argument);
|
||||
|
||||
List<CommandArgument<T, ?>> getArguments();
|
||||
|
||||
/*
|
||||
* literals
|
||||
*/
|
||||
|
||||
void setExecutor(@Nullable CommandExecutor<T> executor);
|
||||
|
||||
@Nullable CommandExecutor<T> getExecutor();
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if there is already a literal with the same name
|
||||
* @throws IllegalStateException if a greedy argument was added to the node previously
|
||||
*/
|
||||
void addLiteral(@NotNull CommandNode<T> commandNode);
|
||||
|
||||
@Nullable CommandNode<T> getLiteral(@NotNull String name);
|
||||
|
||||
@NotNull Set<CommandNode<T>> getLiterals();
|
||||
|
||||
@Nullable String getPermission();
|
||||
|
||||
void setPermission(@Nullable String permission);
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package de.kentoj.kencommandapi.api;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.argument.ArgumentType;
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import de.kentoj.kencommandapi.api.nodes.CommandNode;
|
||||
|
||||
public interface KenCommandApi<T, S extends CommandContext<T, ?>> {
|
||||
|
||||
CommandNode<T, S> createNode(String... name);
|
||||
|
||||
<V> CommandArgument<T, S, V> createArgument(String id, ArgumentType<T, S, V> type);
|
||||
|
||||
void invoke(CommandNode<T, S> node, T sender, String[] args);
|
||||
}
|
||||
|
|
@ -1,13 +1,21 @@
|
|||
package de.kentoj.kencommandapi.api.argument;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
|
||||
|
||||
public interface ArgumentType<T, S extends CommandContext<T, ?>, V> {
|
||||
public interface ArgumentType<T, V> {
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if string invalid
|
||||
*/
|
||||
V parseInput(String string);
|
||||
|
||||
SuggestionProvider<T, S> defaultSuggestionProvider();
|
||||
SuggestionProvider<T> defaultSuggestionProvider();
|
||||
|
||||
/**
|
||||
* @return whether this argument is greedy. Greedy arguments consume all remaining input instead of a single word.
|
||||
* For example, given the 2 words "hello" "world" as input, a greedy argument parses "hello world" as a single value.
|
||||
* Greedy arguments may not be followed by another argument or a literal
|
||||
*/
|
||||
default boolean isGreedy() {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,30 @@
|
|||
package de.kentoj.kencommandapi.api.argument;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
|
||||
import de.kentoj.kencommandapi.internal.CommandArgumentImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface CommandArgument<T, S extends CommandContext<T, ?>, V> {
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface CommandArgument<T, V> {
|
||||
|
||||
static <T, V> CommandArgument<T, V> arg(String id, ArgumentType<T, V> type) {
|
||||
return new CommandArgumentImpl<>(id, type);
|
||||
}
|
||||
|
||||
String getId();
|
||||
|
||||
ArgumentType<T, S, V> getType();
|
||||
ArgumentType<T, V> getType();
|
||||
|
||||
void setDefaultValue(@Nullable V defaultValue);
|
||||
void setDefaultValueProvider(@Nullable Function<T, V> getDefaultValueProvider);
|
||||
|
||||
@Nullable V getDefaultValue();
|
||||
@Nullable Function<T, V> getDefaultValueProvider();
|
||||
|
||||
void setSuggestionProvider(@Nullable SuggestionProvider<T, S> suggestionProvider);
|
||||
void setSuggestionProvider(@Nullable SuggestionProvider<T> suggestionProvider);
|
||||
|
||||
/**
|
||||
* @return the explicitly set SuggestionProvider or the default suggestions for the type if unset/null
|
||||
*/
|
||||
@NotNull SuggestionProvider<T, S> getSuggestionProvider();
|
||||
@NotNull SuggestionProvider<T> getSuggestionProvider();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
package de.kentoj.kencommandapi.api.argument;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface SuggestionProvider<T, S extends CommandContext<T, ?>> {
|
||||
|
||||
CompletableFuture<Set<String>> suggest(S context);
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package de.kentoj.kencommandapi.api.argument.suggestion;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.argument.SuggestionProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class NoSuggestionProvider<T, S extends CommandContext<T, ?>> implements SuggestionProvider<T, S> {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Set<String>> suggest(S ctx) {
|
||||
return CompletableFuture.completedFuture(Collections.emptySet());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package de.kentoj.kencommandapi.api.argument.types;
|
||||
|
||||
import de.kentoj.kencommandapi.api.argument.ArgumentType;
|
||||
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
|
||||
import de.kentoj.kencommandapi.api.suggestion.NoSuggestionProvider;
|
||||
|
||||
public class IntegerArgumentType<T> implements ArgumentType<T, Integer> {
|
||||
|
||||
@Override
|
||||
public Integer parseInput(String string) {
|
||||
return Integer.parseInt(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestionProvider<T> defaultSuggestionProvider() {
|
||||
return new NoSuggestionProvider<>();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,12 @@
|
|||
package de.kentoj.kencommandapi.api.types;
|
||||
package de.kentoj.kencommandapi.api.argument.types;
|
||||
|
||||
import de.kentoj.kencommandapi.api.argument.ArgumentType;
|
||||
import de.kentoj.kencommandapi.api.argument.SuggestionProvider;
|
||||
import de.kentoj.kencommandapi.api.argument.suggestion.NoSuggestionProvider;
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import lombok.AllArgsConstructor;
|
||||
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
|
||||
import de.kentoj.kencommandapi.api.suggestion.NoSuggestionProvider;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class StringArgumentType<T, S extends CommandContext<T, ?>> implements ArgumentType<T, S, String> {
|
||||
public class StringArgumentType<T> implements ArgumentType<T, String> {
|
||||
|
||||
private final boolean isGreedy;
|
||||
|
||||
|
|
@ -22,7 +20,7 @@ public class StringArgumentType<T, S extends CommandContext<T, ?>> implements Ar
|
|||
}
|
||||
|
||||
@Override
|
||||
public SuggestionProvider<T, S> defaultSuggestionProvider() {
|
||||
public SuggestionProvider<T> defaultSuggestionProvider() {
|
||||
return new NoSuggestionProvider<>();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package de.kentoj.kencommandapi.api.invocation;
|
||||
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CommandContext<T> {
|
||||
@Getter
|
||||
private final T sender;
|
||||
@Getter
|
||||
private final Map<String, ParsedArgument<T, ?>> parsedArguments;
|
||||
|
||||
public <V> ParsedArgument<T, V> getParsedArgument(String name) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
return (ParsedArgument<T, V>) parsedArguments.get(name);
|
||||
} catch (ClassCastException e) {
|
||||
throw new RuntimeException("requested argument with wrong type", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> V getArg(String name) {
|
||||
return (V) getParsedArgument(name).getValue();
|
||||
}
|
||||
|
||||
public <V> V getArg(CommandArgument<T, V> arg) {
|
||||
return getArg(arg.getId());
|
||||
}
|
||||
|
||||
public <V> ParsedArgument<T, V> getParsedArgument(CommandArgument<T, V> arg) {
|
||||
return getParsedArgument(arg.getId());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package de.kentoj.kencommandapi.api.invocation;
|
||||
|
||||
public interface CommandExecutor<T> {
|
||||
|
||||
Result execute(CommandContext<T> ctx);
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package de.kentoj.kencommandapi.api.invocation;
|
||||
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
|
||||
public interface ParsedArgument<T, V> {
|
||||
|
||||
V getValue();
|
||||
|
||||
CommandArgument<T, V> getArgument();
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package de.kentoj.kencommandapi.api.invocation;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public class Result {
|
||||
|
||||
public static Result success() {
|
||||
return new Result(null);
|
||||
}
|
||||
|
||||
public static Result error(String error) {
|
||||
return new Result(error);
|
||||
}
|
||||
|
||||
private final @Nullable String error;
|
||||
|
||||
public boolean isError() {
|
||||
return error != null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package de.kentoj.kencommandapi.api.nodes;
|
||||
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ArgumentNode<T, S extends CommandContext<T, ?>> {
|
||||
|
||||
<V> void addArgument(CommandArgument<T, S, V> argument);
|
||||
|
||||
List<CommandArgument<T, S, ?>> getArguments();
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package de.kentoj.kencommandapi.api.nodes;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.processing.CommandExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @param <S> type of command context
|
||||
*/
|
||||
public interface CommandNode<T, S extends CommandContext<T, ?>> extends ArgumentNode<T, S>, LiteralNode<T, S> {
|
||||
|
||||
@NotNull String[] getNames();
|
||||
|
||||
void setExecutor(@Nullable CommandExecutor<T, S> executor);
|
||||
|
||||
@Nullable CommandExecutor<T, S> getExecutor();
|
||||
|
||||
void setDescription(@Nullable String description);
|
||||
|
||||
@Nullable String getDescription();
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package de.kentoj.kencommandapi.api.nodes;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface LiteralNode<T, S extends CommandContext<T, ?>> {
|
||||
|
||||
void addLiteral(@NotNull CommandNode<T, S> commandNode);
|
||||
|
||||
@Nullable CommandNode<T, S> getLiteral(@NotNull String name);
|
||||
|
||||
@NotNull Set<CommandNode<T, S>> getLiterals();
|
||||
|
||||
@Nullable String getPermission();
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package de.kentoj.kencommandapi.api.processing;
|
||||
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
|
||||
public interface CommandContext<T, S extends CommandContext<T, ?>> {
|
||||
|
||||
T getSender();
|
||||
|
||||
/**
|
||||
* @return Object that is parsed from the raw argument
|
||||
* @throws RuntimeException if no value given and no default value supplied
|
||||
*/
|
||||
<V> ParsedArgument<T, S, V> getParsedArgument(String name);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default <V> V getArg(String name) {
|
||||
return (V) getParsedArgument(name).getValue();
|
||||
}
|
||||
|
||||
default <V> V getArg(CommandArgument<T, S, V> arg) {
|
||||
return getArg(arg.getId());
|
||||
}
|
||||
|
||||
default <V> ParsedArgument<T, S, V> getParsedArgument(CommandArgument<T, S, V> arg) {
|
||||
return getParsedArgument(arg.getId());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package de.kentoj.kencommandapi.api.processing;
|
||||
|
||||
public class CommandException extends RuntimeException {
|
||||
public CommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CommandException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public CommandException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package de.kentoj.kencommandapi.api.processing;
|
||||
|
||||
public interface CommandExecutor<T, S extends CommandContext<T, ?>> {
|
||||
|
||||
void execute(S ctx);
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package de.kentoj.kencommandapi.api.processing;
|
||||
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
|
||||
public interface ParsedArgument<T, S extends CommandContext<T, ?>, V> {
|
||||
|
||||
V getValue();
|
||||
|
||||
CommandArgument<T, S, V> getArgument();
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package de.kentoj.kencommandapi.api.suggestion;
|
||||
|
||||
import de.kentoj.kencommandapi.api.invocation.CommandContext;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class NoSuggestionProvider<T> implements SuggestionProvider<T> {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Set<String>> suggest(CommandContext<T> __) {
|
||||
return CompletableFuture.completedFuture(Collections.emptySet());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package de.kentoj.kencommandapi.api.suggestion;
|
||||
|
||||
import de.kentoj.kencommandapi.api.invocation.CommandContext;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface SuggestionProvider<T> {
|
||||
|
||||
CompletableFuture<Set<String>> suggest(CommandContext<T> context);
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package de.kentoj.kencommandapi.api.types;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.argument.ArgumentType;
|
||||
import de.kentoj.kencommandapi.api.argument.SuggestionProvider;
|
||||
import de.kentoj.kencommandapi.api.argument.suggestion.NoSuggestionProvider;
|
||||
|
||||
public class IntegerArgumentType<T, S extends CommandContext<T, ?>> implements ArgumentType<T, S, Integer> {
|
||||
|
||||
@Override
|
||||
public Integer parseInput(String string) {
|
||||
return Integer.parseInt(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestionProvider<T, S> defaultSuggestionProvider() {
|
||||
return new NoSuggestionProvider<>();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
package de.kentoj.kencommandapi.impl;
|
||||
|
||||
import de.kentoj.kencommandapi.api.KenCommandApi;
|
||||
import de.kentoj.kencommandapi.api.argument.ArgumentType;
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import de.kentoj.kencommandapi.api.nodes.CommandNode;
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.processing.CommandException;
|
||||
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
|
||||
import de.kentoj.kencommandapi.impl.processing.ParsedArgumentImpl;
|
||||
import de.kentoj.kencommandapi.impl.structure.CommandArgumentImpl;
|
||||
import de.kentoj.kencommandapi.impl.structure.CommandNodeImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public abstract class AbstractKenCommandApi<T, S extends CommandContext<T, ?>> implements KenCommandApi<T, S> {
|
||||
|
||||
public abstract S createContext(T sender, Map<String, ParsedArgument<T, S, ?>> parsedArguments);
|
||||
|
||||
public abstract void sendErrorMessage(T t, CommandException exception);
|
||||
|
||||
public abstract boolean hasPermission(T t, String permission);
|
||||
|
||||
@Override
|
||||
public CommandNode<T, S> createNode(String... name) {
|
||||
return new CommandNodeImpl<>(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> CommandArgument<T, S, V> createArgument(String id, ArgumentType<T, S, V> type) {
|
||||
return new CommandArgumentImpl<>(id, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(CommandNode<T, S> node, T sender, String[] args) {
|
||||
var iter = Arrays.stream(args).iterator();
|
||||
|
||||
try {
|
||||
var executableLiteral = processLiterals(sender, node, iter);
|
||||
var parsedArguments = parseArguments(executableLiteral, iter);
|
||||
assert executableLiteral.getExecutor() != null;
|
||||
executableLiteral.getExecutor().execute(createContext(sender, parsedArguments));
|
||||
} catch (CommandException ex) {
|
||||
sendErrorMessage(sender, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private CommandNode<T, S> processLiterals(T sender, CommandNode<T, S> commandNode, Iterator<String> iter) {
|
||||
if (!iter.hasNext()) {
|
||||
// literals/arguments are optional if the node itself is executable
|
||||
if (commandNode.getExecutor() == null) throw new CommandException("no literal given but required");
|
||||
return commandNode;
|
||||
}
|
||||
|
||||
if (commandNode.getLiterals().isEmpty()) {
|
||||
return commandNode;
|
||||
}
|
||||
|
||||
final var arg = iter.next();
|
||||
final var literal = commandNode.getLiteral(arg);
|
||||
if (literal == null) {
|
||||
throw new CommandException("Invalid literal: " + arg);
|
||||
}
|
||||
if (literal.getPermission() != null && !hasPermission(sender, literal.getPermission())) {
|
||||
throw new CommandException("No permission");
|
||||
}
|
||||
return processLiterals(sender, literal, iter);
|
||||
}
|
||||
|
||||
private Map<String, ParsedArgument<T, S, ?>> parseArguments(CommandNode<T, S> commandNode, Iterator<String> iter) {
|
||||
Map<String, ParsedArgument<T, S, ?>> parsedArguments = new HashMap<>();
|
||||
for (var argument : commandNode.getArguments()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var arg = ((CommandArgument<T, S, Object>) argument);
|
||||
|
||||
if (!iter.hasNext()) {
|
||||
var defaultValue = arg.getDefaultValue();
|
||||
if (defaultValue == null) throw new CommandException("no argument given but required");
|
||||
parsedArguments.put(arg.getId(), new ParsedArgumentImpl<>(defaultValue, arg));
|
||||
continue;
|
||||
}
|
||||
|
||||
var rawArgument = getRawArgument(arg, iter);
|
||||
var parsedArg = arg.getType().parseInput(rawArgument);
|
||||
parsedArguments.put(arg.getId(), new ParsedArgumentImpl<>(parsedArg, arg));
|
||||
}
|
||||
return parsedArguments;
|
||||
}
|
||||
|
||||
private String getRawArgument(CommandArgument<T, S, Object> arg, Iterator<String> iter) {
|
||||
var rawArgument = "";
|
||||
if (arg.getType().isGreedy()) {
|
||||
while (iter.hasNext()) {
|
||||
//noinspection StringConcatenationInLoop
|
||||
rawArgument += iter.next();
|
||||
}
|
||||
} else {
|
||||
rawArgument = iter.next();
|
||||
}
|
||||
return rawArgument;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package de.kentoj.kencommandapi.impl.processing;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CommandContextImpl<T, S extends CommandContext<T, ?>> implements CommandContext<T, S> {
|
||||
@Getter
|
||||
private final T sender;
|
||||
@Getter
|
||||
private final Map<String, ParsedArgument<T, S, ?>> parsedArguments;
|
||||
|
||||
@Override
|
||||
public <V> ParsedArgument<T, S, V> getParsedArgument(String name) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
return (ParsedArgument<T, S, V>) parsedArguments.get(name);
|
||||
} catch (ClassCastException e) {
|
||||
throw new RuntimeException("requested argument with wrong type", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package de.kentoj.kencommandapi.impl.processing;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class ParsedArgumentImpl<T, S extends CommandContext<T, ?>, V> implements ParsedArgument<T, S, V> {
|
||||
V value;
|
||||
CommandArgument<T, S, V> argument;
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package de.kentoj.kencommandapi.impl.processing;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.nodes.CommandNode;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class TabCompleter<T, S extends CommandContext<T, ?>> {
|
||||
|
||||
private List<String> tabComplete(CommandNode<T, S> node, String[] args) {
|
||||
var lastNode = node;
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
var tmp = node.getLiteral(args[i]);
|
||||
if (tmp == null) {
|
||||
break;
|
||||
}
|
||||
lastNode = tmp;
|
||||
i++;
|
||||
}
|
||||
|
||||
return node.getLiterals().stream().map(CommandNode::getNames).flatMap(Arrays::stream).toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package de.kentoj.kencommandapi.impl.structure;
|
||||
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.argument.ArgumentType;
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import de.kentoj.kencommandapi.api.argument.SuggestionProvider;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CommandArgumentImpl<T, S extends CommandContext<T, ?>, V> implements CommandArgument<T, S, V> {
|
||||
|
||||
@Getter
|
||||
private final String id;
|
||||
@Getter
|
||||
private final ArgumentType<T, S, V> type;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private @Nullable V defaultValue;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private @Nullable SuggestionProvider<T, S> suggestionProvider;
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
package de.kentoj.kencommandapi.impl.structure;
|
||||
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import de.kentoj.kencommandapi.api.nodes.CommandNode;
|
||||
import de.kentoj.kencommandapi.api.processing.CommandContext;
|
||||
import de.kentoj.kencommandapi.api.processing.CommandExecutor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class CommandNodeImpl<T, S extends CommandContext<T, ?>> implements CommandNode<T, S> {
|
||||
|
||||
@Getter
|
||||
private final String[] names;
|
||||
@Getter
|
||||
private final Set<CommandNode<T, S>> literals = new HashSet<>();
|
||||
@Getter
|
||||
private final List<CommandArgument<T, S, ?>> arguments = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private @Nullable CommandExecutor<T, S> executor;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String description;
|
||||
|
||||
@Getter
|
||||
private @Nullable String permission = null;
|
||||
|
||||
public CommandNodeImpl(String[] names) {
|
||||
if (names.length == 0) throw new IllegalArgumentException("at least one name is required");
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLiteral(@NotNull CommandNode<T, S> commandNode) {
|
||||
for (String name : commandNode.getNames()) {
|
||||
if (getLiteral(name) != null) throw new IllegalArgumentException("literal with same name is already added");
|
||||
}
|
||||
literals.add(commandNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandNode<T, S> getLiteral(@NotNull String name) {
|
||||
for (var literal : literals) {
|
||||
for (String literalName : literal.getNames()) {
|
||||
if (literalName.equalsIgnoreCase(name)) return literal;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> void addArgument(CommandArgument<T, S, V> argument) {
|
||||
if (arguments.stream()
|
||||
.anyMatch(arg -> arg.getId().equals(argument.getId()))
|
||||
) throw new IllegalArgumentException("argument with same identifier is already added");
|
||||
if (!arguments.isEmpty() && arguments.getLast().getType().isGreedy())
|
||||
throw new IllegalStateException("can't append argument to a node, whose last argument type is greedy: "
|
||||
+ arguments.getLast().getType().getClass().getCanonicalName());
|
||||
|
||||
arguments.add(argument);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package de.kentoj.kencommandapi.internal;
|
||||
|
||||
import de.kentoj.kencommandapi.api.argument.ArgumentType;
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CommandArgumentImpl<T, V> implements CommandArgument<T, V> {
|
||||
|
||||
@Getter
|
||||
private final String id;
|
||||
@Getter
|
||||
private final ArgumentType<T, V> type;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private @Nullable Function<T, V> defaultValueProvider;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private @Nullable SuggestionProvider<T> suggestionProvider;
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package de.kentoj.kencommandapi.internal;
|
||||
|
||||
import de.kentoj.kencommandapi.api.CommandNode;
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class CommandNodeImpl<T> implements CommandNode<T> {
|
||||
|
||||
@Getter
|
||||
private final String[] names;
|
||||
@Getter
|
||||
private final Set<CommandNode<T>> literals = new HashSet<>();
|
||||
@Getter
|
||||
private final List<CommandArgument<T, ?>> arguments = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private @Nullable CommandExecutor<T> executor;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String description;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private @Nullable String permission = null;
|
||||
|
||||
public CommandNodeImpl(String[] names) {
|
||||
if (names.length == 0) throw new IllegalArgumentException("at least one name is required");
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLiteral(@NotNull CommandNode<T> commandNode) {
|
||||
if (isGreedy()) throw new IllegalStateException("can't add literal to a node with a greedy argument");
|
||||
for (String name : commandNode.getNames())
|
||||
if (getLiteral(name) != null)
|
||||
throw new IllegalArgumentException("literal with same name is already exists");
|
||||
literals.add(commandNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandNode<T> getLiteral(@NotNull String name) {
|
||||
for (var literal : literals)
|
||||
for (String literalName : literal.getNames())
|
||||
if (literalName.equalsIgnoreCase(name)) return literal;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> void addArgument(CommandArgument<T, V> argument) {
|
||||
if (isGreedy()) throw new IllegalStateException("can't add literal to a node with a greedy argument");
|
||||
if (arguments.stream().anyMatch(arg -> arg.getId().equals(argument.getId())))
|
||||
throw new IllegalArgumentException("argument with same identifier is already added");
|
||||
arguments.add(argument);
|
||||
}
|
||||
|
||||
private boolean isGreedy() {
|
||||
var arg = arguments.getLast();
|
||||
return arg != null && arg.getType().isGreedy();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package de.kentoj.kencommandapi.internal;
|
||||
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class ParsedArgumentImpl<T, V> implements ParsedArgument<T, V> {
|
||||
V value;
|
||||
CommandArgument<T, V> argument;
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package de.kentoj.kencommandapi.internal.parser;
|
||||
|
||||
import de.kentoj.kencommandapi.api.CommandNode;
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
|
||||
import de.kentoj.kencommandapi.internal.ParsedArgumentImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CommandParser<T> {
|
||||
|
||||
private final PermissionChecker<T> hasPermissionMethod;
|
||||
|
||||
public ParseResult<T> parseLiteral(CommandNode<T> rootNode, T sender, String[] args) {
|
||||
var cur = rootNode;
|
||||
final Iterator<String> argStrIter = Arrays.stream(args).iterator();
|
||||
final Map<String, ParsedArgument<T, ?>> parsedArguments = new HashMap<>();
|
||||
|
||||
while (argStrIter.hasNext()) {
|
||||
for (var nodeArg : cur.getArguments()) {
|
||||
ParsedArgument<T, Object> parsed;
|
||||
try {
|
||||
parsed = parseArgument(argStrIter, sender, nodeArg);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return new ParseResult.IllegalArgument<>(cur, ex.getMessage());
|
||||
}
|
||||
if (parsed == null) return new ParseResult.IncompleteArgument<>(cur, nodeArg);
|
||||
parsedArguments.put(nodeArg.getId(), parsed);
|
||||
}
|
||||
|
||||
if (!argStrIter.hasNext()) {
|
||||
if (cur.getExecutor() == null)
|
||||
return new ParseResult.IncompleteCommand<>(cur);
|
||||
return new ParseResult.Success<>(cur, parsedArguments);
|
||||
}
|
||||
|
||||
var argStr = argStrIter.next();
|
||||
var nextNode = cur.getLiteral(argStr);
|
||||
if (nextNode == null)
|
||||
return new ParseResult.UnknownLiteral<>(cur, argStr);
|
||||
if (nextNode.getPermission() != null && !hasPermissionMethod.hasPermission(sender, nextNode.getPermission()))
|
||||
return new ParseResult.NoPermission<>(cur, argStr);
|
||||
cur = nextNode;
|
||||
}
|
||||
|
||||
throw new RuntimeException("code reached unexpected line");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ParsedArgument<T, Object> parseArgument(Iterator<String> args, T sender, CommandArgument<T, ?> nodeArg) {
|
||||
Object value;
|
||||
|
||||
if (args.hasNext()) {
|
||||
var argStr = getRawArgument(args, nodeArg);
|
||||
value = nodeArg.getType().parseInput(argStr);
|
||||
} else {
|
||||
if (nodeArg.getDefaultValueProvider() == null)
|
||||
return null;
|
||||
value = nodeArg.getDefaultValueProvider().apply(sender);
|
||||
}
|
||||
|
||||
return new ParsedArgumentImpl<>(value, (CommandArgument<T, Object>) nodeArg);
|
||||
}
|
||||
|
||||
private String getRawArgument(Iterator<String> args, CommandArgument<T, ?> arg) {
|
||||
StringBuilder rawArgument = new StringBuilder();
|
||||
|
||||
if (arg.getType().isGreedy()) {
|
||||
while (args.hasNext()) {
|
||||
rawArgument.append(args.next());
|
||||
}
|
||||
} else {
|
||||
rawArgument = new StringBuilder(args.next());
|
||||
}
|
||||
|
||||
return rawArgument.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package de.kentoj.kencommandapi.internal.parser;
|
||||
|
||||
import de.kentoj.kencommandapi.api.CommandNode;
|
||||
import de.kentoj.kencommandapi.api.argument.CommandArgument;
|
||||
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public abstract sealed class ParseResult<T> permits ParseResult.IllegalArgument, ParseResult.IncompleteArgument,
|
||||
ParseResult.IncompleteCommand, ParseResult.NoPermission, ParseResult.Success, ParseResult.UnknownLiteral {
|
||||
|
||||
/**
|
||||
* @return last successfully literal
|
||||
*/
|
||||
public abstract CommandNode<T> getLiteral();
|
||||
|
||||
public abstract String getMessage();
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static final class IncompleteArgument<T> extends ParseResult<T> {
|
||||
@Getter
|
||||
private final CommandNode<T> literal;
|
||||
private final CommandArgument<T, ?> argument;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Missing value for argument: " + argument.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static final class UnknownLiteral<T> extends ParseResult<T> {
|
||||
private final CommandNode<T> previousLiteral;
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Unknown literal: " + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandNode<T> getLiteral() {
|
||||
return previousLiteral;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static final class IncompleteCommand<T> extends ParseResult<T> {
|
||||
@Getter
|
||||
private final CommandNode<T> literal;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Incomplete command";
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static final class Success<T> extends ParseResult<T> {
|
||||
@Getter
|
||||
private final CommandNode<T> literal;
|
||||
@Getter
|
||||
private final Map<String, ParsedArgument<T, ?>> parsedArguments;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static final class NoPermission<T> extends ParseResult<T> {
|
||||
@Getter
|
||||
private final CommandNode<T> literal;
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Unknown literal: " + name;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static final class IllegalArgument<T> extends ParseResult<T> {
|
||||
@Getter
|
||||
private final CommandNode<T> literal;
|
||||
@Getter
|
||||
private final String message;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package de.kentoj.kencommandapi.internal.parser;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PermissionChecker<T> {
|
||||
boolean hasPermission(T sender, String permission);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue