Skip to main content Link Menu Expand (external link) Document Search Copy Copied

Using Nested Options

Header Image

Ahoy, mateys! It’s time to set sail on a new adventure in command line parsing with Apache Commons CLI. In our last article, we learned about grouping options together. This time, we’re going to dive into the topic of nesting options within other options.

If you’re new to the concept of command line parsing, don’t worry. We’ll guide you through the process step by step, using pirate-themed examples that will make the journey both informative and entertaining. So hoist the Jolly Roger, and let’s set sail!

Nesting Options Within Other Options

Nesting options within other options is a powerful feature that can help you build complex command line interfaces with ease. Essentially, it allows you to create options that depend on other options being present.

For example, let’s say we’re building a command line tool for managing a pirate crew. We might have an option called --add-crew-member that allows us to add a new crew member to our roster. But what if we want to specify the crew member’s rank as well?

Instead of creating a separate option for the rank, we can nest it within the --add-crew-member option. Here’s what that would look like:

Usage: pirate-tool --add-crew-member <name> [--rank <rank>]

In this example, the --rank option is nested within the --add-crew-member option. It’s only valid if the --add-crew-member option is present. If we try to use the --rank option without using the --add-crew-member option, we’ll get an error.

Let’s see how this works in practice. Here’s some code that creates a parser with a nested option:

Options options = new Options();

Option addCrewMember = new Option("a", "add-crew-member", true, "Add a new crew member");
addCrewMember.setArgName("name");
options.addOption(addCrewMember);

Option rank = Option.builder("r")
                    .longOpt("rank")
                    .hasArg(true)
                    .argName("rank")
                    .desc("Specify the crew member's rank")
                    .build();
addCrewMember.addOption(rank);

CommandLineParser parser = new DefaultParser();
CommandLine cmd = parser.parse(options, args);

In this example, we create an option called addCrewMember, which takes a single argument (the crew member’s name). We then create a nested option called rank, which is only valid if the addCrewMember option is present.

When we parse the command line arguments, the CommandLine object will contain both the addCrewMember option and the rank option (if it was specified). We can access the values of these options using the getOptionValue() method:

if (cmd.hasOption("add-crew-member")) {
    String name = cmd.getOptionValue("add-crew-member");
    String rank = cmd.getOptionValue("rank");

    // Add the crew member to our roster (with the specified rank, if applicable)
}

And there you have it! With just a few lines of code, we’ve created a command line tool that can add new crew members to our pirate crew, along with their rank.

Specifying Option Hierarchy

In the previous section, we learned about nesting options within other options. But what if we want to specify a hierarchy of options that goes beyond just one level of nesting?

For example, let’s say we’re building a command line tool for managing a pirate ship. We might have a top-level option called --ship that allows us to specify the ship’s name. But what if we want to specify additional details about the ship, such as its type (sloop, brigantine, etc.) and its size (small, medium, large)?

To do this, we can create a hierarchy of options by using groups. A group is a collection of options that are related to each other in some way. We can create a group for each level of the hierarchy, and then nest the groups within each other to create a tree-like structure.

Here’s what the usage message for our --ship option might look like:

Usage: pirate-tool --ship <name> [--type <type>] [--size <size>]

In this example, we have a top-level option called --ship, which takes a single argument (the ship’s name). We also have two nested options, --type and --size, which are related to the ship.

Let’s see how this works in code. Here’s an example that creates a parser with a hierarchy of options:

Options options = new Options();

Option ship = new Option("s", "ship", true, "Specify the ship's name");
ship.setArgName("name");
options.addOption(ship);

OptionGroup typeGroup = new OptionGroup();
Option sloop = new Option("sloop", false, "Sloop");
Option brigantine = new Option("brigantine", false, "Brigantine");
typeGroup.addOption(sloop);
typeGroup.addOption(brigantine);
options.addOptionGroup(typeGroup);

