Skip to content

Commit

Permalink
Can now parse basic parameter types
Browse files Browse the repository at this point in the history
  • Loading branch information
prokopyl committed Nov 18, 2020
1 parent 3261c1b commit 6282592
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package fr.zcraft.quartzlib.components.commands;

@FunctionalInterface
public interface ArgumentType<T> {
T parse(String raw);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package fr.zcraft.quartzlib.components.commands;

import fr.zcraft.quartzlib.components.commands.ArgumentType;

public class ArgumentTypeHandler<T> {
private final Class<T> resultType;
private final ArgumentType<T> typeHandler;

public ArgumentTypeHandler(Class<T> resultType, ArgumentType<T> typeHandler) {
this.resultType = resultType;
this.typeHandler = typeHandler;
}

public ArgumentType<T> getTypeHandler() {
return typeHandler;
}

public Class<T> getResultType() {
return resultType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package fr.zcraft.quartzlib.components.commands;

import fr.zcraft.quartzlib.components.commands.arguments.primitive.IntegerTypeHandler;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

class ArgumentTypeHandlerCollection {
private final Map<Class<?>, ArgumentTypeHandler<?>> argumentTypeHandlerMap = new HashMap<>();

public ArgumentTypeHandlerCollection () {
this.registerNativeTypes();
}

public <T> void register(ArgumentTypeHandler<T> typeHandler)
{
argumentTypeHandlerMap.put(typeHandler.getResultType(), typeHandler);
}

public Optional<ArgumentTypeHandler<?>> findTypeHandler(Class<?> resultType) {
return Optional.ofNullable(argumentTypeHandlerMap.get(resultType));
}

private void registerNativeTypes () {
register(new ArgumentTypeHandler<>(Integer.class, new IntegerTypeHandler()));

register(new ArgumentTypeHandler<>(String.class, s -> s));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
package fr.zcraft.quartzlib.components.commands.internal;

import sun.reflect.generics.reflectiveObjects.NotImplementedException;
package fr.zcraft.quartzlib.components.commands;

import java.util.ArrayList;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fr.zcraft.quartzlib.components.commands.internal;
package fr.zcraft.quartzlib.components.commands;

import java.util.Arrays;
import java.util.HashMap;
Expand All @@ -11,15 +11,15 @@ class CommandGroup extends CommandNode {

private final Map<String, CommandNode> subCommands = new HashMap<>();

public CommandGroup(Class<?> commandGroupClass, Supplier<?> classInstanceSupplier, String name) {
this(commandGroupClass, classInstanceSupplier, name, null);
public CommandGroup(Class<?> commandGroupClass, Supplier<?> classInstanceSupplier, String name, ArgumentTypeHandlerCollection typeHandlerCollection) {
this(commandGroupClass, classInstanceSupplier, name, typeHandlerCollection, null);
}

public CommandGroup(Class<?> commandGroupClass, Supplier<?> classInstanceSupplier, String name, CommandGroup parent) {
public CommandGroup(Class<?> commandGroupClass, Supplier<?> classInstanceSupplier, String name, ArgumentTypeHandlerCollection typeHandlerCollection, CommandGroup parent) {
super(name, parent);
this.commandGroupClass = commandGroupClass;
this.classInstanceSupplier = classInstanceSupplier;
DiscoveryUtils.getCommandMethods(commandGroupClass).forEach(this::addMethod);
DiscoveryUtils.getCommandMethods(commandGroupClass, typeHandlerCollection).forEach(this::addMethod);
}

public Iterable<CommandNode> getSubCommands () {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package fr.zcraft.quartzlib.components.commands;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

public class CommandManager {
private final Map<String, CommandNode> rootCommands = new HashMap<>();
private final ArgumentTypeHandlerCollection typeHandlerCollection = new ArgumentTypeHandlerCollection();

public <T> void registerCommand(String name, Class<T> commandType, Supplier<T> commandClassSupplier) {
CommandGroup group = new CommandGroup(commandType, commandClassSupplier, name, typeHandlerCollection);
rootCommands.put(name, group);
}

public void run(String commandName, String... args) {
((CommandGroup) rootCommands.get(commandName)).run(args); // TODO
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package fr.zcraft.quartzlib.components.commands;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

class CommandMethod {
private final Method method;
private final String name;
private final CommandMethodArgument[] arguments;

CommandMethod(Method method, ArgumentTypeHandlerCollection typeHandlerCollection) {
this.method = method;
this.name = method.getName();

arguments = Arrays.stream(method.getParameters())
.map(p -> new CommandMethodArgument(p, typeHandlerCollection))
.toArray(CommandMethodArgument[]::new);
}

public String getName() {
return name;
}

public void run(Object target, String[] args) {
Object[] parsedArgs = parseArguments(args);
try {
this.method.invoke(target, parsedArgs);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace(); // TODO
}
}

private Object[] parseArguments(String[] args) {
Object[] parsed = new Object[args.length];

for (int i = 0; i < args.length; i++) {
parsed[i] = arguments[i].parse(args[i]);
}

return parsed;
}

public CommandMethodArgument[] getArguments() {
return arguments;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package fr.zcraft.quartzlib.components.commands;

import java.lang.reflect.Parameter;

public class CommandMethodArgument {
private final Parameter parameter;
private final ArgumentTypeHandler<?> typeHandler;

public CommandMethodArgument(Parameter parameter, ArgumentTypeHandlerCollection typeHandlerCollection) {
this.parameter = parameter;
this.typeHandler = typeHandlerCollection.findTypeHandler(parameter.getType()).get(); // FIXME: handle unknown types
}

public Object parse(String raw) {
return this.typeHandler.getTypeHandler().parse(raw);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fr.zcraft.quartzlib.components.commands.internal;
package fr.zcraft.quartzlib.components.commands;

abstract class CommandNode {
private final String name;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package fr.zcraft.quartzlib.components.commands.internal;
package fr.zcraft.quartzlib.components.commands;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.function.Supplier;
import java.util.stream.Stream;

abstract class DiscoveryUtils {
public static Stream<CommandMethod> getCommandMethods(Class<?> commandGroupClass) {
public static Stream<CommandMethod> getCommandMethods(Class<?> commandGroupClass, ArgumentTypeHandlerCollection typeHandlerCollection) {
return Arrays.stream(commandGroupClass.getDeclaredMethods())
.filter(m -> Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers()))
.map(CommandMethod::new);
.map((Method method) -> new CommandMethod(method, typeHandlerCollection));
}

public static Supplier<?> getClassConstructorSupplier (Class<?> commandGroupClass) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package fr.zcraft.quartzlib.components.commands.arguments.primitive;

import fr.zcraft.quartzlib.components.commands.ArgumentType;

public class IntegerTypeHandler implements ArgumentType<Integer> {
@Override
public Integer parse(String raw) {
return Integer.parseInt(raw, 10); // TODO: handle exceptions
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fr.zcraft.quartzlib.components.commands.internal;
package fr.zcraft.quartzlib.components.commands;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.stream.StreamSupport;
Expand All @@ -16,20 +17,27 @@ static public void staticMethod () {}
}

public class CommandGraphTests {
private CommandManager commands;

@BeforeEach
public void beforeEach () {
commands = new CommandManager();
}

@Test public void canDiscoverBasicSubcommands() {
class FooCommand {
public void add () {}
public void get () {}
public void list () {}
}

CommandGroup commandGroup = new CommandGroup(FooCommand.class, () -> new FooCommand(), "foo");
CommandGroup commandGroup = new CommandGroup(FooCommand.class, () -> new FooCommand(), "foo", new ArgumentTypeHandlerCollection());
String[] commandNames = StreamSupport.stream(commandGroup.getSubCommands().spliterator(), false).map(CommandNode::getName).toArray(String[]::new);
Assertions.assertArrayEquals(new String[] {"add", "get", "list"}, commandNames);
}

@Test public void onlyDiscoversPublicMethods() {
CommandGroup commandGroup = new CommandGroup(CommandWithStatics.class, () -> new CommandWithStatics(), "foo");
CommandGroup commandGroup = new CommandGroup(CommandWithStatics.class, () -> new CommandWithStatics(), "foo", new ArgumentTypeHandlerCollection());
String[] commandNames = StreamSupport.stream(commandGroup.getSubCommands().spliterator(), false).map(CommandNode::getName).toArray(String[]::new);
Assertions.assertArrayEquals(new String[] {"add", "delete"}, commandNames);
}
Expand All @@ -43,9 +51,32 @@ class FooCommand {
public void list () { ran[2] = true; }
}

FooCommand f = new FooCommand();
CommandGroup commandGroup = new CommandGroup(FooCommand.class, () -> new FooCommand(),"foo");
commandGroup.run("get");
commands.registerCommand("foo", FooCommand.class, () -> new FooCommand());
commands.run("foo", "get");
Assertions.assertArrayEquals(new boolean[] { false, true, false }, ran);
}

@Test public void canReceiveStringArguments() {
final String[] argValue = {""};

class FooCommand {
public void add (String arg) { argValue[0] = arg; }
}

commands.registerCommand("foo", FooCommand.class, () -> new FooCommand());
commands.run("foo", "add", "pomf");
Assertions.assertArrayEquals(new String[] { "pomf" }, argValue);
}

@Test public void canReceiveParsedArguments() {
final int[] argValue = {0};

class FooCommand {
public void add (Integer arg) { argValue[0] = arg; }
}

commands.registerCommand("foo", FooCommand.class, () -> new FooCommand());
commands.run("foo", "add", "42");
Assertions.assertArrayEquals(new int[] { 42 }, argValue);
}
}

0 comments on commit 6282592

Please sign in to comment.