argparser
Class ArgParser

java.lang.Object
  extended byargparser.ArgParser

public class ArgParser
extends java.lang.Object

ArgParser is used to parse the command line arguments for a java application program. It provides a compact way to specify options and match them against command line arguments, with support for range checking, multiple option names (aliases), single word options, multiple values associated with an option, multiple option invocation, generating help information, custom argument parsing, and reading arguments from a file. The last feature is particularly useful and makes it easy to create ad-hoc configuration files for an application.

Basic Example

Here is a simple example in which an application has three command line options: -theta (followed by a floating point value), -file (followed by a string value), and -debug, which causes a boolean value to be set.


 static public void main (String[] args)
  {
    // create 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 addOption. The method matchAllArgs is then used to match these options against the command line arguments. Values associated with each option are returned in the value field of special ``holder'' classes (e.g., DoubleHolder, StringHolder, etc.).

The first argument to addOption is a string that specifies (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 DoubleHolder or 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 matchAllArgs(args,idx,exitFlags) to obtain an array of unmatched arguments which can then be processed separately

Range Specification

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.

Multiple Option Names

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");
 

Single Word Options

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
 

Multiple Option Values

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 XN, 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.

Multiple Option Invocation

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 DoubleHolder, initialized to 1.2, 1000, and -78, and store them in vec.

Generating help information

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 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 getHelpMessage for the 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 ArgParser(synopsis,false). Help options can also be specified by the application using addOption and the conversion code %h. Help options can be disabled using 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)
 

Custom Argument Parsing

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 matchAllArgs(args,idx,exitFlags) returns an array of all 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 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());
        }
     }
 
matchArg(args,idx) matches one option at location idx 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, getUnmatchedArgument will return a copy of the unmatched argument.

Reading Arguments From a File

The method prependArgs can be used to automatically read 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

Field Summary
static int EXIT_ON_ERROR
          Indicates that the program should exit with an appropriate message in the event of an erroneous or malformed argument.
static int EXIT_ON_UNMATCHED
          Indicates that the program should exit with an appropriate message in the event of an unmatched argument.
 
Constructor Summary
ArgParser(java.lang.String synopsisString)
          Creates an ArgParser with a synopsis string, and the default help options -help and -?.
ArgParser(java.lang.String synopsisString, boolean defaultHelp)
          Creates an ArgParser with a synopsis string.
 
Method Summary
 void addOption(java.lang.String spec, java.lang.Object resHolder)
          Adds a new option description to the parser.
 java.io.PrintStream getDefaultPrintStream()
          Returns the default print stream used for outputting help and error information.
 java.lang.String getErrorMessage()
          Returns the parser's error message.
 int getHelpIndentation()
          Gets the indentation used by getHelpMessage.
 java.lang.String getHelpMessage()
          Returns a string describing the allowed options in detail.
 boolean getHelpOptionsEnabled()
          Indicates whether or not help options are enabled.
 java.lang.String getSynopsisString()
          Returns the synopsis string used by the parser.
 java.lang.String getUnmatchedArgument()
          Returns the value of an unmatched argument discovered matchArg or matchAllArgs.
static java.lang.String getValidConversionCodes()
          Returns a string containing the valid conversion codes.
 void matchAllArgs(java.lang.String[] args)
          Matches arguments within an argument list.
 java.lang.String[] matchAllArgs(java.lang.String[] args, int idx, int exitFlags)
          Matches arguments within an argument list and returns those which were not matched.
 int matchArg(java.lang.String[] args, int idx)
          Matches one option starting at a specified location in an argument list.
static java.lang.String[] prependArgs(java.io.File file, java.lang.String[] args)
          Reads in a set of strings from a file and prepends them to an argument list.
static java.lang.String[] prependArgs(java.io.Reader reader, java.lang.String[] args)
          Reads in a set of strings from a reader and prepends them to an argument list.
 void printErrorAndExit(java.lang.String msg)
          Prints an error message, along with a pointer to help options, if available, and causes the program to exit with code 1.
 void setDefaultPrintStream(java.io.PrintStream stream)
          Sets the default print stream used for outputting help and error information.
 void setHelpIndentation(int indent)
          Sets the indentation used by getHelpMessage.
 void setHelpOptionsEnabled(boolean enable)
          Enables or disables help options.
 void setSynopsisString(java.lang.String s)
          Sets the synopsis string used by the parser.
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

EXIT_ON_ERROR

public static int EXIT_ON_ERROR
Indicates that the program should exit with an appropriate message in the event of an erroneous or malformed argument.


EXIT_ON_UNMATCHED

public static int EXIT_ON_UNMATCHED
Indicates that the program should exit with an appropriate message in the event of an unmatched argument.

Constructor Detail

ArgParser

