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