@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.
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 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 })
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.
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
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.
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)
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 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.
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.
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.
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.
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.