Oh, I see. Thanks for the clarification! And thanks for the quick resolution! :-)
On Thu, Nov 19, 2020 at 12:17 Paul King <pa...@asert.com.au> wrote: > 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 >>>>>>>>>> >>>>>>>>>>