Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are detected CLI options different when using DefaultParser instead of GnuParser?

I'm about to migrate some legacy code to contain less deprecated warnings from 3rd-party libraries. For Apache commons-cli library (version: 1.3.1) I detected in the official JavaDoc that GnuParser is deprecated and DefaultParser should be used instead:

@deprecated since 1.3, use the {@link DefaultParser} instead

However, the following code snippet stops working as expected:

Options options = new Options();    
Option optionGSTypes = new Option(
        "gst","gs-types", true,
        "the supported types, comma-separated: article, category, template, all");
optionGSTypes.setArgs(3);
optionGSTypes.setValueSeparator(',');
options.addOption(optionGSTypes);

// ... other options

// parsed option values are correct, yet this is deprecated
CommandLineParser parser = new GnuParser(); 
CommandLine commands = parser.parse(options, args);

// ... interpret parsed 'commands' and related actual values via CLI

Note that setValueSeparator(',') is used here to define a custom separator char , to enable the CLI to support sevaral gst-types (see code snippet).

As input the following program arguments are used to call the CLI:

java -jar MyCLI.jar -gst category -gsd 4

Obviously, several other arguments might also have been added after the gsd parameter. The expected and correctly parsed options for the separator-less use of the "gst" argument are (via GnuParser):

  1. "category" (and nothing else)

However, when I change my code and switch towards the recommended parser via:

CommandLineParser parser = new DefaultParser();

the resulting, parsed values are detected incorrectly as:

  1. "category"
  2. "-gsd"
  3. "4"

Hint: I used a debugger to verify the incorrect result of the parse process via inspecting the field values in org.apache.commons.cli.Option via the returned commands variable.

My expectation would be that the internal change of the parser should not yield different results, as this breaks existing code. Has anyone ever encountered the same behavior with Apache Commons-CLI when switching to DefaultParser and several option values and custom separators?

Is there a difference in the construction/usage of DefaultParser that I might have overseen?

like image 394
MWiesner Avatar asked Dec 31 '15 16:12

MWiesner


2 Answers

I think the problem might be the call to optionGSTypes.setArgs(3);, according to the JavaDoc, it instructs commons-cli to "Sets the number of argument values this Option can take.", i.e. you instruct commons-cli to take the next three commnadline arguments as arguments for the "gst" argument.

Additionally the setValueSeparator(',') seems to define what usually the equal sign is used for, (see the JavaDoc), i.e. options with format like "key=value", so not what you are actually looking for.

In your case I think the easiest option is to specify the option argument as simple string and do the parsing yourself. This way you can fully control which values are allowed and also provide a better error message.

like image 24
centic Avatar answered Nov 10 '22 14:11

centic


Stepping through the code of DefaultParser, this seems like a bug.

First DefaultParser recognizes -gst as a short option by calling Options.hasShortOption("-gst") which returns true.

So far so good.

Now when it comes to deciding whether to interpret -gsd as an argument value to -gst DefaultParser needs to figure out if -gsd is itself an option (and therefore can't be an argument to -gst). It does so by calling its own isShortOption("-gsd"). This however returns false for reasons that become obvious if you look at the code:

private boolean isShortOption(String token)
{
    // short options (-S, -SV, -S=V, -SV1=V2, -S1S2)
    return token.startsWith("-") && token.length() >= 2 && 
           options.hasShortOption(token.substring(1, 2));
}

This extracts the first letter from the -gst option therefore calling Options.hasShortOption("g") which returns false. The code seems to be designed to work for POSIX-style single letter options, but it breaks down for multi-letter single-hyphen options like the ones you are using.

However you turn it, -gst getting recognized as a short option, but -gsd not getting recognized seems like a bug to me.

like image 195
Sven Schoenung Avatar answered Nov 10 '22 16:11

Sven Schoenung