Skip to content

Commit

Permalink
[#736] API: Allow removal of ArgSpec from CommandSpec.
Browse files Browse the repository at this point in the history
Closes #736
  • Loading branch information
remkop committed Jul 11, 2019
1 parent 6e13b37 commit 56959b8
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 18 deletions.
13 changes: 7 additions & 6 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,21 +130,22 @@ public class MyCommand implements Callable<Integer> {

## <a name="4.0.0-rc-1-fixes"></a> Fixed issues
- [#752][#658][#496] Add `picocli-spring-boot-starter` module including a `PicocliSpringFactory` and auto-configuration. Thanks to [Thibaud Lepretre](https://github.com/kakawait) for the pull request.
- [#696][#741] Automatically split lines in TextTable. Thanks to [Sualeh Fatehi](https://github.com/sualeh) for the pull request.
- [#736] API: Allow removal of `ArgSpec` from `CommandSpec`. Thanks to [AkosCz](https://github.com/akoscz) for the feature request.
- [#756] API: Make synopsis indent for multi-line synopsis configurable (related to #739).
- [#761] API: Add `ParseResult.matchedArgs()` method to return all matched arguments in order; change `ParseResult.matchedOptions()` and `ParseResult.matchedPositionals()` to return the full list of matched options and positional parameters, including duplicates if the option or positional parameter was matched multiple times. Thanks to [Michael D. Adams](https://github.com/adamsmd) for the feature request.
- [#760] API: Deprecate `CommandLine.setSplitQuotedStrings`: the vast majority of applications want to `split` while respecting quotes.
- [#754] Allow boolean options to get value from fallback instead of defaultProvider. Thanks to [Michael D. Adams](https://github.com/adamsmd) for the feature request.
- [#754] API/Enhancement: Allow boolean options to get value from fallback instead of defaultProvider. Thanks to [Michael D. Adams](https://github.com/adamsmd) for the feature request.
- [#696][#741] Enhancement: Automatically split lines in TextTable. Thanks to [Sualeh Fatehi](https://github.com/sualeh) for the pull request.
- [#744] Enhancement: Composite Argument Groups: more informative error messages. Thanks to [Andreas Deininger](https://github.com/deining) for raising this.
- [#745] Enhancement: Picocli should disallow `split` regex for single-value type options. Thanks to [Andreas Deininger](https://github.com/deining) for raising this.
- [#748] Enhancement: Provide API to use a custom Layout in usage help message: ensure `Help.createDefaultLayout()` is used internally so that subclasses overriding this method can control the Layout that is used.
- [#595] Enhancement: Support for quoted arguments containing nested quoted substrings, allowing end-users to control how values are split in parts when a `split` regex is defined.
- [#739] Bugfix: infinite loop or exception when command name plus synopsis heading length equals or exceeds usage help message width. Thanks to [Arturo Alonso](https://github.com/thefang12) for raising this.
- [#746] Bugfix: Apply default values to options and positional parameters in argument groups. Thanks to [Andreas Deininger](https://github.com/deining) for raising this.
- [#742] Bugfix: Default values prevent correct parsing in argument groups. Thanks to [Andreas Deininger](https://github.com/deining) for raising this.
- [#759] Bugfix: Correct tracing when custom end-of-option delimiter is matched on the command line.
- [#738] Bugfix: `setTrimQuotes` does not trim quotes from option names. Thanks to [Judd Gaddie](https://github.com/juddgaddie) for raising this.
- [#758] Bugfix: Duplicate name exception in argument group: better / more concise error message. Thanks to [Andreas Deininger](https://github.com/deining) for raising this.
- [#744] Enhancement: Composite Argument Groups: more informative error messages. Thanks to [Andreas Deininger](https://github.com/deining) for raising this.
- [#745] Enhancement: Picocli should disallow `split` regex for single-value type options. Thanks to [Andreas Deininger](https://github.com/deining) for raising this.
- [#748] Enhancement: Provide API to use a custom Layout in usage help message: ensure `Help.createDefaultLayout()` is used internally so that subclasses overriding this method can control the Layout that is used.
- [#595] Support for quoted arguments containing nested quoted substrings, allowing end-users to control how values are split in parts when a `split` regex is defined.
- [#751] Build: Make build more portable.
- [#753] Doc: Improve documentation for multi-value fields: mention the `split` attribute. Thanks to [feinstein](https://github.com/feinstein).
- [#740] Doc: Update user manual to replace `parse` examples with `parseArgs`.
Expand Down
68 changes: 57 additions & 11 deletions src/main/java/picocli/CommandLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -5445,6 +5445,43 @@ private CommandSpec addArg(ArgSpec arg) {
return this;
}

/** (INCUBATING) Removes the specified option spec or positional parameter spec from the list of configured arguments to expect.
* @param arg the option spec or positional parameter spec to remove
* @return this CommandSpec for method chaining
* @throws UnsupportedOperationException if the specified ArgSpec is part of a {@link ArgGroupSpec}
* @throws NoSuchElementException if the specified ArgSpec is not part of this {@code CommandSpec}
* @since 4.0 */
public CommandSpec remove(ArgSpec arg) {
if (arg.group() != null) {
throw new UnsupportedOperationException("Cannot remove ArgSpec that is part of an ArgGroup");
}
int removed = remove(arg, optionsByNameMap);
removed += remove(arg, posixOptionsByKeyMap);
removed += remove(arg, negatedOptionsByNameMap);
requiredArgs.remove(arg);
options.remove(arg);
if (positionalParameters.remove(arg)) {
removed++;
}
if (removed == 0) {
throw new NoSuchElementException(String.valueOf(arg));
}
arg.commandSpec = null;
arg.messages(null);
return this;
}
private static <T extends Object> int remove(ArgSpec arg, Map<T, OptionSpec> map) {
int result = 0;
for (Iterator<Map.Entry<T, OptionSpec>> iterator = map.entrySet().iterator(); iterator.hasNext(); ){
Map.Entry<?, OptionSpec> entry = iterator.next();
if (entry.getValue() == arg) {
iterator.remove();
result++;
}
}
return result;
}

/** Adds the specified {@linkplain ArgGroupSpec argument group} to the groups in this command.
* @param group the group spec to add
* @return this CommandSpec for method chaining
Expand Down Expand Up @@ -7523,23 +7560,26 @@ abstract static class Builder<T extends Builder<T>> {
Builder(ArgSpec original) {
userObject = original.userObject;
arity = original.arity;
converters = original.converters;
defaultValue = original.defaultValue;
description = original.description;
getter = original.getter;
setter = original.setter;
hidden = original.hidden;
paramLabel = original.paramLabel;
hideParamSyntax = original.hideParamSyntax;
descriptionKey = original.descriptionKey;
required = original.required;
interactive = original.interactive;
paramLabel = original.paramLabel;
hideParamSyntax = original.hideParamSyntax;
splitRegex = original.splitRegex;
hidden = original.hidden;
setTypeInfo(original.typeInfo);
converters = original.converters;
defaultValue = original.defaultValue;
initialValue = original.initialValue;
hasInitialValue = original.hasInitialValue;
showDefaultValue = original.showDefaultValue;
completionCandidates = original.completionCandidates;
splitRegex = original.splitRegex;
toString = original.toString;
descriptionKey = original.descriptionKey;
parameterConsumer = original.parameterConsumer;
setTypeInfo(original.typeInfo);
toString = original.toString;
getter = original.getter;
setter = original.setter;
scope = original.scope;
}
Builder(IAnnotatedElement source) {
userObject = source.userObject();
Expand Down Expand Up @@ -7879,6 +7919,9 @@ public static OptionSpec.Builder builder(String name, String... names) {
}
public static OptionSpec.Builder builder(String[] names) { return new Builder(names); }
public static OptionSpec.Builder builder(IAnnotatedElement source, IFactory factory) { return new Builder(source, factory); }
/** Returns a Builder initialized from the specified {@code OptionSpec}.
* @since 4.0 */
public static OptionSpec.Builder builder(OptionSpec original) { return new Builder(original); }

/** Ensures all attributes of this {@code OptionSpec} have a valid value; throws an {@link InitializationException} if this cannot be achieved. */
private OptionSpec(Builder builder) {
Expand Down Expand Up @@ -8153,6 +8196,9 @@ private void initCapacity() {
capacity = builderCapacity == null ? Range.parameterCapacity(arity(), index) : builderCapacity;
}
public static Builder builder() { return new Builder(); }
/** Returns a Builder initialized from the specified {@code PositionalSpec}.
* @since 4.0 */
public static Builder builder(PositionalParamSpec original) { return new Builder(original); }
public static Builder builder(IAnnotatedElement source, IFactory factory) { return new Builder(source, factory); }
/** Returns a new Builder initialized with the attributes from this {@code PositionalParamSpec}. Calling {@code build} immediately will return a copy of this {@code PositionalParamSpec}.
* @return a builder that can create a copy of this spec
Expand Down
31 changes: 31 additions & 0 deletions src/test/java/picocli/CommandLineModelTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1576,4 +1576,35 @@ public void testIsAddMethodSubcommandsReturnsSetValue() {
spec.setAddMethodSubcommands(null);
assertTrue(spec.isAddMethodSubcommands());
}

@Test
public void testRemoveArg() {
class MyApp {
// hide this option on all OS's other than Darwin (OSX)
@Option(names = {"-o", "--open"},
description = "On OSX open the finder to the data dir that was downloaded.")
boolean open = false;
}
MyApp myApp = new MyApp();
CommandLine cmd = new CommandLine(myApp);

String expectBefore = String.format("" +
"Usage: <main class> [-o]%n" +
" -o, --open On OSX open the finder to the data dir that was downloaded.%n");
assertEquals(expectBefore, cmd.getUsageMessage(Ansi.OFF));

// replace the old "--open" option with a different one that is hidden
CommandSpec spec = cmd.getCommandSpec();
OptionSpec old = spec.findOption("--open");
OptionSpec newOpenOption = OptionSpec.builder(old).hidden(true).build();
spec.remove(old);
spec.add(newOpenOption);

String expectAfter = String.format("" +
"Usage: <main class>%n");
assertEquals(expectAfter, cmd.getUsageMessage(Ansi.OFF));
assertFalse(myApp.open);
cmd.parseArgs("--open");
assertTrue(myApp.open);
}
}
2 changes: 1 addition & 1 deletion src/test/java/picocli/ModelOptionSpecTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public void testOptionSpecRequiresNonNullName() {
@Test()
public void testOptionSpecRequiresNonNullNameArray() {
try {
OptionSpec.builder(null).build();
OptionSpec.builder((String[]) null).build();
fail("Expected exception");
} catch (InitializationException ex) {
assertEquals("OptionSpec names cannot be null. Specify at least one option name.", ex.getMessage());
Expand Down

0 comments on commit 56959b8

Please sign in to comment.