I was just giving approx 72 hrs for feedback. Out of courtesy/caution more
than a hard requirement for a change like this. I'll merge now.

cheers, Paul.

On Thu, Nov 19, 2020 at 9:09 AM Remko Popma <remko.po...@gmail.com> wrote:

> Paul, just curious: is there anything preventing
> https://github.com/apache/groovy/pull/1420 from being merged?
>
> On Tue, Nov 17, 2020 at 6:25 PM Paul King <pa...@asert.com.au> wrote:
>
>> Looks good, thanks!
>>
>> On Tue, Nov 17, 2020 at 6:11 PM Remko Popma <remko.po...@gmail.com>
>> wrote:
>>
>>> You beat me to it! Thanks!
>>> I updated the description, please take a look.
>>>
>>>
>>> On Tue, Nov 17, 2020 at 16:45 Paul King <pa...@asert.com.au> wrote:
>>>
>>>> I created this:
>>>> https://issues.apache.org/jira/browse/GROOVY-9817
>>>>
>>>> On Tue, Nov 17, 2020 at 2:11 PM Remko Popma <remko.po...@gmail.com>
>>>> wrote:
>>>>
>>>>> Eric and Paul,
>>>>> Thank you both for your responses!
>>>>>
>>>>> Paul,
>>>>> Thank you for your quick turnaround on supporting array annotations!
>>>>>
>>>>> I will create a Jira ticket when I get to my PC.
>>>>>
>>>>> Remko
>>>>>
>>>>> On Nov 17, 2020, at 12:24, Paul King <pa...@asert.com.au> wrote:
>>>>>
>>>>> 
>>>>> The following runs fine after adding in array support:
>>>>>
>>>>> import java.lang.annotation.*
>>>>> import org.codehaus.groovy.runtime.InvokerHelper
>>>>>
>>>>> class ClosureTest {
>>>>>     static class Demo {
>>>>>         @Option(names = "-x",
>>>>>                 completionCandidates = {["A", "B", "C"]},
>>>>>                 converter = [{ str ->
>>>>> java.security.MessageDigest.getInstance(str) }])
>>>>>         java.security.MessageDigest digest
>>>>>     }
>>>>>
>>>>>     static void main(String[] args) {
>>>>>         def annotation =
>>>>> Demo.getDeclaredField("digest").getAnnotation(Option)
>>>>>         Class comp = annotation.completionCandidates()
>>>>>         assert comp != null
>>>>>         assert Closure.isAssignableFrom(comp)
>>>>>         assert ["A", "B", "C"] ==
>>>>> InvokerHelper.invokeConstructorOf(comp, [null, null] as Object[])()
>>>>>
>>>>>         Class[] conv = annotation.converter()
>>>>>         assert conv != null
>>>>>         assert conv.length == 1
>>>>>         assert Closure.isAssignableFrom(conv[0])
>>>>>         assert 'SHA-1' == InvokerHelper.invokeConstructorOf(conv[0],
>>>>> [null, null] as Object[])('SHA-1').algorithm
>>>>>     }
>>>>> }
>>>>>
>>>>> interface ITypeConverter<K> {
>>>>>     K convert(String value) throws Exception
>>>>> }
>>>>>
>>>>> class NoCompletionCandidates {}
>>>>>
>>>>> @Retention(RetentionPolicy.RUNTIME)
>>>>> @Target([ElementType.FIELD])
>>>>> @interface Option {
>>>>>     Class<? extends ITypeConverter<?>>[] converter() default []
>>>>>     Class<? extends Iterable<String>> completionCandidates() default
>>>>> NoCompletionCandidates
>>>>>     String names()
>>>>> }
>>>>>
>>>>> Probably worth adding. Did you want to create a Jira?
>>>>>
>>>>> Cheers, Paul.
>>>>>
>>>>>
>>>>> On Tue, Nov 17, 2020 at 12:32 PM Paul King <pa...@asert.com.au> wrote:
>>>>>
>>>>>> The Closure to Class conversion doesn't currently support arrays. If
>>>>>> you change  converter() to take just a single convert, your example
>>>>>> works for me.
>>>>>>
>>>>>> Supporting arrays might be an interesting enhancement. I'll take a
>>>>>> look at what would be involved.
>>>>>>
>>>>>> Cheers, Paul.
>>>>>>
>>>>>>
>>>>>> On Tue, Nov 17, 2020 at 11:02 AM Remko Popma <remko.po...@gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>> I’m probably overlooking something simple but I’m not seeing it yet.
>>>>>>>
>>>>>>> The below code demonstrates the issue when trying to pass a Groovy
>>>>>>> closure to the @Option(converter = ...)attribute:
>>>>>>>
>>>>>>> class ClosureTest {
>>>>>>>     static class Demo {
>>>>>>>         @picocli.CommandLine.Option(names = "-x",
>>>>>>>                 completionCandidates = {["A", "B", "C"]},
>>>>>>>                 converter = [{ str -> 
>>>>>>> java.security.MessageDigest.getInstance(str) }])
>>>>>>>         java.security.MessageDigest digest
>>>>>>>     }
>>>>>>>
>>>>>>>     static void main(String[] args) {
>>>>>>>         def annotation = 
>>>>>>> Demo.class.getDeclaredField("digest").getAnnotation(picocli.CommandLine.Option)
>>>>>>>         Class ok = annotation.completionCandidates()
>>>>>>>         assert ok != null
>>>>>>>         assert Closure.class.isAssignableFrom(ok)
>>>>>>>         assert ["A", "B", "C"] == ((Closure) ok.getConstructor(Object, 
>>>>>>> Object).newInstance(null, null)).call()
>>>>>>>
>>>>>>>         Class[] bad = annotation.converter()
>>>>>>>         assert bad != null
>>>>>>>         assert bad.length == 1 // this assert fails:
>>>>>>>         //Exception in thread "main" Assertion failed:
>>>>>>>         //
>>>>>>>         //assert bad.length == 1
>>>>>>>         //       |   |      |
>>>>>>>         //       []  0      false
>>>>>>>         //
>>>>>>>         //      at 
>>>>>>> org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:434)
>>>>>>>         //      at 
>>>>>>> org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(ScriptBytecodeAdapter.java:670)
>>>>>>>         //      at closure.ClosureTest.main(ClosureTest.groovy:18)
>>>>>>>     }
>>>>>>> }
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Mon, Nov 16, 2020 at 21:16 Remko Popma <remko.po...@gmail.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> PS
>>>>>>>>
>>>>>>>> The ITypeConverter interface definition is here:
>>>>>>>> https://picocli.info/apidocs/picocli/CommandLine.ITypeConverter.html
>>>>>>>>
>>>>>>>>
>>>>>>>> On Mon, Nov 16, 2020 at 21:08 Remko Popma <remko.po...@gmail.com>
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>> Hi all,
>>>>>>>>>
>>>>>>>>> I have a question about passing closures to annotations in Groovy.
>>>>>>>>> To illustrate, consider the @Option annotation in the picocli
>>>>>>>>> library.
>>>>>>>>> Relevant attributes are `completionCandidates` and `converter`,
>>>>>>>>> defined in Java as follows:
>>>>>>>>>
>>>>>>>>> @Retention(RetentionPolicy.RUNTIME)
>>>>>>>>> @Target({ElementType.FIELD, ElementType.METHOD,
>>>>>>>>> ElementType.PARAMETER})
>>>>>>>>> public @interface Option {
>>>>>>>>>   Class<? extends ITypeConverter<?>>[] converter() default {};
>>>>>>>>>   Class<? extends Iterable<String>> completionCandidates() default
>>>>>>>>> NoCompletionCandidates.class;
>>>>>>>>>   ...
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> I am working on a change to picocli
>>>>>>>>> <https://github.com/remkop/picocli/issues/1258> that would allow
>>>>>>>>> users to specify closures for these and other attributes.
>>>>>>>>> User code could look like this:
>>>>>>>>>
>>>>>>>>> @Option(names = '-s', completionCandidates = {["A", "B", "C"]})
>>>>>>>>> @Field String s
>>>>>>>>>
>>>>>>>>> @Option(names = '-a', converter = [{ str ->
>>>>>>>>> MessageDigest.getInstance(str) }] )
>>>>>>>>> @Field MessageDigest algorithm
>>>>>>>>>
>>>>>>>>> I think this would be a nice addition and would make picocli more
>>>>>>>>> "groovy".
>>>>>>>>>
>>>>>>>>> I have a prototype implementation, but it appears that only the
>>>>>>>>> first example ( completionCandidates = {["A", "B", "C"]} ) works
>>>>>>>>> as expected.
>>>>>>>>> When stepping through my prototype test in a debugger, it looks
>>>>>>>>> like the second example (the converter attribute) receives a
>>>>>>>>> zero-length array of classes when invoked from Groovy. I tried with 
>>>>>>>>> Groovy
>>>>>>>>> 2.4.10 and 3.0.6.
>>>>>>>>>
>>>>>>>>> Is this a known limitation of Groovy?
>>>>>>>>> Is there a way to work around this?
>>>>>>>>>
>>>>>>>>> I can provide an example project to reproduce this if that is
>>>>>>>>> helpful.
>>>>>>>>>
>>>>>>>>> Kind regards,
>>>>>>>>> Remko
>>>>>>>>>
>>>>>>>>>

Reply via email to