Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java CLI Parser

I know that this question has been asked already, but I'm looking for Java cli parser with a specific functionality. I want it to be able to define command line tree, thus using subcommands (and more than one level depth). Thus i can have 3-4 level of commands before i get to the options. And these subcommands are mutually exclusive. Thanks

like image 739
Sophie Avatar asked Apr 15 '12 06:04

Sophie


People also ask

What is Java command parser?

public interface CommandLineParser. A class that implements the CommandLineParser interface can parse a String array according to the Options specified and return a CommandLine . Version: $Id: CommandLineParser.java 1443102 2013-02-06 18:12:16Z tn $

How do you parse a command line argument in Java?

To parse command line parameters, Commons CLI uses the DefaultParser implementation of the CommandlineParser interface. DefaultParser has a method called parse() which accepts the options object and the args from the command line. Finally, you can use the HelpFormatter to print the help information to the user.

What is Picocli?

Picocli is a one-file framework for creating Java command line applications with almost zero code. Picocli aims to be the easiest way to create rich command line applications that can run on and off the JVM.

What is Java command line?

The java command-line argument is an argument i.e. passed at the time of running the java program. The arguments passed from the console can be received in the java program and it can be used as an input. So, it provides a convenient way to check the behavior of the program for the different values.


2 Answers

Can be done with JCommander. Every JCommander object is, in its essence, a command with arbitrary number of parameters and/or arbitrary number of nested sub-commands, where the top JCommander object is the root command. Command parameters are always specific to the command they have been declared for and do not interfere with other commands' parameters. The interface for adding a sub-command is not very intuitive but is possible (see the addCommand method())

Here's a proof-of-concept test class:

public class Test{

@Test
public void nestedSubCommandTest() {
    GeneralOptions generalOpts = new GeneralOptions();
    JCommander jc = new JCommander(generalOpts);

    Command command = new Command();
    JCommander jc_command = addCommand(jc, "command", command);

    SubCommand1 subcommand1 = new SubCommand1();
    JCommander jc_subcommand1 = addCommand(jc_command, "subcommand1",
            subcommand1);

    SubCommand2 subcommand2 = new SubCommand2();
    JCommander jc_subcommand2 = addCommand(jc_subcommand1, "subcommand2",
            subcommand2);

    SubCommand3 subcommand3 = new SubCommand3();
    addCommand(jc_subcommand2, "subcommand3", subcommand3);

    jc.parse("--general-opt", 
        "command", "--opt", 
        "subcommand1",
        "subcommand2", "--sub-opt2", 
        "subcommand3", "--sub-opt3");

    assertTrue(generalOpts.opt);// --general-opt was set
    assertTrue(command.opt);// command --opt was set
    assertFalse(subcommand1.opt);// subcommand1 --sub-opt1 was not set
    assertTrue(subcommand2.opt);// subcommand2 --sub-opt2 was set
    assertTrue(subcommand3.opt);// subcommand3 --sub-opt3 was set
}

private static JCommander addCommand(JCommander parentCommand,
        String commandName, Object commandObject) {
    parentCommand.addCommand(commandName, commandObject);
    return parentCommand.getCommands().get(commandName);
}

public static class GeneralOptions {
    @Parameter(names = "--general-opt")
    public boolean opt;
}

@Parameters
public static class Command {
    @Parameter(names = "--opt")
    public boolean opt;
}

@Parameters
public static class SubCommand1 {
    @Parameter(names = "--sub-opt1")
    public boolean opt;
}

@Parameters
public static class SubCommand2 {
    @Parameter(names = "--sub-opt2")
    public boolean opt;
}

@Parameters
public static class SubCommand3 {
    @Parameter(names = "--sub-opt3")
    public boolean opt;
}
}

Edit: How to reuse commands.

Solution 1, use inheritance:

  public class CommonArgs{
    @Parameter(names="--common-opt")
    public boolean isCommonOpt;
  }

  @Parameters(description = "my command 1")
  public class MyCommand1 extends CommonArgs{}

  @Parameters(description = "my command 2")
  public class MyCommand2 extends CommonArgs{}

I think the usage and behaviour is faily obvious for this one. The one drawback is that you can only extend from one class, which may limit reusability in the future.

Solution 2, using composition pattern (see the doc here):

  public class CommonArgs{
    @Parameter(names="--common-opt")
    public boolean isCommonOpt;
  }

  @Parameters(description = "my command 1")
  public class MyCommand1{
    @ParametersDelegate
    public CommonArgs commonArgs = new CommonArgs();
  }

  @Parameters(description = "my command 2")
  public class MyCommand2{
    @ParametersDelegate
    public CommonArgs commonArgs = new CommonArgs();
  }

Here, the nested commonArgs classes' parameters will be treated as if they were direct parameters of the command class. You can add as many delegates as you wish, or even nest delegates inside other delegates and so on. To get the value of the delegated option after parsing, just do myCommand1.commonArgs.isCommonOpt, etc.

like image 71
rodion Avatar answered Nov 16 '22 01:11

rodion


picocli supports nested subcommands to arbitrary depth.

CommandLine commandLine = new CommandLine(new MainCommand())
        .addSubcommand("cmd1", new ChildCommand1()) // 1st level
        .addSubcommand("cmd2", new ChildCommand2())
        .addSubcommand("cmd3", new CommandLine(new ChildCommand3()) // 2nd level
                .addSubcommand("cmd3sub1", new GrandChild3Command1())
                .addSubcommand("cmd3sub2", new GrandChild3Command2())
                .addSubcommand("cmd3sub3", new CommandLine(new GrandChild3Command3()) // 3rd
                        .addSubcommand("cmd3sub3sub1", new GreatGrandChild3Command3_1())
                        .addSubcommand("cmd3sub3sub2", new GreatGrandChild3Command3_2())
                                // etc
                )
        );

You may also like its usage help with ANSI styles and colors.

Note that usage help lists the registered subcommands in addition to options and positional parameters.

enter image description here

The usage help is easily customized with annotations.

enter image description here

  • annotation-based
  • git-style subcommands
  • nested sub-subcommands
  • strongly typed option parameters
  • strongly typed positional parameters
  • customizable type conversion
  • multi-value options
  • intuitive model for how many arguments a field consumes
  • fluent API
  • POSIX-style clustered short options
  • GNU style long options
  • allows any option prefix
  • ANSI colors in usage help
  • customizable usage help
  • single source file: include as source to keep your application a single jar
like image 28
Remko Popma Avatar answered Nov 16 '22 00:11

Remko Popma