From c825d20e0548d63a7c355e90732352d1227596bc Mon Sep 17 00:00:00 2001 From: Remko Popma Date: Mon, 6 May 2024 21:07:11 +0900 Subject: [PATCH] [#2270] Custom type converter for primitive `boolean` options should not be ignored --- RELEASE-NOTES.md | 4 +- picocli-tests-java8/build.gradle | 5 +- .../src/test/java/picocli/Issue2270.java | 78 +++++++++++++++++++ src/main/java/picocli/CommandLine.java | 2 +- 4 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 picocli-tests-java8/src/test/java/picocli/Issue2270.java diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 84883aa08..29c3bc02e 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -22,17 +22,17 @@ Artifacts in this release are signed by Remko Popma (6601 E5C0 8DCC BB96). `PropertiesDefaultProvider` now tries to load properties from the classpath if the file cannot be found in the user.home directory. - ## Fixed issues * [#2102][#2107] Enhancement: `PropertiesDefaultProvider` should try to load properties from classpath (last). Thanks to [Lumír Návrat](https://github.com/rimuln) for the pull request. * [#2202] Enhancement: Change log level from WARN to INFO when bean not found in ApplicationContext. Thanks to [Desmond Kirrane](https://github.com/dkirrane) for raising this. * [#2248] Enhancement: Don't show hidden commands in JLine3 command description. Thanks to [Reinhard Handler](https://github.com/rehand) for the pull request. +* [#2170] Enhancement: Use `...` vararg instead of array parameter to match overridden method signature. Thanks to [Michael Vorburger](https://github.com/vorburger) for the pull request. * [#2058] Bugfix: `defaultValue` should not be applied in addition to user-specified value for options with a custom `IParameterConsumer`. Thanks to [Staffan Arvidsson McShane](https://github.com/StaffanArvidsson) for raising this. * [#2148] Bugfix: Fix NPE in jline3 `Example.jar` as `ConfigurationPath` cannot be `null` anymore. Thanks to [llzen44](https://github.com/llzen44) for the pull request. * [#2232] Bugfix: fix bug for `Optional` arguments with initial value. Thanks to [hq6](https://github.com/hq6) for raising this. * [#2149] Bugfix: `@Option`-annotated setter method not invoked with default value when used in mixin for both command and subcommand. Thanks to [Zhonghao Wang](https://github.com/JBWKZsf) for raising this. -* [#2170] Enhancement: Use `...` vararg instead of array parameter to match overridden method signature. Thanks to [Michael Vorburger](https://github.com/vorburger) for the pull request. +* [#2270] Bugfix: Custom type converter for primitive `boolean` options should not be ignored. Thanks to [Sven Kammerer](https://codeberg.org/sven.k) for raising this. * [#2234] BUILD: fix errorprone `TruthSelfEquals` warnings * [#2172] BUILD: Fix broken build. Thanks to [Michael Vorburger](https://github.com/vorburger) for the pull request. * [#2174] BUILD: Fix .gitattributes related CR/LF problems. Thanks to [Michael Vorburger](https://github.com/vorburger) for the pull request. diff --git a/picocli-tests-java8/build.gradle b/picocli-tests-java8/build.gradle index 123e5d83c..565d5eb7f 100644 --- a/picocli-tests-java8/build.gradle +++ b/picocli-tests-java8/build.gradle @@ -16,9 +16,8 @@ test { dependencies { api rootProject - testImplementation supportDependencies.junit5Api - testRuntimeOnly supportDependencies.junit5Engine - testImplementation supportDependencies.systemLambda + testImplementation supportDependencies.junit + testImplementation supportDependencies.hamcrestCore } jar { diff --git a/picocli-tests-java8/src/test/java/picocli/Issue2270.java b/picocli-tests-java8/src/test/java/picocli/Issue2270.java new file mode 100644 index 000000000..5d087330f --- /dev/null +++ b/picocli-tests-java8/src/test/java/picocli/Issue2270.java @@ -0,0 +1,78 @@ +package picocli; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import picocli.CommandLine.ITypeConverter; +import picocli.CommandLine.Option; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.junit.Assert.*; + +public class Issue2270 { + static class CmdObjectBoolean { + @Option(names = "--test-Boolean", defaultValue = "dHJ1ZQ==" /* base64-encoded 'true' */) + Boolean testBoolean; + } + static class CmdPrimitiveBoolean { + @Option(names = "--test-boolean", defaultValue = "dHJ1ZQ==" /* base64-encoded 'true' */) + boolean testboolean; + } + + static class Base64BooleanTypeConverter implements ITypeConverter { + + static List invocations = new ArrayList<>(); + + public Boolean convert(String value) { + invocations.add(value); + System.out.printf("converter invocation %s: called with value %s%n", + invocations.size(), value); + if (Boolean.parseBoolean(value)) { + return true; + } + return Boolean.parseBoolean(new String(Base64.getDecoder().decode(value))); + } + } + + @Before + public void beforeTests() { + CommandLine.tracer().setLevel(CommandLine.TraceLevel.DEBUG); + } + + @After + public void afterTests() { + CommandLine.tracer().setLevel(CommandLine.TraceLevel.WARN); + } + + @Test + public void testObjectBoolean() { + Base64BooleanTypeConverter.invocations.clear(); + CmdObjectBoolean cmd = new CmdObjectBoolean(); + new CommandLine(cmd) + .registerConverter(Boolean.class, new Base64BooleanTypeConverter()) + .parseArgs(); + assertThat(Base64BooleanTypeConverter.invocations.size(), greaterThanOrEqualTo(1)); + assertEquals(Arrays.asList("dHJ1ZQ==", "true"), Base64BooleanTypeConverter.invocations); + + assertTrue(cmd.testBoolean); + } + + @Test + public void testPrimitiveBoolean() { + Base64BooleanTypeConverter.invocations.clear(); + CmdPrimitiveBoolean cmd = new CmdPrimitiveBoolean(); + new CommandLine(cmd) + .registerConverter(Boolean.TYPE, new Base64BooleanTypeConverter()) + .parseArgs(); + assertThat(Base64BooleanTypeConverter.invocations.size(), greaterThanOrEqualTo(1)); + assertEquals(Arrays.asList("dHJ1ZQ==", "true"), Base64BooleanTypeConverter.invocations); + + assertTrue(cmd.testboolean); + } +} diff --git a/src/main/java/picocli/CommandLine.java b/src/main/java/picocli/CommandLine.java index 46953013a..d1910f0d5 100644 --- a/src/main/java/picocli/CommandLine.java +++ b/src/main/java/picocli/CommandLine.java @@ -14910,7 +14910,7 @@ private boolean booleanValue(ArgSpec argSpec, Object value) { } String stringValue = String.valueOf(value); if (empty(stringValue) || "null".equals(stringValue) || "Optional.empty".equals(value)) { return false; } - ITypeConverter converter = getTypeConverter(new Class[]{Boolean.class}, argSpec, 0); + ITypeConverter converter = getTypeConverter(argSpec.auxiliaryTypes(), argSpec, 0); return (Boolean) tryConvert(argSpec, -1, converter, stringValue, 0); }