additional tests and doco tweaks
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/dac0bd84 Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/dac0bd84 Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/dac0bd84 Branch: refs/heads/master Commit: dac0bd84bf042e6ff489053918d5bd8203482a0e Parents: 952ac01 Author: paulk <pa...@asert.com.au> Authored: Sun Apr 10 14:09:52 2016 +1000 Committer: paulk <pa...@asert.com.au> Committed: Wed Apr 13 20:38:54 2016 +1000 ---------------------------------------------------------------------- src/main/groovy/cli/Option.java | 4 +- src/main/groovy/cli/Unparsed.java | 2 +- src/main/groovy/util/CliBuilder.groovy | 5 +- .../doc/core-domain-specific-languages.adoc | 8 +- src/test/groovy/util/CliBuilderTest.groovy | 95 ++++++++++++++++++++ 5 files changed, 107 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/dac0bd84/src/main/groovy/cli/Option.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/cli/Option.java b/src/main/groovy/cli/Option.java index 453a51a..9b48861 100644 --- a/src/main/groovy/cli/Option.java +++ b/src/main/groovy/cli/Option.java @@ -26,7 +26,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Indicates that a method or field can be used to set a CLI option. + * Indicates that a method or property can be used to set a CLI option. */ @java.lang.annotation.Documented @Retention(RetentionPolicy.RUNTIME) @@ -40,7 +40,7 @@ public @interface Option { String description() default ""; /** - * The short name of this option. Defaults to the name of member being annotated. + * The short name of this option. Defaults to the name of member being annotated if the longName is empty. * * @return the short name of this option */ http://git-wip-us.apache.org/repos/asf/groovy/blob/dac0bd84/src/main/groovy/cli/Unparsed.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/cli/Unparsed.java b/src/main/groovy/cli/Unparsed.java index d615cf1..a741413 100644 --- a/src/main/groovy/cli/Unparsed.java +++ b/src/main/groovy/cli/Unparsed.java @@ -24,7 +24,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Indicates that a method will contain the remaining arguments. + * Indicates that a method or property will contain the remaining arguments. */ @java.lang.annotation.Documented @Retention(RetentionPolicy.RUNTIME) http://git-wip-us.apache.org/repos/asf/groovy/blob/dac0bd84/src/main/groovy/util/CliBuilder.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/util/CliBuilder.groovy b/src/main/groovy/util/CliBuilder.groovy index 30537b0..91d2938 100644 --- a/src/main/groovy/util/CliBuilder.groovy +++ b/src/main/groovy/util/CliBuilder.groovy @@ -501,8 +501,9 @@ class CliBuilder { if (namesAreSetters) { def isBoolArg = m.parameterTypes.size() > 0 && m.parameterTypes[0].simpleName.toLowerCase() == 'boolean' boolean isFlag = (isBoolArg && !hasArg) || noArg - if (cli.hasOption(name) || isFlag) { - m.invoke(t, [isFlag ? cli.hasOption(name) : optionValue(cli, name)] as Object[]) + if (cli.hasOption(name) || isFlag || cli.defaultValue(name)) { + m.invoke(t, [isFlag ? cli.hasOption(name) : + cli.hasOption(name) ? optionValue(cli, name) : cli.defaultValue(name)] as Object[]) } } else { def isBoolRetType = m.returnType.simpleName.toLowerCase() == 'boolean' http://git-wip-us.apache.org/repos/asf/groovy/blob/dac0bd84/src/spec/doc/core-domain-specific-languages.adoc ---------------------------------------------------------------------- diff --git a/src/spec/doc/core-domain-specific-languages.adoc b/src/spec/doc/core-domain-specific-languages.adoc index 6c5245a..8210f5b 100644 --- a/src/spec/doc/core-domain-specific-languages.adoc +++ b/src/spec/doc/core-domain-specific-languages.adoc @@ -1233,7 +1233,8 @@ and populates it. You simply call the interface methods to interrogate the optio Alternatively, perhaps you already have a domain class containing the option information. You can simply annotate properties or setters from that class to enable `CliBuilder` to appropriately -populate your domain object. +populate your domain object. Each annotation both describes that option's properties through the annotation +attributes and indicates the setter the `CliBuilder` will use to populate that option in your domain object. Here is how such a specification can be defined: @@ -1430,7 +1431,7 @@ e.g. `String x = someVariable ?: 'some default'`. But sometimes you wish to make options specification to minimise the interrogators work in later stages. `CliBuilder` supports the `defaultValue` property to cater for this scenario. -Here is how you could use it using the deynamic api style: +Here is how you could use it using the dynamic api style: [source,groovy] ---- @@ -1452,6 +1453,9 @@ Which would be used like this: include::{projectdir}/src/spec/test/builder/CliBuilderTest.groovy[tags=withDefaultValueInterface,indent=0] ---- +You can also use the `defaultValue` annotation attribute when using annotations with an instance, +though it's probably just as easy to provide an initial value for the property (or backing field). + ===== Use with `TypeChecked` The dynamic api style of using `CliBuilder` is inherently dynamic but you have a few options http://git-wip-us.apache.org/repos/asf/groovy/blob/dac0bd84/src/test/groovy/util/CliBuilderTest.groovy ---------------------------------------------------------------------- diff --git a/src/test/groovy/util/CliBuilderTest.groovy b/src/test/groovy/util/CliBuilderTest.groovy index 4ec52d0..826e2cd 100644 --- a/src/test/groovy/util/CliBuilderTest.groovy +++ b/src/test/groovy/util/CliBuilderTest.groovy @@ -21,6 +21,7 @@ package groovy.util import groovy.cli.Option import groovy.cli.Unparsed import groovy.transform.ToString +import groovy.transform.TypeChecked import org.apache.commons.cli.BasicParser import org.apache.commons.cli.DefaultParser import org.apache.commons.cli.GnuParser @@ -519,6 +520,100 @@ usage: groovy this.remaining = remaining } } + class DefaultValueC { + @Option(shortName='f', defaultValue='one') String from + @Option(shortName='t', defaultValue='35') int to + @Option(shortName='b') int by = 1 + } + + void testDefaultValueClass() { + def cli = new CliBuilder() + def options = new DefaultValueC() + cli.parseFromInstance(options, '-f two'.split()) + assert options.from == 'two' + assert options.to == 35 + assert options.by == 1 + + options = new DefaultValueC() + cli.parseFromInstance(options, '-t 45 --by 2'.split()) + assert options.from == 'one' + assert options.to == 45 + assert options.by == 2 + } + + class ValSepC { + @Option(numberOfArguments=2) String[] a + @Option(numberOfArgumentsString='2', valueSeparator=',') String[] b + @Option(numberOfArgumentsString='+', valueSeparator=',') String[] c + @Unparsed remaining + } + + void testValSepClass() { + def cli = new CliBuilder() + + def options = new ValSepC() + cli.parseFromInstance(options, '-a 1 2 3 4'.split()) + assert options.a == ['1', '2'] + assert options.remaining == ['3', '4'] + + options = new ValSepC() + cli.parseFromInstance(options, '-a1 -a2 3'.split()) + assert options.a == ['1', '2'] + assert options.remaining == ['3'] + + options = new ValSepC() + cli.parseFromInstance(options, ['-b1,2'] as String[]) + assert options.b == ['1', '2'] + + options = new ValSepC() + cli.parseFromInstance(options, ['-c', '1'] as String[]) + assert options.c == ['1'] + + options = new ValSepC() + cli.parseFromInstance(options, ['-c1'] as String[]) + assert options.c == ['1'] + + options = new ValSepC() + cli.parseFromInstance(options, ['-c1,2,3'] as String[]) + assert options.c == ['1', '2', '3'] + } + + class WithConvertC { + @Option(convert={ it.toLowerCase() }) String a + @Option(convert={ it.toUpperCase() }) String b + @Option(convert={ Date.parse("yyyy-MM-dd", it) }) Date d + @Unparsed List remaining + } + + void testConvertClass() { + Date newYears = Date.parse("yyyy-MM-dd", "2016-01-01") + def argz = '''-a John -b Mary -d 2016-01-01 and some more'''.split() + def cli = new CliBuilder() + def options = new WithConvertC() + cli.parseFromInstance(options, argz) + assert options.a == 'john' + assert options.b == 'MARY' + assert options.d == newYears + assert options.remaining == ['and', 'some', 'more'] + } + + class TypeCheckedC { + @Option String name + @Option int age + @Unparsed List remaining + } + + @TypeChecked + void testTypeCheckedClass() { + def argz = "--name John --age 21 and some more".split() + def cli = new CliBuilder() + def options = new TypeCheckedC() + cli.parseFromInstance(options, argz) + String n = options.name + int a = options.age + assert n == 'John' && a == 21 + assert options.remaining == ['and', 'some', 'more'] + } void testParseFromInstance() { def p2 = new PersonC()