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 >>>>>>>> >>>>>>>>