Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1409 documentation tests #1463

Merged
merged 8 commits into from
Nov 16, 2021
54 changes: 54 additions & 0 deletions docs/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3138,6 +3138,60 @@ usage help shows the wrong default value
----
====

==== Default Values in Unreferenced Argument Groups
MadFoal marked this conversation as resolved.
Show resolved Hide resolved
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.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is tricky to give a short and clear description of the problem we are trying to solve here... How about something like this?

If none of the options in an argument group is specified on the command line, picocli will not instantiate the user object for that group. So how does the application access default values for the group options? Essentially, the application needs to do some extra work for groups, that is not necessary for options outside of argument groups.

Then maybe follow this by a recommended usage (see also below).


.Java
[source,java,role="primary"]
----
@Command(name = "test", description = "demonstrates Default Value declaration")
class MyApp {
@ArgGroup Outer outer;

class Outer {
MadFoal marked this conversation as resolved.
Show resolved Hide resolved
@Options(names = "-x", defaultValue = "XX") String x;
@ArgGroup(exclusive = "true") Inner inner;
}

class Inner {
MadFoal marked this conversation as resolved.
Show resolved Hide resolved
@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;
}
remkop marked this conversation as resolved.
Show resolved Hide resolved

// remaining business logic...
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
@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()
@ArgGroup lateinit var inner: Inner
}

class Inner {
@Options(names = "-a", defaultValue = "AA") var a = "AA"
@Options(names = "-b", defaultValue = "BB") var b = "BB"
}

remkop marked this conversation as resolved.
Show resolved Hide resolved
}
----
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.
Copy link
Owner

@remkop remkop Nov 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be an idea to phrase this as a recommendation?
And maybe put it in before the example code, what do you think?

The recommended way to work with default values for options in groups is as follows:

  • ...
  • ...

Putting it all together, see the example below.



=== Positional Parameters

When a `@Parameters` positional parameter is part of a group, its `index` is the index _within the group_, not within the command.
Expand Down
217 changes: 142 additions & 75 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1711,81 +1711,81 @@
});
});
</script>
<style>
Copy link
Owner

@remkop remkop Nov 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to modify the generated index.html. Can you revert this change? (just leave the index.html as it is)