public ArgParser(java.lang.String synopsisString)
Creates an ArgParser with a synopsis string, and the default help options -help and -?.

Parameters:
synopsisString - string that briefly describes program usage, for use by getHelpMessage.
See Also:
getSynopsisString(), getHelpMessage()

ArgParser

public ArgParser(java.lang.String synopsisString,
                 boolean defaultHelp)
Creates an ArgParser with a synopsis string. The help options -help and -? are added if defaultHelp is true.

Parameters:
synopsisString - string that briefly describes program usage, for use by getHelpMessage.
defaultHelp - if true, adds the default help options
See Also:
getSynopsisString(), getHelpMessage()
Method Detail

getValidConversionCodes

public static java.lang.String getValidConversionCodes()
Returns a string containing the valid conversion codes. These are the characters which may follow the % character in the specification string of addOption.

Returns:
Valid conversion codes
See Also:
addOption(java.lang.String, java.lang.Object)

getSynopsisString

public java.lang.String getSynopsisString()
Returns the synopsis string used by the parser. The synopsis string is a short description of how to invoke the program, and usually looks something like

"java somepackage.SomeClass [options] files ..."

It is used in help and error messages.

Returns:
synopsis string
See Also:
setSynopsisString(java.lang.String), getHelpMessage()

setSynopsisString

public void setSynopsisString(java.lang.String s)
Sets the synopsis string used by the parser.

Parameters:
s - new synopsis string
See Also:
getSynopsisString(), getHelpMessage()

getHelpOptionsEnabled

public boolean getHelpOptionsEnabled()
Indicates whether or not help options are enabled.

Returns:
true if help options are enabled
See Also:
setHelpOptionsEnabled(boolean), addOption(java.lang.String, java.lang.Object)

setHelpOptionsEnabled

public void setHelpOptionsEnabled(boolean enable)
Enables or disables help options. Help options are those associated with a conversion code of %h. If help options are enabled, and a help option is matched, then the string produced by getHelpMessage is printed to the default print stream and the program exits with code 0. Otherwise, arguments which match help options are ignored.

Parameters:
enable - enables help options if true.
See Also:
getHelpOptionsEnabled(), addOption(java.lang.String, java.lang.Object), setDefaultPrintStream(java.io.PrintStream)

getDefaultPrintStream

public java.io.PrintStream getDefaultPrintStream()
Returns the default print stream used for outputting help and error information.

Returns:
default print stream
See Also:
setDefaultPrintStream(java.io.PrintStream)

setDefaultPrintStream

public void setDefaultPrintStream(java.io.PrintStream stream)
Sets the default print stream used for outputting help and error information.

Parameters:
stream - new default print stream
See Also:
getDefaultPrintStream()

getHelpIndentation

public int getHelpIndentation()
Gets the indentation used by getHelpMessage.

Returns:
number of indentation columns
See Also:
setHelpIndentation(int), getHelpMessage()

setHelpIndentation

public void setHelpIndentation(int indent)
Sets the indentation used by getHelpMessage. This is the number of columns that an option's help information is indented. If the option's name and value information can fit within this number of columns, then all information about the option is placed on one line. Otherwise, the indented help information is placed on a separate line.

Parameters:
indent - number of indentation columns
See Also:
getHelpIndentation(), getHelpMessage()

addOption

public void addOption(java.lang.String spec,
                      java.lang.Object resHolder)
               throws java.lang.IllegalArgumentException
Adds a new option description to the parser. The method takes two arguments: a specification string, and a result holder in which to store the associated value.

The specification string has the general form

