mock command API

This commit is contained in:
dfsek
2021-03-07 23:44:19 -07:00
parent c44d26cc18
commit 0d58201e3f
14 changed files with 301 additions and 9 deletions

View File

@@ -0,0 +1,9 @@
package com.dfsek.terra.api.command;
import java.util.List;
public interface CommandManager {
void execute(String command, List<String> args);
void register(String name, Class<? extends CommandTemplate> clazz);
}

View File

@@ -0,0 +1,5 @@
package com.dfsek.terra.api.command;
public interface CommandTemplate {
void execute(ExecutionState state);
}

View File

@@ -0,0 +1,45 @@
package com.dfsek.terra.api.command;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public final class ExecutionState {
private final Set<String> flags = new HashSet<>();
private final Map<String, String> args = new HashMap<>();
protected ExecutionState() {
}
protected void addFlag(String flag) {
flags.add(flag);
}
protected void addArgument(String arg, String value) {
args.put(arg, value);
}
@SuppressWarnings("unchecked")
public <T> T getArgument(String argument, Class<T> clazz) {
String value = args.get(argument);
if(value == null) throw new IllegalArgumentException("Argument \"" + argument + "\" does not exist!");
if(clazz == int.class || clazz == Integer.class) {
return (T) new Integer(Integer.parseInt(value));
}
if(clazz == double.class || clazz == Double.class) {
return (T) new Double(Double.parseDouble(value));
}
// TODO: type loaders
return (T) value;
}
public boolean hasFlag(String flag) {
return flags.contains(flag);
}
}

View File

