A Groovy-based command line interface builder that leverages the picocli framework to provide both dynamic API and annotation-based approaches for parsing command line arguments
npx @tessl/cli install tessl/maven-org-apache-groovy--groovy-cli-picocli@5.0.0Groovy CLI Picocli is a command line interface builder for Groovy applications that integrates with the picocli framework. It provides both dynamic API and annotation-based approaches for parsing command line arguments, handling option validation, generating help messages, and accessing parsed values through a convenient interface.
dependencies {
implementation 'org.apache.groovy:groovy-cli-picocli:5.0.0'
}import groovy.cli.picocli.CliBuilder
import groovy.cli.picocli.OptionAccessor
import groovy.cli.Option
import groovy.cli.Unparsed
import groovy.cli.TypedOption
import groovy.cli.CliBuilderExceptionimport groovy.cli.picocli.CliBuilder
def cli = new CliBuilder(name: 'myapp')
cli.h(longOpt: 'help', 'Show usage information')
cli.v(longOpt: 'verbose', 'Enable verbose output')
cli.f(longOpt: 'file', args: 1, argName: 'filename', 'Input file to process')
def options = cli.parse(args)
if (!options) {
// Parse failed, error message already printed
return
}
if (options.h) {
cli.usage()
return
}
println "Verbose: ${options.v}"
println "File: ${options.f}"
println "Arguments: ${options.arguments()}"import groovy.cli.picocli.CliBuilder
import groovy.cli.Option
import groovy.cli.Unparsed
interface MyOptions {
@Option(shortName='h', description='Show usage information')
boolean help()
@Option(shortName='v', description='Enable verbose output')
boolean verbose()
@Option(shortName='f', description='Input file to process')
String file()
@Unparsed
List<String> remaining()
}
def cli = new CliBuilder()
def options = cli.parseFromSpec(MyOptions, args)
if (options.help()) {
cli.usage()
}import groovy.cli.picocli.CliBuilder
import groovy.cli.Option
import groovy.cli.Unparsed
class MyOptions {
@Option(shortName='h', description='Show usage information')
boolean help
@Option(shortName='v', description='Enable verbose output')
boolean verbose
@Option(shortName='f', description='Input file to process')
String file
@Unparsed
List<String> remaining
}
def cli = new CliBuilder()
def options = new MyOptions()
cli.parseFromInstance(options, args)The Groovy CLI Picocli library is built around several key components:
@Option and @Unparsed annotations for declarative option definitionThe main command line builder class for creating CLI parsers with flexible configuration options.
class CliBuilder {
// Constructor
CliBuilder()
CliBuilder(Map args)
// Configuration Properties
String usage // Command synopsis displayed in usage help
String name // Program name displayed in synopsis
boolean posix // Enable/disable POSIX short option clustering (default: true)
boolean expandArgumentFiles // Whether @-file arguments are expanded (default: true)
boolean stopAtNonOption // How to handle non-option arguments (default: true)
boolean acceptLongOptionsWithSingleHyphen // Accept long options with single hyphen (default: false)
PrintWriter writer // Writer for usage help messages (default: System.out)
PrintWriter errorWriter // Writer for error messages (default: System.err)
String header // Additional message displayed after usage summary
String footer // Additional message displayed after options
int width // Usage message width
ParserSpec parser // Fine-grained parser behaviour control
UsageMessageSpec usageMessage // Fine-grained usage message control
// Parsing Methods
OptionAccessor parse(String[] args)
OptionAccessor parse(List args)
<T> T parseFromSpec(Class<T> optionsClass, String[] args)
<T> T parseFromInstance(T optionInstance, String[] args)
// Option Definition (Dynamic API)
TypedOption option(Map args, Class type, String description)
def invokeMethod(String name, Object args) // Dynamic method for option definition
// Utility Methods
void usage() // Print usage message
void setUsage(String usage)
void setFooter(String footer)
void setHeader(String header)
void setWidth(int width)
}Dynamic Option Definition Examples:
// Short option with description
cli.h('Show help')
// Short option with long option and description
cli.h(longOpt: 'help', 'Show help')
// Option with argument
cli.f(longOpt: 'file', args: 1, argName: 'filename', 'Input file')
// Option with multiple arguments
cli.lib(args: 3, valueSeparator: ',', 'Library paths')
// Option with type conversion
cli.port(type: Integer, 'Port number')
// Option with Map type for key=value pairs
cli.D(type: Map, argName: 'property=value', 'System properties')
// Long-only option (no short name)
cli._(longOpt: 'verbose', 'Enable verbose output')Provides access to parsed command line options and arguments with type-safe value retrieval.
class OptionAccessor {
ParseResult parseResult // Underlying picocli parse result
// Constructor
OptionAccessor(ParseResult parseResult)
// Option Value Access
boolean hasOption(TypedOption typedOption)
<T> T getOptionValue(TypedOption<T> typedOption)
<T> T getOptionValue(TypedOption<T> typedOption, T defaultValue)
<T> T getAt(TypedOption<T> typedOption) // Alternative syntax []
<T> T getAt(TypedOption<T> typedOption, T defaultValue)
Properties getOptionProperties(String name) // For Map-type options
<T> T defaultValue(String name) // Get default value for option name
// Argument Access
List<String> arguments() // Get remaining non-option arguments
// Dynamic Property Access
def invokeMethod(String name, Object args) // Dynamic method dispatch
def getProperty(String name) // Dynamic property access
}Usage Examples:
def cli = new CliBuilder()
def helpOption = cli.h(longOpt: 'help', 'Show help')
def fileOption = cli.f(longOpt: 'file', args: 1, 'Input file')
def options = cli.parse(args)
// Check if option was specified
if (options.hasOption(helpOption)) {
cli.usage()
}
// Get option value with type safety
String filename = options.getOptionValue(fileOption)
String filenameWithDefault = options.getOptionValue(fileOption, "default.txt")
// Alternative bracket syntax
String filename2 = options[fileOption]
String filename3 = options[fileOption, "default.txt"]
// Dynamic property access (for simple cases)
boolean help = options.h
String file = options.f
// Get remaining arguments
List<String> remaining = options.arguments()
// Access Map-type option properties (for -D key=value style options)
Properties props = options.getOptionProperties("D")
String value = props.getProperty("key")
// Get default value for an option
String defaultFile = options.defaultValue("file") // Returns default if specifiedMarks methods or fields as CLI options in annotation-based usage with comprehensive configuration options.
@interface Option {
String description() default "" // Description of the option
String shortName() default "" // Short name (single character)
String longName() default "" // Long name (multi-character)
String valueSeparator() default "" // Value separator for multivalued options
boolean optionalArg() default false // Whether option has optional argument
int numberOfArguments() default 1 // Number of arguments
String numberOfArgumentsString() default "" // Number of arguments as string ('+', '*')
String defaultValue() default "" // Default value as string
Class convert() default Undefined.class // Custom conversion closure class
}Target: Methods, Fields
Usage Examples:
interface MyOptions {
@Option(shortName='h', longName='help', description='Show usage information')
boolean help()
@Option(shortName='v', description='Enable verbose output')
boolean verbose()
@Option(shortName='f', longName='file', description='Input file to process')
String file()
@Option(shortName='p', longName='port', description='Port number', defaultValue='8080')
int port()
@Option(longName='libs', valueSeparator=',', numberOfArgumentsString='+',
description='Comma-separated library paths')
String[] libraries()
@Option(shortName='D', numberOfArgumentsString='*',
description='System properties as key=value pairs')
Map<String, String> properties()
}
class MyOptions {
@Option(shortName='h', description='Show help')
boolean help
@Option(shortName='f', description='Input file')
String file
@Option(shortName='c', description='Count', defaultValue='1')
int count
}Marks methods or fields to contain remaining non-option arguments after parsing.
@interface Unparsed {
String description() default "ARGUMENTS" // Description for remaining arguments
}Target: Methods, Fields
Usage Examples:
interface MyOptions {
@Option(shortName='v', description='Verbose output')
boolean verbose()
@Unparsed(description='Input files to process')
List<String> files()
}
class MyOptions {
@Option(shortName='v', description='Verbose output')
boolean verbose
@Unparsed
List<String> remaining
}Container for typed option information that extends HashMap and provides access to default values.
class TypedOption<T> extends HashMap<String, Object> {
// Constructor
TypedOption()
// Methods
T defaultValue() // Get default value for the option
// Properties (inherited from HashMap and option definition)
String opt // Short option name
String longOpt // Long option name
Class<T> type // Option value type
String description // Option description
Object cliOption // Reference to underlying picocli OptionSpec
Closure convert // Custom conversion closure if specified
// Inherits all HashMap methods:
// put(String, Object), get(String), containsKey(String), etc.
}Usage Example:
def cli = new CliBuilder()
def portOption = cli.port(type: Integer, defaultValue: '8080', 'Port number')
// Access default value
Integer defaultPort = portOption.defaultValue() // Returns 8080
// Use in parsing
def options = cli.parse(args)
Integer port = options.getOptionValue(portOption, portOption.defaultValue())Exception thrown for CLI builder configuration errors and validation issues.
class CliBuilderException extends RuntimeException {
// Constructor (inherits all RuntimeException constructors)
CliBuilderException()
CliBuilderException(String message)
CliBuilderException(String message, Throwable cause)
CliBuilderException(Throwable cause)
}// Import types from picocli for advanced configuration
import picocli.CommandLine.Model.ParserSpec
import picocli.CommandLine.Model.UsageMessageSpec
import picocli.CommandLine.ParseResult
// Groovy transform for annotation defaults
import groovy.transform.Undefinedparse() method returns null when parsing fails, with error messages written to the error writer (default: System.err)CliBuilderException is thrown for invalid option configurations or builder setup issuesExample Error Handling:
import groovy.cli.picocli.CliBuilder
import groovy.cli.CliBuilderException
try {
def cli = new CliBuilder()
cli.h('Help')
cli.f(args: 1, 'File name')
def options = cli.parse(args)
if (!options) {
// Parse failed, error already printed to System.err
System.exit(1)
}
// Safe to use options here
if (options.h) {
cli.usage()
}
} catch (CliBuilderException e) {
System.err.println("Configuration error: ${e.message}")
System.exit(2)
}// Using convert closure for custom type conversion
cli.date(convert: { String input ->
Date.parse('yyyy-MM-dd', input)
}, 'Date in yyyy-MM-dd format')def cli = new CliBuilder(
name: 'myapp',
posix: false, // Disable POSIX clustering
expandArgumentFiles: true, // Enable @file expansion
stopAtNonOption: false, // Continue parsing after non-options
acceptLongOptionsWithSingleHyphen: true // Accept --option and -option
)def cli = new CliBuilder(
usage: 'myapp [options] files...',
header: 'Process files with various options:',
footer: 'Examples:\n myapp -v file1.txt file2.txt\n myapp --help',
width: 120
)