Parser Annotation

@Parser

The @Parser annotation is applied to classes also annotated with @Command to configure the parser behaviour for that single command when parsers are created using SingleCommand.singleCommand().

It may also be used as an argument to the @Cli annotation in order to configure parser behaviour for a CLI via the parserConfiguration field of that annotation.

Default Configuration

To get the default parser configuration you do not need to specify any fields on the annotation i.e. simply provide a basic annotation:

@Parser()

This has the same effect as not specifying any parser configuration since in that case Airline will use its defaults which are sufficient for most basic uses.

Option Parsers

Option Parsers are used to control how Airline parses options, Airline comes with a variety of implementations of these and there are several fields of the @Parser annotation that are used to configure these.

Firstly there are the useDefaultOptionParsers and defaultParsersFirst fields, the former indicates that the default set of option parsers should be used and the latter indicates whether the defaults are configured before/after any other option parsers that you might configure. Both of these default to true so there is no need to specify either unless you want to disable the default parsers or use alternative parsers in preference to the defaults e.g.

@Parser(useDefaultOptionParsers = true, 
        defaultParsersFirst = false)

The optionParsers field is used to provide an array of classes that implement the OptionParser interface and thus can be used to customise the option parsers used or to change the order in which they are used.

For example with the default parsers if we had an option that had arity 2 with the default parsers the user would be expected to specify it as --option foo bar. However often in reality if we have an arity 2 argument we are expecting users to pass in a pair of values and it is quite common to do this as --option foo=bar which with the default configuration would be an error. If we wanted to enable this style of parsing we could do so by enabling the built-in MaybePairValueOptionParser e.g.

@Parser(defaultParsersFirst = false, 
        optionParsers = { MaybePairValueOptionParser.class })

Type Converter

If we wanted to use a custom type converter as detailed in Supported Types then we can use the typeConverter field to do this e.g.

@Parser(typeConverter = ExtendedTypeConverter.class)

The provided class must implement the TypeConverter interface.

Numeric Type Converter

If we wanted to customise how numeric values are converted as detailed in Supported Types then we can use the numericTypeConverter field to do this e.g.

@Parser(numericTypeConverter = KiloAs1000.class)

The provided class must implement the NumericTypeConverter interface.

In this example the numeric type converter is set to use the built-in KiloAs1000 numeric converter which allows for users to abbreviate numbers e.g. 4k would be converted to 4000

Boolean Flag Negation

Often when using flag arguments i.e. those with boolean type and arity zero it is useful to allow users to specify both a positive and negative version of the flag. For example we might want to have --overwrite and --no-overwrite. In order to do this we need to define an option that has both versions present in its name array and set a flagNegationPrefix on our parser:

@Parser(flagNegationPrefix = "--no-")

Now if a boolean arity zero option is used and its name starts with --no- then the parser will set its value to false, if the name does not start with the configured prefix then it will be set to true as usual.

This behaviour is off by default and must be explicitly enabled.

Command Factory

If we wanted to use a custom command factory then we can use the commandFactory field to do this e.g.

@Parser(commandFactory = ExtendedCommandFactory.class)

Composition Annotations

This feature requires a minimum version of 2.9.0

Per the Composition documentation Airline will further inspect fields on command classes marked with the @AirlineModule annotation to discover additional metadata that may have been composed into separate classes for reuse. Which annotations are followed for this is controlled via the compositionAnnotationClasses field e.g.

@Parser(compositionAnnotationClasses = { "my.custom.Annotation" })

This takes a list of strings, which are the canonical class names of the annotations you want to consider. Note that we intentionally use strings rather than Class objects here so that the annotation type in question does not necessarily need to be present on the classpath at runtime. This is primarily a backwards compatibility feature for users of older versions of Airline as discussed in Historical Composition.

Option and Command Abbreviation

Option and command abbreviation is an advanced feature of the Airline parser that allows users to avoid typing the full option and/or command names provided that the portion they type is unambiguous.

