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

EnumSet<> options #628

Closed
leeatkinson opened this issue Feb 14, 2019 · 5 comments
Closed

EnumSet<> options #628

leeatkinson opened this issue Feb 14, 2019 · 5 comments

Comments

@leeatkinson
Copy link

I have a enum and I want an argument to be an EnumSet so I can set flags on the CLI, for example:

foo --result-types PARTIAL,COMPLETE

with code similar to:

public enum ResultTypes {
    NONE,
    PARTIAL,
    COMPLETE
}

@Option(names = "--result-types")
private EnumSet<ResultTypes> resultTypes = EnumSet.of(ResultTypes.COMPLETE);

But I get the error when executed:

Invalid value for option '--result-types' (<ResultTypes>): expected one of [NONE, PARTIAL, COMPLETE] (case-sensitive) but was 'PARTIAL,COMPLETE'

I have written a converter to parse the string:

public class ResultTypesEnumSetConverter implements CommandLine.ITypeConverter<EnumSet<ResultTypes>> {
    @Override
    public EnumSet<ResultTypes> convert(String value) throws Exception {
        String[] split = value.split(",");
        EnumSet<ResultTypes> types = EnumSet.noneOf(ResultTypes.class);
        for (int i = 0; i < split.length; i++) {
            types.add(ResultTypes.valueOf(split[i]));
        }
        return types;
    }
}

but this doesn't seem to work:

InstantiationException: java.util.EnumSet while processing argument at or before arg[1] 'PARTIAL,COMPLETE'
@remkop
Copy link
Owner

remkop commented Feb 14, 2019

Can you run again with -Dpicocli.trace=DEBUG and show the output?

@remkop
Copy link
Owner

remkop commented Feb 15, 2019

Thank you for raising this issue! I was able to reproduce the problem, please ignore my previous comment.

This is a shortcoming in the current version of picocli.

Both problems have the same cause: picocli detects that EnumSet<ResultTypes> is a Collection that can hold multiple ResultTypes instances. If the original field value is null or has a non-empty collection as the initial/default value, it tries to create a new empty instance of the collection and add the ResultTypes instances found during parsing to this new collection instance. The idea is that when done, it will replace the field value with the new collection.

The problem is that picocli currently has no special treatment for EnumSet, and there is no default constructor so the instantiation fails. Even with the custom type converter, picocli will try to instantiate the empty EnumSet...

At the moment the workaround is to either:

  • use a different Set implementation like HashSet, TreeSet or LinkedHashSet
  • initialize the field with an empty EnumSet with EnumSet.noneOf(ResultTypes.class)

With that last workaround, this test passes:

enum ResultTypes {
    NONE,
    PARTIAL,
    COMPLETE
}
@Test
public void testEnumSet() {
    class App {
        @Option(names = "--result-types", split = ",") //, converter = ResultTypesEnumSetConverter.class)
        //private EnumSet<ResultTypes> resultTypes = EnumSet.of(ResultTypes.COMPLETE);
        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);
}

I am a big fan of using EnumSet wherever possible and I am looking at ways to fix this.

@leeatkinson
Copy link
Author

Many thanks for your prompt response and help.

I've used your workaround and, because I need to set a default value, rather than assigning it in the code (which causes the exception) I can use a IDefaultValueProvider.

@remkop remkop added this to the 3.9.4 milestone Feb 16, 2019
remkop added a commit that referenced this issue Feb 16, 2019
…tional parameters in `EnumSet<>` collections
remkop added a commit that referenced this issue Feb 16, 2019
…tional parameters in `EnumSet<>` collections
@remkop
Copy link
Owner

remkop commented Feb 16, 2019

Fixed in master and the 3.9.x branch. Please verify.
I plan to release 3.9.4 including this fix soon.

@remkop remkop closed this as completed Feb 16, 2019
@remkop
Copy link
Owner

remkop commented Feb 17, 2019

Picocli 3.9.4 with a fix for this issue has been released. Enjoy!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants