You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2019/02/19 03:14:48 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-8975: GroovyCastException on the result of CliBuilder.parseFromSpec (port to 2_5_X)

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new 1391859  GROOVY-8975: GroovyCastException on the result of CliBuilder.parseFromSpec (port to 2_5_X)
1391859 is described below

commit 13918597aae2cbb8dbb2f644c099e514bce39134
Author: Paul King <pa...@asert.com.au>
AuthorDate: Tue Feb 19 13:08:59 2019 +1000

    GROOVY-8975: GroovyCastException on the result of CliBuilder.parseFromSpec (port to 2_5_X)
---
 .../src/main/groovy/groovy/cli/commons/CliBuilder.groovy | 14 +++++++-------
 .../test/groovy/groovy/cli/commons/CliBuilderTest.groovy | 16 ++++++++++++++--
 .../src/main/groovy/groovy/cli/picocli/CliBuilder.groovy |  9 +++++----
 .../test/groovy/groovy/cli/picocli/CliBuilderTest.groovy | 14 ++++++++++++++
 4 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/subprojects/groovy-cli-commons/src/main/groovy/groovy/cli/commons/CliBuilder.groovy b/subprojects/groovy-cli-commons/src/main/groovy/groovy/cli/commons/CliBuilder.groovy
index 88c64ae..f903ea7 100644
--- a/subprojects/groovy-cli-commons/src/main/groovy/groovy/cli/commons/CliBuilder.groovy
+++ b/subprojects/groovy-cli-commons/src/main/groovy/groovy/cli/commons/CliBuilder.groovy
@@ -30,11 +30,11 @@ import org.apache.commons.cli.HelpFormatter
 import org.apache.commons.cli.Option as CliOption
 import org.apache.commons.cli.Options
 import org.apache.commons.cli.ParseException
+import org.codehaus.groovy.runtime.DefaultGroovyMethods
 import org.codehaus.groovy.runtime.InvokerHelper
 import org.codehaus.groovy.runtime.MetaClassHelper
 
 import java.lang.annotation.Annotation
-import java.lang.reflect.Array
 import java.lang.reflect.Field
 import java.lang.reflect.Method
 
