This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-cli.git
The following commit(s) were added to refs/heads/master by this push: new 2130068 Avoid throwing NullPointerException when calling CommandLineParser will null array elements 2130068 is described below commit 21300689546875736fce627970842691da97bd07 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Sat May 11 10:03:37 2024 -0400 Avoid throwing NullPointerException when calling CommandLineParser will null array elements --- src/changes/changes.xml | 1 + .../java/org/apache/commons/cli/DefaultParser.java | 34 ++++----- .../java/org/apache/commons/cli/GnuParser.java | 56 ++++++++------- src/main/java/org/apache/commons/cli/Parser.java | 61 ++++++++-------- .../java/org/apache/commons/cli/PosixParser.java | 83 ++++++++++------------ .../apache/commons/cli/AbstractParserTestCase.java | 17 +++++ 6 files changed, 133 insertions(+), 119 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 746a4a7..ebc9d9f 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -24,6 +24,7 @@ <body> <release version="1.7.1" date="YYYY-MM-DD" description="This release contains new features and bug fixes and requires Java 8 or above."> <action type="fix" issue="CLI-331" dev="ggregory" due-to="Claude Warren, Gary Gregory">Handle reporting of deprecated options when parameters are not String type. #270.</action> + <action type="fix" issue="CLI-331" dev="ggregory" due-to="Claude Warren, Gary Gregory">Avoid throwing NullPointerException when calling CommandLineParser will null array elements.</action> </release> <release version="1.7.0" date="2024-04-13" description="This release contains new features and bug fixes and requires Java 8 or above."> <!-- FIX --> diff --git a/src/main/java/org/apache/commons/cli/DefaultParser.java b/src/main/java/org/apache/commons/cli/DefaultParser.java index a39ce4b..7dd9bfd 100644 --- a/src/main/java/org/apache/commons/cli/DefaultParser.java +++ b/src/main/java/org/apache/commons/cli/DefaultParser.java @@ -541,22 +541,24 @@ public class DefaultParser implements CommandLineParser { * @throws ParseException */ private void handleToken(final String token) throws ParseException { - currentToken = token; - if (skipParsing) { - cmd.addArg(token); - } else if ("--".equals(token)) { - skipParsing = true; - } else if (currentOption != null && currentOption.acceptsArg() && isArgument(token)) { - currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOn(token)); - } else if (token.startsWith("--")) { - handleLongOption(token); - } else if (token.startsWith("-") && !"-".equals(token)) { - handleShortAndLongOption(token); - } else { - handleUnknownToken(token); - } - if (currentOption != null && !currentOption.acceptsArg()) { - currentOption = null; + if (token != null) { + currentToken = token; + if (skipParsing) { + cmd.addArg(token); + } else if ("--".equals(token)) { + skipParsing = true; + } else if (currentOption != null && currentOption.acceptsArg() && isArgument(token)) { + currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOn(token)); + } else if (token.startsWith("--")) { + handleLongOption(token); + } else if (token.startsWith("-") && !"-".equals(token)) { + handleShortAndLongOption(token); + } else { + handleUnknownToken(token); + } + if (currentOption != null && !currentOption.acceptsArg()) { + currentOption = null; + } } } diff --git a/src/main/java/org/apache/commons/cli/GnuParser.java b/src/main/java/org/apache/commons/cli/GnuParser.java index 20b4df3..a5f6c51 100644 --- a/src/main/java/org/apache/commons/cli/GnuParser.java +++ b/src/main/java/org/apache/commons/cli/GnuParser.java @@ -48,37 +48,39 @@ public class GnuParser extends Parser { boolean eatTheRest = false; for (int i = 0; i < arguments.length; i++) { final String arg = arguments[i]; - if ("--".equals(arg)) { - eatTheRest = true; - tokens.add("--"); - } else if ("-".equals(arg)) { - tokens.add("-"); - } else if (arg.startsWith("-")) { - final String opt = Util.stripLeadingHyphens(arg); - if (options.hasOption(opt)) { - tokens.add(arg); - } else { - final int equalPos = DefaultParser.indexOfEqual(opt); - if (equalPos != -1 && options.hasOption(opt.substring(0, equalPos))) { - // the format is --foo=value or -foo=value - tokens.add(arg.substring(0, arg.indexOf(Char.EQUAL))); // --foo - tokens.add(arg.substring(arg.indexOf(Char.EQUAL) + 1)); // value - } else if (options.hasOption(arg.substring(0, 2))) { - // the format is a special properties option (-Dproperty=value) - tokens.add(arg.substring(0, 2)); // -D - tokens.add(arg.substring(2)); // property=value - } else { - eatTheRest = stopAtNonOption; + if (arg != null) { + if ("--".equals(arg)) { + eatTheRest = true; + tokens.add("--"); + } else if ("-".equals(arg)) { + tokens.add("-"); + } else if (arg.startsWith("-")) { + final String opt = Util.stripLeadingHyphens(arg); + if (options.hasOption(opt)) { tokens.add(arg); + } else { + final int equalPos = DefaultParser.indexOfEqual(opt); + if (equalPos != -1 && options.hasOption(opt.substring(0, equalPos))) { + // the format is --foo=value or -foo=value + tokens.add(arg.substring(0, arg.indexOf(Char.EQUAL))); // --foo + tokens.add(arg.substring(arg.indexOf(Char.EQUAL) + 1)); // value + } else if (options.hasOption(arg.substring(0, 2))) { + // the format is a special properties option (-Dproperty=value) + tokens.add(arg.substring(0, 2)); // -D + tokens.add(arg.substring(2)); // property=value + } else { + eatTheRest = stopAtNonOption; + tokens.add(arg); + } } + } else { + tokens.add(arg); } - } else { - tokens.add(arg); - } - if (eatTheRest) { - for (i++; i < arguments.length; i++) { // NOPMD - tokens.add(arguments[i]); + if (eatTheRest) { + for (i++; i < arguments.length; i++) { // NOPMD + tokens.add(arguments[i]); + } } } } diff --git a/src/main/java/org/apache/commons/cli/Parser.java b/src/main/java/org/apache/commons/cli/Parser.java index 8f6ab86..fcd574b 100644 --- a/src/main/java/org/apache/commons/cli/Parser.java +++ b/src/main/java/org/apache/commons/cli/Parser.java @@ -156,41 +156,40 @@ public abstract class Parser implements CommandLineParser { // process each flattened token while (iterator.hasNext()) { final String token = iterator.next(); - // the value is the double-dash - if ("--".equals(token)) { - eatTheRest = true; - } - // the value is a single dash - else if ("-".equals(token)) { - if (stopAtNonOption) { + if (token != null) { + // the value is the double-dash + if ("--".equals(token)) { eatTheRest = true; + } else if ("-".equals(token)) { + // the value is a single dash + if (stopAtNonOption) { + eatTheRest = true; + } else { + cmd.addArg(token); + } + } else if (token.startsWith("-")) { + // the value is an option + if (stopAtNonOption && !getOptions().hasOption(token)) { + eatTheRest = true; + cmd.addArg(token); + } else { + processOption(token, iterator); + } } else { + // the value is an argument cmd.addArg(token); + if (stopAtNonOption) { + eatTheRest = true; + } } - } - // the value is an option - else if (token.startsWith("-")) { - if (stopAtNonOption && !getOptions().hasOption(token)) { - eatTheRest = true; - cmd.addArg(token); - } else { - processOption(token, iterator); - } - } - // the value is an argument - else { - cmd.addArg(token); - if (stopAtNonOption) { - eatTheRest = true; - } - } - // eat the remaining tokens - if (eatTheRest) { - while (iterator.hasNext()) { - final String str = iterator.next(); - // ensure only one double-dash is added - if (!"--".equals(str)) { - cmd.addArg(str); + // eat the remaining tokens + if (eatTheRest) { + while (iterator.hasNext()) { + final String str = iterator.next(); + // ensure only one double-dash is added + if (!"--".equals(str)) { + cmd.addArg(str); + } } } } diff --git a/src/main/java/org/apache/commons/cli/PosixParser.java b/src/main/java/org/apache/commons/cli/PosixParser.java index 0071929..18a826f 100644 --- a/src/main/java/org/apache/commons/cli/PosixParser.java +++ b/src/main/java/org/apache/commons/cli/PosixParser.java @@ -119,63 +119,56 @@ public class PosixParser extends Parser { protected String[] flatten(final Options options, final String[] arguments, final boolean stopAtNonOption) throws ParseException { init(); this.options = options; - // an iterator for the command line tokens final Iterator<String> iter = Arrays.asList(arguments).iterator(); - // process each command line token while (iter.hasNext()) { // get the next command line token final String token = iter.next(); - - // single or double hyphen - if ("-".equals(token) || "--".equals(token)) { - tokens.add(token); - } - - // handle long option --foo or --foo=bar - else if (token.startsWith("--")) { - final int pos = DefaultParser.indexOfEqual(token); - final String opt = pos == -1 ? token : token.substring(0, pos); // --foo - - final List<String> matchingOpts = options.getMatchingOptions(opt); - - if (matchingOpts.isEmpty()) { - processNonOptionToken(token, stopAtNonOption); - } else if (matchingOpts.size() > 1) { - throw new AmbiguousOptionException(opt, matchingOpts); - } else { - currentOption = options.getOption(matchingOpts.get(0)); - - tokens.add("--" + currentOption.getLongOpt()); - if (pos != -1) { - tokens.add(token.substring(pos + 1)); + if (token != null) { + // single or double hyphen + if ("-".equals(token) || "--".equals(token)) { + tokens.add(token); + } else if (token.startsWith("--")) { + // handle long option --foo or --foo=bar + final int pos = DefaultParser.indexOfEqual(token); + final String opt = pos == -1 ? token : token.substring(0, pos); // --foo + + final List<String> matchingOpts = options.getMatchingOptions(opt); + + if (matchingOpts.isEmpty()) { + processNonOptionToken(token, stopAtNonOption); + } else if (matchingOpts.size() > 1) { + throw new AmbiguousOptionException(opt, matchingOpts); + } else { + currentOption = options.getOption(matchingOpts.get(0)); + + tokens.add("--" + currentOption.getLongOpt()); + if (pos != -1) { + tokens.add(token.substring(pos + 1)); + } } - } - } - - else if (token.startsWith("-")) { - if (token.length() == 2 || options.hasOption(token)) { - processOptionToken(token, stopAtNonOption); - } else if (!options.getMatchingOptions(token).isEmpty()) { - final List<String> matchingOpts = options.getMatchingOptions(token); - if (matchingOpts.size() > 1) { - throw new AmbiguousOptionException(token, matchingOpts); + } else if (token.startsWith("-")) { + if (token.length() == 2 || options.hasOption(token)) { + processOptionToken(token, stopAtNonOption); + } else if (!options.getMatchingOptions(token).isEmpty()) { + final List<String> matchingOpts = options.getMatchingOptions(token); + if (matchingOpts.size() > 1) { + throw new AmbiguousOptionException(token, matchingOpts); + } + final Option opt = options.getOption(matchingOpts.get(0)); + processOptionToken("-" + opt.getLongOpt(), stopAtNonOption); } - final Option opt = options.getOption(matchingOpts.get(0)); - processOptionToken("-" + opt.getLongOpt(), stopAtNonOption); - } - // requires bursting - else { - burstToken(token, stopAtNonOption); + // requires bursting + else { + burstToken(token, stopAtNonOption); + } + } else { + processNonOptionToken(token, stopAtNonOption); } - } else { - processNonOptionToken(token, stopAtNonOption); } - gobble(iter); } - return tokens.toArray(Util.EMPTY_STRING_ARRAY); } diff --git a/src/test/java/org/apache/commons/cli/AbstractParserTestCase.java b/src/test/java/org/apache/commons/cli/AbstractParserTestCase.java index 8c33fdd..69c61e4 100644 --- a/src/test/java/org/apache/commons/cli/AbstractParserTestCase.java +++ b/src/test/java/org/apache/commons/cli/AbstractParserTestCase.java @@ -420,6 +420,23 @@ public abstract class AbstractParserTestCase { assertEquals("foobar", cl.getArgList().get(0), "Confirm value of extra arg: " + cl.getArgList().get(0)); } + @Test + public void testMultipleWithNull() throws Exception { + final String[] args = { null, "-c", null, "foobar", null, "-b", null, "toast", null }; + + CommandLine cl = parser.parse(options, args, true); + assertTrue(cl.hasOption("c"), "Confirm -c is set"); + assertEquals(3, cl.getArgList().size(), "Confirm 3 extra args: " + cl.getArgList().size()); + + cl = parser.parse(options, cl.getArgs()); + + assertFalse(cl.hasOption("c"), "Confirm -c is not set"); + assertTrue(cl.hasOption("b"), "Confirm -b is set"); + assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b"); + assertEquals(1, cl.getArgList().size(), "Confirm 1 extra arg: " + cl.getArgList().size()); + assertEquals("foobar", cl.getArgList().get(0), "Confirm value of extra arg: " + cl.getArgList().get(0)); + } + @Test public void testMultipleWithLong() throws Exception { final String[] args = { "--copt", "foobar", "--bfile", "toast" };