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

Add support for IDefaultValueProvider #321

Closed
remkop opened this issue Apr 3, 2018 · 27 comments
Closed

Add support for IDefaultValueProvider #321

remkop opened this issue Apr 3, 2018 · 27 comments

Comments

@remkop
Copy link
Owner

remkop commented Apr 3, 2018

Add support for IDefaultValueProvider on @Command and potentially on individual @Option and @Parameters.

See discussion under #261.

Proposed API:

interface IDefaultValueProvider {
    /** Returns the default value for an option or positional parameter or {@code null}.
     * The returned value is converted to the type of the option/positional parameter
     * via the same type converter used when populating this option/positional
     * parameter from a command line argument.
     * @param argSpec the option or positional parameter, never {@code null}
     * @return the default value for the option or positional parameter, or {@code null} if
     *       this provider has no default value for the specified option or positional parameter
     */
    String defaultValue(ArgSpec argSpec);
}
@remkop
Copy link
Owner Author

remkop commented Apr 3, 2018

Note to self: this ticket can/should be implemented before #322. Given the discussion on #280, there are some trade-offs with a defaulValue annotation attribute, while the benefits of this ticket are more clear.

@remkop remkop modified the milestones: 3.0, 3.1 Apr 22, 2018
@remkop remkop modified the milestones: 3.2, 3.3 Jul 6, 2018
@remkop remkop modified the milestones: 3.4, 3.5 Jul 23, 2018
@remkop remkop modified the milestones: 3.5, 3.6 Aug 3, 2018
@remkop remkop modified the milestones: 3.6, 3.7 Aug 26, 2018
@remkop
Copy link
Owner Author

remkop commented Aug 30, 2018

API-wise, I think the below is sufficient for the first cut:

public @interface Command {
  Class<? extends IDefaultValueProvider> defaultValueProvider() default NoDefaultProvider.class;
  // ...
}
private static class NoDefaultProvider implements IDefaultValueProvider {
    public String defaultValue(ArgSpec argSpec) { throw new UnsupportedOperationException(); }
}

public static class CommandSpec {
  public IDefaultValueProvider defaultValueProvider() { return defaultValueProvider; }
  public CommandSpec defaultValueProvider(IDefaultValueProvider  defaultValueProvider) {
    this.defaultValueProvider = defaultValueProvider;
    return this; 
  }
  // ...
}

No need to have an defaultValueProvider annotation attribute on @option and @parameters.

@NicolasMassart
Copy link
Contributor

NicolasMassart commented Aug 30, 2018

If you can help me, I have difficulties to see how to retrieve the CommandSpec (to get my default value provider) from within the ArgSpec defaultValue() method

@remkop
Copy link
Owner Author

remkop commented Aug 30, 2018

I was thinking to leave ArgSpec.default() untouched, and only add logic to the method in Interpreter where the default values are set before parsing the user input. Away from PC, from memory I think it was setDefaultValue(ArgSpec). I was thinking to also pass the IDefaultProvider to that method.

@NicolasMassart
Copy link
Contributor

Ok, something like that ?

        private void applyDefault(IDefaultValueProvider defaultValueProvider,
            ArgSpec arg, List<ArgSpec> required) throws Exception {

            String defaultValue = defaultValueProvider == null ? arg.defaultValue() : defaultValueProvider.defaultValue(arg) ;

            if (defaultValue == null) { return; }
            if (tracer.isDebug()) {tracer.debug("Applying defaultValue (%s) to %s%n", defaultValue, arg);}
            Range arity = arg.arity().min(Math.max(1, arg.arity().min));

            applyOption(arg, LookBehind.SEPARATE, arity, stack(defaultValue), new HashSet<ArgSpec>(), arg.toString);
            required.remove(arg);
        }

@remkop
Copy link
Owner Author

remkop commented Aug 30, 2018

Yes exactly!

@NicolasMassart
Copy link
Contributor

Also do you have an idea about where to put the test ? I was thinking about something around

public void testOptionSpec_setsDefaultValue_ifNotMatched() {

@NicolasMassart
Copy link
Contributor

I'm going to propose my PR without test for now for you to see if it's ok, but of course I'm going to add these test. Anyway you wont merge it if there's no tests ;)

NicolasMassart added a commit to NicolasMassart/picocli that referenced this issue Aug 30, 2018
Following what was done for Command version provider I added a default value
provider as proposed in remkop#321.
@remkop
Copy link
Owner Author

remkop commented Aug 30, 2018

That was quick! Thanks!
I’ll review as soon as I can.
As for where to put the tests, it may be easier to add a new test class for testing IDefaultProvider.