optionNames %conversionCode [{rangeSpec}] [Xmultiplier] [#valueDescription] [#optionDescription]

where

The result holder must be an object capable of holding a value compatible with the conversion code, or it must be a java.util.Vector. When the option is matched, its associated value is placed in the result holder. If the same option is matched repeatedly, the result holder value will be overwritten, unless the result holder is a java.util.Vector, in which case new holder objects for each match will be allocated and added to the vector. Thus if multiple instances of an option are desired by the program, the result holder should be a java.util.Vector.

If the result holder is not a Vector, then it must correspond as follows to the conversion code:

%i, %d, %x, %o IntHolder, LongHolder, int[], or long[]
%f FloatHolder, DoubleHolder, float[], or double[]
%b, %v BooleanHolder or boolean[]
%s StringHolder or String[]
%c CharHolder or char[]

In addition, if the multiplier is greater than 1, then only the array type indicated above may be used, and the array must be at least as long as the multiplier.

If the result holder is a Vector, then the system will create an appropriate result holder object and add it to the vector. Multiple occurances of the option will cause multiple results to be added to the vector.

The object allocated by the system to store the result will correspond to the conversion code as follows:

%i, %d, %x, %o LongHolder, or long[] if the multiplier value exceeds 1
%f DoubleHolder, or double[] if the multiplier value exceeds 1
%b, %v BooleanHolder, or boolean[] if the multiplier value exceeds 1
%s StringHolder, or String[] if the multiplier value exceeds 1
%c CharHolder, or char[] if the multiplier value exceeds 1

Parameters:
spec - the specification string
resHolder - object in which to store the associated value
Throws:
java.lang.IllegalArgumentException - if there is an error in the specification or if the result holder is of an invalid type.

prependArgs

public static java.lang.String[] prependArgs(java.io.Reader reader,
                                             java.lang.String[] args)
                                      throws java.io.IOException
Reads in a set of strings from a reader and prepends them to an argument list. Strings are delimited by either whitespace or double quotes ". The character # acts as a comment character, causing input to the end of the current line to be ignored.

Parameters:
reader - Reader from which to read the strings
args - Initial set of argument values. Can be specified as null.
Throws:
java.io.IOException - if an error occured while reading.

prependArgs

public static java.lang.String[] prependArgs(java.io.File file,
                                             java.lang.String[] args)
                                      throws java.io.IOException
Reads in a set of strings from a file and prepends them to an argument list. Strings are delimited by either whitespace or double quotes ". The character # acts as a comment character, causing input to the end of the current line to be ignored.

Parameters:
file - File to be read
args - Initial set of argument values. Can be specified as null.
Throws:
java.io.IOException - if an error occured while reading the file.

printErrorAndExit

public void printErrorAndExit(java.lang.String msg)
Prints an error message, along with a pointer to help options, if available, and causes the program to exit with code 1.


matchAllArgs

public void matchAllArgs(java.lang.String[] args)
Matches arguments within an argument list.

In the event of an erroneous or unmatched argument, the method prints a message and exits the program with code 1.

If help options are enabled and one of the arguments matches a help option, then the result of getHelpMessage is printed to the default print stream and the program exits with code 0. If help options are not enabled, they are ignored.

Parameters:
args - argument list
See Also:
getDefaultPrintStream()

matchAllArgs

public java.lang.String[] matchAllArgs(java.lang.String[] args,
                                       int idx,
                                       int exitFlags)
Matches arguments within an argument list and returns those which were not matched. The matching starts at a location in args specified by idx, and unmatched arguments are returned in a String array.

In the event of an erroneous argument, the method either prints a message and exits the program (if EXIT_ON_ERROR is set in exitFlags) or terminates the matching and creates a error message that can be retrieved by getErrorMessage().

In the event of an umatched argument, the method will print a message and exit if EXIT_ON_UNMATCHED is set in errorFlags. Otherwise, the unmatched argument will be appended to the returned array of unmatched values, and the matching will continue at the next location.

If help options are enabled and one of the arguments matches a help option, then the result of getHelpMessage is printed to the the default print stream and the program exits with code 0. If help options are not enabled, then they will not be matched.

Parameters:
args - argument list
idx - starting location in list
exitFlags - conditions causing the program to exit. Should be an or-ed combintion of EXIT_ON_ERROR or EXIT_ON_UNMATCHED.
Returns:
array of arguments that were not matched, or null if all arguments were successfully matched
See Also:
getErrorMessage(), getDefaultPrintStream()

matchArg

public int matchArg(java.lang.String[] args,
                    int idx)
             throws ArgParseException
Matches one option starting at a specified location in an argument list. The method returns the location in the list where the next match should begin.

In the event of an erroneous argument, the method throws an ArgParseException with an appropriate error message. This error message can also be retrieved using getErrorMessage.

In the event of an umatched argument, the method will return idx + 1, and getUnmatchedArgument will return a copy of the unmatched argument. If an argument is matched, getUnmatchedArgument will return null.

If help options are enabled and the argument matches a help option, then the result of getHelpMessage is printed to the the default print stream and the program exits with code 0. If help options are not enabled, then they are ignored.

Parameters:
args - argument list
idx - location in list where match should start
Returns:
location in list where next match should start
Throws:
ArgParseException - if there was an error performing the match (such as improper or insufficient values).
See Also:
setDefaultPrintStream(java.io.PrintStream), getHelpOptionsEnabled(), getErrorMessage(), getUnmatchedArgument()

getHelpMessage

public java.lang.String getHelpMessage()
Returns a string describing the allowed options in detail.

Returns:
help information string.

getErrorMessage

public java.lang.String getErrorMessage()
Returns the parser's error message. This is automatically set whenever an error is encountered in matchArg or matchAllArgs, and is automatically set to null at the beginning of these methods.

Returns:
error message

getUnmatchedArgument

public java.lang.String getUnmatchedArgument()
Returns the value of an unmatched argument discovered matchArg or matchAllArgs. If there was no unmatched argument, null is returned.

Returns:
unmatched argument