Let me paraphrase the question to see if I understand it correctly:
If the end user specifies an option directly on the command line, the command line value should be used, while if that option is not specified on the command line, the value in a file should be used.
Essentially, you are using an @-file with the intention to define default values for one or more options.
However, that is not what @-files were designed for: picocli cannot distinguish between arguments that came from the command line and arguments that came from the @-file.
I would suggest using picocli's default provider mechanism instead.
One idea is to use the built-in PropertiesDefaultProvider
:
import picocli.CommandLine.PropertiesDefaultProvider;
@Command(name = "myapp", defaultValueProvider = PropertiesDefaultProvider.class)
class MyApp { }
PropertiesDefaultProvider
also uses a file, and the values in that file are only used for options that were not specified on the command line.
The tricky bit is the location of this file. PropertiesDefaultProvider
looks for the file in the following locations:
- the path specified by system property
picocli.defaults.${COMMAND-NAME}.path
- a file named
.${COMMAND-NAME}.properties
in the end user's user home directory
(Replace ${COMMAND-NAME}
by the name of the command, so for a command named myapp
, the system property is picocli.defaults.myapp.path
)
To give end users the ability to specify the location of the file, we need to set the system property before picocli completes parsing the command line arguments.
We can do that with an @Option
-annotated setter method. For example:
class MyApp {
@Option(names = "-D")
void setSystemProperty(Map<String, String> properties) {
System.getProperties().putAll(properties);
}
}
This would allow end users to invoke the command with something like this:
myapp --sourceDatabaseType=MySQL -Dpicocli.defaults.myapp.path=.\myapp.options
If this is too verbose, you could go one step further and create a special -@
option, to allow users to invoke the command with something like this:
myapp --sourceDatabaseType=MySQL -@.\myapp.options
The implementation for this would be an annotated setter method, similar to the above:
class MyApp {
@Spec CommandSpec spec; // injected by picocli
@Option(names = "-@")
void setDefaultProviderPath(File path) {
// you could do some validation here:
if (!path.canRead()) {
String msg = String.format("ERROR: file not found: %s", path);
throw new ParameterException(spec.commandLine(), msg);
}
// only set the system property if the file exists
System.setProperty("picocli.defaults.myapp.path", path.toString());
}
}