.hidden {
display: none;
}
.switch {
border-width: 1px 1px 0 1px;
border-style: solid;
border-color: #7a2518;
display: inline-block;
}
.switch--item {
padding: 10px;
background-color: #ffffff;
color: #7a2518;
display: inline-block;
cursor: pointer;
}
.switch--item.selected {
background-color: #7a2519;
color: #ffffff;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
<script type="text/javascript">
function addBlockSwitches() {
$('.primary').each(function() {
primary = $(this);
createSwitchItem(primary, createBlockSwitch(primary)).item.addClass("selected");
primary.children('.title').remove();
});
$('.secondary').each(function(idx, node) {
secondary = $(node);
primary = findPrimary(secondary);
switchItem = createSwitchItem(secondary, primary.children('.switch'));
switchItem.content.addClass('hidden');
findPrimary(secondary).append(switchItem.content);
secondary.remove();
});
}
function createBlockSwitch(primary) {
blockSwitch = $('<div class="switch"></div>');
primary.prepend(blockSwitch);
return blockSwitch;
}
function findPrimary(secondary) {
candidate = secondary.prev();
while (!candidate.is('.primary')) {
candidate = candidate.prev();
}
return candidate;
}
function createSwitchItem(block, blockSwitch) {
blockName = block.children('.title').text();
content = block.children('.content').first().append(block.next('.colist'));
item = $('<div class="switch--item">' + blockName + '</div>');
item.on('click', '', content, function(e) {
$(this).addClass('selected');
$(this).siblings().removeClass('selected');
e.data.siblings('.content').addClass('hidden');
e.data.removeClass('hidden');
});
blockSwitch.append(item);
return {'item': item, 'content': content};
}
$(addBlockSwitches);
</script>
<style>
.hidden {
display: none;
}

.switch {
border-width: 1px 1px 0 1px;
border-style: solid;
border-color: #7a2518;
display: inline-block;
}

.switch--item {
padding: 10px;
background-color: #ffffff;
color: #7a2518;
display: inline-block;
cursor: pointer;
}

.switch--item.selected {
background-color: #7a2519;
color: #ffffff;
}

</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
<script type="text/javascript">
function addBlockSwitches() {
$('.primary').each(function() {
primary = $(this);
createSwitchItem(primary, createBlockSwitch(primary)).item.addClass("selected");
primary.children('.title').remove();
});
$('.secondary').each(function(idx, node) {
secondary = $(node);
primary = findPrimary(secondary);
switchItem = createSwitchItem(secondary, primary.children('.switch'));
switchItem.content.addClass('hidden');
findPrimary(secondary).append(switchItem.content);
secondary.remove();
});
}

function createBlockSwitch(primary) {
blockSwitch = $('<div class="switch"></div>');
primary.prepend(blockSwitch);
return blockSwitch;
}

function findPrimary(secondary) {
candidate = secondary.prev();
while (!candidate.is('.primary')) {
candidate = candidate.prev();
}
return candidate;
}

function createSwitchItem(block, blockSwitch) {
blockName = block.children('.title').text();
content = block.children('.content').first().append(block.next('.colist'));
item = $('<div class="switch--item">' + blockName + '</div>');
item.on('click', '', content, function(e) {
$(this).addClass('selected');
$(this).siblings().removeClass('selected');
e.data.siblings('.content').addClass('hidden');
e.data.removeClass('hidden');
});
blockSwitch.append(item);
return {'item': item, 'content': content};
}

$(addBlockSwitches);

</script>

</head>
<body class="article toc2 toc-left">
Expand Down Expand Up @@ -6104,6 +6104,73 @@ <h4 id="_default_values_in_group_usage_help"><a class="anchor" href="#_default_v
</div>
</div>
</div>


<div class="sect3">
<h4 id="_default_values_in_unreferenced_argument_groups"><a class="anchor" href="#_default_values_in_unreferenced_argument_groups"></a>8.5.2. Default Values in Unreferenced Argument Groups</h4>
<div class="paragraph">
<p>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.</p>
</div>
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="java">
<span class="nd">@Command</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"test"</span><span class="o">,</span> <span class="n">description</span> <span class="o">=</span> <span class="s">"demonstrates Default Value declaration"</span><span class="o">)</span>
<span class="kd">class</span> <span class="nc">MyApp</span> <span class="o">{</span>
<span class="nd">@ArgGroup</span> <span class="kt">Outer</span> <span class="n">outer</span><span class="o">;</span>

<span class="kd">class</span> <span class="nc">Outer</span> <span class="o">{</span>
<span class="nd">@Options</span><span class="o">(</span><span class="n">names</span> <span class="o">= </span><span class="s">"-x"</span><span class="o">,</span> <span class="n">defaultValue</span><span class="o"> = </span><span class="s">"XX"</span><span class="o">)</span> <span class="kt">String</span> <span class="n">x</span><span class="o">;</span>
<span class="nd">@ArgGroup</span><span class="o">(</span><span class="n">exclusive</span> <span class="o">=</span> <span class="s">"true"</span><span class="o">)</span> <span class="kt">Inner</span> <span class="n">inner</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">class</span> <span class="nc">Inner</span> <span class="o">{</span>
<span class="nd">@Options</span><span class="o">(</span><span class="n">names</span> <span class="o">= </span><span class="s">"-a"</span><span class="o">, </span><span class="n">defaultValue</span><span class="o"> = </span><span class="s">"AA"</span><span class="o">)</span> <span class="kt">String</span> <span class="n">a</span> <span class="o">= </span><span class="n">"AA"</span><span class="o">;</span>
<span class="nd">@Options</span><span class="o">(</span><span class="n">names</span> <span class="o">= </span><span class="s">"-b"</span><span class="o">,</span> <span class="n">defaultValue</span><span class="o"> = </span><span class="s">"BB"</span><span class="o">)</span> <span class="kt">String</span><span class="n"> b</span> <span class="o">= </span><span class="n">"BB"</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">public void</span> <span class="kt">run()</span> <span class="o">{</span>
<span class="kd">if</span> <span class="o">(</span><span class="n">outer </span><span class="kd"> == </span> <span class="kt">null</span><span class="o">) {</span><span class=”n”> // no options specified on command line; apply default values</span>
<span class="n">outer </span><span class="o">= </span><span class="nf">new </span><span class="nc">Outer</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">if</span> <span class="o">(</span><span class="n">outer.inner </span><span class="kd"> == </span> <span class="kt">null</span><span class="o">) {</span><span class=”n”> // handle nested sub-groups; apply default for inner group</span>
<span class="n">outer.inner </span><span class="o">= </span><span class="nf">new </span><span class="nc">Inner</span><span class="o">;</span>
<span class="o">}</span>

<span class="n">// remaining business logic...</span>
<span class="o">}</span>
</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="kotlin">
<span class="nd">@Command</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"test"</span><span class="o">,</span> <span class="n">description</span> <span class="o">=</span><span class="p">[</span><span class="s">"demonstrates Default Value declaration"</span><span class="p">]</span><span class="o">)</span>
<span class="kd">class</span> <span class="nc">MyApp</span> <span class="o">{</span>
<span class="nd">@ArgGroup</span> <span class="o">lateinit var </span><span class="n">outer: </span><span class="kt">Outer</span>

<span class="kd">class</span> <span class="nc">Outer</span> <span class="o">{</span>
<span class="nd">@Options</span><span class="o">(</span><span class="n">names</span> <span class="o">= </span><span class="s">"-x"</span><span class="o">, </span><span class="n">defaultValue</span><span class="o"> = </span><span class="s">"XX"</span><span class="o">)</span> <span class="o">lateinit var</span> <span class="n">x:</span> <span class=”kt”>String()</span>
<span class="nd">@ArgGroup</span> <span class="o">lateinit var </span><span class="n">inner: </span><span class="kt">Inner</span>
<span class="o">}</span>


<span class="kd">class</span> <span class="nc">Inner</span> <span class="o">{</span>
<span class="nd">@Options</span><span class="o">(</span><span class="n">names</span> <span class="o">= </span><span class="s">"-a"</span><span class="o">, </span><span class="n">defaultValue</span><span class="o"> = </span><span class="s">"AA"</span><span class="o">) var</span> <span class="n">a = </span><span class=”k”>"AA"</span>
<span class="nd">@Options</span><span class="o">(</span><span class="n">names</span> <span class="o">= </span><span class="s">"-b"</span><span class="o">, </span><span class="n">defaultValue</span><span class="o"> = </span><span class="s">"BB"</span><span class="o">) var</span> <span class="n">b = </span><span class=”k">"BB"</span>
<span class="o">}</span>

<span class="o">}</span></code></pre>
</div>
</div>

<div class="paragraph">
<p>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.</p>
</div>
</div>


<div class="sect2">
<h3 id="_positional_parameters_2"><a class="anchor" href="#_positional_parameters_2"></a>8.6. Positional Parameters</h3>
<div class="paragraph">
Expand Down
Loading