Xenonite Xenonite - 3 months ago 26
Java Question

Java: Apache commons-cli how to handle options that depend on each other

I'm kinda struggling with the Apache commons-cli v1.3 and I haven't found a practical solution to the following problem yet:

I have a command line tool that - depending on the specified parameters - creates a String (or reads it from a local file), possible edits it inline, and optionally displays, writes said String to a local file or sends it via an HTTP request to a server.

So I have the options "c" for "create", "r" for "read", "e" for "edit" (via the cli), "d" for display, "w" for "write", and "p" for "push to server"

Obviously some combinations are possible. E.g. it should be possible to create this String and push it without reading or writing it from/to a file. Also, it should be possible to create and write without pushing, and so on...

So the semantics of the parameters are:

("c" OR ("r" ["e"])) ["d" "w" "p"]


Obviously, when the String is "c"reated, it must not be "r"ead. When "c"reating, i'd use interactive input from a cli-parser. When "r"eading, I want to allow the user to "e"dit via interactive input from the cli. The rest of the parameters are kinda optional.

Next: When "r"eading, a filename/path needs to be specified. Also, when "w"riting, this is necessary. Anyhow, it should be possible to specify a file to read from and a SECOND file to write to. So there would be two arguments for filenames, which are both optional.

The resulting syntax would look like this:

tool -cp
tool -rp "filenametoread"
tool -rdwp "filenametoread" "filenametowrite"
tool -cw "filenametowrite"


and so on.

I'm a bit lost here. How to I configure commons-cli to have two arguments for filenames, which are required based on the parameters (options) specified? Is this even possible?

Answer

Unfortunately, Commons CLI has no way of specifying interdependent options like that. To handle that, you need to do your own if checks. For example, like this

CommandLineParser parser = new PosixParser();
Options options = new Options();
options.addOption(new Option("h", "help", false, "display this message"));
options.addOption(new Option("c", "create", true, "Create a file"));
options.addOption(new Option("r", "read", truee, "Read a file"));
options.addOption(new Option("e", "edit", false, "Edit a file"));
options.addOption(new Option("d", "display", false, "Display a file"));
options.addOption(new Option("w", "write", false, "Write a file"));
options.addOption(new Option("p", "push", false, "Push a file"));

try {
    CommandLine commandLine = parser.parse(options, args);
    if (commandLine.hasOption("h")) {
        showHelp(options);
        System.exit(0);
    }
    // validate the commandline
    // obviously, these can be split up to give more helpful error messages
    if ((!(commandLine.hasOption("c") ^ (commandLine.hasOption("r") || commandLine.hasOption("e"))))
        || !(commandLine.hasOption("d") ^ commandLine.hasOption("w") ^ commandLine.hasOption("p"))) {
        throw new ParseException("Invalid combination");
    }
    if ((!commandLine.hasOption("c") && !commandLine.hasOption("r")) {
        throw new ParseException("Missing required arg");
    }

    // rest of the logic
} catch (ParseException pe) {
    throw new ParseException("Bad arguments\n" + pe.getMessage());
}
Comments