te holder objects for storing results ... DoubleHolder theta = new DoubleHolder(); StringHolder fileName = new StringHolder(); BooleanHolder debug = new BooleanHolder(); // create the parser and specify the allowed options ... ArgParser parser = new ArgParser("java argparser.SimpleExample"); parser.addOption ("-theta %f #theta value (in degrees)", theta); parser.addOption ("-file %s #name of the operating file", fileName); parser.addOption ("-debug %v #enables display of debugging info", debug); // match the arguments ... parser.matchAllArgs (args); // and print out the values System.out.println ("theta=" + theta.value); System.out.println ("fileName=" + fileName.value); System.out.println ("debug=" + debug.value); }
A command line specifying all three options might look like this:
java argparser.SimpleExample -theta 7.8 -debug -file /ai/lloyd/bar
The application creates an instance of ArgParser and then adds descriptions of the allowed options using {@link #addOption addOption}. The method {@link #matchAllArgs(String[]) matchAllArgs} is then used to matchthese options against the command line arguments. Values associated with each option are returned in the value
field of special ``holder'' classes (e.g., {@link argparser.DoubleHolder DoubleHolder}, {@link argparser.StringHolder StringHolder}, etc.).
The first argument to {@link #addOption addOption} is a string thatspecifies (1) the option's name, (2) a conversion code for its associated value (e.g., %f
for floating point, %s
for a string, %v
for a boolean flag), and (3) an optional description (following the #
character) which is used for generating help messages. The second argument is the holder object through which the value is returned. This may be either a type-specific object (such as {@link argparser.DoubleHolder DoubleHolder} or {@link argparser.StringHolder StringHolder}), an array of the appropriate type, or an instance of java.util.Vector
.
By default, arguments that don't match the specified options, are out of range, or are otherwise formatted incorrectly, will cause matchAllArgs
to print a message and exit the program. Alternatively, an application can use {@link #matchAllArgs(String[],int,int) matchAllArgs(args,idx,exitFlags)} to obtainan array of unmatched arguments which can then be processed separately
The values associated with options can also be given range specifications. A range specification appears in curly braces immediately following the conversion code. In the code fragment below, we show how to specify an option
-name
that expects to be provided with one of three string values (
john
,
mary
, or
jane
), an option
-index
that expects to be supplied with a integer value in the range 1 to 256, an option
-size
that expects to be supplied with integer values of either 1, 2, 4, 8, or 16, and an option
-foo
that expects to be supplied with floating point values in the ranges -99 < foo <= -50, or 50 <= foo < 99.
StringHolder name = new StringHolder(); IntHolder index = new IntHolder(); IntHolder size = new IntHolder(); DoubleHolder foo = new DoubleHolder(); parser.addOption ("-name %s {john,mary,jane}", name); parser.addOption ("-index %d {[1,256]}", index); parser.addOption ("-size %d {1,2,4,8,16}", size); parser.addOption ("-foo %f {(-99,-50],[50,99)}", foo);
If an argument value does not lie within a specified range, an error is generated.
An option may be given several names, or aliases, in the form of a comma seperated list:
parser.addOption ("-v,--verbose %v #print lots of info"); parser.addOption ("-of,-outfile,-outputFile %s #output file");
Normally, options are assumed to be "multi-word", meaning that any associated value must follow the option as a separate argument string. For example,
parser.addOption ("-file %s #file name");
will cause the parser to look for two strings in the argument list of the form
-file someFileName
However, if there is no white space separting the option's name from it's conversion code, then values associated with that option will be assumed to be part of the same argument string as the option itself. For example,
parser.addOption ("-file=%s #file name");
will cause the parser to look for a single string in the argument list of the form
-file=someFileName
Such an option is called a "single word" option.
In cases where an option has multiple names, then this single word behavior is invoked if there is no white space between the last indicated name and the conversion code. However, previous names in the list will still be given multi-word behavior if there is white space between the name and the following comma. For example,
parser.addOption ("-nb=,-number ,-n%d #number of blocks");
will cause the parser to look for one, two, and one word constructions of the forms
-nb=N -number N -nN
If may be useful for an option to be followed by several values. For instance, we might have an option
-velocity
which should be followed by three numbers denoting the x, y, and z components of a velocity vector. We can require multiple values for an option by placing a
multiplier specification, of the form
X
N, where N is an integer, after the conversion code (or range specification, if present). For example,
double[] pos = new double[3]; addOption ("-position %fX3 #position of the object", pos);
will cause the parser to look for
-position xx yy zz
in the argument list, where
xx
,
yy
, and
zz
are numbers. The values are stored in the array
pos
. Options requiring multiple values must use arrays to return their values, and cannot be used in single word format.
Normally, if an option appears twice in the command list, the value associated with the second instance simply overwrites the value associated with the first instance. However, the application can instead arrange for the storage of
all values associated with multiple option invocation, by supplying a instance of
java.util.Vector
to serve as the value holder. Then every time the option appears in the argument list, the parser will create a value holder of appropriate type, set it to the current value, and store the holder in the vector. For example, the construction
Vector vec = new Vector(10); parser.addOption ("-foo %f", vec); parser.matchAllArgs(args);
when supplied with an argument list that contains
-foo 1.2 -foo 1000 -foo -78
will create three instances of {@link argparser.DoubleHolder DoubleHolder}, initialized to
1.2
,
1000
, and
-78
, and store them in
vec
.
ArgParser automatically generates help information for the options, and this information may be printed in response to a
help option, or may be queried by the application using {@link #getHelpMessage getHelpMessage}. The information for each option consists of the option's name(s), it's required value(s), and an application-supplied description. Value information is generated automaticlly from the conversion code, range, and multiplier specifications (although this can be overriden, as
described below). The application-supplied description is whatever appears in the specification string after the optional
#
character. The string returned by {@link #getHelpMessage getHelpMessage} forthe
first example above would be
Usage: java argparser.SimpleExample Options include: -help,-? displays help information -theta <float> theta value (in degrees) -file <string> name of the operating file -debug enables display of debugging info
The options
-help
and
-?
are including in the parser by default as help options, and they automatically cause the help message to be printed. To exclude these options, one should use the constructor {@link #ArgParser(String,boolean) ArgParser(synopsis,false)}. Help options can also be specified by the application using {@link #addOption addOption} and the conversion code
%h
. Help optionscan be disabled using {@link #setHelpOptionsEnabled setHelpOptionsEnabled(false)}.
A description of the required values for an option can be specified explicitly by placing a second #
character in the specification string. Everything between the first and second #
characters then becomes the value description, and everything after the second #
character becomes the option description. For example, if the -theta
option above was specified with parser.addOption ("-theta %f #NUMBER#theta value (in degrees)",theta);
instead of parser.addOption ("-theta %f #theta value (in degrees)", theta);
then the corresponding entry in the help message would look like -theta NUMBER theta value (in degrees)
An application may find it necessary to handle arguments that don't fit into the framework of this class. There are a couple of ways to do this. First, the method {@link #matchAllArgs(String[],int,int) matchAllArgs(args,idx,exitFlags)} returns an array ofall unmatched arguments, which can then be handled specially:
String[] unmatched = parser.matchAllArgs (args, 0, parser.EXIT_ON_ERROR); for (int i = 0; i < unmatched.length; i++) { ... handle unmatched arguments ... }
For instance, this would be useful for an applicatoon that accepts an arbitrary number of input file names. The options can be parsed using matchAllArgs
, and the remaining unmatched arguments give the file names. If we need more control over the parsing, we can parse arguments one at a time using {@link #matchArg matchArg}:
int idx = 0; while (idx < args.length) { try { idx = parser.matchArg (args, idx); if (parser.getUnmatchedArgument() != null) { ... handle this unmatched argument ourselves ... } } catch (ArgParserException e) { // malformed or erroneous argument parser.printErrorAndExit (e.getMessage()); } }
{@link #matchArg matchArg(args,idx)} matches one option at locationidx
in the argument list, and then returns the location value that should be used for the next match. If an argument does not match any option, {@link #getUnmatchedArgument getUnmatchedArgument} will return a copy of theunmatched argument. The method {@link #prependArgs prependArgs} can be used to automaticallyread in a set of arguments from a file and prepend them onto an existing argument list. Argument words correspond to white-space-delimited strings, and the file may contain the comment character #
(which comments out everything to the end of the current line). A typical usage looks like this: ... create parser and add options ... args = parser.prependArgs (new File(".configFile"), args); parser.matchAllArgs (args);
This makes it easy to generate simple configuration files for an application.
@author John E. Lloyd, Fall 2004