Skip to content

Commit

Permalink
ArgGroupTest - Used string literals and variable names.
Browse files Browse the repository at this point in the history
Index.adoc - Changed entire section to reflect guidance from PR.
  • Loading branch information
MadFoal authored and remkop committed Nov 16, 2021
1 parent 821d690 commit de47a3a
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 52 deletions.
71 changes: 64 additions & 7 deletions docs/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3156,46 +3156,103 @@ usage help shows the wrong default value
====

==== Using Default Values in Argument Groups
When an argument group is defined with default values in the annotated fields, but during usage does not reference any of the arguments within the group, picocli will instantiate those objects to their declared values. However, picocli will instantiate the other objects to their default annotated values if a single argument within the argument group is defined.
For picocli, there are two recommendations for successfully employing default values. First, using default values requires setting the default value within the annotation and declaratively within your code. The second is deciding how to instantiate your objects.

[source,role="primary"]
----
@Option(names= {"-x"}, defaultValue = "X") String X = "X";
----
Matching the annotation and declaration ensures that picocli sets the default values regardless of the user supplies the arguments.

Next, identify your preferred default behavior. For `+@ArgGroup+`'s there are two distinct ways of instantiating an object.

The first is to instantiate the object declaratively. This behavior is best when you desire no ambiguity in whether or not an `+@ArgGroup+` is instantiated.

.Java
[source,java,role="primary"]
----
@ArgGroup MyGroup myGroup = new MyGroup();
----

.Kotlin
[source,kotlin,role="secondary"]
----
@ArgGroup var myGroup = MyGroup();
----

The second way is to initialize the objects using business logic, such as `+run+` or `+call+` methods. This way is better if the desired behavior is to allow the application to determine whether the user specified a value for the `+@ArgGroup+`. It is recommended that the program called using `+execute+` instead of `+parseArgs+`

