Skip to content

Commit ce86213

Browse files
Claudenwmthmulderspaulk-asert
authored
[CLI-312] Inconsistent behaviour in key/value pairs (Java property style) (#233)
* [CLI-312] Illustrate problem * Added Supplier<T> defaults for getParsedOptionValue * added tests * Fixed test issues * CLI-320: Awkward behavior of Option.builder() for multiple optional args * Fixes for -D with separate single value * cleaned up accidental includes * Removed unneeded line. * added test for missing value * removed extra spaces * removed unecessary function * removed unnecessary throws from test --------- Co-authored-by: Maarten Mulders <mthmulders@apache.org> Co-authored-by: Paul King <paulk@asert.com.au>
1 parent bcfcdde commit ce86213

2 files changed

Lines changed: 109 additions & 0 deletions

File tree

src/main/java/org/apache/commons/cli/DefaultParser.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ private DefaultParser(final boolean allowPartialMatching,
228228
*/
229229
private void checkRequiredArgs() throws ParseException {
230230
if (currentOption != null && currentOption.requiresArg()) {
231+
if (isJavaProperty(currentOption.getKey()) && currentOption.getValuesList().size() == 1) {
232+
return;
233+
}
231234
throw new MissingArgumentException(currentOption);
232235
}
233236
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.cli.bug;
18+
19+
import static org.junit.Assert.assertArrayEquals;
20+
import static org.junit.Assert.assertEquals;
21+
import static org.junit.Assert.fail;
22+
import static org.junit.jupiter.api.Assertions.assertThrows;
23+
24+
import java.util.Properties;
25+
26+
import org.apache.commons.cli.CommandLine;
27+
import org.apache.commons.cli.CommandLineParser;
28+
import org.apache.commons.cli.DefaultParser;
29+
import org.apache.commons.cli.MissingArgumentException;
30+
import org.apache.commons.cli.Option;
31+
import org.apache.commons.cli.Options;
32+
import org.apache.commons.cli.ParseException;
33+
import org.junit.Test;
34+
35+
/**
36+
* Demonstrates inconsistencies in parsing Java property-style options.
37+
*/
38+
public class BugCLI312Test {
39+
@Test
40+
public void testPropertyStyleOption_withGetOptionProperties() throws ParseException {
41+
final Option o1 = Option.builder().option("D").longOpt("define").numberOfArgs(2).valueSeparator('=').build();
42+
43+
final Options options = new Options();
44+
options.addOption(o1);
45+
46+
final CommandLineParser parser = new DefaultParser();
47+
48+
final CommandLine cl = parser.parse(options, "-Dv -Dw=1 -D x=2 -D y -D z=3 other".split(" "));
49+
assertArrayEquals(new String[] {"v", "w", "1", "x", "2", "y", "z", "3"}, cl.getOptionValues('D'));
50+
51+
Properties properties = cl.getOptionProperties("D");
52+
assertEquals("true", properties.getProperty("v"));
53+
assertEquals("1", properties.getProperty("w"));
54+
assertEquals("2", properties.getProperty("x"));
55+
assertEquals("true", properties.getProperty("y"));
56+
assertEquals("3", properties.getProperty("z"));
57+
assertEquals(5, properties.size());
58+
assertEquals("other", cl.getArgList().get(0));
59+
}
60+
61+
@Test
62+
public void testPropertyStyleOption_withGetOptions() throws ParseException {
63+
final Option o1 = Option.builder().option("D").longOpt("define").numberOfArgs(2).valueSeparator('=').build();
64+
65+
final Options options = new Options();
66+
options.addOption(o1);
67+
68+
final CommandLineParser parser = new DefaultParser();
69+
70+
final CommandLine cl = parser.parse(options, "-Dv -Dw=1 -D x=2 -D y -D z=3 other".split(" "));
71+
assertArrayEquals(new String[] {"v", "w", "1", "x", "2", "y", "z", "3"}, cl.getOptionValues('D'));
72+
73+
int defineOptionsFound = 0;
74+
for (final Option o : cl.getOptions()) {
75+
if ("D".equals(o.getOpt())) {
76+
defineOptionsFound++;
77+
78+
if (defineOptionsFound == 1) {
79+
assertArrayEquals(new String[] {"v"}, o.getValues());
80+
} else if (defineOptionsFound == 2) {
81+
assertArrayEquals(new String[] {"w", "1"}, o.getValues());
82+
} else if (defineOptionsFound == 3) {
83+
assertArrayEquals(new String[] {"x", "2"}, o.getValues());
84+
} else if (defineOptionsFound == 4) {
85+
assertArrayEquals(new String[] {"y"}, o.getValues());
86+
} else if (defineOptionsFound == 5) {
87+
assertArrayEquals(new String[] {"z", "3"}, o.getValues());
88+
} else {
89+
fail("Didn't expect " + defineOptionsFound + " occurrences of -D");
90+
}
91+
}
92+
}
93+
assertEquals("other", cl.getArgList().get(0));
94+
}
95+
96+
@Test
97+
public void testNoOptionValues() {
98+
final Option o1 = Option.builder("A").build();
99+
final Option o2 = Option.builder().option("D").longOpt("define").numberOfArgs(2).valueSeparator('=').build();
100+
Options options = new Options().addOption(o1).addOption(o2);
101+
102+
final CommandLineParser parser = new DefaultParser();
103+
104+
assertThrows(MissingArgumentException.class, () -> parser.parse(options, "-D -A".split(" ")));
105+
}
106+
}

0 commit comments

Comments
 (0)