@@ -386,7 +386,7 @@ class CliBuilder {
         def cli = parse(args)
         def cliOptions = [:]
         setOptionsFromAnnotations(cli, optionsClass, cliOptions, false)
-        cliOptions as T
+        DefaultGroovyMethods.asType(cliOptions, optionsClass)
     }
 
     /**
@@ -417,7 +417,7 @@ class CliBuilder {
         }
         optionFields.each { Field f ->
             Annotation annotation = f.getAnnotation(Option)
-            String setterName = "set" + MetaClassHelper.capitalize(f.getName());
+            String setterName = "set" + MetaClassHelper.capitalize(f.getName())
             Method m = optionClass.getMethod(setterName, f.getType())
             def typedOption = processAddAnnotation(annotation, m, true)
             options.addOption(typedOption.cliOption)
@@ -513,7 +513,7 @@ class CliBuilder {
         }
         optionClass.declaredFields.findAll { it.getAnnotation(Option) }.each { Field f ->
             Annotation annotation = f.getAnnotation(Option)
-            String setterName = "set" + MetaClassHelper.capitalize(f.getName());
+            String setterName = "set" + MetaClassHelper.capitalize(f.getName())
             Method m = optionClass.getMethod(setterName, f.getType())
             Map names = calculateNames(annotation.longName(), annotation.shortName(), m, true)
             processSetAnnotation(m, t, names.long ?: names.short, cli, true)
@@ -523,7 +523,7 @@ class CliBuilder {
             processSetRemaining(m, remaining, t, cli, namesAreSetters)
         }
         optionClass.declaredFields.findAll{ it.getAnnotation(Unparsed) }.each { Field f ->
-            String setterName = "set" + MetaClassHelper.capitalize(f.getName());
+            String setterName = "set" + MetaClassHelper.capitalize(f.getName())
             Method m = optionClass.getMethod(setterName, f.getType())
             processSetRemaining(m, remaining, t, cli, namesAreSetters)
         }
@@ -536,12 +536,12 @@ class CliBuilder {
         def type = null
         if (isTyped) {
             type = resultType.componentType
-            result = remaining.collect{ cli.getValue(type, it, null) }
+            result = remaining.collect{ cli.getValue(type, it, null) }.asType(resultType)
         } else {
             result = remaining.toList()
         }
         if (namesAreSetters) {
-            m.invoke(t, isTyped ? [result.toArray(Array.newInstance(type, result.size()))] as Object[] : result)
+            m.invoke(t, isTyped ? [result] as Object[] : result)
         } else {
             Map names = calculateNames("", "", m, namesAreSetters)
             t.put(names.long, { -> result })
diff --git a/subprojects/groovy-cli-commons/src/test/groovy/groovy/cli/commons/CliBuilderTest.groovy b/subprojects/groovy-cli-commons/src/test/groovy/groovy/cli/commons/CliBuilderTest.groovy
index fd8928a..0d4d5a5 100644
--- a/subprojects/groovy-cli-commons/src/test/groovy/groovy/cli/commons/CliBuilderTest.groovy
+++ b/subprojects/groovy-cli-commons/src/test/groovy/groovy/cli/commons/CliBuilderTest.groovy
@@ -622,7 +622,6 @@ usage: groovy
         @Unparsed Integer[] nums()
     }
 
-    // this feature is incubating
     void testTypedUnparsedFromSpec() {
         def argz = '12 34 56'.split()
         def cli = new CliBuilder()
@@ -634,7 +633,6 @@ usage: groovy
         @Unparsed Integer[] nums
     }
 
-    // this feature is incubating
     void testTypedUnparsedFromInstance() {
         def argz = '12 34 56'.split()
         def cli = new CliBuilder()
@@ -661,6 +659,20 @@ usage: groovy
         }
     }
 
+    interface StringIntArray {
+        @Option(shortName='u') String user()
+        @Unparsed Integer[] nums()
+    }
+
+    // GROOVY-8975
+    void testTypedCaseWithRemainingArray() {
+        def cli = new CliBuilder()
+        def argz = '--user abc 12 34'.split()
+        StringIntArray hello = cli.parseFromSpec(StringIntArray, argz)
+        assert hello.user() == 'abc'
+        assert hello.nums() == [12, 34]
+    }
+
     void testParseFromInstanceFlagEdgeCases() {
         def cli = new CliBuilder()
         def options = cli.parseFromSpec(FlagEdgeCasesI, '-abc -efg true --ijk foo --lmn bar baz'.split())
diff --git a/subprojects/groovy-cli-picocli/src/main/groovy/groovy/cli/picocli/CliBuilder.groovy b/subprojects/groovy-cli-picocli/src/main/groovy/groovy/cli/picocli/CliBuilder.groovy
index e53003e..914360a 100644
--- a/subprojects/groovy-cli-picocli/src/main/groovy/groovy/cli/picocli/CliBuilder.groovy
+++ b/subprojects/groovy-cli-picocli/src/main/groovy/groovy/cli/picocli/CliBuilder.groovy
@@ -23,6 +23,7 @@ import groovy.cli.Option
 import groovy.cli.TypedOption
 import groovy.cli.Unparsed
 import groovy.transform.Undefined
+import org.codehaus.groovy.runtime.DefaultGroovyMethods
 import org.codehaus.groovy.runtime.InvokerHelper
 import org.codehaus.groovy.runtime.MetaClassHelper
 import picocli.CommandLine
@@ -658,7 +659,7 @@ class CliBuilder {
         addOptionsFromAnnotations(optionsClass, cliOptions, true)
         addPositionalsFromAnnotations(optionsClass, cliOptions, true)
         parse(args)
-        cliOptions as T
+        DefaultGroovyMethods.asType(cliOptions, optionsClass)
     }
 
     /**
@@ -786,7 +787,7 @@ class CliBuilder {
     private ArgSpecAttributes extractAttributesFromField(Field f, target) {
         def getter = {
             f.accessible = true
-            f.get(target);
+            f.get(target)
         }
         def setter = { newValue ->
             f.accessible = true
@@ -799,7 +800,7 @@ class CliBuilder {
     }
 
     private PositionalParamSpec createPositionalParamSpec(Unparsed unparsed, ArgSpecAttributes attr, Object target) {
-        PositionalParamSpec.Builder builder = PositionalParamSpec.builder();
+        PositionalParamSpec.Builder builder = PositionalParamSpec.builder()
 
         CommandLine.Range arity = CommandLine.Range.valueOf("0..*")
         if (attr.type == Object) { attr.type = String[] }
@@ -922,7 +923,7 @@ class CliBuilder {
     }
 
     /** Commons-cli constant that specifies the number of argument values is infinite */
-    private static final int COMMONS_CLI_UNLIMITED_VALUES = -2;
+    private static final int COMMONS_CLI_UNLIMITED_VALUES = -2
 
     // - argName:        String
     // - longOpt:        String
diff --git a/subprojects/groovy-cli-picocli/src/test/groovy/groovy/cli/picocli/CliBuilderTest.groovy b/subprojects/groovy-cli-picocli/src/test/groovy/groovy/cli/picocli/CliBuilderTest.groovy
index 6740781..6849d3c 100644
--- a/subprojects/groovy-cli-picocli/src/test/groovy/groovy/cli/picocli/CliBuilderTest.groovy
+++ b/subprojects/groovy-cli-picocli/src/test/groovy/groovy/cli/picocli/CliBuilderTest.groovy
@@ -990,6 +990,20 @@ class CliBuilderTest extends GroovyTestCase {
         }
     }
 
+    interface StringIntArray {
+        @Option(shortName='u') String user()
+        @Unparsed Integer[] nums()
+    }
+
+    // GROOVY-8975
+    void testTypedCaseWithRemainingArray() {
+        def cli = new CliBuilder()
+        def argz = '--user abc 12 34'.split()
+        StringIntArray hello = cli.parseFromSpec(StringIntArray, argz)
+        assert hello.user() == 'abc'
+        assert hello.nums() == [12, 34]
+    }
+
     void testAcceptLongOptionsWithSingleHyphen_usage() {
         resetPrintWriter()
         CliBuilder cli = new CliBuilder(acceptLongOptionsWithSingleHyphen: true, writer: printWriter)