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