OptionGroup sizeGroup = new OptionGroup();
Option small = new Option("small", false, "Small");
Option medium = new Option("medium", false, "Medium");
Option large = new Option("large", false, "Large");
sizeGroup.addOption(small);
sizeGroup.addOption(medium);
sizeGroup.addOption(large);
options.addOptionGroup(sizeGroup);

ship.addOptionGroup(typeGroup);
ship.addOptionGroup(sizeGroup);

CommandLineParser parser = new DefaultParser();
CommandLine cmd = parser.parse(options, args);

In this example, we create a top-level option called ship, which takes a single argument (the ship’s name). We then create two option groups, typeGroup and sizeGroup, each of which contains a set of options related to the ship’s type and size, respectively.

We add each option to the appropriate option group, and then add the option groups to the ship option using the addOptionGroup() method. This creates a hierarchy of options, where the typeGroup and sizeGroup options are nested within the ship option.

When we parse the command line arguments, the CommandLine object will contain all of the options that were specified, along with their values. We can access these values using the getOptionValue() method:

if (cmd.hasOption("ship")) {
    String name = cmd.getOptionValue("ship");
    String type = null;
    String size = null;

    if (cmd.hasOption("sloop")) {
        type = "sloop";
    } else if (cmd.hasOption("brigantine")) {
        type = "brigantine";
    }

    if (cmd.hasOption("small")) {
        size = "small";
    } elseif (cmd.hasOption("medium")) {
        size = "medium";
    } else if (cmd.hasOption("large")) {
        size = "large";
    }

    // Do something with the ship information
}

And there you have it! With just a few lines of code, we’ve created a command line tool that can manage a pirate ship with multiple levels of options and groups.

Specifying Option Hierarchy

Nesting options within other options is just one way to create complex command line interfaces. Another approach is to specify an option hierarchy, which allows you to create subcommands and nested subcommands.

Subcommands are essentially separate commands within your main command line tool. For example, our pirate crew management tool might have subcommands for adding new crew members, updating existing crew members, and removing crew members.

Here’s what the usage syntax might look like with subcommands:

Usage: pirate-tool [options] <command> [command-options]

Commands:
  add-member    Add a new crew member
  update-member Update an existing crew member
  remove-member Remove a crew member from the roster

In this example, add-member, update-member, and remove-member are all subcommands of our main pirate-tool command. Each subcommand has its own set of options, which are specified as “command-options”.

Here’s some code that creates a parser with subcommands:

Options options = new Options();

Option addMember = new Option("a", "add-member", true, "Add a new crew member");
addMember.setArgName("name");
options.addOption(addMember);

Option updateMember = new Option("u", "update-member", true, "Update an existing crew member");
updateMember.setArgName("name");
options.addOption(updateMember);

Option removeMember = new Option("r", "remove-member", true, "Remove a crew member from the roster");
removeMember.setArgName("name");
options.addOption(removeMember);

CommandLineParser parser = new DefaultParser();

try {
    CommandLine cmd = parser.parse(options, args);

    if (cmd.hasOption("add-member")) {
        // Add a new crew member
    } else if (cmd.hasOption("update-member")) {
        // Update an existing crew member
    } else if (cmd.hasOption("remove-member")) {
        // Remove a crew member from the roster
    } else {
        // Show help message
    }
} catch (ParseException e) {
    // Handle parsing errors
}

In this example, we create three separate options for our subcommands (addMember, updateMember, and removeMember). We then use a series of if statements to determine which subcommand was specified.

If none of the subcommands were specified (i.e., the user just ran pirate-tool without any arguments), we can show a help message that lists the available subcommands.

Conclusion

In this article, we’ve learned about the concept of specifying option hierarchy. By using subcommands and nested subcommands, we can create powerful command line interfaces that allow users to perform a wide range of tasks with our tool.

We hope you’ve enjoyed this journey into command line parsing with Apache Commons CLI. Remember, with the power of this library, the only limit to what you can do with command line interfaces is your imagination. So go forth, ye scurvy dogs, and build some amazing command line tools!