Skip to content

Commit

Permalink
[#628] Add support for collecting enum multi-value options and posi…
Browse files Browse the repository at this point in the history
…tional parameters in `EnumSet<>` collections
  • Loading branch information
remkop committed Feb 16, 2019
1 parent 0bc5873 commit bc6924e
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 5 deletions.
10 changes: 8 additions & 2 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ The picocli community is pleased to announce picocli 3.9.4.

This release contains bugfixes and enhancements.

From this release, `enum`-typed options and positional parameters that are multi-value can be stored in `EnumSet` collections, as well as other Collections, arrays and Maps.

Bugfixes: `ReflectionConfigGenerator` incorrectly listed superclass fields as fields of the concrete subclass, causing "GraalVM error: Error parsing reflection configuration in json" when creating a native image,
and method subcommands in commands that subclass another command caused `InitializationException`.


This is the forty-nineth public release.
Picocli follows [semantic versioning](http://semver.org/).
Expand All @@ -19,9 +24,10 @@ Picocli follows [semantic versioning](http://semver.org/).


## <a name="3.9.4-fixes"></a> Fixed issues
- [#628] Add support for collecting `enum` multi-value options and positional parameters in `EnumSet<>` collections. Thanks to [Lee Atkinson](https:/leeatkinson) for raising this.
- [#619] Bugfix: Method subcommands in commands that subclass another command caused `InitializationException`: "Another subcommand named 'method' already exists...". Thanks to [PorygonZRocks](https:/PorygonZRocks) for the bug report.
- [#622] Bugfix: ReflectionConfigGenerator incorrectly lists superclass fields as fields of the concrete subclass, causing "GraalVM error: Error parsing reflection configuration in json". Thanks to [Sebastian Thomschke](https:/sebthom) for the bug report.
- [#623] ReflectionConfigGenerator now generates json in alphabetic order.
- [#622] Bugfix: `ReflectionConfigGenerator` incorrectly listed superclass fields as fields of the concrete subclass, causing "GraalVM error: Error parsing reflection configuration in json". Thanks to [Sebastian Thomschke](https:/sebthom) for the bug report.
- [#623] `ReflectionConfigGenerator` now generates json in alphabetic order.

## <a name="3.9.4-deprecated"></a> Deprecations
No features were deprecated in this release.
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/picocli/CommandLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -8836,7 +8836,7 @@ private int applyValuesToCollectionField(ArgSpec argSpec,
List<Object> converted = consumeArguments(argSpec, lookBehind, arity, args, type, argDescription);
if (collection == null || (!collection.isEmpty() && !initialized.contains(argSpec))) {
tracer.debug("Initializing binding for %s with empty %s%n", optionDescription("", argSpec, 0), argSpec.type().getSimpleName());
collection = createCollection(argSpec.type()); // collection type
collection = createCollection(argSpec.type(), type); // collection type, element type
argSpec.setValue(collection);
}
initialized.add(argSpec);
Expand Down Expand Up @@ -9024,7 +9024,7 @@ private boolean is(ArgSpec p, String attribute, boolean value) {
return value;
}
@SuppressWarnings("unchecked")
private Collection<Object> createCollection(Class<?> collectionClass) throws Exception {
private Collection<Object> createCollection(Class<?> collectionClass, Class<?> elementType) throws Exception {
if (collectionClass.isInterface()) {
if (List.class.isAssignableFrom(collectionClass)) {
return new ArrayList<Object>();
Expand All @@ -9037,8 +9037,12 @@ private Collection<Object> createCollection(Class<?> collectionClass) throws Exc
}
return new ArrayList<Object>();
}
if (EnumSet.class.isAssignableFrom(collectionClass) && Enum.class.isAssignableFrom(elementType)) {
Object enumSet = EnumSet.noneOf((Class<Enum>) elementType);
return (Collection<Object>) enumSet;
}
// custom Collection implementation class must have default constructor
return (Collection<Object>) collectionClass.newInstance();
return (Collection<Object>) factory.create(collectionClass);
}
@SuppressWarnings("unchecked") private Map<Object, Object> createMap(Class<?> mapClass) throws Exception {
try { // if it is an implementation class, instantiate it
Expand Down
40 changes: 40 additions & 0 deletions src/test/java/picocli/CommandLineTypeConversionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@

import static java.util.concurrent.TimeUnit.*;
import static org.junit.Assert.*;
import static picocli.CommandLineTypeConversionTest.ResultTypes.COMPLETE;
import static picocli.CommandLineTypeConversionTest.ResultTypes.PARTIAL;

public class CommandLineTypeConversionTest {
// allows tests to set any kind of properties they like, without having to individually roll them back
Expand Down Expand Up @@ -1087,4 +1089,42 @@ class App {
assertEquals("Unmatched arguments: a:c, 1:3", ex.getMessage());
}
}
enum ResultTypes {
NONE,
PARTIAL,
COMPLETE
}
@Test
public void testIssue628EnumSetWithNullInitialValue() {
class App {
@Option(names = "--result-types", split = ",")
private EnumSet<ResultTypes> resultTypes = null;
}
App app = new App();
new CommandLine(app).parseArgs("--result-types", "PARTIAL,COMPLETE");

assertEquals(EnumSet.of(PARTIAL, COMPLETE), app.resultTypes);
}
@Test
public void testIssue628EnumSetWithEmptyInitialValue() {
class App {
@Option(names = "--result-types", split = ",")
private EnumSet<ResultTypes> resultTypes = EnumSet.noneOf(ResultTypes.class);
}
App app = new App();
new CommandLine(app).parseArgs("--result-types", "PARTIAL,COMPLETE");

assertEquals(EnumSet.of(PARTIAL, COMPLETE), app.resultTypes);
}
@Test
public void testIssue628EnumSetWithNonEmptyInitialValue() {
class App {
@Option(names = "--result-types", split = ",")
private EnumSet<ResultTypes> resultTypes = EnumSet.of(ResultTypes.COMPLETE);
}
App app = new App();
new CommandLine(app).parseArgs("--result-types", "PARTIAL,COMPLETE");

assertEquals(EnumSet.of(PARTIAL, COMPLETE), app.resultTypes);
}
}

0 comments on commit bc6924e

Please sign in to comment.