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