@@ -0,0 +1,85 @@
package com.dfsek.terra.api.command;
import com.dfsek.terra.api.command.annotation.Argument;
import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.Subcommand;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TerraCommandManager implements CommandManager {
Map<String, CommandHolder> commands = new HashMap<>();
@Override
public void execute(String commandName, List<String> args) {
ExecutionState state = new ExecutionState();
CommandHolder commandHolder = commands.get(commandName);
Class<? extends CommandTemplate> commandClass = commandHolder.clazz;
if(!commandClass.isAnnotationPresent(Command.class)) {
invoke(commandClass, state);
}
Command command = commandClass.getAnnotation(Command.class);
if(command.arguments().length == 0 && command.subcommands().length == 0) {
if(args.isEmpty()) invoke(commandClass, state);
else throw new IllegalArgumentException("Expected 0 arguments, found " + args.size());
}
if(commandHolder.subcommands.containsKey(args.get(0))) {
invoke(commandClass, state);
}
boolean req = true;
for(Argument argument : command.arguments()) {
if(!req && argument.required())
throw new IllegalStateException("Required arguments must come first! Arguments: " + Arrays.toString(command.arguments()));
req = argument.required();
String arg = args.get(0);
if(arg.startsWith("-")) { // flags have started.
}
}
}
private void invoke(Class<? extends CommandTemplate> clazz, ExecutionState state) {
try {
clazz.getConstructor().newInstance().execute(state);
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
}
@Override
public void register(String name, Class<? extends CommandTemplate> clazz) {
commands.put(name, new CommandHolder(clazz));
}
private static final class CommandHolder {
private final Class<? extends CommandTemplate> clazz;
private final Map<String, Subcommand> subcommands = new HashMap<>();
private CommandHolder(Class<? extends CommandTemplate> clazz) {
this.clazz = clazz;
if(clazz.isAnnotationPresent(Command.class)) {
Command command = clazz.getAnnotation(Command.class);
for(Subcommand subcommand : command.subcommands()) {
subcommands.put(subcommand.value(), subcommand);
for(String alias : subcommand.aliases()) {
subcommands.put(alias, subcommand);
}
}
}
}
}
}

View File

@@ -0,0 +1,16 @@
package com.dfsek.terra.api.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Argument {
String value();
boolean required() default true;
Class<?> type() default String.class;
}

View File

@@ -0,0 +1,16 @@
package com.dfsek.terra.api.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Command {
Argument[] arguments() default {};
Flag[] flags() default {};
Subcommand[] subcommands() default {};
}

View File

@@ -0,0 +1,16 @@
package com.dfsek.terra.api.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Flag {
String value();
String shorthand() default "";
String defaultValue() default "";
}

View File

@@ -0,0 +1,18 @@
package com.dfsek.terra.api.command.annotation;
import com.dfsek.terra.api.command.CommandTemplate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Subcommand {
String value();
String[] aliases() default {};
Class<? extends CommandTemplate> clazz();
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.commands;
import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.ExecutionState;
import com.dfsek.terra.api.command.annotation.Command;
@Command()
public class ReloadCommand implements CommandTemplate {
@Override
public void execute(ExecutionState state) {
}
}

View File

@@ -0,0 +1,29 @@
package com.dfsek.terra.commands;
import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.ExecutionState;
import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.Subcommand;
import com.dfsek.terra.commands.structure.StructureExportCommand;
import com.dfsek.terra.commands.structure.StructureLoadCommand;
@Command(
subcommands = {
@Subcommand(
clazz = StructureExportCommand.class,
value = "export",
aliases = "ex"
),
@Subcommand(
clazz = StructureLoadCommand.class,
value = "load",
aliases = "ld"
)
}
)
public class StructureCommand implements CommandTemplate {
@Override
public void execute(ExecutionState state) {
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.commands.structure;
import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.ExecutionState;
import com.dfsek.terra.api.command.annotation.Command;
@Command
public class StructureExportCommand implements CommandTemplate {
@Override
public void execute(ExecutionState state) {
}
}

View File

@@ -0,0 +1,25 @@
package com.dfsek.terra.commands.structure;
import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.ExecutionState;
import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.Flag;
@Command(
flags = {
@Flag(value = "rotation",
defaultValue = "0",
shorthand = "r"
),
@Flag(value = "load",
defaultValue = "FULL",
shorthand = "l"
)
}
)
public class StructureLoadCommand implements CommandTemplate {
@Override
public void execute(ExecutionState state) {
}
}

View File

@@ -13,7 +13,7 @@ import java.util.Arrays;
import java.util.List;
/**
* Represents a command or subcommand, can be nested via getSubCommands.
* Represents a command or subcommands, can be nested via getSubCommands.
*/
public abstract class Command implements CommandExecutor, TabCompleter {
private final TerraPlugin main;
@@ -29,7 +29,8 @@ public abstract class Command implements CommandExecutor, TabCompleter {
}
/**
* Gets the name of the command/subcommand
* Gets the name of the command/subcommands
*
* @return Name of command
*/
public abstract String getName();
@@ -47,7 +48,7 @@ public abstract class Command implements CommandExecutor, TabCompleter {
* (if defined) will be sent to the player.
*
* @param sender Source of the command
* @param command Command which was executed
* @param command CommandTemplate which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false
@@ -67,7 +68,7 @@ public abstract class Command implements CommandExecutor, TabCompleter {
* (if defined) will be sent to the player.
*
* @param sender Source of the command
* @param command Command which was executed
* @param command CommandTemplate which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false

View File

@@ -19,10 +19,10 @@ public abstract class PlayerCommand extends Command {
* If false is returned, then the "usage" plugin.yml entry for this command
* (if defined) will be sent to the player.
*
* @param sender Source of the command
* @param command Command which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @param sender Source of the command
* @param command CommandTemplate which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false
*/
@Override
@@ -34,6 +34,7 @@ public abstract class PlayerCommand extends Command {
Player p = (Player) sender;
return execute(p, command, label, args);
}
/**
* Executes the given command, returning its success.
* <br>
@@ -41,7 +42,7 @@ public abstract class PlayerCommand extends Command {
* (if defined) will be sent to the player.
*
* @param sender Player that executed command
* @param command Command which was executed
* @param command CommandTemplate which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false