argument parsing

This commit is contained in:
dfsek
2021-03-08 00:14:19 -07:00
parent 0d58201e3f
commit c1b04d1772
8 changed files with 151 additions and 10 deletions

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -13,4 +13,6 @@ public @interface Command {
Flag[] flags() default {};
Subcommand[] subcommands() default {};
String usage() default "";
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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");
}
}

View 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");
}
}
}