package picocli.examples.defaultprovider; import picocli.CommandLine; import picocli.CommandLine.IDefaultValueProvider; import picocli.CommandLine.Model.ArgSpec; import picocli.CommandLine.Model.CommandSpec; import picocli.CommandLine.Model.OptionSpec; import picocli.CommandLine.Model.PositionalParamSpec; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.util.Properties; /** * {@link IDefaultValueProvider IDefaultValueProvider} implementation that loads default values for command line * options and positional parameters from a properties file or {@code Properties} object. *
{@code * @Command(name = "git", defaultValueProvider = PropertiesDefaultProvider.class) * class Git { } * }*
The above will try to load default values from {@code new File(System.getProperty("user.home"), ".git.properties")}. *
*
* The location of the properties file can also be controlled with system property {@code "picocli.defaults.
* The location of the properties file may also be specified programmatically. For example: *
** CommandLine cmd = new CommandLine(new MyCommand()); * File defaultsFile = new File("path/to/config/mycommand.properties"); * cmd.setDefaultValueProvider(new PropertiesDefaultProvider(defaultsFile)); * cmd.execute(args); **
* For options, the key is either the {@linkplain CommandLine.Option#descriptionKey() descriptionKey}, * or the option's {@linkplain OptionSpec#longestName() longest name}. *
* For positional parameters, the key is either the * {@linkplain CommandLine.Parameters#descriptionKey() descriptionKey}, * or the positional parameter's {@linkplain PositionalParamSpec#paramLabel() param label}. *
* End users may not know what the {@code descriptionKey} of your options and positional parameters are, so be sure * to document that with your application. *
** The default values for options and positional parameters of subcommands can be included in the * properties file for the top-level command, so that end users need to maintain only a single file. * This can be achieved by prefixing the key with the command's qualified name. * For example, to give the {@code `git commit`} command's {@code --cleanup} option a * default value of {@code strip}, define a key of {@code git.commit.cleanup} and assign * it a default value. *
* # /home/remko/.git.properties * git.commit.cleanup = strip **/ public class PropertiesDefaultProvider implements IDefaultValueProvider { private Properties properties; /** * Default constructor, used when this default value provider is specified in * the annotations: *
* {@code * @Command(name = "mycmd", * defaultValueProvider = PropertiesDefaultProvider.class) * class MyCommand // ... * } **
* This loads default values from a properties file named * {@code ".mycmd.properties"} in the user home directory. *
* The location of the properties file can also be controlled with system property {@code "picocli.defaults.
* CommandLine cmd = new CommandLine(new MyCommand()); * Properties defaults = getProperties(); * cmd.setDefaultValueProvider(new PropertiesDefaultProvider(defaults)); * cmd.execute(args); ** @param properties the properties containing the default values * @see PropertiesDefaultProvider the PropertiesDefaultProvider class description */ public PropertiesDefaultProvider(Properties properties) { this.properties = properties; } /** * This constructor loads default values from the specified properties file. * This may be used programmatically. For example: *
* CommandLine cmd = new CommandLine(new MyCommand()); * File defaultsFile = new File("path/to/config/file.properties"); * cmd.setDefaultValueProvider(new PropertiesDefaultProvider(defaultsFile)); * cmd.execute(args); ** @param file the file to load default values from. Must be non-{@code null} and * must contain default values in the standard java {@link Properties} format. * @see PropertiesDefaultProvider the PropertiesDefaultProvider class description */ public PropertiesDefaultProvider(File file) { this(createProperties(file)); } private static Properties createProperties(File file) { if (file == null) { throw new NullPointerException("file is null"); } Properties result = new Properties(); if (file.exists() && file.canRead()) { Reader reader = null; try { reader = new FileReader(file); result.load(reader); } catch (IOException ioe) { System.err.println("WARN - could not read defaults from " + file.getAbsolutePath() + ": " + ioe); } } else { System.err.println("WARN - defaults configuration file " + file.getAbsolutePath() + " does not exist or is not readable"); } return result; } private static Properties loadProperties(CommandSpec commandSpec) { if (commandSpec == null) { return null; } Properties p = System.getProperties(); for (String name : commandSpec.names()) { String path = p.getProperty("picocli.defaults." + name + ".path"); File defaultPath = new File(p.getProperty("user.home"), "." + name + ".properties"); File file = path == null ? defaultPath : new File(path); if (file.canRead()) { return createProperties(file); } } return loadProperties(commandSpec.parent()); } @Override public String defaultValue(ArgSpec argSpec) throws Exception { if (properties == null) { properties = loadProperties(argSpec.command()); } if (properties == null || properties.isEmpty()) { return null; } return argSpec.isOption() ? optionDefaultValue((OptionSpec) argSpec) : positionalDefaultValue((PositionalParamSpec) argSpec); } private String optionDefaultValue(OptionSpec option) { String result = getValue(option.descriptionKey(), option.command()); result = result != null ? result : getValue(stripPrefix(option.longestName()), option.command()); return result; } private static String stripPrefix(String prefixed) { for (int i = 0; i < prefixed.length(); i++) { if (Character.isJavaIdentifierPart(prefixed.charAt(i))) { return prefixed.substring(i); } } return prefixed; } private String positionalDefaultValue(PositionalParamSpec positional) { String result = getValue(positional.descriptionKey(), positional.command()); result = result != null ? result : getValue(positional.paramLabel(), positional.command()); return result; } private String getValue(String key, CommandSpec spec) { String result = null; if (spec != null) { String cmd = spec.qualifiedName("."); result = properties.getProperty(cmd + "." + key); } return result != null ? result : properties.getProperty(key); } }