summaryrefslogtreecommitdiffstats
path: root/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParser.java')
-rwxr-xr-xpki/base/silent/src/com/netscape/pkisilent/argparser/ArgParser.java4126
1 files changed, 2129 insertions, 1997 deletions
diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParser.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParser.java
index e396d6f5e..a205d1012 100755
--- a/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParser.java
+++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParser.java
@@ -1,5 +1,4 @@
package com.netscape.pkisilent.argparser;
-
// --- BEGIN COPYRIGHT BLOCK ---
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -19,17 +18,17 @@ package com.netscape.pkisilent.argparser;
// --- END COPYRIGHT BLOCK ---
/**
- * Copyright John E. Lloyd, 2004. All rights reserved. Permission to use,
- * copy, modify and redistribute is granted, provided that this copyright
- * notice is retained and the author is given credit whenever appropriate.
- *
- * This software is distributed "as is", without any warranty, including
- * any implied warranty of merchantability or fitness for a particular
- * use. The author assumes no responsibility for, and shall not be liable
- * for, any special, indirect, or consequential damages, or any damages
- * whatsoever, arising out of or in connection with the use of this
- * software.
- */
+ * Copyright John E. Lloyd, 2004. All rights reserved. Permission to use,
+ * copy, modify and redistribute is granted, provided that this copyright
+ * notice is retained and the author is given credit whenever appropriate.
+ *
+ * This software is distributed "as is", without any warranty, including
+ * any implied warranty of merchantability or fitness for a particular
+ * use. The author assumes no responsibility for, and shall not be liable
+ * for, any special, indirect, or consequential damages, or any damages
+ * whatsoever, arising out of or in connection with the use of this
+ * software.
+ */
import java.io.File;
import java.io.FileReader;
@@ -41,200 +40,191 @@ import java.lang.reflect.Array;
import java.util.Vector;
/**
- * 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 <a href=#rangespec>range
- * checking</a>, <a href=#multipleOptionNames>multiple option names</a>
- * (aliases), <a href=#singleWordOptions>single word options</a>, <a
- * href=#multipleOptionValues>multiple values associated with an option</a>, <a
- * href=#multipleOptionInvocation>multiple option invocation</a>, <a
- * href=#helpInfo>generating help information</a>, <a
- * href=#customArgParsing>custom argument parsing</a>, and <a
- * href=#argsFromAFile>reading arguments from a file</a>. The last feature is
- * particularly useful and makes it easy to create ad-hoc configuration files
- * for an application.
- *
+ * 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
+ * <a href=#rangespec>range checking</a>,
+ * <a href=#multipleOptionNames>multiple option names</a> (aliases),
+ * <a href=#singleWordOptions>single word options</a>,
+ * <a href=#multipleOptionValues>multiple values associated with an option</a>,
+ * <a href=#multipleOptionInvocation>multiple option invocation</a>,
+ * <a href=#helpInfo>generating help information</a>,
+ * <a href=#customArgParsing>custom argument parsing</a>, and
+ * <a href=#argsFromAFile>reading arguments from a file</a>. The
+ * last feature is particularly useful and makes it
+ * easy to create ad-hoc configuration files for an application.
+ *
* <h3><a name="example">Basic Example</a></h3>
- *
- * <p>
- * Here is a simple example in which an application has three command line
- * options: <code>-theta</code> (followed by a floating point value),
- * <code>-file</code> (followed by a string value), and <code>-debug</code>,
- * which causes a boolean value to be set.
+ *
+ * <p>Here is a simple example in which an application has three
+ * command line options:
+ * <code>-theta</code> (followed by a floating point value),
+ * <code>-file</code> (followed by a string value), and
+ * <code>-debug</code>, which causes a boolean value to be set.
*
* <pre>
+ *
+ * static public void main (String[] args)
+ * {
+ * // create holder objects for storing results ...
*
- * 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(&quot;java argparser.SimpleExample&quot;);
- * parser.addOption(&quot;-theta %f #theta value (in degrees)&quot;, theta);
- * parser.addOption(&quot;-file %s #name of the operating file&quot;, fileName);
- * parser.addOption(&quot;-debug %v #enables display of debugging info&quot;, debug);
- *
- * // match the arguments ...
+ * DoubleHolder theta = new DoubleHolder();
+ * StringHolder fileName = new StringHolder();
+ * BooleanHolder debug = new BooleanHolder();
*
- * parser.matchAllArgs(args);
+ * // create the parser and specify the allowed options ...
*
- * // and print out the values
+ * 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 ...
*
- * System.out.println(&quot;theta=&quot; + theta.value);
- * System.out.println(&quot;fileName=&quot; + fileName.value);
- * System.out.println(&quot;debug=&quot; + debug.value);
- * }
+ * 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);
+ * }
* </pre>
- * <p>
- * A command line specifying all three options might look like this:
- *
+ * <p>A command line specifying all three options might look like this:
* <pre>
- * java argparser.SimpleExample -theta 7.8 -debug -file /ai/lloyd/bar
+ * java argparser.SimpleExample -theta 7.8 -debug -file /ai/lloyd/bar
* </pre>
*
- * <p>
- * 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 match these
- * options against the command line arguments. Values associated with each
- * option are returned in the <code>value</code> field of special ``holder''
- * classes (e.g., {@link argparser.DoubleHolder DoubleHolder},
+ * <p>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 match
+ * these options against the command line arguments. Values associated with
+ * each option are returned in the <code>value</code> field of special
+ * ``holder'' classes (e.g., {@link argparser.DoubleHolder DoubleHolder},
* {@link argparser.StringHolder StringHolder}, etc.).
- *
- * <p>
- * The first argument to {@link #addOption addOption} is a string that specifies
- * (1) the option's name, (2) a conversion code for its associated value (e.g.,
- * <code>%f</code> for floating point, <code>%s</code> for a string,
- * <code>%v</code> for a boolean flag), and (3) an optional description
+ *
+ * <p> The first argument to {@link #addOption addOption} is a string that
+ * specifies (1) the option's name, (2) a conversion code for its associated
+ * value (e.g., <code>%f</code> for floating point, <code>%s</code> for a
+ * string, <code>%v</code> for a boolean flag), and (3) an optional description
* (following the <code>#</code> 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 <a
- * href=#multipleOptionInvocation> an instance of <code>java.util.Vector</code>
- * </a>.
- *
- * <p>
- * By default, arguments that don't match the specified options, are <a
+ * 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
+ * <a href=#multipleOptionInvocation> an instance of
+ * <code>java.util.Vector</code></a>.
+ *
+ * <p>By default, arguments that don't match the specified options, are <a
* href=#rangespec>out of range</a>, or are otherwise formatted incorrectly,
- * will cause <code>matchAllArgs</code> to print a message and exit the program.
- * Alternatively, an application can use {@link #matchAllArgs(String[],int,int)
- * matchAllArgs(args,idx,exitFlags)} to obtain an array of unmatched arguments
- * which can then be <a href=#customArgParsing>processed separately</a>
- *
+ * will cause <code>matchAllArgs</code> to print a message and exit the
+ * program. Alternatively, an application can use {@link
+ * #matchAllArgs(String[],int,int) matchAllArgs(args,idx,exitFlags)} to obtain
+ * an array of unmatched arguments which can then be
+ * <a href=#customArgParsing>processed separately</a>
+ *
* <h3><a name="rangespec">Range Specification</a></h3>
- *
+ *
* 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
- * <code>-name</code> that expects to be provided with one of three string
- * values (<code>john</code>, <code>mary</code>, or <code>jane</code>), an
- * option <code>-index</code> that expects to be supplied with a integer value
- * in the range 1 to 256, an option <code>-size</code> that expects to be
+ * conversion code. In the code fragment below, we show how to specify an
+ * option <code>-name</code> that expects to be provided with one of three
+ * string values (<code>john</code>, <code>mary</code>, or <code>jane</code>),
+ * an option <code>-index</code> that expects to be supplied with a integer
+ * value in the range 1 to 256, an option <code>-size</code> that expects to be
* supplied with integer values of either 1, 2, 4, 8, or 16, and an option
* <code>-foo</code> that expects to be supplied with floating point values in
* the ranges -99 < foo <= -50, or 50 <= foo < 99.
- *
+ *
* <pre>
- * StringHolder name = new StringHolder();
- * IntHolder index = new IntHolder();
- * IntHolder size = new IntHolder();
- * DoubleHolder foo = new DoubleHolder();
- *
- * parser.addOption(&quot;-name %s {john,mary,jane}&quot;, name);
- * parser.addOption(&quot;-index %d {[1,256]}&quot;, index);
- * parser.addOption(&quot;-size %d {1,2,4,8,16}&quot;, size);
- * parser.addOption(&quot;-foo %f {(-99,-50],[50,99)}&quot;, foo);
+ * 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);
* </pre>
- *
+ *
* If an argument value does not lie within a specified range, an error is
* generated.
- *
+ *
* <h3><a name="multipleOptionNames">Multiple Option Names</a></h3>
- *
- * An option may be given several names, or aliases, in the form of a comma
- * seperated list:
- *
+ *
+ * An option may be given several names, or aliases, in the form of
+ * a comma seperated list:
+ *
* <pre>
- * parser.addOption(&quot;-v,--verbose %v #print lots of info&quot;);
- * parser.addOption(&quot;-of,-outfile,-outputFile %s #output file&quot;);
+ * parser.addOption ("-v,--verbose %v #print lots of info");
+ * parser.addOption ("-of,-outfile,-outputFile %s #output file");
* </pre>
- *
+ *
* <h3><a name="singleWordOptions">Single Word Options</a></h3>
- *
- * Normally, options are assumed to be "multi-word", meaning that any associated
- * value must follow the option as a separate argument string. For example,
- *
+ *
+ * Normally, options are assumed to be "multi-word", meaning
+ * that any associated value must follow the option as a
+ * separate argument string. For
+ * example,
* <pre>
- * parser.addOption(&quot;-file %s #file name&quot;);
+ * parser.addOption ("-file %s #file name");
* </pre>
- *
- * will cause the parser to look for two strings in the argument list of the
- * form
- *
+ * will cause the parser to look for two strings in the argument list
+ * of the form
* <pre>
* -file someFileName
* </pre>
- *
- * 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,
- *
+ * 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,
* <pre>
- * parser.addOption(&quot;-file=%s #file name&quot;);
+ * parser.addOption ("-file=%s #file name");
* </pre>
- *
- * will cause the parser to look for a single string in the argument list of the
- * form
- *
+ * will cause the parser to look for a single string in the argument
+ * list of the form
* <pre>
* -file=someFileName
* </pre>
- *
* Such an option is called a "single word" option.
- *
+ *
* <p>
- * 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
+ * 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,
- *
* <pre>
- * parser.addOption(&quot;-nb=,-number ,-n%d #number of blocks&quot;);
+ * parser.addOption ("-nb=,-number ,-n%d #number of blocks");
* </pre>
- *
- * will cause the parser to look for one, two, and one word constructions of the
- * forms
- *
+ * will cause the parser to look for one, two, and one word constructions
+ * of the forms
* <pre>
* -nb=N
* -number N
* -nN
* </pre>
- *
+ *
* <h3><a name="multipleOptionValues">Multiple Option Values</a></h3>
- *
- * If may be useful for an option to be followed by several values. For
- * instance, we might have an option <code>-velocity</code> 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
- * <i>multiplier</i> specification, of the form <code>X</code>N, where N is an
- * integer, after the conversion code (or range specification, if present). For
- * example,
+ *
+ * If may be useful for an option to be followed by several values.
+ * For instance, we might have an option <code>-velocity</code>
+ * 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 <i>multiplier</i> specification,
+ * of the form <code>X</code>N, where N is an integer,
+ * after the conversion code (or range specification, if present).
+ * For example,
*
* <pre>
- * double[] pos = new double[3];
- *
- * addOption(&quot;-position %fX3 #position of the object&quot;, pos);
+ * double[] pos = new double[3];
+ *
+ * addOption ("-position %fX3 #position of the object", pos);
* </pre>
- *
* will cause the parser to look for
- *
* <pre>
* -position xx yy zz
* </pre>
@@ -242,53 +232,52 @@ import java.util.Vector;
* in the argument list, where <code>xx</code>, <code>yy</code>, and
* <code>zz</code> are numbers. The values are stored in the array
* <code>pos</code>.
- *
- * Options requiring multiple values must use arrays to return their values, and
- * cannot be used in single word format.
- *
+ *
+ * Options requiring multiple values must use arrays to
+ * return their values, and cannot be used in single word format.
+ *
* <h3><a name="multipleOptionInvocation">Multiple Option Invocation</a></h3>
- *
- * 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.
- *
+ *
+ * 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 <i>all</i>
- * values associated with multiple option invocation, by supplying a instance of
- * <code>java.util.Vector</code> 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
- *
+ * values associated with multiple option invocation, by supplying a instance
+ * of <code>java.util.Vector</code> 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
+ *
* <pre>
- * Vector vec = new Vector(10);
- *
- * parser.addOption(&quot;-foo %f&quot;, vec);
- * parser.matchAllArgs(args);
+ * Vector vec = new Vector(10);
+ *
+ * parser.addOption ("-foo %f", vec);
+ * parser.matchAllArgs(args);
* </pre>
- *
* when supplied with an argument list that contains
- *
* <pre>
* -foo 1.2 -foo 1000 -foo -78
* </pre>
*
* will create three instances of {@link argparser.DoubleHolder DoubleHolder},
- * initialized to <code>1.2</code>, <code>1000</code>, and <code>-78</code>, and
- * store them in <code>vec</code>.
- *
+ * initialized to <code>1.2</code>, <code>1000</code>, and <code>-78</code>,
+ * and store them in <code>vec</code>.
+ *
* <h3><a name="helpInfo">Generating help information</a></h3>
- *
+ *
* ArgParser automatically generates help information for the options, and this
* information may be printed in response to a <i>help</i> 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 <a
- * href=#valueInfo>described below</a>). The application-supplied description is
- * whatever appears in the specification string after the optional
- * <code>#</code> character. The string returned by {@link #getHelpMessage
- * getHelpMessage} for the <a href=#example>first example above</a> would 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
+ * <a href=#valueInfo>described below</a>).
+ * The application-supplied description is whatever
+ * appears in the specification string after the optional <code>#</code>
+ * character. The string returned by {@link #getHelpMessage getHelpMessage} for
+ * the <a href=#example>first example above</a> would be
*
* <pre>
* Usage: java argparser.SimpleExample
@@ -302,47 +291,48 @@ import java.util.Vector;
*
* The options <code>-help</code> and <code>-?</code> 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 <code>%h</code>. Help
- * options can be disabled using {@link #setHelpOptionsEnabled
+ * 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 <code>%h</code>. Help options
+ * can be disabled using {@link #setHelpOptionsEnabled
* setHelpOptionsEnabled(false)}.
- *
- * <p>
- * <a name=valueInfo> A description of the required values for an option can be
- * specified explicitly by placing a second <code>#</code> character in the
- * specification string. Everything between the first and second <code>#</code>
- * characters then becomes the value description, and everything after the
- * second <code>#</code> character becomes the option description. For example,
- * if the <code>-theta</code> option above was specified with
- *
+ *
+ * <p><a name=valueInfo>
+ * A description of the required values for an option can be
+ * specified explicitly
+ * by placing a second <code>#</code> character in the specification
+ * string. Everything between the first and second <code>#</code>
+ * characters then becomes the value description, and everything
+ * after the second <code>#</code> character becomes the option
+ * description.
+ * For example, if the <code>-theta</code> option
+ * above was specified with
* <pre>
- * parser.addOption(&quot;-theta %f #NUMBER#theta value (in degrees)&quot;, theta);
+ * parser.addOption ("-theta %f #NUMBER#theta value (in degrees)",theta);
* </pre>
- *
* instead of
- *
* <pre>
- * parser.addOption(&quot;-theta %f #theta value (in degrees)&quot;, theta);
+ * parser.addOption ("-theta %f #theta value (in degrees)", theta);
* </pre>
- *
- * then the corresponding entry in the help message would look like
- *
+ * then the corresponding entry in the help message would look
+ * like
* <pre>
* -theta NUMBER theta value (in degrees)
* </pre>
- *
+ *
* <h3><a name="customArgParsing">Custom Argument Parsing</a></h3>
*
- * 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.
- *
+ * 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.
+ *
* <p>
* First, the method {@link #matchAllArgs(String[],int,int)
- * matchAllArgs(args,idx,exitFlags)} returns an array of all unmatched
- * arguments, which can then be handled specially:
- *
+ * matchAllArgs(args,idx,exitFlags)} returns an array of
+ * all unmatched arguments, which can then be handled
+ * specially:
* <pre>
* String[] unmatched =
* parser.matchAllArgs (args, 0, parser.EXIT_ON_ERROR);
@@ -352,13 +342,12 @@ import java.util.Vector;
* </pre>
*
* For instance, this would be useful for an applicatoon that accepts an
- * arbitrary number of input file names. The options can be parsed using
- * <code>matchAllArgs</code>, and the remaining unmatched arguments give the
- * file names.
- *
- * <p>
- * If we need more control over the parsing, we can parse arguments one at a
- * time using {@link #matchArg matchArg}:
+ * arbitrary number of input file names. The options can be parsed using
+ * <code>matchAllArgs</code>, and the remaining unmatched arguments
+ * give the file names.
+ *
+ * <p> If we need more control over the parsing, we can parse arguments one at
+ * a time using {@link #matchArg matchArg}:
*
* <pre>
* int idx = 0;
@@ -376,1793 +365,1936 @@ import java.util.Vector;
* }
* }
* </pre>
- *
+ *
* {@link #matchArg matchArg(args,idx)} matches one option at location
* <code>idx</code> 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 the unmatched argument.
- *
+ * that should be used for the next match. If an argument does
+ * not match any option,
+ * {@link #getUnmatchedArgument getUnmatchedArgument} will return a copy of the
+ * unmatched argument.
+ *
* <h3><a name="argsFromAFile">Reading Arguments From a File</a></h3>
- *
- * The method {@link #prependArgs 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 <code>#</code> (which comments out
- * everything to the end of the current line). A typical usage looks like this:
- *
+ *
+ * The method {@link #prependArgs 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 <code>#</code> (which
+ * comments out everything to the end of the current line). A typical usage
+ * looks like this:
+ *
* <pre>
* ... create parser and add options ...
*
* args = parser.prependArgs (new File(".configFile"), args);
- *
+ *
* parser.matchAllArgs (args);
* </pre>
- *
- * This makes it easy to generate simple configuration files for an application.
- *
+ *
+ * This makes it easy to generate simple configuration files for an
+ * application.
+ *
* @author John E. Lloyd, Fall 2004
*/
-public class ArgParser {
- Vector<Record> matchList;
- // int tabSpacing = 8;
- String synopsisString;
- boolean helpOptionsEnabled = true;
- Record defaultHelpOption = null;
- Record firstHelpOption = null;
- PrintStream printStream = System.out;
- int helpIndent = 24;
- String errMsg = null;
- String unmatchedArg = null;
-
- static String validConversionCodes = "iodxcbfsvh";
-
- /**
- * Indicates that the program should exit with an appropriate message in the
- * event of an erroneous or malformed argument.
- */
- public static int EXIT_ON_ERROR = 1;
-
- /**
- * Indicates that the program should exit with an appropriate message in the
- * event of an unmatched argument.
- */
- public static int EXIT_ON_UNMATCHED = 2;
-
- /**
- * Returns a string containing the valid conversion codes. These are the
- * characters which may follow the <code>%</code> character in the
- * specification string of {@link #addOption addOption}.
- *
- * @return Valid conversion codes
- * @see #addOption
- */
- public static String getValidConversionCodes() {
- return validConversionCodes;
- }
-
- static class NameDesc {
- String name;
- // oneWord implies that any value associated with
- // option is concatenated onto the argument string itself
- boolean oneWord;
- NameDesc next = null;
- }
-
- static class RangePnt {
- double dval = 0;
- long lval = 0;
- String sval = null;
- boolean bval = true;
- boolean closed = true;
-
- RangePnt(String s, boolean closed) {
- sval = s;
- this.closed = closed;
- }
-
- RangePnt(double d, boolean closed) {
- dval = d;
- this.closed = closed;
- }
-
- RangePnt(long l, boolean closed) {
- lval = l;
- this.closed = closed;
- }
-
- RangePnt(boolean b, boolean closed) {
- bval = b;
- this.closed = closed;
- }
-
- RangePnt(StringScanner scanner, int type)
- throws IllegalArgumentException {
- String typeName = null;
- try {
- switch (type) {
- case Record.CHAR: {
- typeName = "character";
- lval = scanner.scanChar();
- break;
- }
- case Record.INT:
- case Record.LONG: {
- typeName = "integer";
- lval = scanner.scanInt();
- break;
- }
- case Record.FLOAT:
- case Record.DOUBLE: {
- typeName = "float";
- dval = scanner.scanDouble();
- break;
- }
- case Record.STRING: {
- typeName = "string";
- sval = scanner.scanString();
- break;
- }
- case Record.BOOLEAN: {
- typeName = "boolean";
- bval = scanner.scanBoolean();
- break;
- }
- }
- } catch (StringScanException e) {
- throw new IllegalArgumentException("Malformed "
- + typeName
- + " '"
- + scanner.substring(scanner.getIndex(),
- e.getFailIndex() + 1) + "' in range spec");
- }
- // this.closed = closed;
- }
-
- void setClosed(boolean closed) {
- this.closed = closed;
- }
-
- boolean getClosed() {
- return closed;
- }
-
- int compareTo(double d) {
- if (dval < d) {
- return -1;
- } else if (d == dval) {
- return 0;
- } else {
- return 1;
- }
- }
-
- int compareTo(long l) {
- if (lval < l) {
- return -1;
- } else if (l == lval) {
- return 0;
- } else {
- return 1;
- }
- }
-
- int compareTo(String s) {
- return sval.compareTo(s);
- }
-
- int compareTo(boolean b) {
- if (b == bval) {
- return 0;
- } else {
- return 1;
- }
- }
-
- public String toString() {
- return "{ dval=" + dval + ", lval=" + lval + ", sval=" + sval
- + ", bval=" + bval + ", closed=" + closed + "}";
- }
- }
-
- class RangeAtom {
- RangePnt low = null;
- RangePnt high = null;
- RangeAtom next = null;
-
- RangeAtom(RangePnt p0, RangePnt p1, int type)
- throws IllegalArgumentException {
- int cmp = 0;
- switch (type) {
- case Record.CHAR:
- case Record.INT:
- case Record.LONG: {
- cmp = p0.compareTo(p1.lval);
- break;
- }
- case Record.FLOAT:
- case Record.DOUBLE: {
- cmp = p0.compareTo(p1.dval);
- break;
- }
- case Record.STRING: {
- cmp = p0.compareTo(p1.sval);
- break;
- }
- }
- if (cmp > 0) { // then switch high and low
- low = p1;
- high = p0;
- } else {
- low = p0;
- high = p1;
- }
- }
-
- RangeAtom(RangePnt p0) throws IllegalArgumentException {
- low = p0;
- }
-
- boolean match(double d) {
- int lc = low.compareTo(d);
- if (high != null) {
- int hc = high.compareTo(d);
- return (lc * hc < 0 || (low.closed && lc == 0) || (high.closed && hc == 0));
- } else {
- return lc == 0;
- }
- }
-
- boolean match(long l) {
- int lc = low.compareTo(l);
- if (high != null) {
- int hc = high.compareTo(l);
- return (lc * hc < 0 || (low.closed && lc == 0) || (high.closed && hc == 0));
- } else {
- return lc == 0;
- }
- }
-
- boolean match(String s) {
- int lc = low.compareTo(s);
- if (high != null) {
- int hc = high.compareTo(s);
- return (lc * hc < 0 || (low.closed && lc == 0) || (high.closed && hc == 0));
- } else {
- return lc == 0;
- }
- }
-
- boolean match(boolean b) {
- return low.compareTo(b) == 0;
- }
-
- public String toString() {
- return "low=" + (low == null ? "null" : low.toString()) + ", high="
- + (high == null ? "null" : high.toString());
- }
- }
-
- class Record {
- NameDesc nameList;
- static final int NOTYPE = 0;
- static final int BOOLEAN = 1;
- static final int CHAR = 2;
- static final int INT = 3;
- static final int LONG = 4;
- static final int FLOAT = 5;
- static final int DOUBLE = 6;
- static final int STRING = 7;
- int type;
- int numValues;
- boolean vectorResult = false;
- boolean required = true;
-
- String helpMsg = null;
- String valueDesc = null;
- String rangeDesc = null;
- Object resHolder = null;
- RangeAtom rangeList = null;
- RangeAtom rangeTail = null;
- char convertCode;
- boolean vval = true; // default value for now
-
- NameDesc firstNameDesc() {
- return nameList;
- }
-
- RangeAtom firstRangeAtom() {
- return rangeList;
- }
-
- int numRangeAtoms() {
- int cnt = 0;
- for (RangeAtom ra = rangeList; ra != null; ra = ra.next) {
- cnt++;
- }
- return cnt;
- }
-
- void addRangeAtom(RangeAtom ra) {
- if (rangeList == null) {
- rangeList = ra;
- } else {
- rangeTail.next = ra;
- }
- rangeTail = ra;
- }
-
- boolean withinRange(double d) {
- if (rangeList == null) {
- return true;
- }
- for (RangeAtom ra = rangeList; ra != null; ra = ra.next) {
- if (ra.match(d)) {
- return true;
+public class ArgParser
+{
+ Vector<Record> matchList;
+// int tabSpacing = 8;
+ String synopsisString;
+ boolean helpOptionsEnabled = true;
+ Record defaultHelpOption = null;
+ Record firstHelpOption = null;
+ PrintStream printStream = System.out;
+ int helpIndent = 24;
+ String errMsg = null;
+ String unmatchedArg = null;
+
+ static String validConversionCodes = "iodxcbfsvh";
+
+ /**
+ * Indicates that the program should exit with an appropriate message
+ * in the event of an erroneous or malformed argument.*/
+ public static int EXIT_ON_ERROR = 1;
+
+ /**
+ * Indicates that the program should exit with an appropriate message
+ * in the event of an unmatched argument.*/
+ public static int EXIT_ON_UNMATCHED = 2;
+
+ /**
+ * Returns a string containing the valid conversion codes. These
+ * are the characters which may follow the <code>%</code> character in
+ * the specification string of {@link #addOption addOption}.
+ *
+ * @return Valid conversion codes
+ * @see #addOption
+ */
+ public static String getValidConversionCodes()
+ {
+ return validConversionCodes;
+ }
+
+ static class NameDesc
+ {
+ String name;
+ // oneWord implies that any value associated with
+ // option is concatenated onto the argument string itself
+ boolean oneWord;
+ NameDesc next = null;
+ }
+
+ static class RangePnt
+ {
+ double dval = 0;
+ long lval = 0;
+ String sval = null;
+ boolean bval = true;
+ boolean closed = true;
+
+ RangePnt (String s, boolean closed)
+ { sval = s;
+ this.closed = closed;
+ }
+
+ RangePnt (double d, boolean closed)
+ { dval = d;
+ this.closed = closed;
+ }
+
+ RangePnt (long l, boolean closed)
+ { lval = l;
+ this.closed = closed;
+ }
+
+ RangePnt (boolean b, boolean closed)
+ { bval = b;
+ this.closed = closed;
+ }
+
+ RangePnt (StringScanner scanner, int type)
+ throws IllegalArgumentException
+ {
+ String typeName = null;
+ try
+ { switch (type)
+ {
+ case Record.CHAR:
+ { typeName = "character";
+ lval = scanner.scanChar();
+ break;
+ }
+ case Record.INT:
+ case Record.LONG:
+ { typeName = "integer";
+ lval = scanner.scanInt();
+ break;
+ }
+ case Record.FLOAT:
+ case Record.DOUBLE:
+ { typeName = "float";
+ dval = scanner.scanDouble();
+ break;
+ }
+ case Record.STRING:
+ { typeName = "string";
+ sval = scanner.scanString();
+ break;
+ }
+ case Record.BOOLEAN:
+ { typeName = "boolean";
+ bval = scanner.scanBoolean();
+ break;
+ }
+ }
+ }
+ catch (StringScanException e)
+ { throw new IllegalArgumentException (
+ "Malformed " + typeName + " '" +
+ scanner.substring(scanner.getIndex(),
+ e.getFailIndex()+1) +
+ "' in range spec");
+ }
+// this.closed = closed;
+ }
+
+ void setClosed (boolean closed)
+ { this.closed = closed;
+ }
+
+ boolean getClosed()
+ { return closed;
+ }
+
+ int compareTo (double d)
+ { if (dval < d)
+ { return -1;
+ }
+ else if (d == dval)
+ { return 0;
+ }
+ else
+ { return 1;
+ }
+ }
+
+ int compareTo (long l)
+ { if (lval < l)
+ { return -1;
+ }
+ else if (l == lval)
+ { return 0;
+ }
+ else
+ { return 1;
+ }
+ }
+
+ int compareTo (String s)
+ { return sval.compareTo (s);
+ }
+
+ int compareTo (boolean b)
+ { if (b == bval)
+ { return 0;
+ }
+ else
+ { return 1;
+ }
+ }
+
+ public String toString()
+ { return "{ dval=" + dval + ", lval=" + lval +
+ ", sval=" + sval + ", bval=" + bval +
+ ", closed=" + closed + "}";
+ }
+ }
+
+ class RangeAtom
+ {
+ RangePnt low = null;
+ RangePnt high = null;
+ RangeAtom next = null;
+
+ RangeAtom (RangePnt p0, RangePnt p1, int type)
+ throws IllegalArgumentException
+ {
+ int cmp = 0;
+ switch (type)
+ {
+ case Record.CHAR:
+ case Record.INT:
+ case Record.LONG:
+ { cmp = p0.compareTo (p1.lval);
+ break;
+ }
+ case Record.FLOAT:
+ case Record.DOUBLE:
+ { cmp = p0.compareTo (p1.dval);
+ break;
+ }
+ case Record.STRING:
+ { cmp = p0.compareTo (p1.sval);
+ break;
+ }
+ }
+ if (cmp > 0)
+ { // then switch high and low
+ low = p1;
+ high = p0;
+ }
+ else
+ { low = p0;
+ high = p1;
+ }
+ }
+
+ RangeAtom (RangePnt p0)
+ throws IllegalArgumentException
+ {
+ low = p0;
+ }
+
+ boolean match (double d)
+ { int lc = low.compareTo(d);
+ if (high != null)
+ { int hc = high.compareTo(d);
+ return (lc*hc < 0 ||
+ (low.closed && lc==0) ||
+ (high.closed && hc==0));
+ }
+ else
+ { return lc == 0;
+ }
+ }
+
+ boolean match (long l)
+ { int lc = low.compareTo(l);
+ if (high != null)
+ { int hc = high.compareTo(l);
+ return (lc*hc < 0 ||
+ (low.closed && lc==0) ||
+ (high.closed && hc==0));
+ }
+ else
+ { return lc == 0;
+ }
+ }
+
+ boolean match (String s)
+ { int lc = low.compareTo(s);
+ if (high != null)
+ { int hc = high.compareTo(s);
+ return (lc*hc < 0 ||
+ (low.closed && lc==0) ||
+ (high.closed && hc==0));
+ }
+ else
+ { return lc == 0;
+ }
+ }
+
+ boolean match (boolean b)
+ { return low.compareTo(b) == 0;
+ }
+
+ public String toString()
+ { return "low=" + (low==null ? "null" : low.toString()) +
+ ", high=" + (high==null ? "null" : high.toString());
+ }
+ }
+
+ class Record
+ {
+ NameDesc nameList;
+ static final int NOTYPE = 0;
+ static final int BOOLEAN = 1;
+ static final int CHAR = 2;
+ static final int INT = 3;
+ static final int LONG = 4;
+ static final int FLOAT = 5;
+ static final int DOUBLE = 6;
+ static final int STRING = 7;
+ int type;
+ int numValues;
+ boolean vectorResult = false;
+ boolean required = true;
+
+ String helpMsg = null;
+ String valueDesc = null;
+ String rangeDesc = null;
+ Object resHolder = null;
+ RangeAtom rangeList = null;
+ RangeAtom rangeTail = null;
+ char convertCode;
+ boolean vval = true; // default value for now
+
+ NameDesc firstNameDesc()
+ {
+ return nameList;
+ }
+
+ RangeAtom firstRangeAtom()
+ {
+ return rangeList;
+ }
+
+ int numRangeAtoms()
+ { int cnt = 0;
+ for (RangeAtom ra=rangeList; ra!=null; ra=ra.next)
+ { cnt++;
+ }
+ return cnt;
+ }
+
+ void addRangeAtom (RangeAtom ra)
+ { if (rangeList == null)
+ { rangeList = ra;
+ }
+ else
+ { rangeTail.next = ra;
+ }
+ rangeTail = ra;
+ }
+
+ boolean withinRange (double d)
+ {
+ if (rangeList == null)
+ { return true;
+ }
+ for (RangeAtom ra=rangeList; ra!=null; ra=ra.next)
+ { if (ra.match (d))
+ { return true;
+ }
+ }
+ return false;
+ }
+
+ boolean withinRange (long l)
+ {
+ if (rangeList == null)
+ { return true;
+ }
+ for (RangeAtom ra=rangeList; ra!=null; ra=ra.next)
+ { if (ra.match (l))
+ { return true;
+ }
+ }
+ return false;
+ }
+
+ boolean withinRange (String s)
+ {
+ if (rangeList == null)
+ { return true;
+ }
+ for (RangeAtom ra=rangeList; ra!=null; ra=ra.next)
+ { if (ra.match (s))
+ { return true;
+ }
+ }
+ return false;
+ }
+
+ boolean withinRange (boolean b)
+ {
+ if (rangeList == null)
+ { return true;
+ }
+ for (RangeAtom ra=rangeList; ra!=null; ra=ra.next)
+ { if (ra.match (b))
+ { return true;
+ }
+ }
+ return false;
+ }
+
+ String valTypeName()
+ {
+ switch (convertCode)
+ {
+ case 'i':
+ { return ("integer");
+ }
+ case 'o':
+ { return ("octal integer");
+ }
+ case 'd':
+ { return ("decimal integer");
+ }
+ case 'x':
+ { return ("hex integer");
+ }
+ case 'c':
+ { return ("char");
+ }
+ case 'b':
+ { return ("boolean");
+ }
+ case 'f':
+ { return ("float");
+ }
+ case 's':
+ { return ("string");
+ }
+ }
+ return ("unknown");
+ }
+
+ void scanValue (Object result, String name, String s, int resultIdx)
+ throws ArgParseException
+ {
+ double dval = 0;
+ String sval = null;
+ long lval = 0;
+ boolean bval = false;
+
+ if (s.length()==0)
+ { throw new ArgParseException
+ (name, "requires a contiguous value");
+ }
+ StringScanner scanner = new StringScanner(s);
+ try
+ {
+ switch (convertCode)
+ {
+ case 'i':
+ { lval = scanner.scanInt();
+ break;
+ }
+ case 'o':
+ { lval = scanner.scanInt (8, false);
+ break;
+ }
+ case 'd':
+ { lval = scanner.scanInt (10, false);
+ break;
+ }
+ case 'x':
+ { lval = scanner.scanInt (16, false);
+ break;
+ }
+ case 'c':
+ { lval = scanner.scanChar();
+ break;
+ }
+ case 'b':
+ { bval = scanner.scanBoolean();
+ break;
+ }
+ case 'f':
+ { dval = scanner.scanDouble();
+ break;
+ }
+ case 's':
+ { sval = scanner.getString();
+ break;
+ }
+ }
+ }
+ catch (StringScanException e)
+ { throw new ArgParseException (
+ name, "malformed " + valTypeName() + " '" + s + "'");
+ }
+ scanner.skipWhiteSpace();
+ if (!scanner.atEnd())
+ { throw new ArgParseException (
+ name, "malformed " + valTypeName() + " '" + s + "'");
+ }
+ boolean outOfRange = false;
+ switch (type)
+ {
+ case CHAR:
+ case INT:
+ case LONG:
+ { outOfRange = !withinRange (lval);
+ break;
+ }
+ case FLOAT:
+ case DOUBLE:
+ { outOfRange = !withinRange (dval);
+ break;
+ }
+ case STRING:
+ { outOfRange = !withinRange (sval);
+ break;
+ }
+ case BOOLEAN:
+ { outOfRange = !withinRange (bval);
+ break;
+ }
+ }
+ if (outOfRange)
+ { String errmsg = "value " + s + " not in range ";
+ throw new ArgParseException (
+ name, "value '" + s + "' not in range " + rangeDesc);
+ }
+ if (result.getClass().isArray())
+ {
+ switch (type)
+ {
+ case BOOLEAN:
+ { ((boolean[])result)[resultIdx] = bval;
+ break;
+ }
+ case CHAR:
+ { ((char[])result)[resultIdx] = (char)lval;
+ break;
+ }
+ case INT:
+ { ((int[])result)[resultIdx] = (int)lval;
+ break;
+ }
+ case LONG:
+ { ((long[])result)[resultIdx] = lval;
+ break;
+ }
+ case FLOAT:
+ { ((float[])result)[resultIdx] = (float)dval;
+ break;
+ }
+ case DOUBLE:
+ { ((double[])result)[resultIdx] = dval;
+ break;
+ }
+ case STRING:
+ { ((String[])result)[resultIdx] = sval;
+ break;
+ }
+ }
+ }
+ else
+ {
+ switch (type)
+ {
+ case BOOLEAN:
+ { ((BooleanHolder)result).value = bval;
+ break;
+ }
+ case CHAR:
+ { ((CharHolder)result).value = (char)lval;
+ break;
+ }
+ case INT:
+ { ((IntHolder)result).value = (int)lval;
+ break;
+ }
+ case LONG:
+ { ((LongHolder)result).value = lval;
+ break;
+ }
+ case FLOAT:
+ { ((FloatHolder)result).value = (float)dval;
+ break;
+ }
+ case DOUBLE:
+ { ((DoubleHolder)result).value = dval;
+ break;
+ }
+ case STRING:
+ { ((StringHolder)result).value = sval;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private String firstHelpOptionName()
+ {
+ if (firstHelpOption != null)
+ { return firstHelpOption.nameList.name;
+ }
+ else
+ { return null;
+ }
+ }
+
+ /**
+ * Creates an <code>ArgParser</code> with a synopsis
+ * string, and the default help options <code>-help</code> and
+ * <code>-&#063;</code>.
+ *
+ * @param synopsisString string that briefly describes program usage,
+ * for use by {@link #getHelpMessage getHelpMessage}.
+ * @see ArgParser#getSynopsisString
+ * @see ArgParser#getHelpMessage
+ */
+ public ArgParser(String synopsisString)
+ {
+ this (synopsisString, true);
+ }
+
+ /**
+ * Creates an <code>ArgParser</code> with a synopsis
+ * string. The help options <code>-help</code> and
+ * <code>-?</code> are added if <code>defaultHelp</code>
+ * is true.
+ *
+ * @param synopsisString string that briefly describes program usage,
+ * for use by {@link #getHelpMessage getHelpMessage}.
+ * @param defaultHelp if true, adds the default help options
+ * @see ArgParser#getSynopsisString
+ * @see ArgParser#getHelpMessage
+ */
+ public ArgParser(String synopsisString, boolean defaultHelp)
+ {
+ matchList = new Vector<Record>(128);
+ this.synopsisString = synopsisString;
+ if (defaultHelp)
+ { addOption ("-help,-? %h #displays help information", null);
+ defaultHelpOption = firstHelpOption = matchList.get(0);
+ }
+ }
+
+ /**
+ * 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
+ * <p>
+ * <prec>
+ * "java somepackage.SomeClass [options] files ..."
+ * </prec>
+ *
+ * <p> It is used in help and error messages.
+ *
+ * @return synopsis string
+ * @see ArgParser#setSynopsisString
+ * @see ArgParser#getHelpMessage
+ */
+ public String getSynopsisString ()
+ {
+ return synopsisString;
+ }
+
+ /**
+ * Sets the synopsis string used by the parser.
+ *
+ * @param s new synopsis string
+ * @see ArgParser#getSynopsisString
+ * @see ArgParser#getHelpMessage
+ */
+ public void setSynopsisString (String s)
+ {
+ synopsisString = s;
+ }
+
+ /**
+ * Indicates whether or not help options are enabled.
+ *
+ * @return true if help options are enabled
+ * @see ArgParser#setHelpOptionsEnabled
+ * @see ArgParser#addOption
+ */
+ public boolean getHelpOptionsEnabled ()
+ {
+ return helpOptionsEnabled;
+ }
+
+ /**
+ * Enables or disables help options. Help options are those
+ * associated with a conversion code of <code>%h</code>. If
+ * help options are enabled, and a help option is matched,
+ * then the string produced by
+ * {@link #getHelpMessage getHelpMessage}
+ * is printed to the default print stream and the program
+ * exits with code 0. Otherwise, arguments which match help
+ * options are ignored.
+ *
+ * @param enable enables help options if <code>true</code>.
+ * @see ArgParser#getHelpOptionsEnabled
+ * @see ArgParser#addOption
+ * @see ArgParser#setDefaultPrintStream */
+ public void setHelpOptionsEnabled(boolean enable)
+ { helpOptionsEnabled = enable;
+ }
+
+ /**
+ * Returns the default print stream used for outputting help
+ * and error information.
+ *
+ * @return default print stream
+ * @see ArgParser#setDefaultPrintStream
+ */
+ public PrintStream getDefaultPrintStream()
+ { return printStream;
+ }
+
+ /**
+ * Sets the default print stream used for outputting help
+ * and error information.
+ *
+ * @param stream new default print stream
+ * @see ArgParser#getDefaultPrintStream
+ */
+ public void setDefaultPrintStream (PrintStream stream)
+ {
+ printStream = stream;
+ }
+
+ /**
+ * Gets the indentation used by {@link #getHelpMessage
+ * getHelpMessage}.
+ *
+ * @return number of indentation columns
+ * @see ArgParser#setHelpIndentation
+ * @see ArgParser#getHelpMessage
+ */
+ public int getHelpIndentation()
+ {
+ return helpIndent;
+ }
+
+ /**
+ * Sets the indentation used by {@link #getHelpMessage
+ * 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.
+ *
+ * @param indent number of indentation columns
+ * @see ArgParser#getHelpIndentation
+ * @see ArgParser#getHelpMessage
+ */
+ public void setHelpIndentation (int indent)
+ { helpIndent = indent;
+ }
+
+// public void setTabSpacing (int n)
+// { tabSpacing = n;
+// }
+
+// public int getTabSpacing ()
+// { return tabSpacing;
+// }
+
+ private void scanRangeSpec (Record rec, String s)
+ throws IllegalArgumentException
+ {
+ StringScanner scanner = new StringScanner (s);
+ int i0, i = 1;
+ char c, c0, c1;
+
+ scanner.setStringDelimiters (")],}");
+ c = scanner.getc(); // swallow the first '{'
+ scanner.skipWhiteSpace();
+ while ((c=scanner.peekc()) != '}')
+ { RangePnt p0, p1;
+
+ if (c == '[' || c == '(')
+ {
+ if (rec.convertCode == 'v' || rec.convertCode == 'b')
+ { throw new IllegalArgumentException
+ ("Sub ranges not supported for %b or %v");
+ }
+ c0 = scanner.getc(); // record & swallow character
+ scanner.skipWhiteSpace();
+ p0 = new RangePnt (scanner, rec.type);
+ scanner.skipWhiteSpace();
+ if (scanner.getc() != ',')
+ { throw new IllegalArgumentException
+ ("Missing ',' in subrange specification");
+ }
+ p1 = new RangePnt (scanner, rec.type);
+ scanner.skipWhiteSpace();
+ if ((c1=scanner.getc()) != ']' && c1 != ')')
+ { throw new IllegalArgumentException
+ ("Unterminated subrange");
+ }
+ if (c0 == '(')
+ { p0.setClosed (false);
+ }
+ if (c1 == ')')
+ { p1.setClosed (false);
+ }
+ rec.addRangeAtom (new RangeAtom (p0, p1, rec.type));
+ }
+ else
+ { scanner.skipWhiteSpace();
+ p0 = new RangePnt (scanner, rec.type);
+ rec.addRangeAtom (new RangeAtom (p0));
+ }
+ scanner.skipWhiteSpace();
+ if ((c=scanner.peekc()) == ',')
+ { scanner.getc();
+ scanner.skipWhiteSpace();
+ }
+ else if (c != '}')
+ {
+ throw new IllegalArgumentException
+ ("Range spec: ',' or '}' expected");
+ }
+ }
+ if (rec.numRangeAtoms()==1)
+ { rec.rangeDesc = s.substring (1, s.length()-1);
+ }
+ else
+ { rec.rangeDesc = s;
+ }
+ }
+
+ private int defaultResultType (char convertCode)
+ {
+ switch (convertCode)
+ {
+ case 'i':
+ case 'o':
+ case 'd':
+ case 'x':
+ { return Record.LONG;
+ }
+ case 'c':
+ { return Record.CHAR;
+ }
+ case 'v':
+ case 'b':
+ { return Record.BOOLEAN;
+ }
+ case 'f':
+ { return Record.DOUBLE;
+ }
+ case 's':
+ { return Record.STRING;
+ }
+ }
+ return Record.NOTYPE;
+ }
+
+ /**
+ * 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.
+ *
+ * <p>The specification string has the general form
+ *
+ * <p> <var>optionNames</var>
+ * <code>%</code><var>conversionCode</var>
+ * [<code>{</code><var>rangeSpec</var><code>}</code>]
+ * [<code>X</code><var>multiplier</var>]
+ * [<code>#</code><var>valueDescription</var>]
+ * [<code>#</code><var>optionDescription</var>] </code>
+ *
+ * <p>
+ * where
+ * <ul> <p><li><var>optionNames</var> is a
+ * comma-separated list of names for the option
+ * (such as <code>-f, --file</code>).
+ *
+ * <p><li><var>conversionCode</var> is a single letter,
+ * following a <code>%</code> character, specifying
+ * information about what value the option requires:
+ *
+ * <table>
+ * <tr><td><code>%f</code></td><td>a floating point number</td>
+ * <tr><td><code>%i</code></td><td>an integer, in either decimal,
+ * hex (if preceeded by <code>0x</code>), or
+ * octal (if preceeded by <code>0</code>)</td>
+ * <tr valign=top>
+ * <td><code>%d</code></td><td>a decimal integer</td>
+ * <tr valign=top>
+ * <td><code>%o</code></td><td>an octal integer</td>
+ * <tr valign=top>
+ * <td><code>%h</code></td><td>a hex integer (without the
+ * preceeding <code>0x</code>)</td>
+ * <tr valign=top>
+ * <td><code>%c</code></td><td>a single character, including
+ * escape sequences (such as <code>\n</code> or <code>\007</code>),
+ * and optionally enclosed in single quotes
+ * <tr valign=top>
+ * <td><code>%b</code></td><td>a boolean value (<code>true</code>
+ * or <code>false</code>)</td>
+ * <tr valign=top>
+ * <td><code>%s</code></td><td>a string. This will
+ * be the argument string itself (or its remainder, in
+ * the case of a single word option)</td>
+ * <tr valign=top>
+ * <td><code>%v</code></td><td>no explicit value is expected,
+ * but a boolean value of <code>true</code> (by default)
+ * will be stored into the associated result holder if this
+ * option is matched. If one wishes to have a value of
+ * <code>false</code> stored instead, then the <code>%v</code>
+ * should be followed by a "range spec" containing
+ * <code>false</code>, as in <code>%v{false}</code>.
+ * </table>
+ *
+ * <p><li><var>rangeSpec</var> is an optional range specification,
+ * placed inside curly braces, consisting of a
+ * comma-separated list of range items each specifying
+ * permissible values for the option. A range item may be an
+ * individual value, or it may itself be a subrange,
+ * consisting of two individual values, separated by a comma,
+ * and enclosed in square or round brackets. Square and round
+ * brackets denote closed and open endpoints of a subrange, indicating
+ * that the associated endpoint value is included or excluded
+ * from the subrange.
+ * The values specified in the range spec need to be
+ * consistent with the type of value expected by the option.
+ *
+ * <p><b>Examples:</b>
+ *
+ * <p>A range spec of <code>{2,4,8,16}</code> for an integer
+ * value will allow the integers 2, 4, 8, or 16.
+ *
+ * <p>A range spec of <code>{[-1.0,1.0]}</code> for a floating
+ * point value will allow any floating point number in the
+ * range -1.0 to 1.0.
+ *
+ * <p>A range spec of <code>{(-88,100],1000}</code> for an integer
+ * value will allow values > -88 and <= 100, as well as 1000.
+ *
+ * <p>A range spec of <code>{"foo", "bar", ["aaa","zzz")} </code> for a
+ * string value will allow strings equal to <code>"foo"</code> or
+ * <code>"bar"</code>, plus any string lexically greater than or equal
+ * to <code>"aaa"</code> but less then <code>"zzz"</code>.
+ *
+ * <p><li><var>multiplier</var> is an optional integer,
+ * following a <code>X</code> character,
+ * indicating the number of values which the option expects.
+ * If the multiplier is not specified, it is assumed to be
+ * 1. If the multiplier value is greater than 1, then the
+ * result holder should be either an array (of appropriate
+ * type) with a length greater than or equal to the multiplier
+ * value, or a <code>java.util.Vector</code>
+ * <a href=#vectorHolder>as discussed below</a>.
+ *
+ * <p><li><var>valueDescription</var> is an optional
+ * description of the option's value requirements,
+ * and consists of all
+ * characters between two <code>#</code> characters.
+ * The final <code>#</code> character initiates the
+ * <i>option description</i>, which may be empty.
+ * The value description is used in
+ * <a href=#helpInfo>generating help messages</a>.
+ *
+ * <p><li><var>optionDescription</var> is an optional
+ * description of the option itself, consisting of all
+ * characters between a <code>#</code> character
+ * and the end of the specification string.
+ * The option description is used in
+ * <a href=#helpInfo>generating help messages</a>.
+ * </ul>
+ *
+ * <p>The result holder must be an object capable of holding
+ * a value compatible with the conversion code,
+ * or it must be a <code>java.util.Vector</code>.
+ * 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 <code>java.util.Vector</code>,
+ * 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
+ * <code>java.util.Vector</code>.
+ *
+ * <p>If the result holder is not a <code>Vector</code>, then
+ * it must correspond as follows to the conversion code:
+ *
+ * <table>
+ * <tr valign=top>
+ * <td><code>%i</code>, <code>%d</code>, <code>%x</code>,
+ * <code>%o</code></td>
+ * <td>{@link argparser.IntHolder IntHolder},
+ * {@link argparser.LongHolder LongHolder}, <code>int[]</code>, or
+ * <code>long[]</code></td>
+ * </tr>
+ *
+ * <tr valign=top>
+ * <td><code>%f</code></td>
+ * <td>{@link argparser.FloatHolder FloatHolder},
+ * {@link argparser.DoubleHolder DoubleHolder},
+ * <code>float[]</code>, or
+ * <code>double[]</code></td>
+ * </tr>
+ *
+ * <tr valign=top>
+ * <td><code>%b</code>, <code>%v</code></td>
+ * <td>{@link argparser.BooleanHolder BooleanHolder} or
+ * <code>boolean[]</code></td>
+ * </tr>
+ *
+ * <tr valign=top>
+ * <td><code>%s</code></td>
+ * <td>{@link argparser.StringHolder StringHolder} or
+ * <code>String[]</code></td>
+ * </tr>
+ *
+ * <tr valign=top>
+ * <td><code>%c</code></td>
+ * <td>{@link argparser.CharHolder CharHolder} or
+ * <code>char[]</code></td>
+ * </tr>
+ * </table>
+ *
+ * <p>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.
+ *
+ * <p><a name=vectorHolder>If the result holder is a
+ * <code>Vector</code>, 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.
+ *
+ * <p>The object allocated by the system to store the result
+ * will correspond to the conversion code as follows:
+ *
+ * <table>
+ * <tr valign=top>
+ * <td><code>%i</code>, <code>%d</code>, <code>%x</code>,
+ * <code>%o</code></td>
+ * <td>{@link argparser.LongHolder LongHolder}, or
+ * <code>long[]</code> if the multiplier value exceeds 1</td>
+ * </tr>
+ *
+ * <tr valign=top>
+ * <td><code>%f</code></td>
+ * <td>{@link argparser.DoubleHolder DoubleHolder}, or
+ * <code>double[]</code> if the multiplier value exceeds 1</td>
+ * </tr>
+ *
+ * <tr valign=top>
+ * <td><code>%b</code>, <code>%v</code></td>
+ * <td>{@link argparser.BooleanHolder BooleanHolder}, or
+ * <code>boolean[]</code>
+ * if the multiplier value exceeds 1</td>
+ * </tr>
+ *
+ * <tr valign=top>
+ * <td><code>%s</code></td>
+ * <td>{@link argparser.StringHolder StringHolder}, or
+ * <code>String[]</code>
+ * if the multiplier value exceeds 1</td>
+ * </tr>
+ *
+ * <tr valign=top>
+ * <td><code>%c</code></td>
+ * <td>{@link argparser.CharHolder CharHolder}, or <code>char[]</code>
+ * if the multiplier value exceeds 1</td>
+ * </tr>
+ * </table>
+ *
+ * @param spec the specification string
+ * @param resHolder object in which to store the associated
+ * value
+ * @throws IllegalArgumentException if there is an error in
+ * the specification or if the result holder is of an invalid
+ * type. */
+ public void addOption (String spec, Object resHolder)
+ throws IllegalArgumentException
+ {
+ // null terminated string is easier to parse
+ StringScanner scanner = new StringScanner(spec);
+ Record rec = null;
+ NameDesc nameTail = null;
+ NameDesc ndesc;
+ int i0, i1;
+ char c;
+
+ do
+ { ndesc = new NameDesc();
+ boolean nameEndsInWhiteSpace = false;
+
+ scanner.skipWhiteSpace();
+ i0 = scanner.getIndex();
+ while (!Character.isWhitespace(c=scanner.getc()) &&
+ c != ',' && c != '%' && c != '\000')
+ ;
+ i1 = scanner.getIndex();
+ if (c!='\000')
+ { i1--;
+ }
+ if (i0==i1)
+ { // then c is one of ',' '%' or '\000'
+ throw new IllegalArgumentException
+ ("Null option name given");
+ }
+ if (Character.isWhitespace(c))
+ { nameEndsInWhiteSpace = true;
+ scanner.skipWhiteSpace();
+ c = scanner.getc();
+ }
+ if (c=='\000')
+ { throw new IllegalArgumentException
+ ("No conversion character given");
+ }
+ if (c != ',' && c != '%')
+ { throw new IllegalArgumentException
+ ("Names not separated by ','");
+ }
+ ndesc.name = scanner.substring (i0, i1);
+ if (rec == null)
+ { rec = new Record();
+ rec.nameList = ndesc;
+ }
+ else
+ { nameTail.next = ndesc;
+ }
+ nameTail = ndesc;
+ ndesc.oneWord = !nameEndsInWhiteSpace;
+ }
+ while (c != '%');
+
+ if (!nameTail.oneWord)
+ { for (ndesc=rec.nameList; ndesc!=null; ndesc=ndesc.next)
+ { ndesc.oneWord = false;
+ }
+ }
+ c = scanner.getc();
+ if (c=='\000')
+ { throw new IllegalArgumentException
+ ("No conversion character given");
+ }
+ if (validConversionCodes.indexOf(c) == -1)
+ { throw new IllegalArgumentException
+ ("Conversion code '" + c + "' not one of '" +
+ validConversionCodes + "'");
+ }
+ rec.convertCode = c;
+
+ if (resHolder instanceof Vector)
+ { rec.vectorResult = true;
+ rec.type = defaultResultType (rec.convertCode);
+ }
+ else
+ {
+ switch (rec.convertCode)
+ {
+ case 'i':
+ case 'o':
+ case 'd':
+ case 'x':
+ { if (resHolder instanceof LongHolder ||
+ resHolder instanceof long[])
+ { rec.type = Record.LONG;
+ }
+ else if (resHolder instanceof IntHolder ||
+ resHolder instanceof int[])
+ { rec.type = Record.INT;
+ }
+ else
+ { throw new IllegalArgumentException (
+ "Invalid result holder for %" + c);
+ }
+ break;
+ }
+ case 'c':
+ { if (!(resHolder instanceof CharHolder) &&
+ !(resHolder instanceof char[]))
+ { throw new IllegalArgumentException (
+ "Invalid result holder for %c");
+ }
+ rec.type = Record.CHAR;
+ break;
+ }
+ case 'v':
+ case 'b':
+ { if (!(resHolder instanceof BooleanHolder) &&
+ !(resHolder instanceof boolean[]))
+ { throw new IllegalArgumentException (
+ "Invalid result holder for %" + c);
+ }
+ rec.type = Record.BOOLEAN;
+ break;
+ }
+ case 'f':
+ { if (resHolder instanceof DoubleHolder ||
+ resHolder instanceof double[])
+ { rec.type = Record.DOUBLE;
+ }
+ else if (resHolder instanceof FloatHolder ||
+ resHolder instanceof float[])
+ { rec.type = Record.FLOAT;
+ }
+ else
+ { throw new IllegalArgumentException (
+ "Invalid result holder for %f");
+ }
+ break;
+ }
+ case 's':
+ { if (!(resHolder instanceof StringHolder) &&
+ !(resHolder instanceof String[]))
+ { throw new IllegalArgumentException (
+ "Invalid result holder for %s");
+ }
+ rec.type = Record.STRING;
+ break;
+ }
+ case 'h':
+ { // resHolder is ignored for this type
+ break;
+ }
+ }
+ }
+ if (rec.convertCode == 'h')
+ { rec.resHolder = null;
+ }
+ else
+ { rec.resHolder = resHolder;
+ }
+
+ scanner.skipWhiteSpace();
+ // get the range specification, if any
+ if (scanner.peekc() == '{')
+ {
+ if (rec.convertCode == 'h')
+ { throw new IllegalArgumentException
+ ("Ranges not supported for %h");
+ }
+// int bcnt = 0;
+ i0 = scanner.getIndex(); // beginning of range spec
+ do
+ { c = scanner.getc();
+ if (c=='\000')
+ { throw new IllegalArgumentException
+ ("Unterminated range specification");
+ }
+// else if (c=='[' || c=='(')
+// { bcnt++;
+// }
+// else if (c==']' || c==')')
+// { bcnt--;
+// }
+// if ((rec.convertCode=='v'||rec.convertCode=='b') && bcnt>1)
+// { throw new IllegalArgumentException
+// ("Sub ranges not supported for %b or %v");
+// }
+ }
+ while (c != '}');
+// if (c != ']')
+// { throw new IllegalArgumentException
+// ("Range specification must end with ']'");
+// }
+ i1 = scanner.getIndex(); // end of range spec
+ scanRangeSpec (rec, scanner.substring (i0, i1));
+ if (rec.convertCode == 'v' && rec.rangeList!=null)
+ { rec.vval = rec.rangeList.low.bval;
+ }
+ }
+ // check for value multiplicity information, if any
+ if (scanner.peekc() == 'X')
+ {
+ if (rec.convertCode == 'h')
+ { throw new IllegalArgumentException
+ ("Multipliers not supported for %h");
+ }
+ scanner.getc();
+ try
+ { rec.numValues = (int)scanner.scanInt();
+ }
+ catch (StringScanException e)
+ { throw new IllegalArgumentException
+ ("Malformed value multiplier");
+ }
+ if (rec.numValues <= 0)
+ { throw new IllegalArgumentException
+ ("Value multiplier number must be > 0");
+ }
+ }
+ else
+ { rec.numValues = 1;
+ }
+ if (rec.numValues > 1)
+ { for (ndesc=rec.nameList; ndesc!=null; ndesc=ndesc.next)
+ { if (ndesc.oneWord)
+ { throw new IllegalArgumentException (
+"Multiplier value incompatible with one word option " + ndesc.name);
+ }
+ }
+ }
+ if (resHolder != null && resHolder.getClass().isArray())
+ { if (Array.getLength(resHolder) < rec.numValues)
+ { throw new IllegalArgumentException (
+"Result holder array must have a length >= " + rec.numValues);
+ }
+ }
+ else
+ { if (rec.numValues > 1 && !(resHolder instanceof Vector))
+ { throw new IllegalArgumentException (
+"Multiplier requires result holder to be an array of length >= "
++ rec.numValues);
+ }
+ }
+
+ // skip white space following conversion information
+ scanner.skipWhiteSpace();
+
+ // get the help message, if any
+
+ if (!scanner.atEnd())
+ { if (scanner.getc() != '#')
+ { throw new IllegalArgumentException
+ ("Illegal character(s), expecting '#'");
+ }
+ String helpInfo = scanner.substring (scanner.getIndex());
+ // look for second '#'. If there is one, then info
+ // between the first and second '#' is the value descriptor.
+ int k = helpInfo.indexOf ("#");
+ if (k != -1)
+ { rec.valueDesc = helpInfo.substring (0, k);
+ rec.helpMsg = helpInfo.substring (k+1);
+ }
+ else
+ { rec.helpMsg = helpInfo;
+ }
+ }
+ else
+ { rec.helpMsg = "";
+ }
+
+ // parse helpMsg for required/optional information if present
+ // default to required
+ if (rec.helpMsg.indexOf("(optional") != -1) {
+ rec.required = false;
+ }
+
+ // add option information to match list
+ if (rec.convertCode == 'h' && firstHelpOption == defaultHelpOption)
+ { matchList.remove (defaultHelpOption);
+ firstHelpOption = rec;
+ }
+ matchList.add (rec);
+ }
+
+ Record lastMatchRecord ()
+ { return (Record)matchList.lastElement();
+ }
+
+ private Record getRecord (String arg, ObjectHolder ndescHolder)
+ {
+ NameDesc ndesc;
+ for (int i=0; i<matchList.size(); i++)
+ { Record rec = (Record)matchList.get(i);
+ for (ndesc=rec.nameList; ndesc!=null; ndesc=ndesc.next)
+ { if (rec.convertCode != 'v' && ndesc.oneWord)
+ { if (arg.startsWith (ndesc.name))
+ { if (ndescHolder != null)
+ { ndescHolder.value = ndesc;
+ }
+ return rec;
+ }
+ }
+ else
+ { if (arg.equals (ndesc.name))
+ { if (ndescHolder != null)
+ { ndescHolder.value = ndesc;
+ }
+ return rec;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public void checkRequiredArgs() {
+ for (int i=1; i<matchList.size(); i++) {
+ Record rec = (Record)matchList.get(i);
+ StringHolder myString = (StringHolder) rec.resHolder;
+ if (((myString.value == null) || (myString.value.equals(""))) && (rec.required)) {
+ printErrorAndExit("Required parameter " + rec.nameList.name + " is not specified.");
}
}
- return false;
- }
-
- boolean withinRange(long l) {
- if (rangeList == null) {
- return true;
- }
- for (RangeAtom ra = rangeList; ra != null; ra = ra.next) {
- if (ra.match(l)) {
- return true;
- }
- }
- return false;
- }
-
- boolean withinRange(String s) {
- if (rangeList == null) {
- return true;
- }
- for (RangeAtom ra = rangeList; ra != null; ra = ra.next) {
- if (ra.match(s)) {
- return true;
- }
- }
- return false;
- }
-
- boolean withinRange(boolean b) {
- if (rangeList == null) {
- return true;
- }
- for (RangeAtom ra = rangeList; ra != null; ra = ra.next) {
- if (ra.match(b)) {
- return true;
- }
- }
- return false;
- }
-
- String valTypeName() {
- switch (convertCode) {
- case 'i': {
- return ("integer");
- }
- case 'o': {
- return ("octal integer");
- }
- case 'd': {
- return ("decimal integer");
- }
- case 'x': {
- return ("hex integer");
- }
- case 'c': {
- return ("char");
- }
- case 'b': {
- return ("boolean");
- }
- case 'f': {
- return ("float");
- }
- case 's': {
- return ("string");
- }
- }
- return ("unknown");
- }
-
- void scanValue(Object result, String name, String s, int resultIdx)
- throws ArgParseException {
- double dval = 0;
- String sval = null;
- long lval = 0;
- boolean bval = false;
-
- if (s.length() == 0) {
- throw new ArgParseException(name, "requires a contiguous value");
- }
- StringScanner scanner = new StringScanner(s);
- try {
- switch (convertCode) {
- case 'i': {
- lval = scanner.scanInt();
- break;
- }
- case 'o': {
- lval = scanner.scanInt(8, false);
- break;
- }
- case 'd': {
- lval = scanner.scanInt(10, false);
- break;
- }
- case 'x': {
- lval = scanner.scanInt(16, false);
- break;
- }
- case 'c': {
- lval = scanner.scanChar();
- break;
- }
- case 'b': {
- bval = scanner.scanBoolean();
- break;
- }
- case 'f': {
- dval = scanner.scanDouble();
- break;
- }
- case 's': {
- sval = scanner.getString();
- break;
- }
- }
- } catch (StringScanException e) {
- throw new ArgParseException(name, "malformed " + valTypeName()
- + " '" + s + "'");
- }
- scanner.skipWhiteSpace();
- if (!scanner.atEnd()) {
- throw new ArgParseException(name, "malformed " + valTypeName()
- + " '" + s + "'");
- }
- boolean outOfRange = false;
- switch (type) {
- case CHAR:
- case INT:
- case LONG: {
- outOfRange = !withinRange(lval);
- break;
- }
- case FLOAT:
- case DOUBLE: {
- outOfRange = !withinRange(dval);
- break;
- }
- case STRING: {
- outOfRange = !withinRange(sval);
- break;
- }
- case BOOLEAN: {
- outOfRange = !withinRange(bval);
- break;
- }
- }
- if (outOfRange) {
- String errmsg = "value " + s + " not in range ";
- throw new ArgParseException(name, "value '" + s
- + "' not in range " + rangeDesc);
- }
- if (result.getClass().isArray()) {
- switch (type) {
- case BOOLEAN: {
- ((boolean[]) result)[resultIdx] = bval;
- break;
- }
- case CHAR: {
- ((char[]) result)[resultIdx] = (char) lval;
- break;
- }
- case INT: {
- ((int[]) result)[resultIdx] = (int) lval;
- break;
- }
- case LONG: {
- ((long[]) result)[resultIdx] = lval;
- break;
- }
- case FLOAT: {
- ((float[]) result)[resultIdx] = (float) dval;
- break;
- }
- case DOUBLE: {
- ((double[]) result)[resultIdx] = dval;
- break;
- }
- case STRING: {
- ((String[]) result)[resultIdx] = sval;
- break;
- }
- }
- } else {
- switch (type) {
- case BOOLEAN: {
- ((BooleanHolder) result).value = bval;
- break;
- }
- case CHAR: {
- ((CharHolder) result).value = (char) lval;
- break;
- }
- case INT: {
- ((IntHolder) result).value = (int) lval;
- break;
- }
- case LONG: {
- ((LongHolder) result).value = lval;
- break;
- }
- case FLOAT: {
- ((FloatHolder) result).value = (float) dval;
- break;
- }
- case DOUBLE: {
- ((DoubleHolder) result).value = dval;
- break;
- }
- case STRING: {
- ((StringHolder) result).value = sval;
- break;
- }
- }
- }
- }
- }
-
- private String firstHelpOptionName() {
- if (firstHelpOption != null) {
- return firstHelpOption.nameList.name;
- } else {
- return null;
- }
- }
-
- /**
- * Creates an <code>ArgParser</code> with a synopsis string, and the default
- * help options <code>-help</code> and <code>-&#063;</code>.
- *
- * @param synopsisString string that briefly describes program usage, for
- * use by {@link #getHelpMessage getHelpMessage}.
- * @see ArgParser#getSynopsisString
- * @see ArgParser#getHelpMessage
- */
- public ArgParser(String synopsisString) {
- this(synopsisString, true);
- }
-
- /**
- * Creates an <code>ArgParser</code> with a synopsis string. The help
- * options <code>-help</code> and <code>-?</code> are added if
- * <code>defaultHelp</code> is true.
- *
- * @param synopsisString string that briefly describes program usage, for
- * use by {@link #getHelpMessage getHelpMessage}.
- * @param defaultHelp if true, adds the default help options
- * @see ArgParser#getSynopsisString
- * @see ArgParser#getHelpMessage
- */
- public ArgParser(String synopsisString, boolean defaultHelp) {
- matchList = new Vector<Record>(128);
- this.synopsisString = synopsisString;
- if (defaultHelp) {
- addOption("-help,-? %h #displays help information", null);
- defaultHelpOption = firstHelpOption = matchList.get(0);
- }
- }
-
- /**
- * 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
- * <p>
- * <prec> "java somepackage.SomeClass [options] files ..." </prec>
- *
- * <p>
- * It is used in help and error messages.
- *
- * @return synopsis string
- * @see ArgParser#setSynopsisString
- * @see ArgParser#getHelpMessage
- */
- public String getSynopsisString() {
- return synopsisString;
- }
-
- /**
- * Sets the synopsis string used by the parser.
- *
- * @param s new synopsis string
- * @see ArgParser#getSynopsisString
- * @see ArgParser#getHelpMessage
- */
- public void setSynopsisString(String s) {
- synopsisString = s;
- }
-
- /**
- * Indicates whether or not help options are enabled.
- *
- * @return true if help options are enabled
- * @see ArgParser#setHelpOptionsEnabled
- * @see ArgParser#addOption
- */
- public boolean getHelpOptionsEnabled() {
- return helpOptionsEnabled;
- }
-
- /**
- * Enables or disables help options. Help options are those associated with
- * a conversion code of <code>%h</code>. If help options are enabled, and a
- * help option is matched, then the string produced by
- * {@link #getHelpMessage getHelpMessage} is printed to the default print
- * stream and the program exits with code 0. Otherwise, arguments which
- * match help options are ignored.
- *
- * @param enable enables help options if <code>true</code>.
- * @see ArgParser#getHelpOptionsEnabled
- * @see ArgParser#addOption
- * @see ArgParser#setDefaultPrintStream
- */
- public void setHelpOptionsEnabled(boolean enable) {
- helpOptionsEnabled = enable;
- }
-
- /**
- * Returns the default print stream used for outputting help and error
- * information.
- *
- * @return default print stream
- * @see ArgParser#setDefaultPrintStream
- */
- public PrintStream getDefaultPrintStream() {
- return printStream;
- }
-
- /**
- * Sets the default print stream used for outputting help and error
- * information.
- *
- * @param stream new default print stream
- * @see ArgParser#getDefaultPrintStream
- */
- public void setDefaultPrintStream(PrintStream stream) {
- printStream = stream;
- }
-
- /**
- * Gets the indentation used by {@link #getHelpMessage getHelpMessage}.
- *
- * @return number of indentation columns
- * @see ArgParser#setHelpIndentation
- * @see ArgParser#getHelpMessage
- */
- public int getHelpIndentation() {
- return helpIndent;
- }
-
- /**
- * Sets the indentation used by {@link #getHelpMessage 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.
- *
- * @param indent number of indentation columns
- * @see ArgParser#getHelpIndentation
- * @see ArgParser#getHelpMessage
- */
- public void setHelpIndentation(int indent) {
- helpIndent = indent;
- }
-
- // public void setTabSpacing (int n)
- // { tabSpacing = n;
- // }
-
- // public int getTabSpacing ()
- // { return tabSpacing;
- // }
-
- private void scanRangeSpec(Record rec, String s)
- throws IllegalArgumentException {
- StringScanner scanner = new StringScanner(s);
- int i0, i = 1;
- char c, c0, c1;
-
- scanner.setStringDelimiters(")],}");
- c = scanner.getc(); // swallow the first '{'
- scanner.skipWhiteSpace();
- while ((c = scanner.peekc()) != '}') {
- RangePnt p0, p1;
-
- if (c == '[' || c == '(') {
- if (rec.convertCode == 'v' || rec.convertCode == 'b') {
- throw new IllegalArgumentException(
- "Sub ranges not supported for %b or %v");
- }
- c0 = scanner.getc(); // record & swallow character
- scanner.skipWhiteSpace();
- p0 = new RangePnt(scanner, rec.type);
- scanner.skipWhiteSpace();
- if (scanner.getc() != ',') {
- throw new IllegalArgumentException(
- "Missing ',' in subrange specification");
- }
- p1 = new RangePnt(scanner, rec.type);
- scanner.skipWhiteSpace();
- if ((c1 = scanner.getc()) != ']' && c1 != ')') {
- throw new IllegalArgumentException("Unterminated subrange");
- }
- if (c0 == '(') {
- p0.setClosed(false);
- }
- if (c1 == ')') {
- p1.setClosed(false);
- }
- rec.addRangeAtom(new RangeAtom(p0, p1, rec.type));
- } else {
- scanner.skipWhiteSpace();
- p0 = new RangePnt(scanner, rec.type);
- rec.addRangeAtom(new RangeAtom(p0));
- }
- scanner.skipWhiteSpace();
- if ((c = scanner.peekc()) == ',') {
- scanner.getc();
- scanner.skipWhiteSpace();
- } else if (c != '}') {
- throw new IllegalArgumentException(
- "Range spec: ',' or '}' expected");
- }
- }
- if (rec.numRangeAtoms() == 1) {
- rec.rangeDesc = s.substring(1, s.length() - 1);
- } else {
- rec.rangeDesc = s;
- }
- }
-
- private int defaultResultType(char convertCode) {
- switch (convertCode) {
- case 'i':
- case 'o':
- case 'd':
- case 'x': {
- return Record.LONG;
- }
- case 'c': {
- return Record.CHAR;
- }
- case 'v':
- case 'b': {
- return Record.BOOLEAN;
- }
- case 'f': {
- return Record.DOUBLE;
- }
- case 's': {
- return Record.STRING;
- }
- }
- return Record.NOTYPE;
- }
-
- /**
- * 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.
- *
- * <p>
- * The specification string has the general form
- *
- * <p>
- * <var>optionNames</var> <code>%</code><var>conversionCode</var> [
- * <code>{</code><var>rangeSpec</var><code>}</code>] [<code>X</code>
- * <var>multiplier</var>] [<code>#</code><var>valueDescription</var>] [
- * <code>#</code><var>optionDescription</var>] </code>
- *
- * <p>
- * where
- * <ul>
- * <p>
- * <li><var>optionNames</var> is a comma-separated list of names for the
- * option (such as <code>-f, --file</code>).
- *
- * <p>
- * <li><var>conversionCode</var> is a single letter, following a
- * <code>%</code> character, specifying information about what value the
- * option requires:
- *
- * <table>
- * <tr>
- * <td><code>%f</code></td>
- * <td>a floating point number</td>
- * <tr>
- * <td><code>%i</code></td>
- * <td>an integer, in either decimal, hex (if preceeded by <code>0x</code>),
- * or octal (if preceeded by <code>0</code>)</td>
- * <tr valign=top>
- * <td><code>%d</code></td>
- * <td>a decimal integer</td>
- * <tr valign=top>
- * <td><code>%o</code></td>
- * <td>an octal integer</td>
- * <tr valign=top>
- * <td><code>%h</code></td>
- * <td>a hex integer (without the preceeding <code>0x</code>)</td>
- * <tr valign=top>
- * <td><code>%c</code></td>
- * <td>a single character, including escape sequences (such as
- * <code>\n</code> or <code>\007</code>), and optionally enclosed in single
- * quotes
- * <tr valign=top>
- * <td><code>%b</code></td>
- * <td>a boolean value (<code>true</code> or <code>false</code>)</td>
- * <tr valign=top>
- * <td><code>%s</code></td>
- * <td>a string. This will be the argument string itself (or its remainder,
- * in the case of a single word option)</td>
- * <tr valign=top>
- * <td><code>%v</code></td>
- * <td>no explicit value is expected, but a boolean value of
- * <code>true</code> (by default) will be stored into the associated result
- * holder if this option is matched. If one wishes to have a value of
- * <code>false</code> stored instead, then the <code>%v</code> should be
- * followed by a "range spec" containing <code>false</code>, as in
- * <code>%v{false}</code>.
- * </table>
- *
- * <p>
- * <li><var>rangeSpec</var> is an optional range specification, placed
- * inside curly braces, consisting of a comma-separated list of range items
- * each specifying permissible values for the option. A range item may be an
- * individual value, or it may itself be a subrange, consisting of two
- * individual values, separated by a comma, and enclosed in square or round
- * brackets. Square and round brackets denote closed and open endpoints of a
- * subrange, indicating that the associated endpoint value is included or
- * excluded from the subrange. The values specified in the range spec need
- * to be consistent with the type of value expected by the option.
- *
- * <p>
- * <b>Examples:</b>
- *
- * <p>
- * A range spec of <code>{2,4,8,16}</code> for an integer value will allow
- * the integers 2, 4, 8, or 16.
- *
- * <p>
- * A range spec of <code>{[-1.0,1.0]}</code> for a floating point value will
- * allow any floating point number in the range -1.0 to 1.0.
- *
- * <p>
- * A range spec of <code>{(-88,100],1000}</code> for an integer value will
- * allow values > -88 and <= 100, as well as 1000.
- *
- * <p>
- * A range spec of <code>{"foo", "bar", ["aaa","zzz")} </code> for a string
- * value will allow strings equal to <code>"foo"</code> or
- * <code>"bar"</code>, plus any string lexically greater than or equal to
- * <code>"aaa"</code> but less then <code>"zzz"</code>.
- *
- * <p>
- * <li><var>multiplier</var> is an optional integer, following a
- * <code>X</code> character, indicating the number of values which the
- * option expects. If the multiplier is not specified, it is assumed to be
- * 1. If the multiplier value is greater than 1, then the result holder
- * should be either an array (of appropriate type) with a length greater
- * than or equal to the multiplier value, or a <code>java.util.Vector</code>
- * <a href=#vectorHolder>as discussed below</a>.
- *
- * <p>
- * <li><var>valueDescription</var> is an optional description of the
- * option's value requirements, and consists of all characters between two
- * <code>#</code> characters. The final <code>#</code> character initiates
- * the <i>option description</i>, which may be empty. The value description
- * is used in <a href=#helpInfo>generating help messages</a>.
- *
- * <p>
- * <li><var>optionDescription</var> is an optional description of the option
- * itself, consisting of all characters between a <code>#</code> character
- * and the end of the specification string. The option description is used
- * in <a href=#helpInfo>generating help messages</a>.
- * </ul>
- *
- * <p>
- * The result holder must be an object capable of holding a value compatible
- * with the conversion code, or it must be a <code>java.util.Vector</code>.
- * 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
- * <code>java.util.Vector</code>, 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 <code>java.util.Vector</code>.
- *
- * <p>
- * If the result holder is not a <code>Vector</code>, then it must
- * correspond as follows to the conversion code:
- *
- * <table>
- * <tr valign=top>
- * <td><code>%i</code>, <code>%d</code>, <code>%x</code>, <code>%o</code></td>
- * <td>{@link argparser.IntHolder IntHolder}, {@link argparser.LongHolder
- * LongHolder}, <code>int[]</code>, or <code>long[]</code></td>
- * </tr>
- *
- * <tr valign=top>
- * <td><code>%f</code></td>
- * <td>{@link argparser.FloatHolder FloatHolder},
- * {@link argparser.DoubleHolder DoubleHolder}, <code>float[]</code>, or
- * <code>double[]</code></td>
- * </tr>
- *
- * <tr valign=top>
- * <td><code>%b</code>, <code>%v</code></td>
- * <td>{@link argparser.BooleanHolder BooleanHolder} or
- * <code>boolean[]</code></td>
- * </tr>
- *
- * <tr valign=top>
- * <td><code>%s</code></td>
- * <td>{@link argparser.StringHolder StringHolder} or <code>String[]</code></td>
- * </tr>
- *
- * <tr valign=top>
- * <td><code>%c</code></td>
- * <td>{@link argparser.CharHolder CharHolder} or <code>char[]</code></td>
- * </tr>
- * </table>
- *
- * <p>
- * 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.
- *
- * <p>
- * <a name=vectorHolder>If the result holder is a <code>Vector</code>, 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.
- *
- * <p>
- * The object allocated by the system to store the result will correspond to
- * the conversion code as follows:
- *
- * <table>
- * <tr valign=top>
- * <td><code>%i</code>, <code>%d</code>, <code>%x</code>, <code>%o</code></td>
- * <td>{@link argparser.LongHolder LongHolder}, or <code>long[]</code> if
- * the multiplier value exceeds 1</td>
- * </tr>
- *
- * <tr valign=top>
- * <td><code>%f</code></td>
- * <td>{@link argparser.DoubleHolder DoubleHolder}, or <code>double[]</code>
- * if the multiplier value exceeds 1</td>
- * </tr>
- *
- * <tr valign=top>
- * <td><code>%b</code>, <code>%v</code></td>
- * <td>{@link argparser.BooleanHolder BooleanHolder}, or
- * <code>boolean[]</code> if the multiplier value exceeds 1</td>
- * </tr>
- *
- * <tr valign=top>
- * <td><code>%s</code></td>
- * <td>{@link argparser.StringHolder StringHolder}, or <code>String[]</code>
- * if the multiplier value exceeds 1</td>
- * </tr>
- *
- * <tr valign=top>
- * <td><code>%c</code></td>
- * <td>{@link argparser.CharHolder CharHolder}, or <code>char[]</code> if
- * the multiplier value exceeds 1</td>
- * </tr>
- * </table>
- *
- * @param spec the specification string
- * @param resHolder object in which to store the associated value
- * @throws IllegalArgumentException if there is an error in the
- * specification or if the result holder is of an invalid type.
- */
- public void addOption(String spec, Object resHolder)
- throws IllegalArgumentException {
- // null terminated string is easier to parse
- StringScanner scanner = new StringScanner(spec);
- Record rec = null;
- NameDesc nameTail = null;
- NameDesc ndesc;
- int i0, i1;
- char c;
-
- do {
- ndesc = new NameDesc();
- boolean nameEndsInWhiteSpace = false;
-
- scanner.skipWhiteSpace();
- i0 = scanner.getIndex();
- while (!Character.isWhitespace(c = scanner.getc()) && c != ','
- && c != '%' && c != '\000')
- ;
- i1 = scanner.getIndex();
- if (c != '\000') {
- i1--;
- }
- if (i0 == i1) { // then c is one of ',' '%' or '\000'
- throw new IllegalArgumentException("Null option name given");
- }
- if (Character.isWhitespace(c)) {
- nameEndsInWhiteSpace = true;
- scanner.skipWhiteSpace();
- c = scanner.getc();
- }
- if (c == '\000') {
- throw new IllegalArgumentException(
- "No conversion character given");
- }
- if (c != ',' && c != '%') {
- throw new IllegalArgumentException("Names not separated by ','");
- }
- ndesc.name = scanner.substring(i0, i1);
- if (rec == null) {
- rec = new Record();
- rec.nameList = ndesc;
- } else {
- nameTail.next = ndesc;
- }
- nameTail = ndesc;
- ndesc.oneWord = !nameEndsInWhiteSpace;
- } while (c != '%');
-
- if (!nameTail.oneWord) {
- for (ndesc = rec.nameList; ndesc != null; ndesc = ndesc.next) {
- ndesc.oneWord = false;
- }
- }
- c = scanner.getc();
- if (c == '\000') {
- throw new IllegalArgumentException("No conversion character given");
- }
- if (validConversionCodes.indexOf(c) == -1) {
- throw new IllegalArgumentException("Conversion code '" + c
- + "' not one of '" + validConversionCodes + "'");
- }
- rec.convertCode = c;
-
- if (resHolder instanceof Vector) {
- rec.vectorResult = true;
- rec.type = defaultResultType(rec.convertCode);
- } else {
- switch (rec.convertCode) {
- case 'i':
- case 'o':
- case 'd':
- case 'x': {
- if (resHolder instanceof LongHolder
- || resHolder instanceof long[]) {
- rec.type = Record.LONG;
- } else if (resHolder instanceof IntHolder
- || resHolder instanceof int[]) {
- rec.type = Record.INT;
- } else {
- throw new IllegalArgumentException(
- "Invalid result holder for %" + c);
- }
- break;
- }
- case 'c': {
- if (!(resHolder instanceof CharHolder)
- && !(resHolder instanceof char[])) {
- throw new IllegalArgumentException(
- "Invalid result holder for %c");
- }
- rec.type = Record.CHAR;
- break;
- }
- case 'v':
- case 'b': {
- if (!(resHolder instanceof BooleanHolder)
- && !(resHolder instanceof boolean[])) {
- throw new IllegalArgumentException(
- "Invalid result holder for %" + c);
- }
- rec.type = Record.BOOLEAN;
- break;
- }
- case 'f': {
- if (resHolder instanceof DoubleHolder
- || resHolder instanceof double[]) {
- rec.type = Record.DOUBLE;
- } else if (resHolder instanceof FloatHolder
- || resHolder instanceof float[]) {
- rec.type = Record.FLOAT;
- } else {
- throw new IllegalArgumentException(
- "Invalid result holder for %f");
- }
- break;
- }
- case 's': {
- if (!(resHolder instanceof StringHolder)
- && !(resHolder instanceof String[])) {
- throw new IllegalArgumentException(
- "Invalid result holder for %s");
- }
- rec.type = Record.STRING;
- break;
- }
- case 'h': { // resHolder is ignored for this type
- break;
- }
- }
- }
- if (rec.convertCode == 'h') {
- rec.resHolder = null;
- } else {
- rec.resHolder = resHolder;
- }
-
- scanner.skipWhiteSpace();
- // get the range specification, if any
- if (scanner.peekc() == '{') {
- if (rec.convertCode == 'h') {
- throw new IllegalArgumentException(
- "Ranges not supported for %h");
- }
- // int bcnt = 0;
- i0 = scanner.getIndex(); // beginning of range spec
- do {
- c = scanner.getc();
- if (c == '\000') {
- throw new IllegalArgumentException(
- "Unterminated range specification");
- }
- // else if (c=='[' || c=='(')
- // { bcnt++;
- // }
- // else if (c==']' || c==')')
- // { bcnt--;
- // }
- // if ((rec.convertCode=='v'||rec.convertCode=='b') && bcnt>1)
- // { throw new IllegalArgumentException
- // ("Sub ranges not supported for %b or %v");
- // }
- } while (c != '}');
- // if (c != ']')
- // { throw new IllegalArgumentException
- // ("Range specification must end with ']'");
- // }
- i1 = scanner.getIndex(); // end of range spec
- scanRangeSpec(rec, scanner.substring(i0, i1));
- if (rec.convertCode == 'v' && rec.rangeList != null) {
- rec.vval = rec.rangeList.low.bval;
- }
- }
- // check for value multiplicity information, if any
- if (scanner.peekc() == 'X') {
- if (rec.convertCode == 'h') {
- throw new IllegalArgumentException(
- "Multipliers not supported for %h");
- }
- scanner.getc();
- try {
- rec.numValues = (int) scanner.scanInt();
- } catch (StringScanException e) {
- throw new IllegalArgumentException("Malformed value multiplier");
- }
- if (rec.numValues <= 0) {
- throw new IllegalArgumentException(
- "Value multiplier number must be > 0");
- }
- } else {
- rec.numValues = 1;
- }
- if (rec.numValues > 1) {
- for (ndesc = rec.nameList; ndesc != null; ndesc = ndesc.next) {
- if (ndesc.oneWord) {
- throw new IllegalArgumentException(
- "Multiplier value incompatible with one word option "
- + ndesc.name);
- }
- }
- }
- if (resHolder != null && resHolder.getClass().isArray()) {
- if (Array.getLength(resHolder) < rec.numValues) {
- throw new IllegalArgumentException(
- "Result holder array must have a length >= "
- + rec.numValues);
- }
- } else {
- if (rec.numValues > 1 && !(resHolder instanceof Vector)) {
- throw new IllegalArgumentException(
- "Multiplier requires result holder to be an array of length >= "
- + rec.numValues);
- }
- }
-
- // skip white space following conversion information
- scanner.skipWhiteSpace();
+ }
+
+
+ Object getResultHolder (String arg)
+ {
+ Record rec = getRecord(arg, null);
+ return (rec != null) ? rec.resHolder : null;
+ }
+
+ String getOptionName (String arg)
+ {
+ ObjectHolder ndescHolder = new ObjectHolder();
+ Record rec = getRecord(arg, ndescHolder);
+ return (rec != null) ? ((NameDesc)ndescHolder.value).name : null;
+ }
+
+ String getOptionRangeDesc (String arg)
+ {
+ Record rec = getRecord(arg, null);
+ return (rec != null) ? rec.rangeDesc : null;
+ }
+
+ String getOptionTypeName (String arg)
+ {
+ Record rec = getRecord(arg, null);
+ return (rec != null) ? rec.valTypeName() : null;
+ }
+
+ private Object createResultHolder (Record rec)
+ {
+ if (rec.numValues == 1)
+ { switch (rec.type)
+ { case Record.LONG:
+ { return new LongHolder();
+ }
+ case Record.CHAR:
+ { return new CharHolder();
+ }
+ case Record.BOOLEAN:
+ { return new BooleanHolder();
+ }
+ case Record.DOUBLE:
+ { return new DoubleHolder();
+ }
+ case Record.STRING:
+ { return new StringHolder();
+ }
+ }
+ }
+ else
+ { switch (rec.type)
+ { case Record.LONG:
+ { return new long[rec.numValues];
+ }
+ case Record.CHAR:
+ { return new char[rec.numValues];
+ }
+ case Record.BOOLEAN:
+ { return new boolean[rec.numValues];
+ }
+ case Record.DOUBLE:
+ { return new double[rec.numValues];
+ }
+ case Record.STRING:
+ { return new String[rec.numValues];
+ }
+ }
+ }
+ return null; // can't happen
+ }
+
+ static void stringToArgs (Vector<String> vec, String s,
+ boolean allowQuotedStrings)
+ throws StringScanException
+ {
+ StringScanner scanner = new StringScanner(s);
+ scanner.skipWhiteSpace();
+ while (!scanner.atEnd())
+ { if (allowQuotedStrings)
+ { vec.add (scanner.scanString());
+ }
+ else
+ { vec.add (scanner.scanNonWhiteSpaceString());
+ }
+ scanner.skipWhiteSpace();
+ }
+ }
+
+ /**
+ * 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 <code>"</code>. The character <code>#</code> acts as
+ * a comment character, causing input to the end of the current line to
+ * be ignored.
+ *
+ * @param reader Reader from which to read the strings
+ * @param args Initial set of argument values. Can be
+ * specified as <code>null</code>.
+ * @throws IOException if an error occured while reading.
+ */
+ public static String[] prependArgs (Reader reader, String[] args)
+ throws IOException
+ {
+ if (args == null)
+ { args = new String[0];
+ }
+ LineNumberReader lineReader = new LineNumberReader (reader);
+ Vector<String> vec = new Vector<String>(100, 100);
+ String line;
+ int i, k;
+
+ while ((line = lineReader.readLine()) != null)
+ { int commentIdx = line.indexOf ("#");
+ if (commentIdx != -1)
+ { line = line.substring (0, commentIdx);
+ }
+ try
+ { stringToArgs (vec, line, /*allowQuotedStings=*/true);
+ }
+ catch (StringScanException e)
+ { throw new IOException (
+ "malformed string, line "+lineReader.getLineNumber());
+ }
+ }
+ String[] result = new String[vec.size()+args.length];
+ for (i=0; i<vec.size(); i++)
+ { result[i] = (String)vec.get(i);
+ }
+ for (k=0; k<args.length; k++)
+ { result[i++] = args[k];
+ }
+ return result;
+ }
+
+ /**
+ * 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 <code>"</code>. The character <code>#</code> acts as a
+ * comment character, causing input to the end of the current line to
+ * be ignored.
+ *
+ * @param file File to be read
+ * @param args Initial set of argument values. Can be
+ * specified as <code>null</code>.
+ * @throws IOException if an error occured while reading the file.
+ */
+ public static String[] prependArgs (File file, String[] args)
+ throws IOException
+ {
+ if (args == null)
+ { args = new String[0];
+ }
+ if (!file.canRead())
+ { return args;
+ }
+ try
+ { return prependArgs (new FileReader (file), args);
+ }
+ catch (IOException e)
+ { throw new IOException (
+"File " + file.getName() + ": " + e.getMessage());
+ }
+ }
+
+ /**
+ * Sets the parser's error message.
+ *
+ * @param s Error message
+ */
+ protected void setError (String msg)
+ {
+ errMsg = msg;
+ }
+
+ /**
+ * Prints an error message, along with a pointer to help options,
+ * if available, and causes the program to exit with code 1.
+ */
+ public void printErrorAndExit (String msg)
+ {
+ if (helpOptionsEnabled && firstHelpOptionName() != null)
+ { msg += "\nUse "+firstHelpOptionName()+" for help information";
+ }
+ if (printStream != null)
+ { printStream.println (msg);
+ }
+ System.exit(1);
+ }
+
+ /**
+ * Matches arguments within an argument list.
+ *
+ * <p>In the event of an erroneous or unmatched argument, the method
+ * prints a message and exits the program with code 1.
+ *
+ * <p>If help options are enabled and one of the arguments matches a
+ * help option, then the result of {@link #getHelpMessage
+ * getHelpMessage} is printed to the default print stream and the
+ * program exits with code 0. If help options are not enabled, they
+ * are ignored.
+ *
+ * @param args argument list
+ * @see ArgParser#getDefaultPrintStream
+ */
+ public void matchAllArgs (String[] args)
+ {
+ matchAllArgs (args, 0, EXIT_ON_UNMATCHED | EXIT_ON_ERROR);
+ }
+
+ /**
+ * Matches arguments within an argument list and returns
+ * those which were not matched. The matching starts at a location
+ * in <code>args</code> specified by <code>idx</code>, and
+ * unmatched arguments are returned in a String array.
+ *
+ * <p>In the event of an erroneous argument, the method either prints a
+ * message and exits the program (if {@link #EXIT_ON_ERROR} is
+ * set in <code>exitFlags</code>)
+ * or terminates the matching and creates a error message that
+ * can be retrieved by {@link #getErrorMessage}.
+ *
+ * <p>In the event of an umatched argument, the method will print a
+ * message and exit if {@link #EXIT_ON_UNMATCHED} is set
+ * in <code>errorFlags</code>.
+ * Otherwise, the unmatched argument will be appended to the returned
+ * array of unmatched values, and the matching will continue at the
+ * next location.
+ *
+ * <p>If help options are enabled and one of the arguments matches a
+ * help option, then the result of {@link #getHelpMessage
+ * 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.
+ *
+ * @param args argument list
+ * @param idx starting location in list
+ * @param exitFlags conditions causing the program to exit. Should be
+ * an or-ed combintion of {@link #EXIT_ON_ERROR} or {@link
+ * #EXIT_ON_UNMATCHED}.
+ * @return array of arguments that were not matched, or
+ * <code>null</code> if all arguments were successfully matched
+ * @see ArgParser#getErrorMessage
+ * @see ArgParser#getDefaultPrintStream
+ */
+ public String[] matchAllArgs (String[] args, int idx, int exitFlags)
+ {
+ Vector<String> unmatched = new Vector<String>(10);
+
+ while (idx < args.length)
+ { try
+ { idx = matchArg (args, idx);
+ if (unmatchedArg != null)
+ { if ((exitFlags & EXIT_ON_UNMATCHED) != 0)
+ { printErrorAndExit (
+ "Unrecognized argument: " + unmatchedArg);
+ }
+ else
+ { unmatched.add (unmatchedArg);
+ }
+ }
+ }
+ catch (ArgParseException e)
+ { if ((exitFlags & EXIT_ON_ERROR) != 0)
+ { printErrorAndExit (e.getMessage());
+ }
+ break;
+ }
+ }
+ if (unmatched.size() == 0)
+ { return null;
+ }
+ else
+ { return (String[])unmatched.toArray(new String[0]);
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * <p>In the event of an erroneous argument, the method throws
+ * an {@link argparser.ArgParseException ArgParseException}
+ * with an appropriate error message. This error
+ * message can also be retrieved using
+ * {@link #getErrorMessage getErrorMessage}.
+ *
+ * <p>In the event of an umatched argument, the method will return idx
+ * + 1, and {@link #getUnmatchedArgument getUnmatchedArgument} will
+ * return a copy of the unmatched argument. If an argument is matched,
+ * {@link #getUnmatchedArgument getUnmatchedArgument} will return
+ * <code>null</code>.
+ *
+ * <p>If help options are enabled and the argument matches a help
+ * option, then the result of {@link #getHelpMessage 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.
+ *
+ * @param args argument list
+ * @param idx location in list where match should start
+ * @return 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 ArgParser#setDefaultPrintStream
+ * @see ArgParser#getHelpOptionsEnabled
+ * @see ArgParser#getErrorMessage
+ * @see ArgParser#getUnmatchedArgument
+ */
+ @SuppressWarnings("unchecked")
+ public int matchArg (String[] args, int idx)
+ throws ArgParseException
+ {
+ unmatchedArg = null;
+ setError (null);
+ try
+ { ObjectHolder ndescHolder = new ObjectHolder();
+ Record rec = getRecord (args[idx], ndescHolder);
+ if (rec == null || (rec.convertCode=='h' && !helpOptionsEnabled))
+ { // didn't match
+ unmatchedArg = new String(args[idx]);
+ return idx+1;
+ }
+ NameDesc ndesc = (NameDesc)ndescHolder.value;
+ Object result;
+ if (rec.resHolder instanceof Vector)
+ { result = createResultHolder (rec);
+ }
+ else
+ { result = rec.resHolder;
+ }
+ if (rec.convertCode == 'h')
+ { if (helpOptionsEnabled)
+ { printStream.println (getHelpMessage());
+ System.exit (0);
+ }
+ else
+ { return idx+1;
+ }
+ }
+ else if (rec.convertCode != 'v')
+ { if (ndesc.oneWord)
+ { rec.scanValue (
+ result, ndesc.name,
+ args[idx].substring (ndesc.name.length()), 0);
+ }
+ else
+ { if (idx+rec.numValues >= args.length)
+ { throw new ArgParseException (
+ ndesc.name, "requires " + rec.numValues + " value" +
+ (rec.numValues > 1 ? "s" : ""));
+ }
+ for (int k=0; k<rec.numValues; k++)
+ { rec.scanValue (result, ndesc.name, args[++idx], k);
+ }
+ }
+ }
+ else
+ { if (rec.resHolder instanceof BooleanHolder)
+ { ((BooleanHolder)result).value = rec.vval;
+ }
+ else
+ { for (int k=0; k<rec.numValues; k++)
+ { ((boolean[])result)[k] = rec.vval;
+ }
+ }
+ }
+ if (rec.resHolder instanceof Vector)
+ { ((Vector<Object>)rec.resHolder).add (result);
+ }
+ }
+ catch (ArgParseException e)
+ { setError (e.getMessage());
+ throw e;
+ }
+ return idx+1;
+ }
+
+ private String spaceString (int n)
+ {
+ StringBuffer sbuf = new StringBuffer(n);
+ for (int i=0; i<n; i++)
+ { sbuf.append(' ');
+ }
+ return sbuf.toString();
+ }
+
+// public String getShortHelpMessage ()
+// {
+// String s;
+// Record rec;
+// NameDesc ndesc;
+// int initialIndent = 8;
+// int col = initialIndent;
+
+// if (maxcols <= 0)
+// { maxcols = 80;
+// }
+// if (matchList.size() > 0)
+// { ps.print (spaceString(initialIndent));
+// }
+// for (int i=0; i<matchList.size(); i++)
+// { rec = (Record)matchList.get(i);
+// s = "[";
+// for (ndesc=rec.nameList; ndesc!=null; ndesc=ndesc.next)
+// { s = s + ndesc.name;
+// if (ndesc.oneWord == false)
+// { s = s + " ";
+// }
+// if (ndesc.next != null)
+// { s = s + ",";
+// }
+// }
+// if (rec.convertCode != 'v' && rec.convertCode != 'h')
+// { if (rec.valueDesc != null)
+// { s += rec.valueDesc;
+// }
+// else
+// { s = s + "<" + rec.valTypeName() + ">";
+// if (rec.numValues > 1)
+// { s += "X" + rec.numValues;
+// }
+// }
+// }
+// s = s + "]";
+// /*
+// (col+=s.length()) > (maxcols-1) => we will spill over edge.
+// we use (maxcols-1) because if we go right to the edge
+// (maxcols), we get wrap new line inserted "for us".
+// i != 0 means we print the first entry, no matter
+// how long it is. Subsequent entries are printed
+// full length anyway. */
+
+// if ((col+=s.length()) > (maxcols-1) && i != 0)
+// { col = initialIndent+s.length();
+// ps.print ("\n" + spaceString(initialIndent));
+// }
+// ps.print (s);
+// }
+// if (matchList.size() > 0)
+// { ps.print ('\n');
+// ps.flush();
+// }
+// }
+
+ /**
+ * Returns a string describing the allowed options
+ * in detail.
+ *
+ * @return help information string.
+ */
+ public String getHelpMessage ()
+ {
+ Record rec;
+ NameDesc ndesc;
+ boolean hasOneWordAlias = false;
+ String s;
+
+ s = "Usage: " + synopsisString + "\n";
+ s += "Options include:\n\n";
+ for (int i=0; i<matchList.size(); i++)
+ { String optionInfo = "";
+ rec = (Record)matchList.get(i);
+ if (rec.convertCode=='h' && !helpOptionsEnabled)
+ { continue;
+ }
+ for (ndesc=rec.nameList; ndesc!=null; ndesc=ndesc.next)
+ { if (ndesc.oneWord)
+ { hasOneWordAlias = true;
+ break;
+ }
+ }
+ for (ndesc=rec.nameList; ndesc!=null; ndesc=ndesc.next)
+ { optionInfo += ndesc.name;
+ if (hasOneWordAlias && !ndesc.oneWord)
+ { optionInfo += " ";
+ }
+ if (ndesc.next != null)
+ { optionInfo += ",";
+ }
+ }
+ if (!hasOneWordAlias)
+ { optionInfo += " ";
+ }
+ if (rec.convertCode != 'v' && rec.convertCode != 'h')
+ { if (rec.valueDesc != null)
+ { optionInfo += rec.valueDesc;
+ }
+ else
+ { if (rec.rangeDesc != null)
+ { optionInfo += "<" + rec.valTypeName() + " "
+ + rec.rangeDesc + ">";
+ }
+ else
+ { optionInfo += "<" + rec.valTypeName() + ">";
+ }
+ }
+ }
+ if (rec.numValues > 1)
+ { optionInfo += "X" + rec.numValues;
+ }
+ s += optionInfo;
+ if (rec.helpMsg.length() > 0)
+ { int pad = helpIndent - optionInfo.length();
+ if (pad < 2)
+ { //s += '\n';
+ pad = helpIndent;
+ }
+// s += spaceString(pad) + rec.helpMsg;
+ s += spaceString(4) + rec.helpMsg;
+ }
+ s += '\n';
+ }
+ return s;
+ }
+
+ /**
+ * Returns the parser's error message. This is automatically
+ * set whenever an error is encountered in <code>matchArg</code>
+ * or <code>matchAllArgs</code>, and is automatically set to
+ * <code>null</code> at the beginning of these methods.
+ *
+ * @return error message
+ */
+ public String getErrorMessage()
+ {
+ return errMsg;
+ }
+
+ /**
+ * Returns the value of an unmatched argument discovered {@link
+ * #matchArg matchArg} or {@link #matchAllArgs(String[],int,int)
+ * matchAllArgs}. If there was no unmatched argument,
+ * <code>null</code> is returned.
+ *
+ * @return unmatched argument
+ */
+ public String getUnmatchedArgument()
+ {
+ return unmatchedArg;
+ }
+}
- // get the help message, if any
- if (!scanner.atEnd()) {
- if (scanner.getc() != '#') {
- throw new IllegalArgumentException(
- "Illegal character(s), expecting '#'");
- }
- String helpInfo = scanner.substring(scanner.getIndex());
- // look for second '#'. If there is one, then info
- // between the first and second '#' is the value descriptor.
- int k = helpInfo.indexOf("#");
- if (k != -1) {
- rec.valueDesc = helpInfo.substring(0, k);
- rec.helpMsg = helpInfo.substring(k + 1);
- } else {
- rec.helpMsg = helpInfo;
- }
- } else {
- rec.helpMsg = "";
- }
-
- // parse helpMsg for required/optional information if present
- // default to required
- if (rec.helpMsg.indexOf("(optional") != -1) {
- rec.required = false;
- }
-
- // add option information to match list
- if (rec.convertCode == 'h' && firstHelpOption == defaultHelpOption) {
- matchList.remove(defaultHelpOption);
- firstHelpOption = rec;
- }
- matchList.add(rec);
- }
-
- Record lastMatchRecord() {
- return (Record) matchList.lastElement();
- }
-
- private Record getRecord(String arg, ObjectHolder ndescHolder) {
- NameDesc ndesc;
- for (int i = 0; i < matchList.size(); i++) {
- Record rec = (Record) matchList.get(i);
- for (ndesc = rec.nameList; ndesc != null; ndesc = ndesc.next) {
- if (rec.convertCode != 'v' && ndesc.oneWord) {
- if (arg.startsWith(ndesc.name)) {
- if (ndescHolder != null) {
- ndescHolder.value = ndesc;
- }
- return rec;
- }
- } else {
- if (arg.equals(ndesc.name)) {
- if (ndescHolder != null) {
- ndescHolder.value = ndesc;
- }
- return rec;
- }
- }
- }
- }
- return null;
- }
-
- public void checkRequiredArgs() {
- for (int i = 1; i < matchList.size(); i++) {
- Record rec = (Record) matchList.get(i);
- StringHolder myString = (StringHolder) rec.resHolder;
- if (((myString.value == null) || (myString.value.equals("")))
- && (rec.required)) {
- printErrorAndExit("Required parameter " + rec.nameList.name
- + " is not specified.");
- }
- }
- }
-
- Object getResultHolder(String arg) {
- Record rec = getRecord(arg, null);
- return (rec != null) ? rec.resHolder : null;
- }
-
- String getOptionName(String arg) {
- ObjectHolder ndescHolder = new ObjectHolder();
- Record rec = getRecord(arg, ndescHolder);
- return (rec != null) ? ((NameDesc) ndescHolder.value).name : null;
- }
-
- String getOptionRangeDesc(String arg) {
- Record rec = getRecord(arg, null);
- return (rec != null) ? rec.rangeDesc : null;
- }
-
- String getOptionTypeName(String arg) {
- Record rec = getRecord(arg, null);
- return (rec != null) ? rec.valTypeName() : null;
- }
-
- private Object createResultHolder(Record rec) {
- if (rec.numValues == 1) {
- switch (rec.type) {
- case Record.LONG: {
- return new LongHolder();
- }
- case Record.CHAR: {
- return new CharHolder();
- }
- case Record.BOOLEAN: {
- return new BooleanHolder();
- }
- case Record.DOUBLE: {
- return new DoubleHolder();
- }
- case Record.STRING: {
- return new StringHolder();
- }
- }
- } else {
- switch (rec.type) {
- case Record.LONG: {
- return new long[rec.numValues];
- }
- case Record.CHAR: {
- return new char[rec.numValues];
- }
- case Record.BOOLEAN: {
- return new boolean[rec.numValues];
- }
- case Record.DOUBLE: {
- return new double[rec.numValues];
- }
- case Record.STRING: {
- return new String[rec.numValues];
- }
- }
- }
- return null; // can't happen
- }
-
- static void stringToArgs(Vector<String> vec, String s,
- boolean allowQuotedStrings) throws StringScanException {
- StringScanner scanner = new StringScanner(s);
- scanner.skipWhiteSpace();
- while (!scanner.atEnd()) {
- if (allowQuotedStrings) {
- vec.add(scanner.scanString());
- } else {
- vec.add(scanner.scanNonWhiteSpaceString());
- }
- scanner.skipWhiteSpace();
- }
- }
-
- /**
- * 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
- * <code>"</code>. The character <code>#</code> acts as a comment character,
- * causing input to the end of the current line to be ignored.
- *
- * @param reader Reader from which to read the strings
- * @param args Initial set of argument values. Can be specified as
- * <code>null</code>.
- * @throws IOException if an error occured while reading.
- */
- public static String[] prependArgs(Reader reader, String[] args)
- throws IOException {
- if (args == null) {
- args = new String[0];
- }
- LineNumberReader lineReader = new LineNumberReader(reader);
- Vector<String> vec = new Vector<String>(100, 100);
- String line;
- int i, k;
-
- while ((line = lineReader.readLine()) != null) {
- int commentIdx = line.indexOf("#");
- if (commentIdx != -1) {
- line = line.substring(0, commentIdx);
- }
- try {
- stringToArgs(vec, line, /* allowQuotedStings= */true);
- } catch (StringScanException e) {
- throw new IOException("malformed string, line "
- + lineReader.getLineNumber());
- }
- }
- String[] result = new String[vec.size() + args.length];
- for (i = 0; i < vec.size(); i++) {
- result[i] = (String) vec.get(i);
- }
- for (k = 0; k < args.length; k++) {
- result[i++] = args[k];
- }
- return result;
- }
-
- /**
- * 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
- * <code>"</code>. The character <code>#</code> acts as a comment character,
- * causing input to the end of the current line to be ignored.
- *
- * @param file File to be read
- * @param args Initial set of argument values. Can be specified as
- * <code>null</code>.
- * @throws IOException if an error occured while reading the file.
- */
- public static String[] prependArgs(File file, String[] args)
- throws IOException {
- if (args == null) {
- args = new String[0];
- }
- if (!file.canRead()) {
- return args;
- }
- try {
- return prependArgs(new FileReader(file), args);
- } catch (IOException e) {
- throw new IOException("File " + file.getName() + ": "
- + e.getMessage());
- }
- }
-
- /**
- * Sets the parser's error message.
- *
- * @param s Error message
- */
- protected void setError(String msg) {
- errMsg = msg;
- }
-
- /**
- * Prints an error message, along with a pointer to help options, if
- * available, and causes the program to exit with code 1.
- */
- public void printErrorAndExit(String msg) {
- if (helpOptionsEnabled && firstHelpOptionName() != null) {
- msg += "\nUse " + firstHelpOptionName() + " for help information";
- }
- if (printStream != null) {
- printStream.println(msg);
- }
- System.exit(1);
- }
-
- /**
- * Matches arguments within an argument list.
- *
- * <p>
- * In the event of an erroneous or unmatched argument, the method prints a
- * message and exits the program with code 1.
- *
- * <p>
- * If help options are enabled and one of the arguments matches a help
- * option, then the result of {@link #getHelpMessage getHelpMessage} is
- * printed to the default print stream and the program exits with code 0. If
- * help options are not enabled, they are ignored.
- *
- * @param args argument list
- * @see ArgParser#getDefaultPrintStream
- */
- public void matchAllArgs(String[] args) {
- matchAllArgs(args, 0, EXIT_ON_UNMATCHED | EXIT_ON_ERROR);
- }
-
- /**
- * Matches arguments within an argument list and returns those which were
- * not matched. The matching starts at a location in <code>args</code>
- * specified by <code>idx</code>, and unmatched arguments are returned in a
- * String array.
- *
- * <p>
- * In the event of an erroneous argument, the method either prints a message
- * and exits the program (if {@link #EXIT_ON_ERROR} is set in
- * <code>exitFlags</code>) or terminates the matching and creates a error
- * message that can be retrieved by {@link #getErrorMessage}.
- *
- * <p>
- * In the event of an umatched argument, the method will print a message and
- * exit if {@link #EXIT_ON_UNMATCHED} is set in <code>errorFlags</code>.
- * Otherwise, the unmatched argument will be appended to the returned array
- * of unmatched values, and the matching will continue at the next location.
- *
- * <p>
- * If help options are enabled and one of the arguments matches a help
- * option, then the result of {@link #getHelpMessage 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.
- *
- * @param args argument list
- * @param idx starting location in list
- * @param exitFlags conditions causing the program to exit. Should be an
- * or-ed combintion of {@link #EXIT_ON_ERROR} or
- * {@link #EXIT_ON_UNMATCHED}.
- * @return array of arguments that were not matched, or <code>null</code> if
- * all arguments were successfully matched
- * @see ArgParser#getErrorMessage
- * @see ArgParser#getDefaultPrintStream
- */
- public String[] matchAllArgs(String[] args, int idx, int exitFlags) {
- Vector<String> unmatched = new Vector<String>(10);
-
- while (idx < args.length) {
- try {
- idx = matchArg(args, idx);
- if (unmatchedArg != null) {
- if ((exitFlags & EXIT_ON_UNMATCHED) != 0) {
- printErrorAndExit("Unrecognized argument: "
- + unmatchedArg);
- } else {
- unmatched.add(unmatchedArg);
- }
- }
- } catch (ArgParseException e) {
- if ((exitFlags & EXIT_ON_ERROR) != 0) {
- printErrorAndExit(e.getMessage());
- }
- break;
- }
- }
- if (unmatched.size() == 0) {
- return null;
- } else {
- return (String[]) unmatched.toArray(new String[0]);
- }
- }
-
- /**
- * 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.
- *
- * <p>
- * In the event of an erroneous argument, the method throws an
- * {@link argparser.ArgParseException ArgParseException} with an appropriate
- * error message. This error message can also be retrieved using
- * {@link #getErrorMessage getErrorMessage}.
- *
- * <p>
- * In the event of an umatched argument, the method will return idx + 1, and
- * {@link #getUnmatchedArgument getUnmatchedArgument} will return a copy of
- * the unmatched argument. If an argument is matched,
- * {@link #getUnmatchedArgument getUnmatchedArgument} will return
- * <code>null</code>.
- *
- * <p>
- * If help options are enabled and the argument matches a help option, then
- * the result of {@link #getHelpMessage 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.
- *
- * @param args argument list
- * @param idx location in list where match should start
- * @return 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 ArgParser#setDefaultPrintStream
- * @see ArgParser#getHelpOptionsEnabled
- * @see ArgParser#getErrorMessage
- * @see ArgParser#getUnmatchedArgument
- */
- @SuppressWarnings("unchecked")
- public int matchArg(String[] args, int idx) throws ArgParseException {
- unmatchedArg = null;
- setError(null);
- try {
- ObjectHolder ndescHolder = new ObjectHolder();
- Record rec = getRecord(args[idx], ndescHolder);
- if (rec == null || (rec.convertCode == 'h' && !helpOptionsEnabled)) { // didn't
- // match
- unmatchedArg = new String(args[idx]);
- return idx + 1;
- }
- NameDesc ndesc = (NameDesc) ndescHolder.value;
- Object result;
- if (rec.resHolder instanceof Vector) {
- result = createResultHolder(rec);
- } else {
- result = rec.resHolder;
- }
- if (rec.convertCode == 'h') {
- if (helpOptionsEnabled) {
- printStream.println(getHelpMessage());
- System.exit(0);
- } else {
- return idx + 1;
- }
- } else if (rec.convertCode != 'v') {
- if (ndesc.oneWord) {
- rec.scanValue(result, ndesc.name,
- args[idx].substring(ndesc.name.length()), 0);
- } else {
- if (idx + rec.numValues >= args.length) {
- throw new ArgParseException(ndesc.name, "requires "
- + rec.numValues + " value"
- + (rec.numValues > 1 ? "s" : ""));
- }
- for (int k = 0; k < rec.numValues; k++) {
- rec.scanValue(result, ndesc.name, args[++idx], k);
- }
- }
- } else {
- if (rec.resHolder instanceof BooleanHolder) {
- ((BooleanHolder) result).value = rec.vval;
- } else {
- for (int k = 0; k < rec.numValues; k++) {
- ((boolean[]) result)[k] = rec.vval;
- }
- }
- }
- if (rec.resHolder instanceof Vector) {
- ((Vector<Object>) rec.resHolder).add(result);
- }
- } catch (ArgParseException e) {
- setError(e.getMessage());
- throw e;
- }
- return idx + 1;
- }
-
- private String spaceString(int n) {
- StringBuffer sbuf = new StringBuffer(n);
- for (int i = 0; i < n; i++) {
- sbuf.append(' ');
- }
- return sbuf.toString();
- }
-
- // public String getShortHelpMessage ()
- // {
- // String s;
- // Record rec;
- // NameDesc ndesc;
- // int initialIndent = 8;
- // int col = initialIndent;
-
- // if (maxcols <= 0)
- // { maxcols = 80;
- // }
- // if (matchList.size() > 0)
- // { ps.print (spaceString(initialIndent));
- // }
- // for (int i=0; i<matchList.size(); i++)
- // { rec = (Record)matchList.get(i);
- // s = "[";
- // for (ndesc=rec.nameList; ndesc!=null; ndesc=ndesc.next)
- // { s = s + ndesc.name;
- // if (ndesc.oneWord == false)
- // { s = s + " ";
- // }
- // if (ndesc.next != null)
- // { s = s + ",";
- // }
- // }
- // if (rec.convertCode != 'v' && rec.convertCode != 'h')
- // { if (rec.valueDesc != null)
- // { s += rec.valueDesc;
- // }
- // else
- // { s = s + "<" + rec.valTypeName() + ">";
- // if (rec.numValues > 1)
- // { s += "X" + rec.numValues;
- // }
- // }
- // }
- // s = s + "]";
- // /*
- // (col+=s.length()) > (maxcols-1) => we will spill over edge.
- // we use (maxcols-1) because if we go right to the edge
- // (maxcols), we get wrap new line inserted "for us".
- // i != 0 means we print the first entry, no matter
- // how long it is. Subsequent entries are printed
- // full length anyway. */
-
- // if ((col+=s.length()) > (maxcols-1) && i != 0)
- // { col = initialIndent+s.length();
- // ps.print ("\n" + spaceString(initialIndent));
- // }
- // ps.print (s);
- // }
- // if (matchList.size() > 0)
- // { ps.print ('\n');
- // ps.flush();
- // }
- // }
-
- /**
- * Returns a string describing the allowed options in detail.
- *
- * @return help information string.
- */
- public String getHelpMessage() {
- Record rec;
- NameDesc ndesc;
- boolean hasOneWordAlias = false;
- String s;
-
- s = "Usage: " + synopsisString + "\n";
- s += "Options include:\n\n";
- for (int i = 0; i < matchList.size(); i++) {
- String optionInfo = "";
- rec = (Record) matchList.get(i);
- if (rec.convertCode == 'h' && !helpOptionsEnabled) {
- continue;
- }
- for (ndesc = rec.nameList; ndesc != null; ndesc = ndesc.next) {
- if (ndesc.oneWord) {
- hasOneWordAlias = true;
- break;
- }
- }
- for (ndesc = rec.nameList; ndesc != null; ndesc = ndesc.next) {
- optionInfo += ndesc.name;
- if (hasOneWordAlias && !ndesc.oneWord) {
- optionInfo += " ";
- }
- if (ndesc.next != null) {
- optionInfo += ",";
- }
- }
- if (!hasOneWordAlias) {
- optionInfo += " ";
- }
- if (rec.convertCode != 'v' && rec.convertCode != 'h') {
- if (rec.valueDesc != null) {
- optionInfo += rec.valueDesc;
- } else {
- if (rec.rangeDesc != null) {
- optionInfo += "<" + rec.valTypeName() + " "
- + rec.rangeDesc + ">";
- } else {
- optionInfo += "<" + rec.valTypeName() + ">";
- }
- }
- }
- if (rec.numValues > 1) {
- optionInfo += "X" + rec.numValues;
- }
- s += optionInfo;
- if (rec.helpMsg.length() > 0) {
- int pad = helpIndent - optionInfo.length();
- if (pad < 2) { // s += '\n';
- pad = helpIndent;
- }
- // s += spaceString(pad) + rec.helpMsg;
- s += spaceString(4) + rec.helpMsg;
- }
- s += '\n';
- }
- return s;
- }
-
- /**
- * Returns the parser's error message. This is automatically set whenever an
- * error is encountered in <code>matchArg</code> or
- * <code>matchAllArgs</code>, and is automatically set to <code>null</code>
- * at the beginning of these methods.
- *
- * @return error message
- */
- public String getErrorMessage() {
- return errMsg;
- }
-
- /**
- * Returns the value of an unmatched argument discovered {@link #matchArg
- * matchArg} or {@link #matchAllArgs(String[],int,int) matchAllArgs}. If
- * there was no unmatched argument, <code>null</code> is returned.
- *
- * @return unmatched argument
- */
- public String getUnmatchedArgument() {
- return unmatchedArg;
- }
-}