Skip to content

Commit

Permalink
Can now run basic commands without parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
prokopyl committed Nov 17, 2020
1 parent f07790e commit 3261c1b
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
package fr.zcraft.quartzlib.components.commands.internal;

import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import java.util.ArrayList;
import java.util.List;

class CommandEndpoint extends CommandNode {
private final List<CommandMethod> methods = new ArrayList<>();

CommandEndpoint(String name) {
super(name);
super(name, null);
}

@Override
void run(Object instance, String[] args) {
this.methods.get(0).run(instance, args);
}

void addMethod(CommandMethod method) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,23 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import java.util.function.Supplier;

class CommandGroup extends CommandNode {
private final Class<?> commandGroupClass;
private final CommandGroup parent;
private final Supplier<?> classInstanceSupplier;

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

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

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

public Iterable<CommandNode> getSubCommands () {
Expand All @@ -39,9 +37,15 @@ private void addMethod(CommandMethod method) {
endpoint.addMethod(method);
}

// Private utils TODO: move to DiscoveryUtils?
void run(String... args) {
Object commandObject = classInstanceSupplier.get();
run(commandObject, args);
}

private static Stream<CommandMethod> getCommandMethods(Class<?> commandGroupClass) {
return Arrays.stream(commandGroupClass.getDeclaredMethods()).map(CommandMethod::new);
@Override
void run(Object instance, String[] args) {
String commandName = args[0];
CommandNode subCommand = subCommands.get(commandName);
subCommand.run(instance, Arrays.copyOfRange(args, 1, args.length));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fr.zcraft.quartzlib.components.commands.internal;

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

class CommandMethod {
Expand All @@ -14,4 +15,12 @@ class CommandMethod {
public String getName() {
return name;
}

public void run(Object target, String[] args) {
try {
this.method.invoke(target, (Object[]) args);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace(); // TODO
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@

abstract class CommandNode {
private final String name;
private final CommandGroup parent;

protected CommandNode(String name) {
protected CommandNode(String name, CommandGroup parent) {
this.name = name;
this.parent = parent;
}

public String getName() {
return name;
}

public CommandGroup getParent() {
return parent;
}

abstract void run(Object instance, String[] args);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package fr.zcraft.quartzlib.components.commands.internal;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
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) {
return Arrays.stream(commandGroupClass.getDeclaredMethods())
.filter(m -> Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers()))
.map(CommandMethod::new);
}

public static Supplier<?> getClassConstructorSupplier (Class<?> commandGroupClass) {
Constructor<?> constructor = commandGroupClass.getDeclaredConstructors()[0];
return () -> {
try {
return constructor.newInstance();
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new RuntimeException(e); // TODO
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@

import java.util.stream.StreamSupport;

// This is outside because inner classes cannot have statics
class CommandWithStatics {
public void add () {}
private void get () {}
protected void list () {}
public void delete () {}
void update () {}
static public void staticMethod () {}
}

public class CommandGraphTests {
@Test public void canDiscoverBasicSubcommands() {
class FooCommand {
Expand All @@ -13,8 +23,29 @@ public void get () {}
public void list () {}
}

CommandGroup commandGroup = new CommandGroup(FooCommand.class, "foo");
CommandGroup commandGroup = new CommandGroup(FooCommand.class, () -> new FooCommand(), "foo");
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");
String[] commandNames = StreamSupport.stream(commandGroup.getSubCommands().spliterator(), false).map(CommandNode::getName).toArray(String[]::new);
Assertions.assertArrayEquals(new String[] {"add", "delete"}, commandNames);
}

@Test public void canRunBasicSubcommands() {
final boolean[] ran = {false, false, false};

class FooCommand {
public void add () { ran[0] = true; }
public void get () { ran[1] = true; }
public void list () { ran[2] = true; }
}

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

0 comments on commit 3261c1b

Please sign in to comment.