From 3261c1bf724bff2aa17026d50845573a954212b9 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Tue, 17 Nov 2020 04:42:54 +0100 Subject: [PATCH] Can now run basic commands without parameters --- .../commands/internal/CommandEndpoint.java | 9 ++++- .../commands/internal/CommandGroup.java | 30 +++++++++-------- .../commands/internal/CommandMethod.java | 9 +++++ .../commands/internal/CommandNode.java | 10 +++++- .../commands/internal/DiscoveryUtils.java | 27 +++++++++++++++ .../commands/internal/CommandGraphTests.java | 33 ++++++++++++++++++- 6 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 src/main/java/fr/zcraft/quartzlib/components/commands/internal/DiscoveryUtils.java diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandEndpoint.java b/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandEndpoint.java index 87cdaf02..8e48fffe 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandEndpoint.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandEndpoint.java @@ -1,5 +1,7 @@ package fr.zcraft.quartzlib.components.commands.internal; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + import java.util.ArrayList; import java.util.List; @@ -7,7 +9,12 @@ class CommandEndpoint extends CommandNode { private final List 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) { diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandGroup.java b/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandGroup.java index c0dfc935..d6260695 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandGroup.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandGroup.java @@ -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 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 getSubCommands () { @@ -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 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)); } } diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandMethod.java b/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandMethod.java index fcaa99fa..d97e03bc 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandMethod.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandMethod.java @@ -1,5 +1,6 @@ package fr.zcraft.quartzlib.components.commands.internal; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; class CommandMethod { @@ -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 + } + } } diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandNode.java b/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandNode.java index c6d38306..75b69d13 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandNode.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/internal/CommandNode.java @@ -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); } diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/internal/DiscoveryUtils.java b/src/main/java/fr/zcraft/quartzlib/components/commands/internal/DiscoveryUtils.java new file mode 100644 index 00000000..09a30f1e --- /dev/null +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/internal/DiscoveryUtils.java @@ -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 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 + } + }; + } +} diff --git a/src/test/java/fr/zcraft/quartzlib/components/commands/internal/CommandGraphTests.java b/src/test/java/fr/zcraft/quartzlib/components/commands/internal/CommandGraphTests.java index c5c72dd4..fa0210b4 100644 --- a/src/test/java/fr/zcraft/quartzlib/components/commands/internal/CommandGraphTests.java +++ b/src/test/java/fr/zcraft/quartzlib/components/commands/internal/CommandGraphTests.java @@ -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 { @@ -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); + } }