NicolasMassart added a commit to NicolasMassart/picocli that referenced this issue Aug 31, 2018
According to PR remkop#458 review:
 - fixed javadoc and since version
 - added a setter in Command facade for default provider
 - replaced use of defaultValueProvider field by its getter
@NicolasMassart
Copy link
Contributor

Agree fort the new test class.

NicolasMassart added a commit to NicolasMassart/picocli that referenced this issue Aug 31, 2018
@NicolasMassart
Copy link
Contributor

NicolasMassart commented Aug 31, 2018

@remkop let me know what you think of the PR #458. For me it seems over but...

@remkop
Copy link
Owner Author

remkop commented Sep 1, 2018

Thanks for the contribution!
I’ll take a look as soon as I can get to my PC.

I guess the last thing is a mention of this new mechanism in the Default Values section of the user manual. I can take care of that unless you get to it first.

@NicolasMassart
Copy link
Contributor

Will try.

@remkop
Copy link
Owner Author

remkop commented Sep 2, 2018

Ok, then I'll wait for you on the docs.
Would you mind rebasing your branch on master?

remkop added a commit that referenced this issue Sep 2, 2018
…ultValueProvider

#321 add support for default value provider
@remkop
Copy link
Owner Author

remkop commented Sep 2, 2018

I merged the PR. Many thanks! There are some other tickets I'm already working on so it may take me a bit before I will get to the user manual. If you feel like doing another PR for the user manual, go for it! :-)

@remkop remkop modified the milestones: 3.7, 3.6 Sep 2, 2018
@NicolasMassart
Copy link
Contributor

I will have a look at the doc part, I promise. Meanwhile I'm going to use the system to be sure it works well ;)

@NicolasMassart
Copy link
Contributor

I would need to add something to the PR I did, a boolean to indicate if the defaultprovider should override default values or not. I will propose another PR for that and the doc of course.

@remkop
Copy link
Owner Author

remkop commented Sep 3, 2018

What is the use case for this boolean? If someone wants a default value in the code to “win”, they can simply omit it from the config file, no?

I would prefer to keep it simple. We can always enhance later if really necessary.

@remkop
Copy link
Owner Author

remkop commented Sep 3, 2018

Alternatively, IDefaultProvider implementors can inspect the specified ArgSpec and return its default value if one exists. Again, there should be no need for a boolean in the picocli API.

@NicolasMassart
Copy link
Contributor

Yes sometimes, when you are head down in the code you don't see obvious things...

@remkop
Copy link
Owner Author

remkop commented Sep 4, 2018

No worries. :-)

@NicolasMassart
Copy link
Contributor

Looking deeper, it seems that it however requires a fix in my previous PR. Because if I have a default or initial value, the default provider value can't apply. So that's why I wanted to go in this twisted addition I guess. But the right way to do is to fix it instead of adding more logic...
So I will provide a PR and fixed tests.

NicolasMassart added a commit to NicolasMassart/picocli that referenced this issue Sep 4, 2018
Fixed misbehaviour that was introduced in PR remkop#458, see remkop#321
Now default provider overrides default or initial values only if the return
value of the provider is not null.
remkop pushed a commit that referenced this issue Sep 4, 2018
Fixed misbehaviour that was introduced in PR #458, see #321
Now default provider overrides default or initial values only if the return
value of the provider is not null.
@remkop remkop closed this as completed in 708fe50 Sep 9, 2018
@remkop
Copy link
Owner Author

remkop commented Sep 9, 2018

I went ahead and added some text to the user manual and release notes. Feel free to suggest further improvements.

Again thanks for the contribution!

@NicolasMassart
Copy link
Contributor

Sorry for the delay for doc. I will review what you wrote but next time I will do more. Just that work always comes first...

@remkop
Copy link
Owner Author

remkop commented Sep 9, 2018

No worries!
Grateful for what you did!

@remkop
Copy link
Owner Author

remkop commented Sep 9, 2018

I just realized we forgot about ${DEFAULT-VALUE} replacement in the usage help description.
That is tricky because the ArgSpec does not have a reference to the default value provider...
I guess we need to add that, or a reference to the CommandSpec, when an ArgSpec is added to the CommandSpec.

@remkop remkop reopened this Sep 9, 2018
@remkop
Copy link
Owner Author

remkop commented Sep 10, 2018

I've given it some thought and I am leaning towards changing ArgSpec.defaultValueString() to return the value from the default provider (if one exists).

That meets the first goal (description text containing ${DEFAULT-VALUE} should be rendered with the default value), and at the same time allows application authors to distinguish between the default value that was programmatically set on an option (can be obtained with ArgSpec.defaultValue()) and the default value from the provider (can be obtained with IDefaultProvider.defaultValue(ArgSpec)).

(Also need to update the Javadoc for these ArgSpec methods to clarify where the values come from.)

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