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