diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 486ec8f34..94e2d4982 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -26,6 +26,7 @@ No features have been promoted in this picocli release. ## Fixed issues +- [#283] Enhancement and API Change: Provide `getMissing` method on MissingParameterException to get a reference to the problematic options and positional parameters. Thanks to [jcapsule](https://github.com/jcapsule) for the suggestion. - [#323] Enhancement: Remove dependency on java.sql package: picocli should only require the java.base module when running in Java 9. - [#325] Enhancement: Allow custom type converter to map empty String to custom default value for empty options. Thanks to [jesselong](https://github.com/jesselong) for the suggestion. - [#303] Enhancement: Improve validation to prevent common mistakes. diff --git a/src/main/java/picocli/CommandLine.java b/src/main/java/picocli/CommandLine.java index 6640fbeeb..d63da94a1 100644 --- a/src/main/java/picocli/CommandLine.java +++ b/src/main/java/picocli/CommandLine.java @@ -5191,7 +5191,7 @@ private void assertNoMissingParameters(ArgSpec argSpec, int arity, Stack if (arity > args.size()) { if (arity == 1) { if (argSpec.isOption()) { - throw new MissingParameterException(CommandLine.this, "Missing required parameter for " + + throw new MissingParameterException(CommandLine.this, argSpec, "Missing required parameter for " + optionDescription("", argSpec, 0)); } Range indexRange = ((PositionalParamSpec) argSpec).index(); @@ -5213,13 +5213,13 @@ private void assertNoMissingParameters(ArgSpec argSpec, int arity, Stack } else { msg += (count > 1 ? "s: " : ": "); } - throw new MissingParameterException(CommandLine.this, msg + names); + throw new MissingParameterException(CommandLine.this, argSpec, msg + names); } if (args.isEmpty()) { - throw new MissingParameterException(CommandLine.this, optionDescription("", argSpec, 0) + + throw new MissingParameterException(CommandLine.this, argSpec, optionDescription("", argSpec, 0) + " requires at least " + arity + " values, but none were specified."); } - throw new MissingParameterException(CommandLine.this, optionDescription("", argSpec, 0) + + throw new MissingParameterException(CommandLine.this, argSpec, optionDescription("", argSpec, 0) + " requires at least " + arity + " values, but only " + args.size() + " were specified: " + reverse(args)); } } @@ -7371,20 +7371,23 @@ private static ParameterException create(CommandLine cmd, Exception ex, String a */ public static class MissingParameterException extends ParameterException { private static final long serialVersionUID = 5075678535706338753L; - public MissingParameterException(CommandLine commandLine, String msg) { + private final List missing; + public MissingParameterException(CommandLine commandLine, ArgSpec missing, String msg) { this(commandLine, Arrays.asList(missing), msg); } + public MissingParameterException(CommandLine commandLine, Collection missing, String msg) { super(commandLine, msg); + this.missing = Collections.unmodifiableList(new ArrayList(missing)); } - + public List getMissing() { return missing; } private static MissingParameterException create(CommandLine cmd, Collection missing, String separator) { if (missing.size() == 1) { - return new MissingParameterException(cmd, "Missing required option '" + return new MissingParameterException(cmd, missing, "Missing required option '" + describe(missing.iterator().next(), separator) + "'"); } List names = new ArrayList(missing.size()); for (ArgSpec argSpec : missing) { names.add(describe(argSpec, separator)); } - return new MissingParameterException(cmd, "Missing required options " + names.toString()); + return new MissingParameterException(cmd, missing, "Missing required options " + names.toString()); } private static String describe(ArgSpec argSpec, String separator) { String prefix = (argSpec.isOption()) diff --git a/src/test/java/picocli/CommandLineArityTest.java b/src/test/java/picocli/CommandLineArityTest.java index e1019d4d5..5a8f276dc 100644 --- a/src/test/java/picocli/CommandLineArityTest.java +++ b/src/test/java/picocli/CommandLineArityTest.java @@ -272,6 +272,8 @@ class Example { fail("Should not accept missing mandatory parameter"); } catch (MissingParameterException ex) { assertEquals("Missing required parameter: ", ex.getMessage()); + assertEquals(1, ex.getMissing().size()); + assertEquals("", ex.getMissing().get(0).paramLabel()); } } @Test @@ -662,6 +664,8 @@ class OptionsNoArityAndParameters { fail("expected MissingParameterException"); } catch (MissingParameterException ok) { assertEquals("Missing required parameter for option '-chars' ()", ok.getMessage()); + assertEquals(1, ok.getMissing().size()); + assertTrue(ok.getMissing().get(0).toString(), ok.getMissing().get(0) instanceof Model.OptionSpec); } } @@ -917,6 +921,8 @@ class Arg { fail("Expected MissingParameterException"); } catch (MissingParameterException ex) { assertEquals("positional parameter at index 0..* () requires at least 2 values, but only 1 were specified: [p3]", ex.getMessage()); + assertEquals(1, ex.getMissing().size()); + assertTrue(ex.getMissing().get(0).toString(), ex.getMissing().get(0) instanceof Model.PositionalParamSpec); } } diff --git a/src/test/java/picocli/CommandLineModelTest.java b/src/test/java/picocli/CommandLineModelTest.java index 50457edf4..e22b8e0cf 100644 --- a/src/test/java/picocli/CommandLineModelTest.java +++ b/src/test/java/picocli/CommandLineModelTest.java @@ -261,6 +261,8 @@ public void testVersionHelp_helpCommand() { commandLine.parse(); } catch (MissingParameterException ex) { assertEquals("Missing required option '-x=PARAM'", ex.getMessage()); + assertEquals(1, ex.getMissing().size()); + assertSame(ex.getMissing().get(0).toString(), parent.posixOptionsMap().get('x'), ex.getMissing().get(0)); } } diff --git a/src/test/java/picocli/CommandLineTest.java b/src/test/java/picocli/CommandLineTest.java index 68a7528a3..940196e88 100644 --- a/src/test/java/picocli/CommandLineTest.java +++ b/src/test/java/picocli/CommandLineTest.java @@ -1681,6 +1681,8 @@ class Args { fail("MissingParameterException expected"); } catch (MissingParameterException ex) { assertEquals("positional parameter at index 0..* () requires at least 2 values, but only 1 were specified: [a,b,c,d,e]", ex.getMessage()); + assertEquals(1, ex.getMissing().size()); + assertTrue(ex.getMissing().get(0).toString(), ex.getMissing().get(0) instanceof Model.PositionalParamSpec); } try { CommandLine.populateCommand(new Args()); // 0 arg: should fail