mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-04-14 11:46:06 +00:00
argument parsing
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package com.dfsek.terra.api.command;
|
||||
|
||||
import com.dfsek.terra.api.command.exception.CommandException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CommandManager {
|
||||
void execute(String command, List<String> args);
|
||||
void execute(String command, List<String> args) throws CommandException;
|
||||
|
||||
void register(String name, Class<? extends CommandTemplate> clazz);
|
||||
}
|
||||
|
||||
@@ -3,19 +3,24 @@ 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 com.dfsek.terra.api.command.exception.CommandException;
|
||||
import com.dfsek.terra.api.command.exception.InvalidArgumentsException;
|
||||
import com.dfsek.terra.api.command.exception.MalformedCommandException;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
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<>();
|
||||
private final Map<String, CommandHolder> commands = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void execute(String commandName, List<String> args) {
|
||||
public void execute(String commandName, List<String> argsIn) throws CommandException {
|
||||
|
||||
List<String> args = new ArrayList<>(argsIn);
|
||||
ExecutionState state = new ExecutionState();
|
||||
|
||||
CommandHolder commandHolder = commands.get(commandName);
|
||||
@@ -29,34 +34,45 @@ public class TerraCommandManager implements CommandManager {
|
||||
|
||||
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());
|
||||
else throw new InvalidArgumentsException("Expected 0 arguments, found " + args.size());
|
||||
}
|
||||
|
||||
if(commandHolder.subcommands.containsKey(args.get(0))) {
|
||||
invoke(commandClass, state);
|
||||
invoke(commandHolder.subcommands.get(args.get(0)).clazz(), 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()));
|
||||
throw new MalformedCommandException("Required arguments must come first! Arguments: " + Arrays.toString(command.arguments()));
|
||||
req = argument.required();
|
||||
|
||||
if(args.isEmpty()) {
|
||||
if(req) throw new InvalidArgumentsException("Invalid arguments: " + command.usage());
|
||||
break;
|
||||
}
|
||||
|
||||
String arg = args.get(0);
|
||||
|
||||
if(arg.startsWith("-")) { // flags have started.
|
||||
|
||||
if(req) throw new InvalidArgumentsException("Flags must come after arguments.");
|
||||
break;
|
||||
}
|
||||
|
||||
state.addArgument(argument.value(), args.remove(0));
|
||||
}
|
||||
|
||||
|
||||
invoke(commandClass, state);
|
||||
}
|
||||
|
||||
private void invoke(Class<? extends CommandTemplate> clazz, ExecutionState state) {
|
||||
private void invoke(Class<? extends CommandTemplate> clazz, ExecutionState state) throws MalformedCommandException {
|
||||
try {
|
||||
System.out.println("invocation");
|
||||
clazz.getConstructor().newInstance().execute(state);
|
||||
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
throw new MalformedCommandException("Unable to reflectively instantiate command: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,4 +13,6 @@ public @interface Command {
|
||||
Flag[] flags() default {};
|
||||
|
||||
Subcommand[] subcommands() default {};
|
||||
|
||||
String usage() default "";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
public abstract class CommandException extends Exception {
|
||||
private static final long serialVersionUID = -2955328495045879822L;
|
||||
|
||||
public CommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CommandException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
public class InvalidArgumentsException extends CommandException {
|
||||
private static final long serialVersionUID = 7563619667472569824L;
|
||||
|
||||
public InvalidArgumentsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidArgumentsException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
/**
|
||||
* Thrown when command is incorrectly defined.
|
||||
*/
|
||||
public class MalformedCommandException extends CommandException {
|
||||
private static final long serialVersionUID = -5417760860407895496L;
|
||||
|
||||
public MalformedCommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MalformedCommandException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,6 @@ import com.dfsek.terra.api.command.annotation.Command;
|
||||
public class StructureExportCommand implements CommandTemplate {
|
||||
@Override
|
||||
public void execute(ExecutionState state) {
|
||||
|
||||
System.out.println("export command");
|
||||
}
|
||||
}
|
||||
|
||||
79
common/src/test/java/command/CommandTest.java
Normal file
79
common/src/test/java/command/CommandTest.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package command;
|
||||
|
||||
import com.dfsek.terra.api.command.CommandManager;
|
||||
import com.dfsek.terra.api.command.CommandTemplate;
|
||||
import com.dfsek.terra.api.command.ExecutionState;
|
||||
import com.dfsek.terra.api.command.TerraCommandManager;
|
||||
import com.dfsek.terra.api.command.annotation.Argument;
|
||||
import com.dfsek.terra.api.command.annotation.Command;
|
||||
import com.dfsek.terra.api.command.exception.CommandException;
|
||||
import com.dfsek.terra.api.command.exception.MalformedCommandException;
|
||||
import com.dfsek.terra.commands.StructureCommand;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class CommandTest {
|
||||
@Test
|
||||
public void subcommand() throws CommandException {
|
||||
CommandManager manager = new TerraCommandManager();
|
||||
manager.register("structure", StructureCommand.class);
|
||||
|
||||
manager.execute("structure", Arrays.asList("export"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void args() throws CommandException {
|
||||
CommandManager manager = new TerraCommandManager();
|
||||
manager.register("test", DemoCommand.class);
|
||||
|
||||
manager.execute("test", Arrays.asList("first", "2"));
|
||||
manager.execute("test", Arrays.asList("first", "2", "3.4"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requiredArgsFirst() throws CommandException {
|
||||
CommandManager manager = new TerraCommandManager();
|
||||
manager.register("test", DemoInvalidCommand.class);
|
||||
|
||||
try {
|
||||
manager.execute("test", Arrays.asList("first", "2"));
|
||||
fail();
|
||||
} catch(MalformedCommandException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
@Command(arguments = {
|
||||
@Argument(value = "arg0"),
|
||||
@Argument(value = "arg1", type = int.class),
|
||||
@Argument(value = "arg2", type = double.class, required = false)
|
||||
})
|
||||
public static final class DemoCommand implements CommandTemplate {
|
||||
|
||||
@Override
|
||||
public void execute(ExecutionState state) {
|
||||
System.out.println(state.getArgument("arg0", String.class));
|
||||
System.out.println(state.getArgument("arg1", String.class));
|
||||
try {
|
||||
System.out.println(state.getArgument("arg2", String.class));
|
||||
} catch(IllegalArgumentException e) {
|
||||
System.out.println("arg2 undefined.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Command(arguments = {
|
||||
@Argument(value = "arg0"),
|
||||
@Argument(value = "arg2", type = double.class, required = false), // optional arguments must be last. this command is invalid.
|
||||
@Argument(value = "arg1", type = int.class)
|
||||
})
|
||||
public static final class DemoInvalidCommand implements CommandTemplate {
|
||||
|
||||
@Override
|
||||
public void execute(ExecutionState state) {
|
||||
throw new Error("this should never be reached");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user