Kap4Lin
Kap4Lin

Reputation: 109

picocli : parse arguments without boiler plate

I generally store away all the command line options to a different class, say, CliArguments. This avoids the noise in the main class. This is what I have with picocli:

public final class MyApp {

    private static final CliArguments cliArgs = new CliArguments();

    private MyApp() {}

    public static void main (String[] args) {
      if (parseArgs (args)) {
         new MyApp().execute();
      }
    }

    /* want to avoid this boiler plate */
    private static boolean parseArgs(String[] args) {
      CommandLine cmd = new CommandLine ( cliArgs );
      try {
        cmd.parseArgs( args );
        if (cmd.isUsageHelpRequested()) {
          cmd.usage( cmd.getOut() );
          return false;
        }
        else if ( cmd.isVersionHelpRequested() ) {
          cmd.printVersionHelp (cmd.getOut());
          return false;
        }
        logger.info("{}", cliArgs);
        return true;
     }
     catch ( ParameterException ex ) {
       logger.error ("Failure to parse : {}", ex);
       return false;
     }
   }

   private void execute() {
     // execution logic
   }
}

How do I avoid the boiler plate method, pargeArgs(String[])? The CliArguments class, technically, should not implement a Callable or Runnable. I can make MyApp be a Callable or Runnable. but to CommandLine, new MyApp() is not a command, new CliArguments() is.

If I want to do something like this:

   final int exitCode = new CommandLine(new MyApp()).execute(args);
   if (0 != exitCode) {
     logger.error("Failed to parse");
     System.exit(exitCode);
   }

how do I push off all the @Option specification to a different class, CliArguments while still having the execution control in MyApp?

I am sure I am missing something straight forward.

Upvotes: 1

Views: 394

Answers (1)

Remko Popma
Remko Popma

Reputation: 36754

The simplest way to achieve this is by making CliArguments a mixin in MyApp. We can then put the business logic in MyApp, and make it a Runnable or Callable so we can bootstrap the application with new CommandLine(new MyApp()).execute(args).

For example:

@Command(mixinStandardHelpOptions = true, version = "1.0.0")
public class CliArgs {
    @Option(names = "-x") boolean x;
    @Option(names = "-y") boolean y;
}

@Command(name = "myapp", description = "...")
public class MyApp implements Runnable {

    // options defined in the mixin are added to this command
    // also, @Command attributes from the mixin are applied to this command
    @Mixin
    CliArgs cliArgs;

    public void run() {
        System.out.printf("-x=%s%n", cliArgs.x);
        System.out.printf("-y=%s%n", cliArgs.y);
    }

    public void main(String... args) {
        System.exit(new CommandLine(new MyApp()).execute(args));
    }
}

The options defined in the CliArgs mixin become part of the MyApp mixee. Also, any @Command attributes defined in CliArgs become part of the MyApp command.

You can now run:

java MyApp -x

and this will print

-x=true
-y=false

Since the mixin has @Command(mixinStandardHelpOptions = true), the MyApp command also has --help and --version options that work as you would expect.

Upvotes: 1

Related Questions