diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index a431259c2..920c43ce5 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -25,7 +25,8 @@ Artifacts in this release are signed by Remko Popma (6601 E5C0 8DCC BB96). * [#1886][#1896] Bugfix: AsciiDoc generator now correctly outputs options even if all options are in ArgGroups. Thanks to [Ruud Senden](https://github.com/rsenden) for the discussion and the pull request. * [#1878][#1876] Bugfix: Annotation processor now avoids loading resource bundles at compile time. Thanks to [Ruud Senden](https://github.com/rsenden) for the discussion and the pull request. * [#1911] Avoid using boxed boolean in `CommandLine.Interpreter.applyValueToSingleValuedField`. Thanks to [Jiehong](https://github.com/Jiehong) for the pull request. -* [#1870] Bugfix: `StringIndexOutOfBoundsException` in usage help when command has too many (and long) aliases. Thanks to [Martin](https://github.com/martlin2cz) for raising this.ji +* [#1870] Bugfix: `StringIndexOutOfBoundsException` in usage help when command has too many (and long) aliases. Thanks to [Martin](https://github.com/martlin2cz) for raising this. +* [#1904] Bugfix: Apply `fallbackValue` to vararg multi-value options, not just single-value options. Thanks to [Andreas Sewe](https://github.com/sewe) for raising this. * [#1930] Bugfix: Ensure tests pass in environments for Java 5-18. * [#1881] DOC: Many documentation improvements. Thanks to [Andreas Deininger](https://github.com/deining) for the pull request. * [#1855][#1857] DOC: Add new user manual section called [Rare Use Cases](https://picocli.info/#_rare_use_cases) detailing `System.exit` usage. Thanks to [Tadaya Tsuyukubo](https://github.com/ttddyy) for the pull request. diff --git a/src/main/java/picocli/CommandLine.java b/src/main/java/picocli/CommandLine.java index 979892685..a5284fa93 100644 --- a/src/main/java/picocli/CommandLine.java +++ b/src/main/java/picocli/CommandLine.java @@ -14318,7 +14318,8 @@ private void consumeMapArguments(ArgSpec argSpec, String fallback = consumed == 0 && argSpec.isOption() && !OptionSpec.DEFAULT_FALLBACK_VALUE.equals(((OptionSpec) argSpec).fallbackValue()) ? ((OptionSpec) argSpec).fallbackValue() : null; - if (fallback != null && (args.isEmpty() || !varargCanConsumeNextValue(argSpec, args.peek()))) { + if (fallback != null && (args.isEmpty() || !varargCanConsumeNextValue(argSpec, args.peek()) + || !canConsumeOneMapArgument(argSpec, lookBehind, alreadyUnquoted, arity, consumed, args.peek(), classes, keyConverter, valueConverter, argDescription))) { args.push(fallback); } for (int i = consumed; consumed < arity.max && !args.isEmpty(); i++) { @@ -14539,7 +14540,8 @@ private List consumeArguments(ArgSpec argSpec, String fallback = consumed == 0 && argSpec.isOption() && !OptionSpec.DEFAULT_FALLBACK_VALUE.equals(((OptionSpec) argSpec).fallbackValue()) ? ((OptionSpec) argSpec).fallbackValue() : null; - if (fallback != null && (args.isEmpty() || !varargCanConsumeNextValue(argSpec, args.peek()))) { + if (fallback != null && (args.isEmpty() || !varargCanConsumeNextValue(argSpec, args.peek()) + || (!canConsumeOneArgument(argSpec, lookBehind, alreadyUnquoted, arity, consumed, args.peek(), argDescription)))) { args.push(fallback); } for (int i = consumed; consumed < arity.max && !args.isEmpty(); i++) { diff --git a/src/test/java/picocli/FallbackTest.java b/src/test/java/picocli/FallbackTest.java index f598c7788..6354c0f48 100644 --- a/src/test/java/picocli/FallbackTest.java +++ b/src/test/java/picocli/FallbackTest.java @@ -11,6 +11,11 @@ import picocli.CommandLine.ParameterException; import picocli.CommandLine.Parameters; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + import static org.junit.Assert.*; public class FallbackTest { @@ -108,4 +113,76 @@ class App { App app = CommandLine.populateCommand(new App(), "-x"); assertNull(app.x); } + static class Issue1904 { + enum DebugFacility { FOO, BAR, BAZ, ALL, DEFAULT, FALLBACK } + + @Option( + names = { "--debug" }, + paramLabel = "FACILITY", + split = ",", + arity = "0..*", + defaultValue = "DEFAULT", + fallbackValue = "FALLBACK") + Collection facilities; + + @Option(names = { "--map" }, arity = "0..*", + defaultValue = "DEFAULT=0", + fallbackValue = "FALLBACK=1") + Map map; + + @Option(names = "-x") + String x; + + @Parameters + List remainder; + } + + @Test + public void testIssue1904CollectionFallback_NoOptions() { + Issue1904 obj = CommandLine.populateCommand(new Issue1904()); // options are not specified + assertEquals(Collections.singletonList(Issue1904.DebugFacility.DEFAULT), obj.facilities); + assertEquals(Collections.singletonMap(Issue1904.DebugFacility.DEFAULT, "0"), obj.map); + } + + @Test + public void testIssue1904CollectionFallback_NoArgs() { + Issue1904 obj = CommandLine.populateCommand(new Issue1904(), "--debug"); // no args specified + assertEquals(Collections.singletonList(Issue1904.DebugFacility.FALLBACK), obj.facilities); + assertEquals(Collections.singletonMap(Issue1904.DebugFacility.DEFAULT, "0"), obj.map); + } + + @Test + public void testIssue1904CollectionFallback_NoArgs_map() { + Issue1904 obj = CommandLine.populateCommand(new Issue1904(), "--map"); // no args specified + assertEquals(Collections.singletonList(Issue1904.DebugFacility.DEFAULT), obj.facilities); + assertEquals(Collections.singletonMap(Issue1904.DebugFacility.FALLBACK, "1"), obj.map); + } + + @Test + public void testIssue1904CollectionFallback_otherOption() { + Issue1904 obj = CommandLine.populateCommand(new Issue1904(), "--debug", "-x", "xarg"); // other option + assertEquals(Collections.singletonList(Issue1904.DebugFacility.FALLBACK), obj.facilities); + assertEquals(Collections.singletonMap(Issue1904.DebugFacility.DEFAULT, "0"), obj.map); + } + + @Test + public void testIssue1904CollectionFallback_otherOption_map() { + Issue1904 obj = CommandLine.populateCommand(new Issue1904(), "--map", "-x", "xarg"); // other option + assertEquals(Collections.singletonList(Issue1904.DebugFacility.DEFAULT), obj.facilities); + assertEquals(Collections.singletonMap(Issue1904.DebugFacility.FALLBACK, "1"), obj.map); + } + + @Test + public void testIssue1904CollectionFallback_positional() { + Issue1904 obj = CommandLine.populateCommand(new Issue1904(), "--debug", "123"); // positional + assertEquals(Collections.singletonList(Issue1904.DebugFacility.FALLBACK), obj.facilities); + assertEquals(Collections.singletonMap(Issue1904.DebugFacility.DEFAULT, "0"), obj.map); + } + + @Test + public void testIssue1904CollectionFallback_positional_map() { + Issue1904 obj = CommandLine.populateCommand(new Issue1904(), "--map", "123"); // positional + assertEquals(Collections.singletonList(Issue1904.DebugFacility.DEFAULT), obj.facilities); + assertEquals(Collections.singletonMap(Issue1904.DebugFacility.FALLBACK, "1"), obj.map); + } }