.Java
[source,java,role="primary"]
----
@Command(name = "test", description = "demonstrates Default Value declaration")
class MyApp {
@ArgGroup Outer outer = new Outer();
@ArgGroup Outer outer;
static class Outer {
@Options(names = "-x", defaultValue = "XX") String x;
@ArgGroup(exclusive = "true") Inner inner = new Inner();
@Options(names = "-x", defaultValue = "XX") String x = "XX";
@ArgGroup(exclusive = "true") Inner inner;
}
static class Inner {
@Options(names = "-a", defaultValue = "AA") String a = "AA";
@Options(names = "-b", defaultValue = "BB") String b = "BB";
}
public void run() {
if (outer == null) { // no options specified on command line; apply default values
outer = new Outer;
}
if (outer.inner == null) { // handle nested sub-groups; apply default for inner group
outer.inner = new Inner;
}
// remaining business logic...
}
}
public static void main() {
final MyApp obj = new MyApp();
new CommandLine(obj).execute("-x", "ANOTHER_VALUE");
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
@Command(name = "test", description =["demonstrates Default Value declaration"])
import sun.tools.jar.CommandLine@Command(name = "test", description =["demonstrates Default Value declaration"])
class MyApp {
@ArgGroup lateinit var outer: Outer
class Outer {
@Options(names = "-x", defaultValue = "XX") lateinit var x: String()
@Options(names = "-x", defaultValue = "XX") var x = "XX";
@ArgGroup lateinit var inner: Inner
}
class Inner {
@Options(names = "-a", defaultValue = "AA") var a : "AA"
@Options(names = "-b", defaultValue = "BB") var b : "BB"
}
override fun run() {
if (outer == null) { // no options specified on command line; apply default values
outer = Outer();
}
if (outer.inner == null) { // handle nested sub-groups; apply default for inner group
outer.inner = Inner();
}
// remaining business logic...
}
}
fun main(args: Array<String>) {
var obj = MyApp();
var commandLine = CommandLine(obj);
commandLine.execute(args);
}
----
In this example above, there are two cases why we need to write our class with both annotated and declared values. The first reason is that if we do not supply any arguments, the annotated "defaultValues" field will provide the instantiated value. The second reason is if we provide an "x" value but no "a" or "b" value, the variables use their declared value instead of the annotated value. Thus, in our example, by setting the annotated "defaultValue" and the declared value to the same default value, we ensure that we always have the correct default value for different combinations of arguments.


=== Positional Parameters
Expand Down
98 changes: 53 additions & 45 deletions src/test/java/picocli/ArgGroupTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4105,18 +4105,18 @@ static class OneOrTwo {

static class GroupOne {
@Option(names = { "-1a", "--option-1a" },required=true,description = "option A of group 1")
String _1a;
String a1;

@Option(names = { "-1b", "--option-1b" },required=true,description = "option B of group 1")
String _1b;
String b1;
}

static class GroupTwo {
@Option(names = { "-2a", "--option-2a" },required=true, defaultValue = "Default 2A", description = "option A of group 2")
private String _2a = "Default 2A";
private String a2 = "Default 2A";

@Option(names = { "-2b", "--option-2b" },required=true, defaultValue = "Default 2B", description = "option B of group 2")
private String _2b = "Default 2B";
private String b2 = "Default 2B";
}
public void run() {
if (optXAndGroupOneOrGroupTwo == null) {
Expand All @@ -4132,9 +4132,17 @@ public void run() {
optXAndGroupOneOrGroupTwo.oneORtwo.two = new GroupTwo();
}

// something something ham sandviche
}
}


// String literals for Issue 1409
final String sampleX = "ANOTHER VALUE";
final String errorX = "Default value for X incorrect";
final String errorA1 = "Default value for a1 incorrect";
final String errorB1 = "Default value for b1 incorrect";
final String errorA2 = "Default value for a2 incorrect";
final String errorB2 = "Default value for b2 incorrect";

/**
* Tests issue 1409 https://github.com/remkop/picocli/issues/1409
Expand All @@ -4148,12 +4156,12 @@ public void run() {
@Test
public void testIssue1409() {
final Issue1409 obj = new Issue1409();
new CommandLine(obj).execute("-x", "ANOTHER_VALUE");
assertEquals("Default value for X incorrect","ANOTHER_VALUE", obj.optXAndGroupOneOrGroupTwo.x);
assertEquals("Default value for _1a incorrect",null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1a);
assertEquals("Default value for _1b incorrect",null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1b);
assertEquals("Default value for _2a incorrect","Default 2A", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2a);
assertEquals("Default value for _2b incorrect","Default 2B", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2b);
new CommandLine(obj).execute("-x", sampleX);
assertEquals(errorX,sampleX, obj.optXAndGroupOneOrGroupTwo.x);
assertEquals(errorA1,null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.a1);
assertEquals(errorB1,null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.b1);
assertEquals(errorA2,"Default 2A", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.a2);
assertEquals(errorB2,"Default 2B", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.b2);
}

/**
Expand All @@ -4168,12 +4176,12 @@ public void testIssue1409() {
@Test
public void testIssue1409InitializeGroup1() {
final Issue1409 obj = new Issue1409();
new CommandLine(obj).execute("-x", "ANOTHER_VALUE", "-1a=x", "-1b=z");
assertEquals("Default value for X incorrect","ANOTHER_VALUE", obj.optXAndGroupOneOrGroupTwo.x);
assertEquals("Default value for _1a incorrect","x", obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1a);
assertEquals("Default value for _1b incorrect","z", obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1b);
assertEquals("Default value for _2a incorrect","Default 2A", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2a);
assertEquals("Default value for _2b incorrect","Default 2B", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2b);
new CommandLine(obj).execute("-x", sampleX, "-1a=x", "-1b=z");
assertEquals(errorX,sampleX, obj.optXAndGroupOneOrGroupTwo.x);
assertEquals(errorA1,"x", obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.a1);
assertEquals(errorB1,"z", obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.b1);
assertEquals(errorA2,"Default 2A", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.a2);
assertEquals(errorB2,"Default 2B", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.b2);
}

/**
Expand All @@ -4188,12 +4196,12 @@ public void testIssue1409InitializeGroup1() {
@Test
public void testIssue1409InitializeGroup2() {
final Issue1409 obj = new Issue1409();
new CommandLine(obj).execute("-x", "ANOTHER_VALUE", "-2a=x", "-2b=z");
assertEquals("Default value for X incorrect","ANOTHER_VALUE", obj.optXAndGroupOneOrGroupTwo.x);
assertEquals("Default value for _1a incorrect",null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1a);
assertEquals("Default value for _1b incorrect",null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1b);
assertEquals("Default value for _2a incorrect","x", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2a);
assertEquals("Default value for _2b incorrect","z", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2b);
new CommandLine(obj).execute("-x", sampleX, "-2a=x", "-2b=z");
assertEquals(errorX,sampleX, obj.optXAndGroupOneOrGroupTwo.x);
assertEquals(errorA1,null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.a1);
assertEquals(errorB1,null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.b1);
assertEquals(errorA2,"x", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.a2);
assertEquals(errorB2,"z", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.b2);
}

/**
Expand Down Expand Up @@ -4229,18 +4237,18 @@ static class OneOrTwo {

static class GroupOne {
@Option(names = { "-1a", "--option-1a" },required=true,description = "option A of group 1")
String _1a;
String a1;

@Option(names = { "-1b", "--option-1b" },required=true,description = "option B of group 1")
String _1b;
String b1;
}

static class GroupTwo {
@Option(names = { "-2a", "--option-2a" },required=true, defaultValue = "Default 2A", description = "option A of group 2")
private String _2a = "Default 2A";
private String a2 = "Default 2A"; // default value declared

@Option(names = { "-2b", "--option-2b" },required=true, defaultValue = "Default 2B", description = "option B of group 2")
private String _2b = "Default 2B";
private String b2 = "Default 2B"; // default value declared
}

}
Expand All @@ -4257,12 +4265,12 @@ static class GroupTwo {
@Test
public void testIssue1409Mod() {
final Issue1409Mod obj = new Issue1409Mod();
new CommandLine(obj).parseArgs("-x", "ANOTHER_VALUE");
assertEquals("Default value for X incorrect","ANOTHER_VALUE", obj.optXAndGroupOneOrGroupTwo.x);
assertEquals("Default value for _1a incorrect",null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1a);
assertEquals("Default value for _1b incorrect",null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1b);
assertEquals("Default value for _2a incorrect","Default 2A", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2a);
assertEquals("Default value for _2b incorrect","Default 2B", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2b);
new CommandLine(obj).parseArgs("-x", sampleX);
assertEquals(errorX,sampleX, obj.optXAndGroupOneOrGroupTwo.x);
assertEquals(errorA1,null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.a1);
assertEquals(errorB1,null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.b1);
assertEquals(errorA2,"Default 2A", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.a2);
assertEquals(errorB2,"Default 2B", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.b2);
}

/**
Expand All @@ -4277,12 +4285,12 @@ public void testIssue1409Mod() {
@Test
public void testIssue1409ModInitializeGroup1() {
final Issue1409Mod obj = new Issue1409Mod();
new CommandLine(obj).parseArgs("-x", "ANOTHER_VALUE", "-1a=x", "-1b=z");
assertEquals("Default value for X incorrect","ANOTHER_VALUE", obj.optXAndGroupOneOrGroupTwo.x);
assertEquals("Default value for _1a incorrect","x", obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1a);
assertEquals("Default value for _1b incorrect","z", obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1b);
assertEquals("Default value for _2a incorrect","Default 2A", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2a);
assertEquals("Default value for _2b incorrect","Default 2B", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2b);
new CommandLine(obj).parseArgs("-x", sampleX, "-1a=x", "-1b=z");
assertEquals(errorX,sampleX, obj.optXAndGroupOneOrGroupTwo.x);
assertEquals(errorA1,"x", obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.a1);
assertEquals(errorB1,"z", obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.b1);
assertEquals(errorA2,"Default 2A", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.a2);
assertEquals(errorB2,"Default 2B", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.b2);
}

/**
Expand All @@ -4297,12 +4305,12 @@ public void testIssue1409ModInitializeGroup1() {
@Test
public void testIssue1409ModInitializeGroup2() {
final Issue1409Mod obj = new Issue1409Mod();
new CommandLine(obj).parseArgs("-x", "ANOTHER_VALUE", "-2a=x", "-2b=z");
assertEquals("Default value for X incorrect","ANOTHER_VALUE", obj.optXAndGroupOneOrGroupTwo.x);
assertEquals("Default value for _1a incorrect",null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1a);
assertEquals("Default value for _1b incorrect",null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one._1b);
assertEquals("Default value for _2a incorrect","x", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2a);
assertEquals("Default value for _2b incorrect","z", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two._2b);
new CommandLine(obj).parseArgs("-x", sampleX, "-2a=x", "-2b=z");
assertEquals(errorX,sampleX, obj.optXAndGroupOneOrGroupTwo.x);
assertEquals(errorA1,null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.a1);
assertEquals(errorB1,null, obj.optXAndGroupOneOrGroupTwo.oneORtwo.one.b1);
assertEquals(errorA2,"x", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.a2);
assertEquals(errorB2,"z", obj.optXAndGroupOneOrGroupTwo.oneORtwo.two.b2);
}

}

0 comments on commit de47a3a

Please sign in to comment.