The Apache Commons Command Line Interface (CLI), is a handy little toolkit for handling command line arguments. Java programs, as with almost all other programs running on an OS that supports a CLI, permits passing of arguments from the user as an ordered collection of strings.
The problem with Commons CLI is that it doesn’t appear to manage a scenario that I wanted supported in xml2csv, whereby:
- The configuration file option (
-c
) is mandatory. xml2csv cannot do anything meaningful without a configuration file, so one must be specified; EXCEPT - When the user requests help (
-h
) or version information (-v
), then obviously a configuration file is not required. - In the usage message, I want to see both
–h
and–v
as available command line options.
I could find a couple of search hits that gave me an answer to 1 & 2, but I needed to make a further change to support 3.
This is how I created the Options objects to model this:
static { // mainOptions contains all the options together, including help and version. Options mainOptions = new Options(); Option option = new Option(OPT_CONFIG_FILE, "configuration-file", true, "A single file containing the configuration to use."); option.setRequired(true); mainOptions.addOption(option); option = new Option(OPT_OUT_DIR, "output-directory", true, "The directory to which the output CSV files will be written. " + "If not specified, current working directory will be used. Directory must exist and be writeable."); mainOptions.addOption(option); option = new Option(OPT_TRIM_WHITESPACE, "preserve-whitespace", false, "If specified then whitespace will not be removed from the start and end of output fields."); mainOptions.addOption(option); option = new Option(OPT_APPEND_OUTPUT, "append-output", false, "If specified, all output will be appended to any existing output files. If an existing file is" + " appended to then field names will not be output."); mainOptions.addOption(option); // helpOptions contains only the help and version options, it's important that these are both optional. // Note how both mainOptions and helpOptions contains help and verbose options. Options helpOptions = new Options(); option = new Option(OPT_HELP, "help", false, "Show help on using xml2csv and terminate."); mainOptions.addOption(option); helpOptions.addOption(option); option = new Option(OPT_VERSION, "version", false, "Show version information and terminate."); mainOptions.addOption(option); helpOptions.addOption(option); MAIN_OPTIONS = mainOptions; HELP_OPTIONS = helpOptions; }
Once the options have been created, then the algorithm for using them is as follows:
- Parse the arguments against
HELP_OPTIONS
. - If
–v
and–h
are specified then carry out the relevant actions. Note that–h
needs to print the usage information as generated fromMAIN_OPTIONS
. - If neither
–v
nor–h
are specified then parse againstMAIN_OPTIONS
. If mandatory (-c
) options are not specified then an error is printed and a usage message is shown.
The code is as follows:
/** * Instance entry point for command line invocation. * * @param args command line arguments */ public void execute(String[] args) { if (LOG.isInfoEnabled()) { LOG.info("xml2csv execute invoked {}", StringUtil.toString(args)); } try { if (!showHelpOrVersion(args)) { BasicParser parser = new BasicParser(); CommandLine cmdLine = parser.parse(MAIN_OPTIONS, args); LOG.info("Successfully parsed main options."); boolean trimWhitespace = Boolean.parseBoolean(cmdLine.getOptionValue(OPT_TRIM_WHITESPACE)); boolean appendOutput = Boolean.parseBoolean(cmdLine.getOptionValue(OPT_APPEND_OUTPUT)); String[] xmlInputs = cmdLine.getArgs(); String outputDirName = cmdLine.getOptionValue(OPT_OUT_DIR); String configFileName = cmdLine.getOptionValue(OPT_CONFIG_FILE); execute(configFileName, xmlInputs, outputDirName, appendOutput, trimWhitespace); } } catch (ProgramException pe) { LOG.error("A fatal error caused xml2csv to abort", pe); // All we can do is print out the error and terminate the program System.err.print(getAllCauses(pe)); } catch (ParseException pe) { // Thrown when the command line arguments are invalid LOG.debug("Invalid arguments specified: {}", pe.getMessage()); System.err.println("Invalid arguments specified: " + pe.getMessage()); printHelp(); } } /** * If the arguments contain a request for help or verson information, then show these and return true, otherwise return false. * * @param args command line arguments. * @return true if the help or version options have been specified, false otherwise (i.e. normal processing should resume). * @throws ParseException if an option parsing exception occurs. */ private boolean showHelpOrVersion(String[] args) throws ParseException { CommandLineParser parser = new BasicParser(); CommandLine cmdLine = parser.parse(HELP_OPTIONS, args, true); if (cmdLine.getOptions().length == 0) { return false; } LOG.info("Showing help or version information and terminating."); if (cmdLine.hasOption(OPT_HELP)) { printHelp(); } else if (cmdLine.hasOption(OPT_VERSION)) { printVersionInfo(); } else { throw new BugException("Options set up is wrong. Found help or version, but neither believes they have been passed."); } return true; }
You can see the code in action in xml2csv, check out Program.java.