For example say we had a CLI which contained a remove and a replace command, by default users always have to type the full command name. However if we enable allowCommandAbbreviation then users only need to type enough characters to unambiguously identify the command e.g.

@Parser(allowCommandAbbreviation = true)

With that enabled a user can now type rem for remove and rep for replace, since the abbreviation must be unambiguous typing re would not be acceptable as it could refer to either command.

Similarly we can allow the same for option names via the allowOptionAbbreviation, option abbreviation is slightly more restrictive in that at least 3 characters must be typed i.e. short names such as -a can never be abbreviated.

Arguments Separator

By default Airline supports a separator of -- which can be used to separate options from arguments where arguments may be misinterpreted as options. After this separator is seen any further command line arguments are treated as arguments and not as options.

If you wish to customise the separator then you can this like so:

@Parser(argumentsSeparator = "@@")

Would instead use @@ as the arguments separator.

Aliases

Airline supports a User Defined Aliases system which allows for users to define custom aliases for use with your Airline powered CLIs.

To specify where to get user defined aliases from you use some combination of the userAliasesFile, userAliasesSearchLocation, and the userAliasesPrefix fields e.g.

@Parser(userAliasesFile = "example.config",
        userAliasesSearchLocation = { "~/example/" },
        userAliasesPrefix = "alias.")

Here we define that aliases will be defined in a file example.config which should be found under ~/example (where ~ is treated as special value for users home directory).

The treatment of special values is controlled by the choice of Resource Locators which can be changed via the userAliasLocators property. The use of the default setup, which includes support for ~/ as a reference to the users home directory, is controlled via the useDefaultAliasLocators field and defaultAliasLocatorsFirst. For example if we wished to enable use of an environment variable to locate user aliases we could do the following:

@Parser(userAliasesFile = "example.config",
        userAliasesSearchLocation = { "${EXAMPLE_CONFIG_DIR}", "~/example/" },
        userAliasesPrefix = "alias.",
        defaultAliasLocatorsFirst = false,
        useDefaultAliasLocators = true,
        userAliasLocators = { EnvVarLocator.class })

Here we add a new search location ${EXAMPLE_CONFIG_DIR} and we configure the use of the EnvVarLocator which understands how to resolve that location. We prefer our configured locators so the environment variable location, if set and resolved, will be the first used.

Alias Definition Controls

If you are using aliases then there are two remaining options that you may also be interested in. The aliasesMayChain field which defaults to false controls whether user defined aliases are allowed to reference each other i.e. whether users can define aliases in terms or other aliases. When set to true then aliases may be defined in terms of each other provided that a circular reference does not exist.

@Parser(userAliasesFile = "example.config",
        userAliasesSearchLocation = { "~/example/" },
        userAliasesPrefix = "alias.",
        aliasesMayChain = true)

You can use the aliasesOverrideBuiltIns field to control whether user defined aliases are allowed to override built-in commands and this also defaults to false. To understand this consider that you had defined a command test and then a user defines an alias test - which version should Airline use? By default Airline will defer to the built-in command, however in some cases you may want to allow the users alias to take precedence in which case you can set this field to true e.g.

@Parser(userAliasesFile = "example.config",
        userAliasesSearchLocation = { "~/example/" },
        userAliasesPrefix = "alias.",
        aliasesOverrideBuiltIns = true)

Finally if you want to provide some pre-defined aliases you can do so via the aliases property which takes an array of @Alias annotations e.g.

@Parser(aliases = {
  @Alias(name = "rem", 
         arguments = { "remove" })
})

Here we define a single alias rem which invokes the remove command.


Improving this Documentation

This documentation is itself open source and lives in GitHub under the docs/ directory.

I am not a professional technical writer and as the developer of this software I can often make assumptions of knowledge that you as a user reading this may not have. Improvements to the documentation are always welcome, if you have suggestions for the documentation please submit pull requests to the main branch.