diff --git a/RELEASE-NOTES.adoc b/RELEASE-NOTES.adoc index d68677866..38b65693e 100644 --- a/RELEASE-NOTES.adoc +++ b/RELEASE-NOTES.adoc @@ -2,6 +2,7 @@ == 0.9.8 - Bugfix and enhancements release for public review. API may change. +* #152 Added support for registering subcommands declaratively with the `@Command(subcommands={...})` annotation. Thanks to https://github.com/nagkumar[nagkumar]. * #146 Show underlying error when type conversion fails * #147 Toggle boolean flags instead of setting to `true` * #148 Long string in default value no longer causes infinite loop when printing usage. Thanks to https://github.com/smartboyathome[smartboyathome]. diff --git a/src/main/java/picocli/CommandLine.java b/src/main/java/picocli/CommandLine.java index 3bc8b1a8f..79f75dd64 100644 --- a/src/main/java/picocli/CommandLine.java +++ b/src/main/java/picocli/CommandLine.java @@ -1247,25 +1247,16 @@ private class Interpreter { " is missing the mandatory @Command annotation with a 'name' attribute"); } try { - Constructor constructor = sub.getConstructor(); - for (Constructor c : sub.getDeclaredConstructors()) { - if (c.getParameterTypes().length == 0) { - constructor = c; - break; - } - System.out.println(Arrays.toString(c.getParameterTypes())); - } - if (constructor == null) { - throw new IllegalArgumentException("Could not instantiate subcommand class " - + sub.getName() + ": missing no-arg constructor."); - } + Constructor constructor = sub.getDeclaredConstructor(); constructor.setAccessible(true); - addSubcommand(subCommand.name(), sub.newInstance()); + commands.put(subCommand.name(), toCommandLine(constructor.newInstance())); } catch (IllegalArgumentException ex) { throw ex; } + catch (NoSuchMethodException ex) { throw new IllegalArgumentException("Cannot instantiate subcommand " + + sub.getName() + ": the class has no constructor", ex); } catch (Exception ex) { throw new IllegalStateException("Could not instantiate and add subcommand " + - sub.getName() + " with name '" + subCommand.name() + "'", ex); + sub.getName() + ": " + ex, ex); } } } diff --git a/src/test/java/picocli/CommandLineTest.java b/src/test/java/picocli/CommandLineTest.java index 9ed91ccf8..4d3be6324 100644 --- a/src/test/java/picocli/CommandLineTest.java +++ b/src/test/java/picocli/CommandLineTest.java @@ -2802,22 +2802,26 @@ class App { assertEquals(expectedOutput, content); } + @Command(name = "subsub1") + static class SubSub1_testDeclarativelyAddSubcommands {private SubSub1_testDeclarativelyAddSubcommands(){}} + + @Command(name = "sub1", subcommands = {SubSub1_testDeclarativelyAddSubcommands.class}) + static class Sub1_testDeclarativelyAddSubcommands {public Sub1_testDeclarativelyAddSubcommands(){}} + + @Command(subcommands = {Sub1_testDeclarativelyAddSubcommands.class}) + static class MainCommand_testDeclarativelyAddSubcommands {} @Test public void testDeclarativelyAddSubcommands() { - @Command(name = "subsub1") - class SubSub1 {private SubSub1(){}} - - @Command(name = "sub1", subcommands = {SubSub1.class}) - class Sub1 {public Sub1(){}} + CommandLine main = new CommandLine(new MainCommand_testDeclarativelyAddSubcommands()); + assertEquals(1, main.getSubcommands().size()); - @Command(subcommands = {Sub1.class}) - class MainCommand {} + CommandLine sub1 = main.getSubcommands().get("sub1"); + assertEquals(Sub1_testDeclarativelyAddSubcommands.class, sub1.getCommand().getClass()); - CommandLine main = new CommandLine(new MainCommand()); - assertEquals(1, main.getSubcommands().size()); - assertEquals(Sub1.class, main.getSubcommands().get("sub1").getCommand().getClass()); + assertEquals(1, sub1.getSubcommands().size()); + CommandLine subsub1 = sub1.getSubcommands().get("subsub1"); + assertEquals(SubSub1_testDeclarativelyAddSubcommands.class, subsub1.getCommand().getClass()); } - @Ignore @Test public void testDeclarativelyAddSubcommandsFailsWithoutNoArgConstructor() { @Command(name = "sub1") class ABC {} @@ -2825,7 +2829,7 @@ public void testDeclarativelyAddSubcommandsFailsWithoutNoArgConstructor() { try { new CommandLine(new MainCommand()); } catch (IllegalArgumentException ex) { - assertEquals("Could not instantiate subcommand class picocli.CommandLineTest$1ABC: missing no-arg constructor.", ex.getMessage()); + assertEquals("Cannot instantiate subcommand picocli.CommandLineTest$1ABC: the class has no constructor", ex.getMessage()); } } @Test