diff options
Diffstat (limited to 'pki/base/silent/src/com/netscape/pkisilent/argparser')
14 files changed, 4060 insertions, 4613 deletions
diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParseException.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParseException.java index c2b927ccd..3a78e5cec 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParseException.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParseException.java @@ -1,4 +1,5 @@ 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 @@ -18,37 +19,35 @@ package com.netscape.pkisilent.argparser; // --- END COPYRIGHT BLOCK --- import java.io.IOException; -/** - * Exception class used by <code>ArgParser</code> when - * command line arguments contain an error. - * - * @author John E. Lloyd, Fall 2004 - * @see ArgParser - */ -public class ArgParseException extends IOException -{ - /** +/** + * Exception class used by <code>ArgParser</code> when command line arguments + * contain an error. + * + * @author John E. Lloyd, Fall 2004 + * @see ArgParser + */ +public class ArgParseException extends IOException { + /** * */ private static final long serialVersionUID = -604960834535589460L; /** - * Creates a new ArgParseException with the given message. - * - * @param msg Exception message - */ - public ArgParseException (String msg) - { super (msg); - } + * Creates a new ArgParseException with the given message. + * + * @param msg Exception message + */ + public ArgParseException(String msg) { + super(msg); + } - /** - * Creates a new ArgParseException from the given - * argument and message. - * - * @param arg Offending argument - * @param msg Error message - */ - public ArgParseException (String arg, String msg) - { super (arg + ": " + msg); - } + /** + * Creates a new ArgParseException from the given argument and message. + * + * @param arg Offending argument + * @param msg Error message + */ + public ArgParseException(String arg, String msg) { + super(arg + ": " + msg); + } } 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 a205d1012..e396d6f5e 100755 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParser.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParser.java @@ -1,4 +1,5 @@ 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 @@ -18,17 +19,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; @@ -40,191 +41,200 @@ 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 ... * - * DoubleHolder theta = new DoubleHolder(); - * StringHolder fileName = new StringHolder(); - * BooleanHolder debug = new BooleanHolder(); + * static public void main(String[] args) { + * // create holder objects for storing results ... * - * // create the parser and specify the allowed options ... + * DoubleHolder theta = new DoubleHolder(); + * StringHolder fileName = new StringHolder(); + * BooleanHolder debug = new BooleanHolder(); * - * 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 ... + * // create the parser and specify the allowed options ... * - * 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); - * } + * ArgParser parser = new ArgParser("java argparser.SimpleExample"); + * parser.addOption("-theta %f #theta value (in degrees)", theta); + * parser.addOption("-file %s #name of the operating file", fileName); + * parser.addOption("-debug %v #enables display of debugging info", debug); + * + * // match the arguments ... + * + * parser.matchAllArgs(args); + * + * // and print out the values + * + * System.out.println("theta=" + theta.value); + * System.out.println("fileName=" + fileName.value); + * System.out.println("debug=" + debug.value); + * } * </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 ("-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); + * 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 ("-v,--verbose %v #print lots of info"); - * parser.addOption ("-of,-outfile,-outputFile %s #output file"); + * 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 ("-file %s #file name"); + * 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 ("-file=%s #file name"); + * 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 ("-nb=,-number ,-n%d #number of blocks"); + * 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 ("-position %fX3 #position of the object", 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> @@ -232,52 +242,53 @@ 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 ("-foo %f", 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 @@ -291,48 +302,47 @@ 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 ("-theta %f #NUMBER#theta value (in degrees)",theta); + * parser.addOption("-theta %f #NUMBER#theta value (in degrees)", theta); * </pre> + * * instead of + * * <pre> - * parser.addOption ("-theta %f #theta value (in degrees)", 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); @@ -342,12 +352,13 @@ 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; @@ -365,1936 +376,1793 @@ 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; - } - } - 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>-?</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."); +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"); } - } - - - 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; - } -} + // 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>-?</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."); + } + } + } + + 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; + } +} diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParserTest.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParserTest.java index 579f0f59a..cf01f989a 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParserTest.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/ArgParserTest.java @@ -1,4 +1,5 @@ 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 @@ -18,17 +19,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.ByteArrayOutputStream; import java.io.PrintStream; @@ -36,1537 +37,1246 @@ import java.lang.reflect.Array; import java.util.Vector; /** - * Testing class for the class ArgParser. Executing the <code>main</code> - * method of this class will perform a suite of tests to help verify correct - * operation of the parser class. - * + * Testing class for the class ArgParser. Executing the <code>main</code> method + * of this class will perform a suite of tests to help verify correct operation + * of the parser class. + * * @author John E. Lloyd, Fall 2004 * @see ArgParser */ -public class ArgParserTest -{ - ArgParser parser; - - static final boolean CLOSED = true; - static final boolean OPEN = false; - - static final boolean ONE_WORD = true; - static final boolean MULTI_WORD = false; - - private static void verify (boolean ok, String msg) - { if (!ok) - { Throwable e = new Throwable(); - System.out.println ("Verification failed:" + msg); - e.printStackTrace(); - System.exit(1); - } - } - - private static String[] argsFromString (String s) - { - Vector<String> vec = new Vector<String>(100); - try - { ArgParser.stringToArgs (vec, s, /*allowQuotedStings=*/false); - } - catch (StringScanException e) - { e.printStackTrace(); - System.exit (1); - } - String[] result = new String[vec.size()]; - for (int i=0; i<vec.size(); i++) - { result[i] = (String)vec.get(i); - } - return result; - } - - static class RngCheck - { - ArgParser.RangePnt low = null; - ArgParser.RangePnt high = null; - int type; - - RngCheck (String s) - { low = new ArgParser.RangePnt (s, CLOSED); - type = 's'; - } - - RngCheck (double d) - { low = new ArgParser.RangePnt (d, CLOSED); - type = 'd'; - } - - RngCheck (long l) - { low = new ArgParser.RangePnt (l, CLOSED); - type = 'l'; - } - - RngCheck (boolean b) - { low = new ArgParser.RangePnt (b, CLOSED); - type = 'b'; - } - - RngCheck (String s1, boolean c1, String s2, boolean c2) - { low = new ArgParser.RangePnt (s1, c1); - high = new ArgParser.RangePnt (s2, c2); - type = 's'; - } - - RngCheck (double d1, boolean c1, double d2, boolean c2) - { low = new ArgParser.RangePnt (d1, c1); - high = new ArgParser.RangePnt (d2, c2); - type = 'd'; - } - - RngCheck (long l1, boolean c1, long l2, boolean c2) - { low = new ArgParser.RangePnt (l1, c1); - high = new ArgParser.RangePnt (l2, c2); - type = 'l'; - } - - void check (ArgParser.RangeAtom ra) - { - verify ((ra.low==null) == (low==null), - "(ra.low==null)=" + (ra.low==null) + - "(low==null)=" + (low==null)); - verify ((ra.high==null) == (high==null), - "(ra.high==null)=" + (ra.high==null) + - "(high==null)=" + (high==null)); - - if (ra.low != null) - { switch (type) - { case 'l': - { verify (ra.low.lval==low.lval, - "ra.low=" + ra.low + " low=" + low); - break; - } - case 'd': - { verify (ra.low.dval==low.dval, - "ra.low=" + ra.low + " low=" + low); - break; - } - case 's': - { verify (ra.low.sval.equals (low.sval), - "ra.low=" + ra.low + " low=" + low); - break; - } - case 'b': - { verify (ra.low.bval==low.bval, - "ra.low=" + ra.low + " low=" + low); - break; - } - } - verify (ra.low.closed==low.closed, - "ra.low=" + ra.low + " low=" + low); - } - if (ra.high != null) - { switch (type) - { case 'l': - { verify (ra.high.lval==high.lval, - "ra.high=" + ra.high + " high=" + high); - break; - } - case 'd': - { verify (ra.high.dval==high.dval, - "ra.high=" + ra.high + " high=" + high); - break; - } - case 's': - { verify (ra.high.sval.equals (high.sval), - "ra.high=" + ra.high + " high=" + high); - break; - } - case 'b': - { verify (ra.high.bval==high.bval, - "ra.high=" + ra.high + " high=" + high); - break; - } - } - verify (ra.high.closed==high.closed, - "ra.high=" + ra.high + " high=" + high); - } - } - } - - ArgParserTest () - { parser = new ArgParser("fubar"); - } - - static void checkException (Exception e, String errmsg) - { if (errmsg != null) - { if (!e.getMessage().equals(errmsg)) - { System.out.println ( -"Expecting exception '" + errmsg + "' but got '" + - e.getMessage() + "'"); - e.printStackTrace(); - (new Throwable()).printStackTrace(); - System.exit(1); - } - } - else - { System.out.println ( -"Unexpected exception '" + e.getMessage() + "'"); - e.printStackTrace(); - (new Throwable()).printStackTrace(); - System.exit(1); - } - } - - void checkPrintHelp (String msg) - { - ByteArrayOutputStream buf = new ByteArrayOutputStream(0x10000); - PrintStream ps = new PrintStream(buf); - ps.println (parser.getHelpMessage()); - System.out.print (buf.toString()); - } - -// void checkGetSynopsis (String msg) -// { -// ByteArrayOutputStream buf = new ByteArrayOutputStream(0x10000); -// PrintStream ps = new PrintStream(buf); -// parser.printSynopsis (ps, 80); -// System.out.print (buf.toString()); -// } - - void checkAdd (String s, Object resHolder, String errmsg) - { - checkAdd (s, resHolder, 0, 0, null, null, null, errmsg); - } - - void add (String s, Object resHolder) - { try - { parser.addOption (s, resHolder); - } - catch (Exception e) - { e.printStackTrace(); - System.exit (1); - } - } - - void checkStringArray (String msg, String[] strs, String[] check) - { - boolean dontMatch = false; - if (strs.length != check.length) - { dontMatch = true; - } - else - { for (int i=0; i<strs.length; i++) - { if (!strs[i].equals (check[i])) - { dontMatch = true; - break; - } - } - } - if (dontMatch) - { System.out.println (msg); - System.out.print ("Expected: "); - for (int i=0; i<check.length; i++) - { System.out.print ("'" + check[i] + "'"); - if (i<check.length-1) - { System.out.print (" "); - } - } - System.out.println (""); - System.out.print ("Got: "); - for (int i=0; i<strs.length; i++) - { System.out.print ("'" + strs[i] + "'"); - if (i<strs.length-1) - { System.out.print (" "); - } - } - System.out.println (""); - System.exit(1); - } - } - - void checkAdd (String s, Object resHolder, int code, int numValues, - Object names, RngCheck[] rngCheck, - String helpMsg, String errmsg) - { - boolean exceptionThrown = false; - String[] namelist = null; - try - { parser.addOption (s, resHolder); - } - catch (Exception e) - { exceptionThrown = true; - checkException (e, errmsg); - } - if (names instanceof String) - { namelist = new String[] { (String)names }; - } - else - { namelist = (String[])names; - } - if (!exceptionThrown) - { verify (errmsg == null, - "Expecting exception " + errmsg); - ArgParser.Record rec = parser.lastMatchRecord(); - verify (rec.convertCode==code, - "code=" + rec.convertCode + ", expecting " + code); - ArgParser.NameDesc nd; - int i=0; - for (nd=rec.firstNameDesc(); nd!=null; nd=nd.next) - { i++; - } - verify (i==namelist.length, - "numNames=" + i + ", expecting " +namelist.length); - i=0; - for (nd=rec.firstNameDesc(); nd!=null; nd=nd.next) - { String ss; - if (!nd.oneWord) - { ss = new String(nd.name) + ' '; - } - else - { ss = nd.name; - } - verify (ss.equals(namelist[i]), - "have name '"+ss+"', expecting '"+namelist[i]+"'"); - i++; - } - ArgParser.RangeAtom ra; - i=0; - for (ra=rec.firstRangeAtom(); ra!=null; ra=ra.next) - { i++; - } - int expectedRangeNum = 0; - if (rngCheck!=null) - { expectedRangeNum = rngCheck.length; - } - verify (i==expectedRangeNum, - "numRangeAtoms="+i+", expecting "+expectedRangeNum); - i=0; - for (ra=rec.firstRangeAtom(); ra!=null; ra=ra.next) - { rngCheck[i++].check(ra); - } - verify (rec.helpMsg.equals(helpMsg), - "helpMsg="+rec.helpMsg+", expecting "+helpMsg); - verify (rec.numValues==numValues, - "numValues="+rec.numValues+", expecting "+numValues); - } - } - - double getDoubleValue (Object obj, int k) - { - if (obj instanceof DoubleHolder) - { return ((DoubleHolder)obj).value; - } - else if (obj instanceof FloatHolder) - { return ((FloatHolder)obj).value; - } - else if (obj instanceof double[]) - { return ((double[])obj)[k]; - } - else if (obj instanceof float[]) - { return ((float[])obj)[k]; - } - else - { verify (false, "object doesn't contain double values"); - return 0; - } - } - - long getLongValue (Object obj, int k) - { - if (obj instanceof LongHolder) - { return ((LongHolder)obj).value; - } - else if (obj instanceof IntHolder) - { return ((IntHolder)obj).value; - } - else if (obj instanceof long[]) - { return ((long[])obj)[k]; - } - else if (obj instanceof int[]) - { return ((int[])obj)[k]; - } - else - { verify (false, "object doesn't contain long values"); - return 0; - } - } - - String getStringValue (Object obj, int k) - { - if (obj instanceof StringHolder) - { return ((StringHolder)obj).value; - } - else if (obj instanceof String[]) - { return ((String[])obj)[k]; - } - else - { verify (false, "object doesn't contain String values"); - return null; - } - } - - boolean getBooleanValue (Object obj, int k) - { - if (obj instanceof BooleanHolder) - { return ((BooleanHolder)obj).value; - } - else if (obj instanceof boolean[]) - { return ((boolean[])obj)[k]; - } - else - { verify (false, "object doesn't contain boolean values"); - return false; - } - } - - char getCharValue (Object obj, int k) - { - if (obj instanceof CharHolder) - { return ((CharHolder)obj).value; - } - else if (obj instanceof char[]) - { return ((char[])obj)[k]; - } - else - { verify (false, "object doesn't contain char values"); - return 0; - } - } - - static class MErr - { - int code; - String valStr; - - MErr (int code, String valStr) - { this.code = code; - this.valStr = valStr; - } - } - - static class MTest - { - String args; - Object result; - int resultIdx; - - MTest (String args, Object result) - { this (args, result, -1); - } - - MTest (String args, Object result, int resultIdx) - { this.args = args; - this.result = result; - this.resultIdx = resultIdx; - } - }; - - void checkMatch (String args[], int idx, String errMsg) - { getMatchResult (args, idx, -1, errMsg, -1); - } - - void checkMatch (String args[], int idx, int cnt, - long check, int resultIdx) - { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx); - long result = getLongValue(rholder,0); - verify (result==check, "result " + result + " vs. " + check); - } - - void checkMatch (String args[], int idx, int cnt, - double check, int resultIdx) - { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx); - double result = getDoubleValue(rholder,0); - verify (result==check, "result " + result + " vs. " + check); - } - - void checkMatch (String args[], int idx, int cnt, - String check, int resultIdx) - { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx); - String result = getStringValue(rholder,0); - verify (result.equals(check), "result " + result + " vs. " + check); - } - - void checkMatch (String args[], int idx, int cnt, - boolean check, int resultIdx) - { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx); - boolean result = getBooleanValue(rholder,0); - verify (result==check, "result " + result + " vs. " + check); - } - - void checkMatch (String args[], int idx, int cnt, - char check, int resultIdx) - { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx); - char result = getCharValue(rholder,0); - verify (result==check, "result " + result + " vs. " + check); - } - - void checkMatch (String args[], int idx, int cnt, - Object checkArray, int resultIdx) - { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx); - if (!checkArray.getClass().isArray()) - { verify (false, "check is not an array"); - } - for (int i=0; i<Array.getLength(checkArray); i++) - { if (checkArray instanceof long[]) - { long result = getLongValue(rholder,i); - long check = ((long[])checkArray)[i]; - verify (result==check, - "result ["+i+"] " + result + " vs. " + check); - } - else if (checkArray instanceof double[]) - { double result = getDoubleValue(rholder,i); - double check = ((double[])checkArray)[i]; - verify (result==check, - "result ["+i+"] " + result + " vs. " + check); - } - else if (checkArray instanceof String[]) - { String result = getStringValue(rholder,i); - String check = ((String[])checkArray)[i]; - verify (result.equals(check), - "result ["+i+"] " + result + " vs. " + check); - } - else if (checkArray instanceof boolean[]) - { boolean result = getBooleanValue(rholder,i); - boolean check = ((boolean[])checkArray)[i]; - verify (result==check, - "result ["+i+"] " + result + " vs. " + check); - } - else if (checkArray instanceof char[]) - { char result = getCharValue(rholder,i); - char check = ((char[])checkArray)[i]; - verify (result==check, - "result ["+i+"] " + result + " vs. " + check); - } - else - { verify (false, "unknown type for checkArray"); - } - } - } - - void checkMatch (MTest test, boolean oneWord) - { String[] argv; - if (oneWord) - { argv = new String[1]; - argv[0] = test.args; - } - else - { argv = argsFromString(test.args); - } - if (test.result instanceof Long) - { checkMatch (argv, 0, argv.length, - ((Long)test.result).longValue(), - test.resultIdx); - } - else if (test.result instanceof Double) - { checkMatch (argv, 0, argv.length, - ((Double)test.result).doubleValue(), - test.resultIdx); - } - else if (test.result instanceof String) - { checkMatch (argv, 0, argv.length, - (String)test.result, - test.resultIdx); - } - else if (test.result instanceof Boolean) - { checkMatch (argv, 0, argv.length, - ((Boolean)test.result).booleanValue(), - test.resultIdx); - } - else if (test.result instanceof Character) - { checkMatch (argv, 0, argv.length, - ((Character)test.result).charValue(), - test.resultIdx); - } - else if (test.result.getClass().isArray()) - { checkMatch (argv, 0, argv.length, test.result, - test.resultIdx); - } - else if (test.result instanceof MErr) - { MErr err = (MErr)test.result; - String argname = parser.getOptionName (argv[0]); - String msg = ""; - - switch (err.code) - { case 'c': - { msg = "requires a contiguous value"; - break; - } - case 'm': - { msg = "malformed " + parser.getOptionTypeName(argv[0]) + - " '" + err.valStr + "'"; - break; - } - case 'r': - { msg = "value '" + err.valStr + "' not in range " + - parser.getOptionRangeDesc(argv[0]); - break; - } - case 'v': - { msg = "requires " + err.valStr + " values"; - break; - } - } - checkMatch (argv, 0, argname + ": " + msg); - } - else - { verify (false, "Unknown result type"); - } - } - - void checkMatches (MTest[] tests, boolean oneWord) - { for (int i=0; i<tests.length; i++) - { checkMatch (tests[i], oneWord); - } - } - - Object getMatchResult (String args[], int idx, int cnt, - String errMsg, int resultIdx) - { - boolean exceptionThrown = false; - int k = 0; - try - { k = parser.matchArg (args, idx); - } - catch (Exception e) - { exceptionThrown = true; - checkException (e, errMsg); - } - if (!exceptionThrown) - { verify (k==idx+cnt, - "Expecting result index " + (idx+cnt) + ", got " + k); - Object result = parser.getResultHolder(args[0]); - if (resultIdx >= 0) - { verify (result instanceof Vector, - "Expecting result to be stored in a vector"); - Vector<?> vec = (Vector<?>)result; - verify (vec.size()==resultIdx+1, - "Expecting result vector size " + (resultIdx+1)); - return vec.get(resultIdx); - } - else - { return result; - } - } - else - { return null; - } - } - - /** - * Runs a set of tests to verify correct operation of the - * ArgParser class. If all the tests run correctly, the - * program prints the message <code>Passed</code> and terminates. - * Otherwise, diagnostic information is printed at the first - * point of failure. - */ - public static void main (String[] args) - { - ArgParserTest test = new ArgParserTest(); - - BooleanHolder bh = new BooleanHolder(); - boolean[] b3 = new boolean[3]; - CharHolder ch = new CharHolder(); - char[] c3 = new char[3]; - IntHolder ih = new IntHolder(); - int[] i3 = new int[3]; - LongHolder lh = new LongHolder(); - long[] l3 = new long[3]; - FloatHolder fh = new FloatHolder(); - float[] f3 = new float[3]; - DoubleHolder dh = new DoubleHolder(); - double[] d3 = new double[3]; - StringHolder sh = new StringHolder(); - String[] s3 = new String[3]; - - test.checkAdd ("-foo %i{[0,10)}X3 #sets the value of foo", -// 0123456789012345 - i3, 'i', 3, new String[] { "-foo " }, - new RngCheck[] { - new RngCheck(0, CLOSED, 10, OPEN) }, - "sets the value of foo", null); - - test.checkAdd ("-arg1,,", null, "Null option name given"); - test.checkAdd ("-arg1,,goo %f ", null, "Null option name given"); - test.checkAdd (" ", null, "Null option name given"); - test.checkAdd ("", null, "Null option name given"); - test.checkAdd (" %v", null, "Null option name given"); - test.checkAdd ("-foo ", null, "No conversion character given"); - test.checkAdd ("-foo %", null, "No conversion character given"); - test.checkAdd ("foo, aaa bbb ",null,"Names not separated by ','"); - test.checkAdd (" foo aaa %d", null, "Names not separated by ','"); - test.checkAdd ("-arg1,-b,", null, "Null option name given"); - test.checkAdd ("-arg1,-b", null, "No conversion character given"); - test.checkAdd ("-arg1 ", null, "No conversion character given"); - test.checkAdd ("-arg1, %v", null, "Null option name given"); - test.checkAdd ("-arg1,%v", null, "Null option name given"); - test.checkAdd ("-foo %V", null, - "Conversion code 'V' not one of 'iodxcbfsvh'"); - test.checkAdd ("-h %hX5",null,"Multipliers not supported for %h"); - test.checkAdd ("-h %h{}",null,"Ranges not supported for %h"); - test.checkAdd ("-help, -h %h #here is how we help you", - null, 'h', 1, new String[] {"-help ", "-h " }, - null, "here is how we help you", null); - - test.checkAdd ( - "-arg1 ,-arg2=%d{0,3,(7,16]}X1 #x3 test", - l3, 'd', 1, new String[] { "-arg1 ", "-arg2=" }, - new RngCheck[] - { new RngCheck(0), - new RngCheck(3), - new RngCheck(7, OPEN, 16, CLOSED), - }, - "x3 test", null); - - test.checkAdd ( - "bbb,ccc%x{[1,2]} #X3 x3 test", - l3, 'x', 1, new String[] { "bbb", "ccc" }, - new RngCheck[] - { new RngCheck(1, CLOSED, 2, CLOSED), - }, - "X3 x3 test", null); - - test.checkAdd ( - " bbb ,ccc, ddd ,e , f=%bX1 #x3 test", - b3, 'b', 1, new String[] { "bbb ", "ccc", "ddd ", "e ", "f=" }, - null, - "x3 test", null); - - test.checkAdd ( - " bbb ,ccc, ddd ,e , f= %bX3 #x3 test", - b3, 'b', 3, new String[] { "bbb ", "ccc ", "ddd ", "e ","f= " }, - null, - "x3 test", null); - - test.checkAdd ( - "-b,--bar %s{[\"john\",\"jerry\"),fred,\"harry\"} #sets bar", - sh, 's', 1, new String[] { "-b ", "--bar " }, - new RngCheck[] { - new RngCheck("jerry",OPEN,"john",CLOSED), - new RngCheck("fred"), - new RngCheck("harry") }, - "sets bar", null); - - test.checkAdd ( - "-c ,coven%f{0.0,9.0,(6,5],[-9.1,10.2]} ", - dh, 'f', 1, new String[] { "-c ", "coven" }, - new RngCheck[] { - new RngCheck(0.0), - new RngCheck(9.0), - new RngCheck(5.0,CLOSED,6.0,OPEN), - new RngCheck(-9.1,CLOSED,10.2,CLOSED) }, - "", null); - - test.checkAdd ( - "-b %b #a boolean value ", - bh, 'b', 1, new String[] { "-b "}, - new RngCheck[] { }, - "a boolean value ", null); - - test.checkAdd ("-a %i", ih, 'i', 1, "-a ", null, "", null); - test.checkAdd ("-a %o", lh, 'o', 1, "-a ", null, "", null); - test.checkAdd ("-a %d", i3, 'd', 1, "-a ", null, "", null); - test.checkAdd ("-a %x", l3, 'x', 1, "-a ", null, "", null); - test.checkAdd ("-a %c", ch, 'c', 1, "-a ", null, "", null); - test.checkAdd ("-a %c", c3, 'c', 1, "-a ", null, "", null); - test.checkAdd ("-a %v", bh, 'v', 1, "-a ", null, "", null); - test.checkAdd ("-a %b", b3, 'b', 1, "-a ", null, "", null); - test.checkAdd ("-a %f", fh, 'f', 1, "-a ", null, "", null); - test.checkAdd ("-a %f", f3, 'f', 1, "-a ", null, "", null); - test.checkAdd ("-a %f", dh, 'f', 1, "-a ", null, "", null); - test.checkAdd ("-a %f", d3, 'f', 1, "-a ", null, "", null); - - test.checkAdd ("-a %i", fh, 'i', 1, "-a ", null, "", - "Invalid result holder for %i"); - test.checkAdd ("-a %c", i3, 'c', 1, "-a ", null, "", - "Invalid result holder for %c"); - test.checkAdd ("-a %v", d3, 'v', 1, "-a ", null, "", - "Invalid result holder for %v"); - test.checkAdd ("-a %f", sh, 'f', 1, "-a ", null, "", - "Invalid result holder for %f"); - test.checkAdd ("-a %s", l3, 's', 1, "-a ", null, "", - "Invalid result holder for %s"); - - test.checkAdd ("-foo %i{} ", ih, 'i', 1, "-foo ", null, "", null); - test.checkAdd ("-foo%i{}", ih, 'i', 1, "-foo", null, "", null); - test.checkAdd ("-foo%i{ }", ih, 'i', 1, "-foo", null, "", null); - test.checkAdd ("-foo%i{ }}", ih, - "Illegal character(s), expecting '#'"); - test.checkAdd ("-foo%i{ ", ih,"Unterminated range specification"); - test.checkAdd ("-foo%i{", ih, "Unterminated range specification"); - test.checkAdd ("-foo%i{0,9", ih, "Unterminated range specification"); - test.checkAdd ("-foo%i{1,2,3)", ih, - "Unterminated range specification"); - - test.checkAdd ("-b %f{0.9}", fh, 'f', 1, "-b ", - new RngCheck[] { new RngCheck(0.9) }, - "", null); - test.checkAdd ("-b %f{ 0.9 ,7, -0.5,-4 ,6 }", fh, 'f', 1, "-b ", - new RngCheck[] { new RngCheck(0.9), - new RngCheck(7.0), - new RngCheck(-0.5), - new RngCheck(-4.0), - new RngCheck(6.0) }, - "", null); - test.checkAdd ("-b %f{ [0.9,7), (-0.5,-4),[9,6] , (10,13.4] }", - fh, 'f', 1, "-b ", - new RngCheck[] { new RngCheck(0.9,CLOSED,7.0,OPEN), - new RngCheck(-4.0,OPEN,-.5,OPEN), - new RngCheck(6.0,CLOSED,9.0,CLOSED), - new RngCheck(10.0,OPEN,13.4,CLOSED), - }, - "", null); - test.checkAdd ("-b %f{(8 9]}", fh, - "Missing ',' in subrange specification"); - test.checkAdd ("-b %f{(8,9,]}", fh, - "Unterminated subrange"); - test.checkAdd ("-b %f{(8,9 ,]}", fh, - "Unterminated subrange"); - test.checkAdd ("-b %f{(8,9 8]}", fh, - "Unterminated subrange"); - test.checkAdd ("-b %f{8 9}", fh, - "Range spec: ',' or '}' expected"); - test.checkAdd ("-b %f{8 *}", fh, - "Range spec: ',' or '}' expected"); - - test.checkAdd ("-b %f{8y}", fh, - "Range spec: ',' or '}' expected"); - test.checkAdd ("-b %f{.}", fh, - "Malformed float '.}' in range spec"); - test.checkAdd ("-b %f{1.0e}", fh, - "Malformed float '1.0e}' in range spec"); - test.checkAdd ("-b %f{[*]}", fh, - "Malformed float '*' in range spec"); - test.checkAdd ("-b %f{1.2e5t}", fh, - "Range spec: ',' or '}' expected"); - - - test.checkAdd ("-b %i{8}", ih, 'i', 1, "-b ", - new RngCheck[] { new RngCheck(8) }, - "", null); - test.checkAdd ("-b %i{8, 9,10 }", ih, 'i', 1, "-b ", - new RngCheck[] { new RngCheck(8), - new RngCheck(9), - new RngCheck(10) }, - "", null); - test.checkAdd ("-b %i{8, [-9,10),[-17,15],(2,-33),(8,9] }", - ih, 'i', 1, "-b ", - new RngCheck[] { new RngCheck(8), - new RngCheck(-9,CLOSED,10,OPEN), - new RngCheck(-17,CLOSED,15,CLOSED), - new RngCheck(-33,OPEN,2,OPEN), - new RngCheck(8,OPEN,9,CLOSED), - }, - "", null); - test.checkAdd ("-b %i{8.7}", ih, - "Range spec: ',' or '}' expected"); - test.checkAdd ("-b %i{6,[*]}", ih, - "Malformed integer '*' in range spec"); - test.checkAdd ("-b %i{g76}", ih, - "Malformed integer 'g' in range spec"); - - test.checkAdd ("-b %s{foobar}", sh, 's', 1, "-b ", - new RngCheck[] { new RngCheck("foobar") }, - "", null); - test.checkAdd ("-b %s{foobar, 0x233,\" \"}", sh, 's', 1, "-b ", - new RngCheck[] { new RngCheck("foobar"), - new RngCheck("0x233"), - new RngCheck(" ") }, - "", null); - test.checkAdd ("-b %s{foobar,(bb,aa], [\"01\",02]}", - sh, 's', 1, "-b ", - new RngCheck[] - { new RngCheck("foobar"), - new RngCheck("aa",CLOSED,"bb",OPEN), - new RngCheck("01",CLOSED,"02",CLOSED), - }, - "", null); - - test.checkAdd ("-b %c{'a'}", ch, 'c', 1, "-b ", - new RngCheck[] { new RngCheck('a') }, - "", null); - test.checkAdd ("-b %c{'\\n', '\\002', 'B'}", ch, 'c', 1, "-b ", - new RngCheck[] { new RngCheck('\n'), - new RngCheck('\002'), - new RngCheck('B') }, - "", null); - test.checkAdd ("-b %c{'q',('g','a'], ['\t','\\003']}", - ch, 'c', 1, "-b ", - new RngCheck[] - { new RngCheck('q'), - new RngCheck('a',CLOSED,'g',OPEN), - new RngCheck('\003',CLOSED,'\t',CLOSED), - }, - "", null); - - test.checkAdd ("-b %b{true}X2", b3, 'b', 2, "-b ", - new RngCheck[] { new RngCheck(true) }, - "", null); - test.checkAdd ("-b %b{ true , false, true }", bh, 'b', 1, "-b ", - new RngCheck[] { new RngCheck(true), - new RngCheck(false), - new RngCheck(true) }, - "", null); - test.checkAdd ("-b %v{true,[true,false)}", bh, - "Sub ranges not supported for %b or %v"); - test.checkAdd ("-b %v{true,[]}", bh, - "Sub ranges not supported for %b or %v"); - test.checkAdd ("-b %b{tru}", bh, - "Malformed boolean 'tru}' in range spec"); - - test.checkAdd ("-b %iX2", i3, 'i', 2, "-b ", null, "", null); - test.checkAdd ("-b %vX3", b3, 'v', 3, "-b ", null, "", null); - test.checkAdd ("-b %v{ }X3", b3, 'v', 3, "-b ", null, "", null); - - test.checkAdd ("-b=%iX2", i3, 'i', 2, "-b", null, "", -"Multiplier value incompatible with one word option -b="); - test.checkAdd ("-b %iX0", i3, 'i', 0, "-b ", null, "", - "Value multiplier number must be > 0"); - test.checkAdd ("-b %iX-6", i3, 'i', 0, "-b ", null, "", - "Value multiplier number must be > 0"); - test.checkAdd ("-b %iXy", i3, 'i', 0, "-b ", null, "", - "Malformed value multiplier"); - test.checkAdd ("-b %iX4", i3, 'i', 4, "-b ", null, "", - "Result holder array must have a length >= 4"); - test.checkAdd ("-b %iX4", ih, 'i', 4, "-b ", null, "", -"Multiplier requires result holder to be an array of length >= 4"); - - test.checkAdd ("-b %i #X4", ih, 'i', 1, "-b ", null, "X4", null); - test.checkAdd ("-b %i #[}X4",ih, 'i', 1, "-b ", null, "[}X4", null); - -// test.checkPrintHelp(""); -// test.checkPrintUsage(""); - - test = new ArgParserTest(); - - test.checkAdd ( - "-intarg %i{1,2,(9,18],[22,27],[33,38),(45,48)} #test int arg", - ih, 'i', 1, "-intarg ", - new RngCheck[] - { new RngCheck (1), - new RngCheck (2), - new RngCheck (9,OPEN,18,CLOSED), - new RngCheck (22,CLOSED,27,CLOSED), - new RngCheck (33,CLOSED,38,OPEN), - new RngCheck (45,OPEN,48,OPEN), - }, - "test int arg", null); - - MTest[] tests; - - tests = new MTest[] - { - new MTest("-intarg 1", new Long(1) ), - new MTest("-intarg 3", new MErr ('r', "3") ), - new MTest("-intarg 9", new MErr ('r', "9") ), - new MTest("-intarg 11", new Long(11) ), - new MTest("-intarg 18", new Long(18)), - new MTest("-intarg 22", new Long(22)), - new MTest("-intarg 25", new Long(25)), - new MTest("-intarg 27", new Long(27)), - new MTest("-intarg 33", new Long(33)), - new MTest("-intarg 35", new Long(35)), - new MTest("-intarg 38", new MErr ('r', "38") ), - new MTest("-intarg 45", new MErr ('r', "45")), - new MTest("-intarg 46", new Long(46)), - new MTest("-intarg 48", new MErr ('r', "48")), - new MTest("-intarg 100", new MErr ('r', "100")), - new MTest("-intarg 0xbeef", new MErr ('r', "0xbeef")), - new MTest("-intarg 0x2f", new Long (0x2f)), - new MTest("-intarg 041", new Long(041) ), - }; - test.checkMatches (tests, MULTI_WORD); - - test.checkAdd ( - "-farg %f{1,2,(9,18],[22,27],[33,38),(45,48)} #test float arg", - dh, 'f', 1, "-farg ", - new RngCheck[] - { - new RngCheck (1.0), - new RngCheck (2.0), - new RngCheck (9.0,OPEN,18.0,CLOSED), - new RngCheck (22.0,CLOSED,27.0,CLOSED), - new RngCheck (33.0,CLOSED,38.0,OPEN), - new RngCheck (45.0,OPEN,48.0,OPEN), - }, - "test float arg", null); - - tests = new MTest[] - { - new MTest("-farg 1", new Double(1)), - new MTest("-farg 3", new MErr('r', "3")), - new MTest("-farg 9", new MErr('r', "9")), - new MTest("-farg 9.0001", new Double(9.0001)), - new MTest("-farg 11", new Double(11)), - new MTest("-farg 18", new Double(18)), - new MTest("-farg 22", new Double(22)), - new MTest("-farg 25", new Double(25)), - new MTest("-farg 27", new Double(27)), - new MTest("-farg 33", new Double(33)), - new MTest("-farg 35", new Double(35)), - new MTest("-farg 37.9999",new Double(37.9999)), - new MTest("-farg 38", new MErr('r', "38")), - new MTest("-farg 45", new MErr('r', "45")), - new MTest("-farg 45.0001", new Double(45.0001)), - new MTest("-farg 46",new Double(46)), - new MTest("-farg 47.9999",new Double(47.9999)), - new MTest("-farg 48", new MErr('r', "48")), - new MTest("-farg 100", new MErr('r', "100")), - new MTest("-farg 0", new MErr('r', "0")), - }; - test.checkMatches (tests, MULTI_WORD); - - test.checkAdd ( - "-sarg %s{1,2,(AA,AZ],[BB,BX],[C3,C8),(d5,d8)} #test string arg", - s3, 's', 1, "-sarg ", - new RngCheck[] - { new RngCheck ("1"), - new RngCheck ("2"), - new RngCheck ("AA",OPEN,"AZ",CLOSED), - new RngCheck ("BB",CLOSED,"BX",CLOSED), - new RngCheck ("C3",CLOSED,"C8",OPEN), - new RngCheck ("d5",OPEN,"d8",OPEN), - }, - "test string arg", null); - - tests = new MTest[] - { - new MTest ("-sarg 1", "1"), - new MTest ("-sarg 3", new MErr('r',"3")), - new MTest ("-sarg AA", new MErr('r',"AA")), - new MTest ("-sarg AM", "AM"), - new MTest ("-sarg AZ", "AZ"), - new MTest ("-sarg BB", "BB"), - new MTest ("-sarg BL", "BL"), - new MTest ("-sarg BX", "BX"), - new MTest ("-sarg C3", "C3"), - new MTest ("-sarg C6", "C6"), - new MTest ("-sarg C8", new MErr('r',"C8")), - new MTest ("-sarg d5", new MErr('r',"d5")), - new MTest ("-sarg d6", "d6"), - new MTest ("-sarg d8", new MErr('r',"d8")), - new MTest ("-sarg zzz", new MErr('r',"zzz")), - new MTest ("-sarg 0", new MErr('r',"0")), - }; - test.checkMatches (tests, MULTI_WORD); - - test = new ArgParserTest(); - - test.checkAdd ( - "-carg %c{1,2,(a,z],['A','Z'],['\\001',\\007),(4,8)}", - c3, 'c', 1, "-carg ", - new RngCheck[] - { new RngCheck ('1'), - new RngCheck ('2'), - new RngCheck ('a',OPEN,'z',CLOSED), - new RngCheck ('A',CLOSED,'Z',CLOSED), - new RngCheck ('\001',CLOSED,'\007',OPEN), - new RngCheck ('4',OPEN,'8',OPEN), - }, - "", null); - - tests = new MTest[] - { - new MTest ("-carg 1", new Character('1')), - new MTest ("-carg 3", new MErr('r',"3")), - new MTest ("-carg a", new MErr('r',"a")), - new MTest ("-carg m", new Character('m')), - new MTest ("-carg z", new Character('z')), - new MTest ("-carg A", new Character('A')), - new MTest ("-carg 'L'", new Character('L')), - new MTest ("-carg 'Z'", new Character('Z')), - new MTest ("-carg \\001", new Character('\001')), - new MTest ("-carg \\005", new Character('\005')), - new MTest ("-carg '\\007'", new MErr('r',"'\\007'")), - new MTest ("-carg '4'", new MErr('r',"'4'")), - new MTest ("-carg 6", new Character('6')), - new MTest ("-carg 8", new MErr('r',"8")), - new MTest ("-carg '\\012'", new MErr('r',"'\\012'")), - new MTest ("-carg 0", new MErr('r',"0")), - }; - test.checkMatches (tests, MULTI_WORD); - - test.checkAdd ( - "-foo=%i{[-50,100]}", ih, 'i', 1, "-foo=", - new RngCheck[] - { new RngCheck (-50,CLOSED,100,CLOSED), - }, - "", null); - - tests = new MTest[] - { - new MTest ("-foo=-51", new MErr('r',"-51")), - new MTest ("-foo=-0x32", new Long(-0x32)), - new MTest ("-foo=-0x33", new MErr('r',"-0x33")), - new MTest ("-foo=-0777", new MErr('r',"-0777")), - new MTest ("-foo=-07", new Long(-07)), - new MTest ("-foo=0", new Long(0)), - new MTest ("-foo=100", new Long(100)), - new MTest ("-foo=0x5e", new Long(0x5e)), - new MTest ("-foo=066", new Long(066)), - new MTest ("-foo=06677", new MErr('r',"06677")), - new MTest ("-foo=0xbeef", new MErr('r',"0xbeef")), - new MTest ("-foo=foo", new MErr('m',"foo")), - new MTest ("-foo=-51d", new MErr('m',"-51d")), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-foo2=%i", ih, 'i', 1, "-foo2=", null, "", null); - tests = new MTest[] - { - new MTest ("-foo2=-51", new Long(-51)), - new MTest ("-foo2=-0x33", new Long(-0x33)), - new MTest ("-foo2=-0777", new Long(-0777)), - new MTest ("-foo2=06677", new Long(06677)), - new MTest ("-foo2=0xbeef", new Long(0xbeef)), - new MTest ("-foo2=foo", new MErr('m',"foo")), - new MTest ("-foo2=-51d", new MErr('m',"-51d")), - new MTest ("-foo2=-51", new Long(-51)), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-foo3 %iX3", i3, 'i', 3, "-foo3 ", null, "", null); - tests = new MTest[] - { - new MTest ("-foo3 -51 678 0x45", - new long[] { -51, 678, 0x45 }), - new MTest ("-foo3 55 16f 55", new MErr ('m', "16f")), - new MTest ("-foo3 55 16", new MErr ('v', "3")), - }; - test.checkMatches (tests, MULTI_WORD); - - Vector<String> vec = new Vector<String>(100); - - test.checkAdd ("-foov3 %iX3", vec,'i',3,"-foov3 ",null,"",null); - tests = new MTest[] - { new MTest ("-foov3 -1 2 4", new long[] {-1, 2, 4}, 0), - new MTest ("-foov3 10 3 9", new long[] {10, 3, 9}, 1), - new MTest ("-foov3 123 1 0", new long[] {123, 1, 0}, 2), - }; - vec.clear(); - test.checkMatches (tests, MULTI_WORD); - test.checkAdd ("-foov %i", vec,'i',1,"-foov ",null,"",null); - tests = new MTest[] - { new MTest ("-foov 11", new Long(11), 0), - new MTest ("-foov 12", new Long(12), 1), - new MTest ("-foov 13", new Long(13), 2), - }; - vec.clear(); - test.checkMatches (tests, MULTI_WORD); - - test.checkAdd ( - "-foo4 %i{[-50,100]}X2", i3, 'i', 2, "-foo4 ", - new RngCheck[] - { new RngCheck (-50,CLOSED,100,CLOSED), - }, - "", null); - tests = new MTest[] - { - new MTest ("-foo4 -49 78", - new long[] { -49, 78 }), - new MTest ("-foo4 -48 102", new MErr ('r', "102")), - }; - test.checkMatches (tests, MULTI_WORD); - - test.checkAdd ( - "-oct=%o{[-062,0144]}", ih, 'o', 1, "-oct=", - new RngCheck[] - { new RngCheck (-50,CLOSED,100,CLOSED), - }, - "", null); - - tests = new MTest[] - { - new MTest ("-oct=-063", new MErr('r',"-063")), - new MTest ("-oct=-0x32", new MErr('m',"-0x32")), - new MTest ("-oct=-0777", new MErr('r',"-0777")), - new MTest ("-oct=-07", new Long(-07)), - new MTest ("-oct=0", new Long(0)), - new MTest ("-oct=100", new Long(64)), - new MTest ("-oct=0xae", new MErr('m',"0xae")), - new MTest ("-oct=66", new Long(066)), - new MTest ("-oct=06677", new MErr('r',"06677")), - new MTest ("-oct=0xbeef", new MErr('m',"0xbeef")), - new MTest ("-oct=foo", new MErr('m',"foo")), - new MTest ("-oct=-51d", new MErr('m',"-51d")), - new MTest ("-oct=78", new MErr('m',"78")), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-oct2=%o", ih, 'o', 1, "-oct2=", null, "", null); - tests = new MTest[] - { - new MTest ("-oct2=-063", new Long(-063)), - new MTest ("-oct2=-0777", new Long(-0777)), - new MTest ("-oct2=06677", new Long(06677)), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ( - "-dec=%d{[-0x32,0x64]}", ih, 'd', 1, "-dec=", - new RngCheck[] - { new RngCheck (-50,CLOSED,100,CLOSED), - }, - "", null); - - tests = new MTest[] - { - new MTest ("-dec=-063", new MErr('r',"-063")), - new MTest ("-dec=-0x32", new MErr('m',"-0x32")), - new MTest ("-dec=-0777", new MErr('r',"-0777")), - new MTest ("-dec=-07", new Long(-07)), - new MTest ("-dec=0", new Long(0)), - new MTest ("-dec=100", new Long(100)), - new MTest ("-dec=0xae", new MErr('m',"0xae")), - new MTest ("-dec=66", new Long(66)), - new MTest ("-dec=06677", new MErr('r',"06677")), - new MTest ("-dec=0xbeef", new MErr('m',"0xbeef")), - new MTest ("-dec=foo", new MErr('m',"foo")), - new MTest ("-dec=-51d", new MErr('m',"-51d")), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-dec2=%d", ih, 'd', 1, "-dec2=", null, "", null); - tests = new MTest[] - { - new MTest ("-dec2=-063", new Long(-63)), - new MTest ("-dec2=-0777", new Long(-777)), - new MTest ("-dec2=06677", new Long(6677)), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ( - "-hex=%x{[-0x32,0x64]}", ih, 'x', 1, "-hex=", - new RngCheck[] - { new RngCheck (-50,CLOSED,100,CLOSED), - }, - "", null); - - tests = new MTest[] - { - new MTest ("-hex=-06", new Long(-0x6)), - new MTest ("-hex=-0x3g2", new MErr('m',"-0x3g2")), - new MTest ("-hex=-0777", new MErr('r',"-0777")), - new MTest ("-hex=-017", new Long(-0x17)), - new MTest ("-hex=0", new Long(0)), - new MTest ("-hex=64", new Long(0x64)), - new MTest ("-hex=5e", new Long(0x5e)), - new MTest ("-hex=66", new MErr('r',"66")), - new MTest ("-hex=06677", new MErr('r',"06677")), - new MTest ("-hex=0xbeef", new MErr('m',"0xbeef")), - new MTest ("-hex=foo", new MErr('m',"foo")), - new MTest ("-hex=-51d", new MErr('r',"-51d")), - new MTest ("-hex=-51g", new MErr('m',"-51g")), - new MTest ("-hex=", new MErr('c',"")), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-hex2=%x", ih, 'x', 1, "-hex2=", null, "", null); - tests = new MTest[] - { - new MTest ("-hex2=-0777", new Long(-0x777)), - new MTest ("-hex2=66", new Long(0x66)), - new MTest ("-hex2=06677", new Long(0x6677)), - new MTest ("-hex2=-51d", new Long(-0x51d)), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ( - "-char=%c{['b','m']}", ch, 'c', 1, "-char=", - new RngCheck[] - { new RngCheck ('b',CLOSED,'m',CLOSED), - }, - "", null); - - tests = new MTest[] - { - new MTest ("-char=a", new MErr('r',"a")), - new MTest ("-char=b", new Character('b')), - new MTest ("-char='b'", new Character('b')), - new MTest ("-char='\142'", new Character('b')), - new MTest ("-char='\141'", new MErr('r',"'\141'")), - new MTest ("-char=\142", new Character('b')), - new MTest ("-char=\141", new MErr('r',"\141")), - new MTest ("-char=m", new Character('m')), - new MTest ("-char=z", new MErr('r', "z")), - new MTest ("-char=bb", new MErr('m', "bb")), - new MTest ("-char='b", new MErr('m', "'b")), - new MTest ("-char='", new MErr('m', "'")), - new MTest ("-char=a'", new MErr('m', "a'")), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-char2=%c", ch, 'c', 1, "-char2=",null,"",null); - tests = new MTest[] - { - new MTest ("-char2=a", new Character('a')), - new MTest ("-char2='\141'", new Character('\141')), - new MTest ("-char2=\141", new Character('\141')), - new MTest ("-char2=z", new Character('z')), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-charv3 %cX3", vec,'c',3,"-charv3 ",null,"",null); - tests = new MTest[] - { new MTest ("-charv3 a b c", new char[] {'a', 'b', 'c'}, 0), - new MTest ("-charv3 'g' f '\\n'", new char[]{'g','f','\n'}, 1), - new MTest ("-charv3 1 \001 3", new char[] {'1', '\001', '3'}, 2), - }; - vec.clear(); - test.checkMatches (tests, MULTI_WORD); - test.checkAdd ("-charv=%c", vec,'c',1,"-charv=",null,"",null); - tests = new MTest[] - { new MTest ("-charv=d", new Character('d'), 0), - new MTest ("-charv='g'", new Character('g'), 1), - new MTest ("-charv=\111", new Character('\111'), 2), - }; - vec.clear(); - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ( - "-bool=%b{true}", bh, 'b', 1, "-bool=", - new RngCheck[] - { new RngCheck (true), - }, - "", null); - - tests = new MTest[] - { - new MTest ("-bool=true", new Boolean(true)), - new MTest ("-bool=false", new MErr('r', "false")), - new MTest ("-bool=fals", new MErr('m', "fals")), - new MTest ("-bool=falsem", new MErr('m', "falsem")), - new MTest ("-bool=truex", new MErr('m', "truex")), - new MTest ("-bool=foo", new MErr('m', "foo")), - new MTest ("-bool=1", new MErr('m', "1")), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ( - "-boo2=%b{true,false}", bh, 'b', 1, "-boo2=", - new RngCheck[] - { new RngCheck (true), - new RngCheck (false), - }, - "", null); - - tests = new MTest[] - { - new MTest ("-boo2=true", new Boolean(true)), - new MTest ("-boo2=false", new Boolean(false)), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-boo3=%b", bh, 'b', 1, "-boo3=", null, "", null); - tests = new MTest[] - { - new MTest ("-boo3=true", new Boolean(true)), - new MTest ("-boo3=false", new Boolean(false)), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-boo3 %bX3", b3, 'b', 3, "-boo3 ", null, "", null); - tests = new MTest[] - { - new MTest ("-boo3 true false true", - new boolean[] { true, false, true }), - new MTest ("-boo3 true fals true", new MErr ('m', "fals")), - }; - test.checkMatches (tests, MULTI_WORD); - - test.checkAdd ("-boov3 %bX3", vec,'b',3,"-boov3 ",null,"",null); - tests = new MTest[] - { new MTest ("-boov3 true true false", - new boolean [] { true, true, false }, 0), - new MTest ("-boov3 false false true", - new boolean [] { false, false, true }, 1), - }; - vec.clear(); - test.checkMatches (tests, MULTI_WORD); - test.checkAdd ("-boov %b", vec,'b',1,"-boov ",null,"",null); - tests = new MTest[] - { new MTest ("-boov true", new Boolean (true), 0), - new MTest ("-boov false", new Boolean (false), 1), - new MTest ("-boov true", new Boolean (true), 2), - }; - vec.clear(); - test.checkMatches (tests, MULTI_WORD); - - - test.checkAdd ("-v3 %vX2", b3, 'v', 2, "-v3 ", null, "", null); - tests = new MTest[] - { new MTest ("-v3", new boolean[] { true, true }), - }; - test.checkMatches (tests, MULTI_WORD); - - test.checkAdd ( - "-vf %v{false,true}X2", b3, 'v', 2, "-vf ", - new RngCheck[] - { new RngCheck(false), - new RngCheck(true), - }, - "", null); - tests = new MTest[] - { new MTest ("-vf", new boolean[] { false, false }), - }; - test.checkMatches (tests, MULTI_WORD); - - test.checkAdd ( - "-str=%s{(john,zzzz]}", sh, 's', 1, "-str=", - new RngCheck[] - { new RngCheck ("john", OPEN, "zzzz", CLOSED), - }, - "", null); - - tests = new MTest[] - { - new MTest ("-str=john", new MErr ('r', "john")), - new MTest ("-str=joho ", "joho "), - new MTest ("-str=joho ", "joho "), - new MTest ("-str=zzzz", "zzzz"), - new MTest ("-str= joho", new MErr ('r', " joho")), - new MTest ("-str=jnhn ", new MErr ('r', "jnhn ")), - new MTest ("-str=zzzzz", new MErr ('r', "zzzzz")), - new MTest ("-str=\"joho\"", new MErr ('r', "\"joho\"")), - new MTest ("-str=\"joho", new MErr('r', "\"joho")), - new MTest ("-str=joho j", "joho j"), // new MErr('m', "joho j")), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-str2=%s", sh,'s',1,"-str2=",null,"",null); - tests = new MTest[] - { - new MTest ("-str2= jnhn", " jnhn"), - new MTest ("-str2=zzzzz", "zzzzz"), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-str3 %sX3",s3,'s',3,"-str3 ",null,"",null); - tests = new MTest[] - { - new MTest ("-str3 foo bar johnny", - new String[] { "foo", "bar", "johnny" }), - new MTest ("-str3 zzzzz \"bad foo", - new String[] { "zzzzz", "\"bad", "foo" - }), // new MErr('m', "\"bad")), - }; - test.checkMatches (tests, MULTI_WORD); - - test.checkAdd ("-strv3 %sX3", vec,'s',3,"-strv3 ",null,"",null); - tests = new MTest[] - { new MTest ("-strv3 foo bar \"hihi\"", - new String[] {"foo", "bar", "\"hihi\""}, 0), - new MTest ("-strv3 a 123 gg", - new String[]{"a", "123", "gg"}, 1), - }; - vec.clear(); - test.checkMatches (tests, MULTI_WORD); - test.checkAdd ("-strv=%s", vec,'s',1,"-strv=",null,"",null); - tests = new MTest[] - { new MTest ("-strv=d", "d", 0), - new MTest ("-strv='g'", "'g'", 1), - new MTest ("-strv=\\111", "\\111", 2), - }; - vec.clear(); - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ( - "-float=%f{(-0.001,1000.0]}", dh, 'f', 1, "-float=", - new RngCheck[] - { new RngCheck (-0.001, OPEN, 1000.0, CLOSED), - }, - "", null); - - tests = new MTest[] - { - new MTest ("-float=-0.000999", new Double(-0.000999)), - new MTest ("-float=1e-3", new Double(0.001)), - new MTest ("-float=12.33e1", new Double(123.3)), - new MTest ("-float=1e3", new Double(1e3)), - new MTest ("-float=1000.000", new Double(1000.0)), - new MTest ("-float=-0.001", new MErr('r', "-0.001")), - new MTest ("-float=-1e-3", new MErr('r', "-1e-3")), - new MTest ("-float=1000.001", new MErr('r', "1000.001")), - new MTest ("-float=.", new MErr('m', ".")), - new MTest ("-float= 124.5 ", new Double (124.5)), - new MTest ("-float=124.5x", new MErr('m', "124.5x")), - new MTest ("-float= foo ", new MErr('m', " foo ")), - new MTest ("-float=1e1", new Double(10)), - new MTest ("-float=1e ", new MErr('m', "1e ")), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-float2=%f", dh,'f',1,"-float2=",null,"",null); - tests = new MTest[] - { - new MTest ("-float2=-0.001", new Double(-0.001)), - new MTest ("-float2=-1e-3", new Double(-1e-3)), - new MTest ("-float2=1000.001", new Double(1000.001)), - }; - test.checkMatches (tests, ONE_WORD); - - test.checkAdd ("-f3 %fX3", d3,'f',3,"-f3 ",null,"",null); - tests = new MTest[] - { - new MTest ("-f3 -0.001 1.23e5 -9.88e-4", - new double[] { -0.001, 1.23e5, -9.88e-4 }), - new MTest ("-f3 7.88 foo 9.0", new MErr ('m', "foo")), - new MTest ("-f3 7.88 . 9.0", new MErr ('m', ".")), - new MTest ("-f3 7.88 3.0 9.0x", new MErr ('m', "9.0x")), - }; - test.checkMatches (tests, MULTI_WORD); - - test.checkAdd ("-fv3 %fX3", vec,'f',3,"-fv3 ",null,"",null); - tests = new MTest[] - { new MTest ("-fv3 1.0 3.444 6.7", - new double[] {1.0, 3.444, 6.7}, 0), - new MTest ("-fv3 13e-5 145.678 0.0001e45", - new double[]{13e-5, 145.678, 0.0001e45}, 1), - new MTest ("-fv3 11.11 3.1245 -1e-4", - new double[] {11.11, 3.1245, -1e-4}, 2), - new MTest ("-fv3 1.0 2 3", - new double[] { 1.0, 2.0, 3.0 }, 3), - }; - vec.clear(); - test.checkMatches (tests, MULTI_WORD); - test.checkAdd ("-fv %f", vec,'f',1,"-fv ",null,"",null); - tests = new MTest[] - { new MTest ("-fv -15.1234", new Double(-15.1234), 0), - new MTest ("-fv -1.234e-7", new Double(-1.234e-7), 1), - new MTest ("-fv 0.001111", new Double(0.001111), 2), - }; - vec.clear(); - test.checkMatches (tests, MULTI_WORD); - - - IntHolder intHolder = new IntHolder(); - StringHolder strHolder = new StringHolder(); - - ArgParser parser = new ArgParser ("test"); - parser.addOption ("-foo %d #an int", intHolder); - parser.addOption ("-bar %s #a string", strHolder); - args = new String[] - { "zzz", "-cat", "-foo", "123", "yyy", "-bar", "xxxx", "xxx" - }; - - String[] unmatchedCheck = new String[] - { "zzz", "-cat", "yyy", "xxx" - }; - - String[] unmatched = parser.matchAllArgs (args, 0, 0); - test.checkStringArray ( - "Unmatched args:", unmatched, unmatchedCheck); - - vec.clear(); - for (int i=0; i<args.length; ) - { try - { i = parser.matchArg (args, i); - if (parser.getUnmatchedArgument() != null) - { vec.add (parser.getUnmatchedArgument()); - } - } - catch (Exception e) - { - } - } - unmatched = (String[])vec.toArray(new String[0]); - test.checkStringArray ( - "My unmatched args:", unmatched, unmatchedCheck); - - System.out.println ("\nPassed\n"); - - } +public class ArgParserTest { + ArgParser parser; + + static final boolean CLOSED = true; + static final boolean OPEN = false; + + static final boolean ONE_WORD = true; + static final boolean MULTI_WORD = false; + + private static void verify(boolean ok, String msg) { + if (!ok) { + Throwable e = new Throwable(); + System.out.println("Verification failed:" + msg); + e.printStackTrace(); + System.exit(1); + } + } + + private static String[] argsFromString(String s) { + Vector<String> vec = new Vector<String>(100); + try { + ArgParser.stringToArgs(vec, s, /* allowQuotedStings= */false); + } catch (StringScanException e) { + e.printStackTrace(); + System.exit(1); + } + String[] result = new String[vec.size()]; + for (int i = 0; i < vec.size(); i++) { + result[i] = (String) vec.get(i); + } + return result; + } + + static class RngCheck { + ArgParser.RangePnt low = null; + ArgParser.RangePnt high = null; + int type; + + RngCheck(String s) { + low = new ArgParser.RangePnt(s, CLOSED); + type = 's'; + } + + RngCheck(double d) { + low = new ArgParser.RangePnt(d, CLOSED); + type = 'd'; + } + + RngCheck(long l) { + low = new ArgParser.RangePnt(l, CLOSED); + type = 'l'; + } + + RngCheck(boolean b) { + low = new ArgParser.RangePnt(b, CLOSED); + type = 'b'; + } + + RngCheck(String s1, boolean c1, String s2, boolean c2) { + low = new ArgParser.RangePnt(s1, c1); + high = new ArgParser.RangePnt(s2, c2); + type = 's'; + } + + RngCheck(double d1, boolean c1, double d2, boolean c2) { + low = new ArgParser.RangePnt(d1, c1); + high = new ArgParser.RangePnt(d2, c2); + type = 'd'; + } + + RngCheck(long l1, boolean c1, long l2, boolean c2) { + low = new ArgParser.RangePnt(l1, c1); + high = new ArgParser.RangePnt(l2, c2); + type = 'l'; + } + + void check(ArgParser.RangeAtom ra) { + verify((ra.low == null) == (low == null), "(ra.low==null)=" + + (ra.low == null) + "(low==null)=" + (low == null)); + verify((ra.high == null) == (high == null), "(ra.high==null)=" + + (ra.high == null) + "(high==null)=" + (high == null)); + + if (ra.low != null) { + switch (type) { + case 'l': { + verify(ra.low.lval == low.lval, "ra.low=" + ra.low + + " low=" + low); + break; + } + case 'd': { + verify(ra.low.dval == low.dval, "ra.low=" + ra.low + + " low=" + low); + break; + } + case 's': { + verify(ra.low.sval.equals(low.sval), "ra.low=" + ra.low + + " low=" + low); + break; + } + case 'b': { + verify(ra.low.bval == low.bval, "ra.low=" + ra.low + + " low=" + low); + break; + } + } + verify(ra.low.closed == low.closed, "ra.low=" + ra.low + + " low=" + low); + } + if (ra.high != null) { + switch (type) { + case 'l': { + verify(ra.high.lval == high.lval, "ra.high=" + ra.high + + " high=" + high); + break; + } + case 'd': { + verify(ra.high.dval == high.dval, "ra.high=" + ra.high + + " high=" + high); + break; + } + case 's': { + verify(ra.high.sval.equals(high.sval), "ra.high=" + ra.high + + " high=" + high); + break; + } + case 'b': { + verify(ra.high.bval == high.bval, "ra.high=" + ra.high + + " high=" + high); + break; + } + } + verify(ra.high.closed == high.closed, "ra.high=" + ra.high + + " high=" + high); + } + } + } + + ArgParserTest() { + parser = new ArgParser("fubar"); + } + + static void checkException(Exception e, String errmsg) { + if (errmsg != null) { + if (!e.getMessage().equals(errmsg)) { + System.out.println("Expecting exception '" + errmsg + + "' but got '" + e.getMessage() + "'"); + e.printStackTrace(); + (new Throwable()).printStackTrace(); + System.exit(1); + } + } else { + System.out.println("Unexpected exception '" + e.getMessage() + "'"); + e.printStackTrace(); + (new Throwable()).printStackTrace(); + System.exit(1); + } + } + + void checkPrintHelp(String msg) { + ByteArrayOutputStream buf = new ByteArrayOutputStream(0x10000); + PrintStream ps = new PrintStream(buf); + ps.println(parser.getHelpMessage()); + System.out.print(buf.toString()); + } + + // void checkGetSynopsis (String msg) + // { + // ByteArrayOutputStream buf = new ByteArrayOutputStream(0x10000); + // PrintStream ps = new PrintStream(buf); + // parser.printSynopsis (ps, 80); + // System.out.print (buf.toString()); + // } + + void checkAdd(String s, Object resHolder, String errmsg) { + checkAdd(s, resHolder, 0, 0, null, null, null, errmsg); + } + + void add(String s, Object resHolder) { + try { + parser.addOption(s, resHolder); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + + void checkStringArray(String msg, String[] strs, String[] check) { + boolean dontMatch = false; + if (strs.length != check.length) { + dontMatch = true; + } else { + for (int i = 0; i < strs.length; i++) { + if (!strs[i].equals(check[i])) { + dontMatch = true; + break; + } + } + } + if (dontMatch) { + System.out.println(msg); + System.out.print("Expected: "); + for (int i = 0; i < check.length; i++) { + System.out.print("'" + check[i] + "'"); + if (i < check.length - 1) { + System.out.print(" "); + } + } + System.out.println(""); + System.out.print("Got: "); + for (int i = 0; i < strs.length; i++) { + System.out.print("'" + strs[i] + "'"); + if (i < strs.length - 1) { + System.out.print(" "); + } + } + System.out.println(""); + System.exit(1); + } + } + + void checkAdd(String s, Object resHolder, int code, int numValues, + Object names, RngCheck[] rngCheck, String helpMsg, String errmsg) { + boolean exceptionThrown = false; + String[] namelist = null; + try { + parser.addOption(s, resHolder); + } catch (Exception e) { + exceptionThrown = true; + checkException(e, errmsg); + } + if (names instanceof String) { + namelist = new String[] { (String) names }; + } else { + namelist = (String[]) names; + } + if (!exceptionThrown) { + verify(errmsg == null, "Expecting exception " + errmsg); + ArgParser.Record rec = parser.lastMatchRecord(); + verify(rec.convertCode == code, "code=" + rec.convertCode + + ", expecting " + code); + ArgParser.NameDesc nd; + int i = 0; + for (nd = rec.firstNameDesc(); nd != null; nd = nd.next) { + i++; + } + verify(i == namelist.length, "numNames=" + i + ", expecting " + + namelist.length); + i = 0; + for (nd = rec.firstNameDesc(); nd != null; nd = nd.next) { + String ss; + if (!nd.oneWord) { + ss = new String(nd.name) + ' '; + } else { + ss = nd.name; + } + verify(ss.equals(namelist[i]), "have name '" + ss + + "', expecting '" + namelist[i] + "'"); + i++; + } + ArgParser.RangeAtom ra; + i = 0; + for (ra = rec.firstRangeAtom(); ra != null; ra = ra.next) { + i++; + } + int expectedRangeNum = 0; + if (rngCheck != null) { + expectedRangeNum = rngCheck.length; + } + verify(i == expectedRangeNum, "numRangeAtoms=" + i + ", expecting " + + expectedRangeNum); + i = 0; + for (ra = rec.firstRangeAtom(); ra != null; ra = ra.next) { + rngCheck[i++].check(ra); + } + verify(rec.helpMsg.equals(helpMsg), "helpMsg=" + rec.helpMsg + + ", expecting " + helpMsg); + verify(rec.numValues == numValues, "numValues=" + rec.numValues + + ", expecting " + numValues); + } + } + + double getDoubleValue(Object obj, int k) { + if (obj instanceof DoubleHolder) { + return ((DoubleHolder) obj).value; + } else if (obj instanceof FloatHolder) { + return ((FloatHolder) obj).value; + } else if (obj instanceof double[]) { + return ((double[]) obj)[k]; + } else if (obj instanceof float[]) { + return ((float[]) obj)[k]; + } else { + verify(false, "object doesn't contain double values"); + return 0; + } + } + + long getLongValue(Object obj, int k) { + if (obj instanceof LongHolder) { + return ((LongHolder) obj).value; + } else if (obj instanceof IntHolder) { + return ((IntHolder) obj).value; + } else if (obj instanceof long[]) { + return ((long[]) obj)[k]; + } else if (obj instanceof int[]) { + return ((int[]) obj)[k]; + } else { + verify(false, "object doesn't contain long values"); + return 0; + } + } + + String getStringValue(Object obj, int k) { + if (obj instanceof StringHolder) { + return ((StringHolder) obj).value; + } else if (obj instanceof String[]) { + return ((String[]) obj)[k]; + } else { + verify(false, "object doesn't contain String values"); + return null; + } + } + + boolean getBooleanValue(Object obj, int k) { + if (obj instanceof BooleanHolder) { + return ((BooleanHolder) obj).value; + } else if (obj instanceof boolean[]) { + return ((boolean[]) obj)[k]; + } else { + verify(false, "object doesn't contain boolean values"); + return false; + } + } + + char getCharValue(Object obj, int k) { + if (obj instanceof CharHolder) { + return ((CharHolder) obj).value; + } else if (obj instanceof char[]) { + return ((char[]) obj)[k]; + } else { + verify(false, "object doesn't contain char values"); + return 0; + } + } + + static class MErr { + int code; + String valStr; + + MErr(int code, String valStr) { + this.code = code; + this.valStr = valStr; + } + } + + static class MTest { + String args; + Object result; + int resultIdx; + + MTest(String args, Object result) { + this(args, result, -1); + } + + MTest(String args, Object result, int resultIdx) { + this.args = args; + this.result = result; + this.resultIdx = resultIdx; + } + }; + + void checkMatch(String args[], int idx, String errMsg) { + getMatchResult(args, idx, -1, errMsg, -1); + } + + void checkMatch(String args[], int idx, int cnt, long check, int resultIdx) { + Object rholder = getMatchResult(args, idx, cnt, null, resultIdx); + long result = getLongValue(rholder, 0); + verify(result == check, "result " + result + " vs. " + check); + } + + void checkMatch(String args[], int idx, int cnt, double check, int resultIdx) { + Object rholder = getMatchResult(args, idx, cnt, null, resultIdx); + double result = getDoubleValue(rholder, 0); + verify(result == check, "result " + result + " vs. " + check); + } + + void checkMatch(String args[], int idx, int cnt, String check, int resultIdx) { + Object rholder = getMatchResult(args, idx, cnt, null, resultIdx); + String result = getStringValue(rholder, 0); + verify(result.equals(check), "result " + result + " vs. " + check); + } + + void checkMatch(String args[], int idx, int cnt, boolean check, + int resultIdx) { + Object rholder = getMatchResult(args, idx, cnt, null, resultIdx); + boolean result = getBooleanValue(rholder, 0); + verify(result == check, "result " + result + " vs. " + check); + } + + void checkMatch(String args[], int idx, int cnt, char check, int resultIdx) { + Object rholder = getMatchResult(args, idx, cnt, null, resultIdx); + char result = getCharValue(rholder, 0); + verify(result == check, "result " + result + " vs. " + check); + } + + void checkMatch(String args[], int idx, int cnt, Object checkArray, + int resultIdx) { + Object rholder = getMatchResult(args, idx, cnt, null, resultIdx); + if (!checkArray.getClass().isArray()) { + verify(false, "check is not an array"); + } + for (int i = 0; i < Array.getLength(checkArray); i++) { + if (checkArray instanceof long[]) { + long result = getLongValue(rholder, i); + long check = ((long[]) checkArray)[i]; + verify(result == check, "result [" + i + "] " + result + + " vs. " + check); + } else if (checkArray instanceof double[]) { + double result = getDoubleValue(rholder, i); + double check = ((double[]) checkArray)[i]; + verify(result == check, "result [" + i + "] " + result + + " vs. " + check); + } else if (checkArray instanceof String[]) { + String result = getStringValue(rholder, i); + String check = ((String[]) checkArray)[i]; + verify(result.equals(check), "result [" + i + "] " + result + + " vs. " + check); + } else if (checkArray instanceof boolean[]) { + boolean result = getBooleanValue(rholder, i); + boolean check = ((boolean[]) checkArray)[i]; + verify(result == check, "result [" + i + "] " + result + + " vs. " + check); + } else if (checkArray instanceof char[]) { + char result = getCharValue(rholder, i); + char check = ((char[]) checkArray)[i]; + verify(result == check, "result [" + i + "] " + result + + " vs. " + check); + } else { + verify(false, "unknown type for checkArray"); + } + } + } + + void checkMatch(MTest test, boolean oneWord) { + String[] argv; + if (oneWord) { + argv = new String[1]; + argv[0] = test.args; + } else { + argv = argsFromString(test.args); + } + if (test.result instanceof Long) { + checkMatch(argv, 0, argv.length, ((Long) test.result).longValue(), + test.resultIdx); + } else if (test.result instanceof Double) { + checkMatch(argv, 0, argv.length, + ((Double) test.result).doubleValue(), test.resultIdx); + } else if (test.result instanceof String) { + checkMatch(argv, 0, argv.length, (String) test.result, + test.resultIdx); + } else if (test.result instanceof Boolean) { + checkMatch(argv, 0, argv.length, + ((Boolean) test.result).booleanValue(), test.resultIdx); + } else if (test.result instanceof Character) { + checkMatch(argv, 0, argv.length, + ((Character) test.result).charValue(), test.resultIdx); + } else if (test.result.getClass().isArray()) { + checkMatch(argv, 0, argv.length, test.result, test.resultIdx); + } else if (test.result instanceof MErr) { + MErr err = (MErr) test.result; + String argname = parser.getOptionName(argv[0]); + String msg = ""; + + switch (err.code) { + case 'c': { + msg = "requires a contiguous value"; + break; + } + case 'm': { + msg = "malformed " + parser.getOptionTypeName(argv[0]) + " '" + + err.valStr + "'"; + break; + } + case 'r': { + msg = "value '" + err.valStr + "' not in range " + + parser.getOptionRangeDesc(argv[0]); + break; + } + case 'v': { + msg = "requires " + err.valStr + " values"; + break; + } + } + checkMatch(argv, 0, argname + ": " + msg); + } else { + verify(false, "Unknown result type"); + } + } + + void checkMatches(MTest[] tests, boolean oneWord) { + for (int i = 0; i < tests.length; i++) { + checkMatch(tests[i], oneWord); + } + } + + Object getMatchResult(String args[], int idx, int cnt, String errMsg, + int resultIdx) { + boolean exceptionThrown = false; + int k = 0; + try { + k = parser.matchArg(args, idx); + } catch (Exception e) { + exceptionThrown = true; + checkException(e, errMsg); + } + if (!exceptionThrown) { + verify(k == idx + cnt, "Expecting result index " + (idx + cnt) + + ", got " + k); + Object result = parser.getResultHolder(args[0]); + if (resultIdx >= 0) { + verify(result instanceof Vector, + "Expecting result to be stored in a vector"); + Vector<?> vec = (Vector<?>) result; + verify(vec.size() == resultIdx + 1, + "Expecting result vector size " + (resultIdx + 1)); + return vec.get(resultIdx); + } else { + return result; + } + } else { + return null; + } + } + + /** + * Runs a set of tests to verify correct operation of the ArgParser class. + * If all the tests run correctly, the program prints the message + * <code>Passed</code> and terminates. Otherwise, diagnostic information is + * printed at the first point of failure. + */ + public static void main(String[] args) { + ArgParserTest test = new ArgParserTest(); + + BooleanHolder bh = new BooleanHolder(); + boolean[] b3 = new boolean[3]; + CharHolder ch = new CharHolder(); + char[] c3 = new char[3]; + IntHolder ih = new IntHolder(); + int[] i3 = new int[3]; + LongHolder lh = new LongHolder(); + long[] l3 = new long[3]; + FloatHolder fh = new FloatHolder(); + float[] f3 = new float[3]; + DoubleHolder dh = new DoubleHolder(); + double[] d3 = new double[3]; + StringHolder sh = new StringHolder(); + String[] s3 = new String[3]; + + test.checkAdd( + "-foo %i{[0,10)}X3 #sets the value of foo", + // 0123456789012345 + i3, 'i', 3, new String[] { "-foo " }, + new RngCheck[] { new RngCheck(0, CLOSED, 10, OPEN) }, + "sets the value of foo", null); + + test.checkAdd("-arg1,,", null, "Null option name given"); + test.checkAdd("-arg1,,goo %f ", null, "Null option name given"); + test.checkAdd(" ", null, "Null option name given"); + test.checkAdd("", null, "Null option name given"); + test.checkAdd(" %v", null, "Null option name given"); + test.checkAdd("-foo ", null, "No conversion character given"); + test.checkAdd("-foo %", null, "No conversion character given"); + test.checkAdd("foo, aaa bbb ", null, "Names not separated by ','"); + test.checkAdd(" foo aaa %d", null, "Names not separated by ','"); + test.checkAdd("-arg1,-b,", null, "Null option name given"); + test.checkAdd("-arg1,-b", null, "No conversion character given"); + test.checkAdd("-arg1 ", null, "No conversion character given"); + test.checkAdd("-arg1, %v", null, "Null option name given"); + test.checkAdd("-arg1,%v", null, "Null option name given"); + test.checkAdd("-foo %V", null, + "Conversion code 'V' not one of 'iodxcbfsvh'"); + test.checkAdd("-h %hX5", null, "Multipliers not supported for %h"); + test.checkAdd("-h %h{}", null, "Ranges not supported for %h"); + test.checkAdd("-help, -h %h #here is how we help you", null, 'h', 1, + new String[] { "-help ", "-h " }, null, + "here is how we help you", null); + + test.checkAdd("-arg1 ,-arg2=%d{0,3,(7,16]}X1 #x3 test", l3, 'd', 1, + new String[] { "-arg1 ", "-arg2=" }, new RngCheck[] { + new RngCheck(0), new RngCheck(3), + new RngCheck(7, OPEN, 16, CLOSED), }, "x3 test", null); + + test.checkAdd("bbb,ccc%x{[1,2]} #X3 x3 test", l3, 'x', 1, new String[] { + "bbb", "ccc" }, new RngCheck[] { new RngCheck(1, CLOSED, 2, + CLOSED), }, "X3 x3 test", null); + + test.checkAdd(" bbb ,ccc, ddd ,e , f=%bX1 #x3 test", b3, 'b', 1, + new String[] { "bbb ", "ccc", "ddd ", "e ", "f=" }, null, + "x3 test", null); + + test.checkAdd(" bbb ,ccc, ddd ,e , f= %bX3 #x3 test", b3, 'b', 3, + new String[] { "bbb ", "ccc ", "ddd ", "e ", "f= " }, null, + "x3 test", null); + + test.checkAdd( + "-b,--bar %s{[\"john\",\"jerry\"),fred,\"harry\"} #sets bar", + sh, 's', 1, new String[] { "-b ", "--bar " }, new RngCheck[] { + new RngCheck("jerry", OPEN, "john", CLOSED), + new RngCheck("fred"), new RngCheck("harry") }, + "sets bar", null); + + test.checkAdd("-c ,coven%f{0.0,9.0,(6,5],[-9.1,10.2]} ", dh, 'f', 1, + new String[] { "-c ", "coven" }, new RngCheck[] { + new RngCheck(0.0), new RngCheck(9.0), + new RngCheck(5.0, CLOSED, 6.0, OPEN), + new RngCheck(-9.1, CLOSED, 10.2, CLOSED) }, "", null); + + test.checkAdd("-b %b #a boolean value ", bh, 'b', 1, + new String[] { "-b " }, new RngCheck[] {}, "a boolean value ", + null); + + test.checkAdd("-a %i", ih, 'i', 1, "-a ", null, "", null); + test.checkAdd("-a %o", lh, 'o', 1, "-a ", null, "", null); + test.checkAdd("-a %d", i3, 'd', 1, "-a ", null, "", null); + test.checkAdd("-a %x", l3, 'x', 1, "-a ", null, "", null); + test.checkAdd("-a %c", ch, 'c', 1, "-a ", null, "", null); + test.checkAdd("-a %c", c3, 'c', 1, "-a ", null, "", null); + test.checkAdd("-a %v", bh, 'v', 1, "-a ", null, "", null); + test.checkAdd("-a %b", b3, 'b', 1, "-a ", null, "", null); + test.checkAdd("-a %f", fh, 'f', 1, "-a ", null, "", null); + test.checkAdd("-a %f", f3, 'f', 1, "-a ", null, "", null); + test.checkAdd("-a %f", dh, 'f', 1, "-a ", null, "", null); + test.checkAdd("-a %f", d3, 'f', 1, "-a ", null, "", null); + + test.checkAdd("-a %i", fh, 'i', 1, "-a ", null, "", + "Invalid result holder for %i"); + test.checkAdd("-a %c", i3, 'c', 1, "-a ", null, "", + "Invalid result holder for %c"); + test.checkAdd("-a %v", d3, 'v', 1, "-a ", null, "", + "Invalid result holder for %v"); + test.checkAdd("-a %f", sh, 'f', 1, "-a ", null, "", + "Invalid result holder for %f"); + test.checkAdd("-a %s", l3, 's', 1, "-a ", null, "", + "Invalid result holder for %s"); + + test.checkAdd("-foo %i{} ", ih, 'i', 1, "-foo ", null, "", null); + test.checkAdd("-foo%i{}", ih, 'i', 1, "-foo", null, "", null); + test.checkAdd("-foo%i{ }", ih, 'i', 1, "-foo", null, "", null); + test.checkAdd("-foo%i{ }}", ih, "Illegal character(s), expecting '#'"); + test.checkAdd("-foo%i{ ", ih, "Unterminated range specification"); + test.checkAdd("-foo%i{", ih, "Unterminated range specification"); + test.checkAdd("-foo%i{0,9", ih, "Unterminated range specification"); + test.checkAdd("-foo%i{1,2,3)", ih, "Unterminated range specification"); + + test.checkAdd("-b %f{0.9}", fh, 'f', 1, "-b ", + new RngCheck[] { new RngCheck(0.9) }, "", null); + test.checkAdd("-b %f{ 0.9 ,7, -0.5,-4 ,6 }", fh, 'f', 1, "-b ", + new RngCheck[] { new RngCheck(0.9), new RngCheck(7.0), + new RngCheck(-0.5), new RngCheck(-4.0), + new RngCheck(6.0) }, "", null); + test.checkAdd("-b %f{ [0.9,7), (-0.5,-4),[9,6] , (10,13.4] }", fh, 'f', + 1, "-b ", new RngCheck[] { + new RngCheck(0.9, CLOSED, 7.0, OPEN), + new RngCheck(-4.0, OPEN, -.5, OPEN), + new RngCheck(6.0, CLOSED, 9.0, CLOSED), + new RngCheck(10.0, OPEN, 13.4, CLOSED), }, "", null); + test.checkAdd("-b %f{(8 9]}", fh, + "Missing ',' in subrange specification"); + test.checkAdd("-b %f{(8,9,]}", fh, "Unterminated subrange"); + test.checkAdd("-b %f{(8,9 ,]}", fh, "Unterminated subrange"); + test.checkAdd("-b %f{(8,9 8]}", fh, "Unterminated subrange"); + test.checkAdd("-b %f{8 9}", fh, "Range spec: ',' or '}' expected"); + test.checkAdd("-b %f{8 *}", fh, "Range spec: ',' or '}' expected"); + + test.checkAdd("-b %f{8y}", fh, "Range spec: ',' or '}' expected"); + test.checkAdd("-b %f{.}", fh, "Malformed float '.}' in range spec"); + test.checkAdd("-b %f{1.0e}", fh, + "Malformed float '1.0e}' in range spec"); + test.checkAdd("-b %f{[*]}", fh, "Malformed float '*' in range spec"); + test.checkAdd("-b %f{1.2e5t}", fh, "Range spec: ',' or '}' expected"); + + test.checkAdd("-b %i{8}", ih, 'i', 1, "-b ", + new RngCheck[] { new RngCheck(8) }, "", null); + test.checkAdd("-b %i{8, 9,10 }", ih, 'i', 1, "-b ", new RngCheck[] { + new RngCheck(8), new RngCheck(9), new RngCheck(10) }, "", null); + test.checkAdd("-b %i{8, [-9,10),[-17,15],(2,-33),(8,9] }", ih, 'i', 1, + "-b ", new RngCheck[] { new RngCheck(8), + new RngCheck(-9, CLOSED, 10, OPEN), + new RngCheck(-17, CLOSED, 15, CLOSED), + new RngCheck(-33, OPEN, 2, OPEN), + new RngCheck(8, OPEN, 9, CLOSED), }, "", null); + test.checkAdd("-b %i{8.7}", ih, "Range spec: ',' or '}' expected"); + test.checkAdd("-b %i{6,[*]}", ih, "Malformed integer '*' in range spec"); + test.checkAdd("-b %i{g76}", ih, "Malformed integer 'g' in range spec"); + + test.checkAdd("-b %s{foobar}", sh, 's', 1, "-b ", + new RngCheck[] { new RngCheck("foobar") }, "", null); + test.checkAdd("-b %s{foobar, 0x233,\" \"}", sh, 's', 1, "-b ", + new RngCheck[] { new RngCheck("foobar"), new RngCheck("0x233"), + new RngCheck(" ") }, "", null); + test.checkAdd("-b %s{foobar,(bb,aa], [\"01\",02]}", sh, 's', 1, "-b ", + new RngCheck[] { new RngCheck("foobar"), + new RngCheck("aa", CLOSED, "bb", OPEN), + new RngCheck("01", CLOSED, "02", CLOSED), }, "", null); + + test.checkAdd("-b %c{'a'}", ch, 'c', 1, "-b ", + new RngCheck[] { new RngCheck('a') }, "", null); + test.checkAdd("-b %c{'\\n', '\\002', 'B'}", ch, 'c', 1, "-b ", + new RngCheck[] { new RngCheck('\n'), new RngCheck('\002'), + new RngCheck('B') }, "", null); + test.checkAdd("-b %c{'q',('g','a'], ['\t','\\003']}", ch, 'c', 1, + "-b ", new RngCheck[] { new RngCheck('q'), + new RngCheck('a', CLOSED, 'g', OPEN), + new RngCheck('\003', CLOSED, '\t', CLOSED), }, "", null); + + test.checkAdd("-b %b{true}X2", b3, 'b', 2, "-b ", + new RngCheck[] { new RngCheck(true) }, "", null); + test.checkAdd("-b %b{ true , false, true }", bh, 'b', 1, "-b ", + new RngCheck[] { new RngCheck(true), new RngCheck(false), + new RngCheck(true) }, "", null); + test.checkAdd("-b %v{true,[true,false)}", bh, + "Sub ranges not supported for %b or %v"); + test.checkAdd("-b %v{true,[]}", bh, + "Sub ranges not supported for %b or %v"); + test.checkAdd("-b %b{tru}", bh, + "Malformed boolean 'tru}' in range spec"); + + test.checkAdd("-b %iX2", i3, 'i', 2, "-b ", null, "", null); + test.checkAdd("-b %vX3", b3, 'v', 3, "-b ", null, "", null); + test.checkAdd("-b %v{ }X3", b3, 'v', 3, "-b ", null, "", null); + + test.checkAdd("-b=%iX2", i3, 'i', 2, "-b", null, "", + "Multiplier value incompatible with one word option -b="); + test.checkAdd("-b %iX0", i3, 'i', 0, "-b ", null, "", + "Value multiplier number must be > 0"); + test.checkAdd("-b %iX-6", i3, 'i', 0, "-b ", null, "", + "Value multiplier number must be > 0"); + test.checkAdd("-b %iXy", i3, 'i', 0, "-b ", null, "", + "Malformed value multiplier"); + test.checkAdd("-b %iX4", i3, 'i', 4, "-b ", null, "", + "Result holder array must have a length >= 4"); + test.checkAdd("-b %iX4", ih, 'i', 4, "-b ", null, "", + "Multiplier requires result holder to be an array of length >= 4"); + + test.checkAdd("-b %i #X4", ih, 'i', 1, "-b ", null, "X4", null); + test.checkAdd("-b %i #[}X4", ih, 'i', 1, "-b ", null, "[}X4", null); + + // test.checkPrintHelp(""); + // test.checkPrintUsage(""); + + test = new ArgParserTest(); + + test.checkAdd( + "-intarg %i{1,2,(9,18],[22,27],[33,38),(45,48)} #test int arg", + ih, 'i', 1, "-intarg ", new RngCheck[] { new RngCheck(1), + new RngCheck(2), new RngCheck(9, OPEN, 18, CLOSED), + new RngCheck(22, CLOSED, 27, CLOSED), + new RngCheck(33, CLOSED, 38, OPEN), + new RngCheck(45, OPEN, 48, OPEN), }, "test int arg", + null); + + MTest[] tests; + + tests = new MTest[] { new MTest("-intarg 1", new Long(1)), + new MTest("-intarg 3", new MErr('r', "3")), + new MTest("-intarg 9", new MErr('r', "9")), + new MTest("-intarg 11", new Long(11)), + new MTest("-intarg 18", new Long(18)), + new MTest("-intarg 22", new Long(22)), + new MTest("-intarg 25", new Long(25)), + new MTest("-intarg 27", new Long(27)), + new MTest("-intarg 33", new Long(33)), + new MTest("-intarg 35", new Long(35)), + new MTest("-intarg 38", new MErr('r', "38")), + new MTest("-intarg 45", new MErr('r', "45")), + new MTest("-intarg 46", new Long(46)), + new MTest("-intarg 48", new MErr('r', "48")), + new MTest("-intarg 100", new MErr('r', "100")), + new MTest("-intarg 0xbeef", new MErr('r', "0xbeef")), + new MTest("-intarg 0x2f", new Long(0x2f)), + new MTest("-intarg 041", new Long(041)), }; + test.checkMatches(tests, MULTI_WORD); + + test.checkAdd( + "-farg %f{1,2,(9,18],[22,27],[33,38),(45,48)} #test float arg", + dh, 'f', 1, "-farg ", new RngCheck[] { new RngCheck(1.0), + new RngCheck(2.0), + new RngCheck(9.0, OPEN, 18.0, CLOSED), + new RngCheck(22.0, CLOSED, 27.0, CLOSED), + new RngCheck(33.0, CLOSED, 38.0, OPEN), + new RngCheck(45.0, OPEN, 48.0, OPEN), }, + "test float arg", null); + + tests = new MTest[] { new MTest("-farg 1", new Double(1)), + new MTest("-farg 3", new MErr('r', "3")), + new MTest("-farg 9", new MErr('r', "9")), + new MTest("-farg 9.0001", new Double(9.0001)), + new MTest("-farg 11", new Double(11)), + new MTest("-farg 18", new Double(18)), + new MTest("-farg 22", new Double(22)), + new MTest("-farg 25", new Double(25)), + new MTest("-farg 27", new Double(27)), + new MTest("-farg 33", new Double(33)), + new MTest("-farg 35", new Double(35)), + new MTest("-farg 37.9999", new Double(37.9999)), + new MTest("-farg 38", new MErr('r', "38")), + new MTest("-farg 45", new MErr('r', "45")), + new MTest("-farg 45.0001", new Double(45.0001)), + new MTest("-farg 46", new Double(46)), + new MTest("-farg 47.9999", new Double(47.9999)), + new MTest("-farg 48", new MErr('r', "48")), + new MTest("-farg 100", new MErr('r', "100")), + new MTest("-farg 0", new MErr('r', "0")), }; + test.checkMatches(tests, MULTI_WORD); + + test.checkAdd( + "-sarg %s{1,2,(AA,AZ],[BB,BX],[C3,C8),(d5,d8)} #test string arg", + s3, 's', 1, "-sarg ", new RngCheck[] { new RngCheck("1"), + new RngCheck("2"), + new RngCheck("AA", OPEN, "AZ", CLOSED), + new RngCheck("BB", CLOSED, "BX", CLOSED), + new RngCheck("C3", CLOSED, "C8", OPEN), + new RngCheck("d5", OPEN, "d8", OPEN), }, + "test string arg", null); + + tests = new MTest[] { new MTest("-sarg 1", "1"), + new MTest("-sarg 3", new MErr('r', "3")), + new MTest("-sarg AA", new MErr('r', "AA")), + new MTest("-sarg AM", "AM"), new MTest("-sarg AZ", "AZ"), + new MTest("-sarg BB", "BB"), new MTest("-sarg BL", "BL"), + new MTest("-sarg BX", "BX"), new MTest("-sarg C3", "C3"), + new MTest("-sarg C6", "C6"), + new MTest("-sarg C8", new MErr('r', "C8")), + new MTest("-sarg d5", new MErr('r', "d5")), + new MTest("-sarg d6", "d6"), + new MTest("-sarg d8", new MErr('r', "d8")), + new MTest("-sarg zzz", new MErr('r', "zzz")), + new MTest("-sarg 0", new MErr('r', "0")), }; + test.checkMatches(tests, MULTI_WORD); + + test = new ArgParserTest(); + + test.checkAdd("-carg %c{1,2,(a,z],['A','Z'],['\\001',\\007),(4,8)}", + c3, 'c', 1, "-carg ", new RngCheck[] { new RngCheck('1'), + new RngCheck('2'), + new RngCheck('a', OPEN, 'z', CLOSED), + new RngCheck('A', CLOSED, 'Z', CLOSED), + new RngCheck('\001', CLOSED, '\007', OPEN), + new RngCheck('4', OPEN, '8', OPEN), }, "", null); + + tests = new MTest[] { new MTest("-carg 1", new Character('1')), + new MTest("-carg 3", new MErr('r', "3")), + new MTest("-carg a", new MErr('r', "a")), + new MTest("-carg m", new Character('m')), + new MTest("-carg z", new Character('z')), + new MTest("-carg A", new Character('A')), + new MTest("-carg 'L'", new Character('L')), + new MTest("-carg 'Z'", new Character('Z')), + new MTest("-carg \\001", new Character('\001')), + new MTest("-carg \\005", new Character('\005')), + new MTest("-carg '\\007'", new MErr('r', "'\\007'")), + new MTest("-carg '4'", new MErr('r', "'4'")), + new MTest("-carg 6", new Character('6')), + new MTest("-carg 8", new MErr('r', "8")), + new MTest("-carg '\\012'", new MErr('r', "'\\012'")), + new MTest("-carg 0", new MErr('r', "0")), }; + test.checkMatches(tests, MULTI_WORD); + + test.checkAdd("-foo=%i{[-50,100]}", ih, 'i', 1, "-foo=", + new RngCheck[] { new RngCheck(-50, CLOSED, 100, CLOSED), }, "", + null); + + tests = new MTest[] { new MTest("-foo=-51", new MErr('r', "-51")), + new MTest("-foo=-0x32", new Long(-0x32)), + new MTest("-foo=-0x33", new MErr('r', "-0x33")), + new MTest("-foo=-0777", new MErr('r', "-0777")), + new MTest("-foo=-07", new Long(-07)), + new MTest("-foo=0", new Long(0)), + new MTest("-foo=100", new Long(100)), + new MTest("-foo=0x5e", new Long(0x5e)), + new MTest("-foo=066", new Long(066)), + new MTest("-foo=06677", new MErr('r', "06677")), + new MTest("-foo=0xbeef", new MErr('r', "0xbeef")), + new MTest("-foo=foo", new MErr('m', "foo")), + new MTest("-foo=-51d", new MErr('m', "-51d")), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-foo2=%i", ih, 'i', 1, "-foo2=", null, "", null); + tests = new MTest[] { new MTest("-foo2=-51", new Long(-51)), + new MTest("-foo2=-0x33", new Long(-0x33)), + new MTest("-foo2=-0777", new Long(-0777)), + new MTest("-foo2=06677", new Long(06677)), + new MTest("-foo2=0xbeef", new Long(0xbeef)), + new MTest("-foo2=foo", new MErr('m', "foo")), + new MTest("-foo2=-51d", new MErr('m', "-51d")), + new MTest("-foo2=-51", new Long(-51)), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-foo3 %iX3", i3, 'i', 3, "-foo3 ", null, "", null); + tests = new MTest[] { + new MTest("-foo3 -51 678 0x45", new long[] { -51, 678, 0x45 }), + new MTest("-foo3 55 16f 55", new MErr('m', "16f")), + new MTest("-foo3 55 16", new MErr('v', "3")), }; + test.checkMatches(tests, MULTI_WORD); + + Vector<String> vec = new Vector<String>(100); + + test.checkAdd("-foov3 %iX3", vec, 'i', 3, "-foov3 ", null, "", null); + tests = new MTest[] { + new MTest("-foov3 -1 2 4", new long[] { -1, 2, 4 }, 0), + new MTest("-foov3 10 3 9", new long[] { 10, 3, 9 }, 1), + new MTest("-foov3 123 1 0", new long[] { 123, 1, 0 }, 2), }; + vec.clear(); + test.checkMatches(tests, MULTI_WORD); + test.checkAdd("-foov %i", vec, 'i', 1, "-foov ", null, "", null); + tests = new MTest[] { new MTest("-foov 11", new Long(11), 0), + new MTest("-foov 12", new Long(12), 1), + new MTest("-foov 13", new Long(13), 2), }; + vec.clear(); + test.checkMatches(tests, MULTI_WORD); + + test.checkAdd("-foo4 %i{[-50,100]}X2", i3, 'i', 2, "-foo4 ", + new RngCheck[] { new RngCheck(-50, CLOSED, 100, CLOSED), }, "", + null); + tests = new MTest[] { + new MTest("-foo4 -49 78", new long[] { -49, 78 }), + new MTest("-foo4 -48 102", new MErr('r', "102")), }; + test.checkMatches(tests, MULTI_WORD); + + test.checkAdd("-oct=%o{[-062,0144]}", ih, 'o', 1, "-oct=", + new RngCheck[] { new RngCheck(-50, CLOSED, 100, CLOSED), }, "", + null); + + tests = new MTest[] { new MTest("-oct=-063", new MErr('r', "-063")), + new MTest("-oct=-0x32", new MErr('m', "-0x32")), + new MTest("-oct=-0777", new MErr('r', "-0777")), + new MTest("-oct=-07", new Long(-07)), + new MTest("-oct=0", new Long(0)), + new MTest("-oct=100", new Long(64)), + new MTest("-oct=0xae", new MErr('m', "0xae")), + new MTest("-oct=66", new Long(066)), + new MTest("-oct=06677", new MErr('r', "06677")), + new MTest("-oct=0xbeef", new MErr('m', "0xbeef")), + new MTest("-oct=foo", new MErr('m', "foo")), + new MTest("-oct=-51d", new MErr('m', "-51d")), + new MTest("-oct=78", new MErr('m', "78")), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-oct2=%o", ih, 'o', 1, "-oct2=", null, "", null); + tests = new MTest[] { new MTest("-oct2=-063", new Long(-063)), + new MTest("-oct2=-0777", new Long(-0777)), + new MTest("-oct2=06677", new Long(06677)), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-dec=%d{[-0x32,0x64]}", ih, 'd', 1, "-dec=", + new RngCheck[] { new RngCheck(-50, CLOSED, 100, CLOSED), }, "", + null); + + tests = new MTest[] { new MTest("-dec=-063", new MErr('r', "-063")), + new MTest("-dec=-0x32", new MErr('m', "-0x32")), + new MTest("-dec=-0777", new MErr('r', "-0777")), + new MTest("-dec=-07", new Long(-07)), + new MTest("-dec=0", new Long(0)), + new MTest("-dec=100", new Long(100)), + new MTest("-dec=0xae", new MErr('m', "0xae")), + new MTest("-dec=66", new Long(66)), + new MTest("-dec=06677", new MErr('r', "06677")), + new MTest("-dec=0xbeef", new MErr('m', "0xbeef")), + new MTest("-dec=foo", new MErr('m', "foo")), + new MTest("-dec=-51d", new MErr('m', "-51d")), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-dec2=%d", ih, 'd', 1, "-dec2=", null, "", null); + tests = new MTest[] { new MTest("-dec2=-063", new Long(-63)), + new MTest("-dec2=-0777", new Long(-777)), + new MTest("-dec2=06677", new Long(6677)), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-hex=%x{[-0x32,0x64]}", ih, 'x', 1, "-hex=", + new RngCheck[] { new RngCheck(-50, CLOSED, 100, CLOSED), }, "", + null); + + tests = new MTest[] { new MTest("-hex=-06", new Long(-0x6)), + new MTest("-hex=-0x3g2", new MErr('m', "-0x3g2")), + new MTest("-hex=-0777", new MErr('r', "-0777")), + new MTest("-hex=-017", new Long(-0x17)), + new MTest("-hex=0", new Long(0)), + new MTest("-hex=64", new Long(0x64)), + new MTest("-hex=5e", new Long(0x5e)), + new MTest("-hex=66", new MErr('r', "66")), + new MTest("-hex=06677", new MErr('r', "06677")), + new MTest("-hex=0xbeef", new MErr('m', "0xbeef")), + new MTest("-hex=foo", new MErr('m', "foo")), + new MTest("-hex=-51d", new MErr('r', "-51d")), + new MTest("-hex=-51g", new MErr('m', "-51g")), + new MTest("-hex=", new MErr('c', "")), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-hex2=%x", ih, 'x', 1, "-hex2=", null, "", null); + tests = new MTest[] { new MTest("-hex2=-0777", new Long(-0x777)), + new MTest("-hex2=66", new Long(0x66)), + new MTest("-hex2=06677", new Long(0x6677)), + new MTest("-hex2=-51d", new Long(-0x51d)), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-char=%c{['b','m']}", ch, 'c', 1, "-char=", + new RngCheck[] { new RngCheck('b', CLOSED, 'm', CLOSED), }, "", + null); + + tests = new MTest[] { new MTest("-char=a", new MErr('r', "a")), + new MTest("-char=b", new Character('b')), + new MTest("-char='b'", new Character('b')), + new MTest("-char='\142'", new Character('b')), + new MTest("-char='\141'", new MErr('r', "'\141'")), + new MTest("-char=\142", new Character('b')), + new MTest("-char=\141", new MErr('r', "\141")), + new MTest("-char=m", new Character('m')), + new MTest("-char=z", new MErr('r', "z")), + new MTest("-char=bb", new MErr('m', "bb")), + new MTest("-char='b", new MErr('m', "'b")), + new MTest("-char='", new MErr('m', "'")), + new MTest("-char=a'", new MErr('m', "a'")), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-char2=%c", ch, 'c', 1, "-char2=", null, "", null); + tests = new MTest[] { new MTest("-char2=a", new Character('a')), + new MTest("-char2='\141'", new Character('\141')), + new MTest("-char2=\141", new Character('\141')), + new MTest("-char2=z", new Character('z')), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-charv3 %cX3", vec, 'c', 3, "-charv3 ", null, "", null); + tests = new MTest[] { + new MTest("-charv3 a b c", new char[] { 'a', 'b', 'c' }, 0), + new MTest("-charv3 'g' f '\\n'", new char[] { 'g', 'f', '\n' }, + 1), + new MTest("-charv3 1 \001 3", new char[] { '1', '\001', '3' }, + 2), }; + vec.clear(); + test.checkMatches(tests, MULTI_WORD); + test.checkAdd("-charv=%c", vec, 'c', 1, "-charv=", null, "", null); + tests = new MTest[] { new MTest("-charv=d", new Character('d'), 0), + new MTest("-charv='g'", new Character('g'), 1), + new MTest("-charv=\111", new Character('\111'), 2), }; + vec.clear(); + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-bool=%b{true}", bh, 'b', 1, "-bool=", + new RngCheck[] { new RngCheck(true), }, "", null); + + tests = new MTest[] { new MTest("-bool=true", new Boolean(true)), + new MTest("-bool=false", new MErr('r', "false")), + new MTest("-bool=fals", new MErr('m', "fals")), + new MTest("-bool=falsem", new MErr('m', "falsem")), + new MTest("-bool=truex", new MErr('m', "truex")), + new MTest("-bool=foo", new MErr('m', "foo")), + new MTest("-bool=1", new MErr('m', "1")), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-boo2=%b{true,false}", bh, 'b', 1, "-boo2=", + new RngCheck[] { new RngCheck(true), new RngCheck(false), }, + "", null); + + tests = new MTest[] { new MTest("-boo2=true", new Boolean(true)), + new MTest("-boo2=false", new Boolean(false)), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-boo3=%b", bh, 'b', 1, "-boo3=", null, "", null); + tests = new MTest[] { new MTest("-boo3=true", new Boolean(true)), + new MTest("-boo3=false", new Boolean(false)), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-boo3 %bX3", b3, 'b', 3, "-boo3 ", null, "", null); + tests = new MTest[] { + new MTest("-boo3 true false true", new boolean[] { true, false, + true }), + new MTest("-boo3 true fals true", new MErr('m', "fals")), }; + test.checkMatches(tests, MULTI_WORD); + + test.checkAdd("-boov3 %bX3", vec, 'b', 3, "-boov3 ", null, "", null); + tests = new MTest[] { + new MTest("-boov3 true true false", new boolean[] { true, true, + false }, 0), + new MTest("-boov3 false false true", new boolean[] { false, + false, true }, 1), }; + vec.clear(); + test.checkMatches(tests, MULTI_WORD); + test.checkAdd("-boov %b", vec, 'b', 1, "-boov ", null, "", null); + tests = new MTest[] { new MTest("-boov true", new Boolean(true), 0), + new MTest("-boov false", new Boolean(false), 1), + new MTest("-boov true", new Boolean(true), 2), }; + vec.clear(); + test.checkMatches(tests, MULTI_WORD); + + test.checkAdd("-v3 %vX2", b3, 'v', 2, "-v3 ", null, "", null); + tests = new MTest[] { new MTest("-v3", new boolean[] { true, true }), }; + test.checkMatches(tests, MULTI_WORD); + + test.checkAdd("-vf %v{false,true}X2", b3, 'v', 2, "-vf ", + new RngCheck[] { new RngCheck(false), new RngCheck(true), }, + "", null); + tests = new MTest[] { new MTest("-vf", new boolean[] { false, false }), }; + test.checkMatches(tests, MULTI_WORD); + + test.checkAdd("-str=%s{(john,zzzz]}", sh, 's', 1, "-str=", + new RngCheck[] { new RngCheck("john", OPEN, "zzzz", CLOSED), }, + "", null); + + tests = new MTest[] { new MTest("-str=john", new MErr('r', "john")), + new MTest("-str=joho ", "joho "), + new MTest("-str=joho ", "joho "), + new MTest("-str=zzzz", "zzzz"), + new MTest("-str= joho", new MErr('r', " joho")), + new MTest("-str=jnhn ", new MErr('r', "jnhn ")), + new MTest("-str=zzzzz", new MErr('r', "zzzzz")), + new MTest("-str=\"joho\"", new MErr('r', "\"joho\"")), + new MTest("-str=\"joho", new MErr('r', "\"joho")), + new MTest("-str=joho j", "joho j"), // new MErr('m', "joho j")), + }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-str2=%s", sh, 's', 1, "-str2=", null, "", null); + tests = new MTest[] { new MTest("-str2= jnhn", " jnhn"), + new MTest("-str2=zzzzz", "zzzzz"), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-str3 %sX3", s3, 's', 3, "-str3 ", null, "", null); + tests = new MTest[] { + new MTest("-str3 foo bar johnny", new String[] { "foo", "bar", + "johnny" }), + new MTest("-str3 zzzzz \"bad foo", new String[] { "zzzzz", + "\"bad", "foo" }), // new MErr('m', "\"bad")), + }; + test.checkMatches(tests, MULTI_WORD); + + test.checkAdd("-strv3 %sX3", vec, 's', 3, "-strv3 ", null, "", null); + tests = new MTest[] { + new MTest("-strv3 foo bar \"hihi\"", new String[] { "foo", + "bar", "\"hihi\"" }, 0), + new MTest("-strv3 a 123 gg", new String[] { "a", "123", "gg" }, + 1), }; + vec.clear(); + test.checkMatches(tests, MULTI_WORD); + test.checkAdd("-strv=%s", vec, 's', 1, "-strv=", null, "", null); + tests = new MTest[] { new MTest("-strv=d", "d", 0), + new MTest("-strv='g'", "'g'", 1), + new MTest("-strv=\\111", "\\111", 2), }; + vec.clear(); + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-float=%f{(-0.001,1000.0]}", dh, 'f', 1, "-float=", + new RngCheck[] { new RngCheck(-0.001, OPEN, 1000.0, CLOSED), }, + "", null); + + tests = new MTest[] { + new MTest("-float=-0.000999", new Double(-0.000999)), + new MTest("-float=1e-3", new Double(0.001)), + new MTest("-float=12.33e1", new Double(123.3)), + new MTest("-float=1e3", new Double(1e3)), + new MTest("-float=1000.000", new Double(1000.0)), + new MTest("-float=-0.001", new MErr('r', "-0.001")), + new MTest("-float=-1e-3", new MErr('r', "-1e-3")), + new MTest("-float=1000.001", new MErr('r', "1000.001")), + new MTest("-float=.", new MErr('m', ".")), + new MTest("-float= 124.5 ", new Double(124.5)), + new MTest("-float=124.5x", new MErr('m', "124.5x")), + new MTest("-float= foo ", new MErr('m', " foo ")), + new MTest("-float=1e1", new Double(10)), + new MTest("-float=1e ", new MErr('m', "1e ")), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-float2=%f", dh, 'f', 1, "-float2=", null, "", null); + tests = new MTest[] { new MTest("-float2=-0.001", new Double(-0.001)), + new MTest("-float2=-1e-3", new Double(-1e-3)), + new MTest("-float2=1000.001", new Double(1000.001)), }; + test.checkMatches(tests, ONE_WORD); + + test.checkAdd("-f3 %fX3", d3, 'f', 3, "-f3 ", null, "", null); + tests = new MTest[] { + new MTest("-f3 -0.001 1.23e5 -9.88e-4", new double[] { -0.001, + 1.23e5, -9.88e-4 }), + new MTest("-f3 7.88 foo 9.0", new MErr('m', "foo")), + new MTest("-f3 7.88 . 9.0", new MErr('m', ".")), + new MTest("-f3 7.88 3.0 9.0x", new MErr('m', "9.0x")), }; + test.checkMatches(tests, MULTI_WORD); + + test.checkAdd("-fv3 %fX3", vec, 'f', 3, "-fv3 ", null, "", null); + tests = new MTest[] { + new MTest("-fv3 1.0 3.444 6.7", + new double[] { 1.0, 3.444, 6.7 }, 0), + new MTest("-fv3 13e-5 145.678 0.0001e45", new double[] { 13e-5, + 145.678, 0.0001e45 }, 1), + new MTest("-fv3 11.11 3.1245 -1e-4", new double[] { 11.11, + 3.1245, -1e-4 }, 2), + new MTest("-fv3 1.0 2 3", new double[] { 1.0, 2.0, 3.0 }, 3), }; + vec.clear(); + test.checkMatches(tests, MULTI_WORD); + test.checkAdd("-fv %f", vec, 'f', 1, "-fv ", null, "", null); + tests = new MTest[] { + new MTest("-fv -15.1234", new Double(-15.1234), 0), + new MTest("-fv -1.234e-7", new Double(-1.234e-7), 1), + new MTest("-fv 0.001111", new Double(0.001111), 2), }; + vec.clear(); + test.checkMatches(tests, MULTI_WORD); + + IntHolder intHolder = new IntHolder(); + StringHolder strHolder = new StringHolder(); + + ArgParser parser = new ArgParser("test"); + parser.addOption("-foo %d #an int", intHolder); + parser.addOption("-bar %s #a string", strHolder); + args = new String[] { "zzz", "-cat", "-foo", "123", "yyy", "-bar", + "xxxx", "xxx" }; + + String[] unmatchedCheck = new String[] { "zzz", "-cat", "yyy", "xxx" }; + + String[] unmatched = parser.matchAllArgs(args, 0, 0); + test.checkStringArray("Unmatched args:", unmatched, unmatchedCheck); + + vec.clear(); + for (int i = 0; i < args.length;) { + try { + i = parser.matchArg(args, i); + if (parser.getUnmatchedArgument() != null) { + vec.add(parser.getUnmatchedArgument()); + } + } catch (Exception e) { + } + } + unmatched = (String[]) vec.toArray(new String[0]); + test.checkStringArray("My unmatched args:", unmatched, unmatchedCheck); + + System.out.println("\nPassed\n"); + + } } diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/BooleanHolder.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/BooleanHolder.java index c5dece115..dba72b6de 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/BooleanHolder.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/BooleanHolder.java @@ -1,4 +1,5 @@ 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 @@ -18,37 +19,34 @@ package com.netscape.pkisilent.argparser; // --- END COPYRIGHT BLOCK --- /** - * Wrapper class which ``holds'' a boolean value, - * enabling methods to return boolean values through - * arguments. - */ -public class BooleanHolder implements java.io.Serializable -{ - /** + * Wrapper class which ``holds'' a boolean value, enabling methods to return + * boolean values through arguments. + */ +public class BooleanHolder implements java.io.Serializable { + /** * */ private static final long serialVersionUID = -2863748864787121510L; /** - * Value of the boolean, set and examined - * by the application as needed. - */ - public boolean value; + * Value of the boolean, set and examined by the application as needed. + */ + public boolean value; - /** - * Constructs a new <code>BooleanHolder</code> with an initial - * value of <code>false</code>. - */ - public BooleanHolder () - { value = false; - } + /** + * Constructs a new <code>BooleanHolder</code> with an initial value of + * <code>false</code>. + */ + public BooleanHolder() { + value = false; + } - /** - * Constructs a new <code>BooleanHolder</code> with a - * specific initial value. - * - * @param b Initial boolean value. - */ - public BooleanHolder (boolean b) - { value = b; - } + /** + * Constructs a new <code>BooleanHolder</code> with a specific initial + * value. + * + * @param b Initial boolean value. + */ + public BooleanHolder(boolean b) { + value = b; + } } diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/CharHolder.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/CharHolder.java index b30259b4f..eed578922 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/CharHolder.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/CharHolder.java @@ -1,4 +1,5 @@ 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 @@ -18,39 +19,32 @@ package com.netscape.pkisilent.argparser; // --- END COPYRIGHT BLOCK --- /** - * Wrapper class which ``holds'' a character value, - * enabling methods to return character values through - * arguments. - */ -public class CharHolder implements java.io.Serializable -{ - /** + * Wrapper class which ``holds'' a character value, enabling methods to return + * character values through arguments. + */ +public class CharHolder implements java.io.Serializable { + /** * */ private static final long serialVersionUID = 7340010668929015745L; /** - * Value of the character, set and examined - * by the application as needed. - */ - public char value; + * Value of the character, set and examined by the application as needed. + */ + public char value; - /** - * Constructs a new <code>CharHolder</code> with an initial - * value of 0. - */ - public CharHolder () - { value = 0; - } + /** + * Constructs a new <code>CharHolder</code> with an initial value of 0. + */ + public CharHolder() { + value = 0; + } - /** - * Constructs a new <code>CharHolder</code> with a - * specific initial value. - * - * @param c Initial character value. - */ - public CharHolder (char c) - { value = c; - } + /** + * Constructs a new <code>CharHolder</code> with a specific initial value. + * + * @param c Initial character value. + */ + public CharHolder(char c) { + value = c; + } } - - diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/DoubleHolder.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/DoubleHolder.java index 293a9cc7e..b75f9f150 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/DoubleHolder.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/DoubleHolder.java @@ -1,4 +1,5 @@ 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 @@ -18,38 +19,32 @@ package com.netscape.pkisilent.argparser; // --- END COPYRIGHT BLOCK --- /** - * Wrapper class which ``holds'' a double value, - * enabling methods to return double values through - * arguments. - */ -public class DoubleHolder implements java.io.Serializable -{ - /** + * Wrapper class which ``holds'' a double value, enabling methods to return + * double values through arguments. + */ +public class DoubleHolder implements java.io.Serializable { + /** * */ private static final long serialVersionUID = 5461991811517552431L; /** - * Value of the double, set and examined - * by the application as needed. - */ - public double value; + * Value of the double, set and examined by the application as needed. + */ + public double value; - /** - * Constructs a new <code>DoubleHolder</code> with an initial - * value of 0. - */ - public DoubleHolder () - { value = 0; - } + /** + * Constructs a new <code>DoubleHolder</code> with an initial value of 0. + */ + public DoubleHolder() { + value = 0; + } - /** - * Constructs a new <code>DoubleHolder</code> with a - * specific initial value. - * - * @param d Initial double value. - */ - public DoubleHolder (double d) - { value = d; - } + /** + * Constructs a new <code>DoubleHolder</code> with a specific initial value. + * + * @param d Initial double value. + */ + public DoubleHolder(double d) { + value = d; + } } - diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/FloatHolder.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/FloatHolder.java index 0fbcc45af..14a13d9e0 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/FloatHolder.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/FloatHolder.java @@ -1,4 +1,5 @@ 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 @@ -18,39 +19,32 @@ package com.netscape.pkisilent.argparser; // --- END COPYRIGHT BLOCK --- /** - * Wrapper class which ``holds'' a float value, - * enabling methods to return float values through - * arguments. - */ -public class FloatHolder implements java.io.Serializable -{ - /** + * Wrapper class which ``holds'' a float value, enabling methods to return float + * values through arguments. + */ +public class FloatHolder implements java.io.Serializable { + /** * */ private static final long serialVersionUID = -7962968109874934361L; /** - * Value of the float, set and examined - * by the application as needed. - */ - public float value; + * Value of the float, set and examined by the application as needed. + */ + public float value; - /** - * Constructs a new <code>FloatHolder</code> with an initial - * value of 0. - */ - public FloatHolder () - { value = 0; - } + /** + * Constructs a new <code>FloatHolder</code> with an initial value of 0. + */ + public FloatHolder() { + value = 0; + } - /** - * Constructs a new <code>FloatHolder</code> with a - * specific initial value. - * - * @param f Initial float value. - */ - public FloatHolder (float f) - { value = f; - } + /** + * Constructs a new <code>FloatHolder</code> with a specific initial value. + * + * @param f Initial float value. + */ + public FloatHolder(float f) { + value = f; + } } - - diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/IntHolder.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/IntHolder.java index efd6d1bfc..446af9eb5 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/IntHolder.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/IntHolder.java @@ -1,4 +1,5 @@ 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 @@ -18,38 +19,32 @@ package com.netscape.pkisilent.argparser; // --- END COPYRIGHT BLOCK --- /** - * Wrapper class which ``holds'' an integer value, - * enabling methods to return integer values through - * arguments. - */ -public class IntHolder implements java.io.Serializable -{ - /** + * Wrapper class which ``holds'' an integer value, enabling methods to return + * integer values through arguments. + */ +public class IntHolder implements java.io.Serializable { + /** * */ private static final long serialVersionUID = -5303361328570056819L; /** - * Value of the integer, set and examined - * by the application as needed. - */ - public int value; + * Value of the integer, set and examined by the application as needed. + */ + public int value; - /** - * Constructs a new <code>IntHolder</code> with an initial - * value of 0. - */ - public IntHolder () - { value = 0; - } + /** + * Constructs a new <code>IntHolder</code> with an initial value of 0. + */ + public IntHolder() { + value = 0; + } - /** - * Constructs a new <code>IntHolder</code> with a - * specific initial value. - * - * @param i Initial integer value. - */ - public IntHolder (int i) - { value = i; - } + /** + * Constructs a new <code>IntHolder</code> with a specific initial value. + * + * @param i Initial integer value. + */ + public IntHolder(int i) { + value = i; + } } - diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/LongHolder.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/LongHolder.java index 84752d604..37aa1c91c 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/LongHolder.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/LongHolder.java @@ -1,4 +1,5 @@ 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 @@ -18,38 +19,32 @@ package com.netscape.pkisilent.argparser; // --- END COPYRIGHT BLOCK --- /** - * Wrapper class which ``holds'' a long value, - * enabling methods to return long values through - * arguments. - */ -public class LongHolder implements java.io.Serializable -{ - /** + * Wrapper class which ``holds'' a long value, enabling methods to return long + * values through arguments. + */ +public class LongHolder implements java.io.Serializable { + /** * */ private static final long serialVersionUID = 1559599139421340971L; /** - * Value of the long, set and examined - * by the application as needed. - */ - public long value; + * Value of the long, set and examined by the application as needed. + */ + public long value; - /** - * Constructs a new <code>LongHolder</code> with an initial - * value of 0. - */ - public LongHolder () - { value = 0; - } + /** + * Constructs a new <code>LongHolder</code> with an initial value of 0. + */ + public LongHolder() { + value = 0; + } - /** - * Constructs a new <code>LongHolder</code> with a - * specific initial value. - * - * @param l Initial long value. - */ - public LongHolder (long l) - { value = l; - } + /** + * Constructs a new <code>LongHolder</code> with a specific initial value. + * + * @param l Initial long value. + */ + public LongHolder(long l) { + value = l; + } } - diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/ObjectHolder.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/ObjectHolder.java index cc8361d97..edb835ee0 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/ObjectHolder.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/ObjectHolder.java @@ -1,4 +1,5 @@ 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 @@ -18,37 +19,34 @@ package com.netscape.pkisilent.argparser; // --- END COPYRIGHT BLOCK --- /** - * Wrapper class which ``holds'' an Object reference, - * enabling methods to return Object references through - * arguments. - */ -public class ObjectHolder implements java.io.Serializable -{ - /** + * Wrapper class which ``holds'' an Object reference, enabling methods to return + * Object references through arguments. + */ +public class ObjectHolder implements java.io.Serializable { + /** * */ private static final long serialVersionUID = 1825881254530066307L; /** - * Value of the Object reference, set and examined - * by the application as needed. - */ - public Object value; + * Value of the Object reference, set and examined by the application as + * needed. + */ + public Object value; - /** - * Constructs a new <code>ObjectHolder</code> with an initial - * value of <code>null</code>. - */ - public ObjectHolder () - { value = null; - } + /** + * Constructs a new <code>ObjectHolder</code> with an initial value of + * <code>null</code>. + */ + public ObjectHolder() { + value = null; + } - /** - * Constructs a new <code>ObjectHolder</code> with a - * specific initial value. - * - * @param o Initial Object reference. - */ - public ObjectHolder (Object o) - { value = o; - } + /** + * Constructs a new <code>ObjectHolder</code> with a specific initial value. + * + * @param o Initial Object reference. + */ + public ObjectHolder(Object o) { + value = o; + } } diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/SimpleExample.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/SimpleExample.java index 8086131b8..21e056060 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/SimpleExample.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/SimpleExample.java @@ -1,4 +1,5 @@ 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 @@ -18,39 +19,35 @@ package com.netscape.pkisilent.argparser; // --- END COPYRIGHT BLOCK --- /** - * Gives a very simple example of the use of - * {@link argparser.ArgParser ArgParser}. + * Gives a very simple example of the use of {@link argparser.ArgParser + * ArgParser}. */ -public class SimpleExample -{ - /** - * Run this to invoke command line parsing. - */ - public static void main (String[] args) - { - // create holder objects for storing results ... - - DoubleHolder theta = new DoubleHolder(); - StringHolder fileName = new StringHolder(); - BooleanHolder debug = new BooleanHolder(); - - // create the parser and specify the allowed options ... - - ArgParser parser = new ArgParser("java argparser.SimpleExample"); - parser.addOption ("-theta %f #theta value (in degrees)", theta); - parser.addOption ("-file %s #name of the operating file", fileName); - parser.addOption ("-debug %v #enables display of debugging info", - debug); - - // and then match the arguments - - parser.matchAllArgs (args); - - // now print out the values - - System.out.println ("theta=" + theta.value); - System.out.println ("fileName=" + fileName.value); - System.out.println ("debug=" + debug.value); - } -} +public class SimpleExample { + /** + * Run this to invoke command line parsing. + */ + public static void main(String[] args) { + // create holder objects for storing results ... + + DoubleHolder theta = new DoubleHolder(); + StringHolder fileName = new StringHolder(); + BooleanHolder debug = new BooleanHolder(); + + // create the parser and specify the allowed options ... + + ArgParser parser = new ArgParser("java argparser.SimpleExample"); + parser.addOption("-theta %f #theta value (in degrees)", theta); + parser.addOption("-file %s #name of the operating file", fileName); + parser.addOption("-debug %v #enables display of debugging info", debug); + // and then match the arguments + + parser.matchAllArgs(args); + + // now print out the values + + System.out.println("theta=" + theta.value); + System.out.println("fileName=" + fileName.value); + System.out.println("debug=" + debug.value); + } +} diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/StringHolder.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/StringHolder.java index 9b41b6a23..ea85c7d04 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/StringHolder.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/StringHolder.java @@ -1,4 +1,5 @@ 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 @@ -18,38 +19,34 @@ package com.netscape.pkisilent.argparser; // --- END COPYRIGHT BLOCK --- /** - * Wrapper class which ``holds'' a String reference, - * enabling methods to return String references through - * arguments. - */ -public class StringHolder implements java.io.Serializable -{ - /** + * Wrapper class which ``holds'' a String reference, enabling methods to return + * String references through arguments. + */ +public class StringHolder implements java.io.Serializable { + /** * */ private static final long serialVersionUID = -3184348746223759310L; /** - * Value of the String reference, set and examined - * by the application as needed. - */ - public String value; + * Value of the String reference, set and examined by the application as + * needed. + */ + public String value; - /** - * Constructs a new <code>StringHolder</code> with an - * initial value of <code>null</code>. - */ - public StringHolder () - { value = null; - } + /** + * Constructs a new <code>StringHolder</code> with an initial value of + * <code>null</code>. + */ + public StringHolder() { + value = null; + } - /** - * Constructs a new <code>StringHolder</code> with a - * specific initial value. - * - * @param s Initial String reference. - */ - public StringHolder (String s) - { value = s; - } + /** + * Constructs a new <code>StringHolder</code> with a specific initial value. + * + * @param s Initial String reference. + */ + public StringHolder(String s) { + value = s; + } } - diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/StringScanException.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/StringScanException.java index 4b71a0f21..9d942f0df 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/StringScanException.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/StringScanException.java @@ -1,4 +1,5 @@ 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,40 +20,37 @@ package com.netscape.pkisilent.argparser; import java.io.IOException; -/** - * Exception class used by <code>StringScanner</code> when - * command line arguments do not parse correctly. - * - * @author John E. Lloyd, Winter 2001 - * @see StringScanner - */ -class StringScanException extends IOException -{ - /** +/** + * Exception class used by <code>StringScanner</code> when command line + * arguments do not parse correctly. + * + * @author John E. Lloyd, Winter 2001 + * @see StringScanner + */ +class StringScanException extends IOException { + /** * */ private static final long serialVersionUID = 4923445904507805754L; int failIdx; - /** - * Creates a new StringScanException with the given message. - * - * @param msg Error message - * @see StringScanner - */ + /** + * Creates a new StringScanException with the given message. + * + * @param msg Error message + * @see StringScanner + */ - public StringScanException (String msg) - { super (msg); - } + public StringScanException(String msg) { + super(msg); + } - public StringScanException (int idx, String msg) - { - super (msg); - failIdx = idx; - } + public StringScanException(int idx, String msg) { + super(msg); + failIdx = idx; + } - public int getFailIndex() - { - return failIdx; - } + public int getFailIndex() { + return failIdx; + } } diff --git a/pki/base/silent/src/com/netscape/pkisilent/argparser/StringScanner.java b/pki/base/silent/src/com/netscape/pkisilent/argparser/StringScanner.java index fae67c791..77d736522 100644 --- a/pki/base/silent/src/com/netscape/pkisilent/argparser/StringScanner.java +++ b/pki/base/silent/src/com/netscape/pkisilent/argparser/StringScanner.java @@ -1,4 +1,5 @@ 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 @@ -18,634 +19,542 @@ 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. - */ - -class StringScanner -{ - private char[] buf; - private int idx; - private int len; - private String stringDelimiters = ""; - - public StringScanner (String s) - { - buf = new char[s.length()+1]; - s.getChars (0, s.length(), buf, 0); - len = s.length(); - buf[len] = 0; - idx = 0; - } - - public int getIndex() - { return idx; - } - - public void setIndex(int i) - { if (i < 0) - { idx = 0; - } - else if (i > len) - { idx = len; - } - else - { idx = i; - } - } - - public void setStringDelimiters (String s) - { stringDelimiters = s; - } - - public String getStringDelimiters() - { return stringDelimiters; - } - - public char scanChar () - throws StringScanException - { - int idxSave = idx; - skipWhiteSpace(); - try - { if (buf[idx] == '\'') - { return scanQuotedChar(); - } - else - { return scanUnquotedChar(); - } - } - catch (StringScanException e) - { idx = idxSave; - throw e; - } - } - - public char scanQuotedChar () - throws StringScanException - { - StringScanException exception = null; - char retval = 0; - int idxSave = idx; - - skipWhiteSpace(); - if (idx == len) - { exception = new StringScanException (idx, "end of input"); - } - else if (buf[idx++] == '\'') - { try - { retval = scanUnquotedChar(); - } - catch (StringScanException e) - { exception = e; - } - if (exception==null) - { if (idx==len) - { exception = new StringScanException - (idx, "end of input"); - } - else if (buf[idx++] != '\'') - { exception = new StringScanException - (idx-1, "unclosed quoted character"); - } - } - } - else - { exception = new StringScanException - (idx-1, "uninitialized quoted character"); - } - if (exception!=null) - { idx = idxSave; - throw exception; - } - return retval; - } - - public char scanUnquotedChar () - throws StringScanException - { - StringScanException exception = null; - char c, retval = 0; - int idxSave = idx; - - if (idx == len) - { exception = new StringScanException (idx, "end of input"); - } - else if ((c = buf[idx++]) == '\\') - { if (idx == len) - { exception = new StringScanException (idx, "end of input"); - } - else - { - c = buf[idx++]; - if (c == '"') - { retval = '"'; - } - else if (c == '\'') - { retval = '\''; - } - else if (c == '\\') - { retval = '\\'; - } - else if (c == 'n') - { retval = '\n'; - } - else if (c == 't') - { retval = '\t'; - } - else if (c == 'b') - { retval = '\b'; - } - else if (c == 'r') - { retval = '\r'; - } - else if (c == 'f') - { retval = '\f'; - } - else if ('0' <= c && c < '8') - { int v = c - '0'; - for (int j=0; j<2; j++) - { if (idx==len) - { break; - } - c = buf[idx]; - if ('0' <= c && c < '8' && (v*8 + (c-'0')) <= 255) - { v = v*8 + (c-'0'); - idx++; - } - else - { break; - } - } - retval = (char)v; - } - else - { exception = new StringScanException - (idx-1, "illegal escape character '" + c + "'"); - } - } - } - else - { retval = c; - } - if (exception!=null) - { idx = idxSave; - throw exception; - } - return retval; - } - - public String scanQuotedString () - throws StringScanException - { - StringScanException exception = null; - StringBuffer sbuf = new StringBuffer(len); - char c; - int idxSave = idx; - - skipWhiteSpace(); - if (idx == len) - { exception = new StringScanException (idx, "end of input"); - } - else if ((c=buf[idx++]) == '"') - { while (idx<len && (c=buf[idx]) != '"' && c != '\n') - { if (c == '\\') - { try - { c = scanUnquotedChar(); - } - catch (StringScanException e) - { exception = e; - break; - } - } - else - { idx++; - } - sbuf.append (c); - } - if (exception == null && idx>=len) - { exception = new StringScanException (len, "end of input"); - } - else if (exception == null && c == '\n') - { exception = new StringScanException - (idx, "unclosed quoted string"); - } - else - { idx++; - } - } - else - { exception = new StringScanException (idx-1, -"quoted string must start with \""); - } - if (exception != null) - { idx = idxSave; - throw exception; - } - return sbuf.toString(); - } - - public String scanNonWhiteSpaceString() - throws StringScanException - { - StringBuffer sbuf = new StringBuffer(len); - int idxSave = idx; - char c; - - skipWhiteSpace(); - if (idx == len) - { StringScanException e = new StringScanException ( - idx, "end of input"); - idx = idxSave; - throw e; - } - else - { c = buf[idx++]; - while (idx<len && !Character.isWhitespace(c) - && stringDelimiters.indexOf(c) == -1) - { sbuf.append(c); - c = buf[idx++]; - } - if (Character.isWhitespace(c) || - stringDelimiters.indexOf(c) != -1) - { idx--; - } - else - { sbuf.append(c); - } - } - return sbuf.toString(); - } - - public String scanString () - throws StringScanException - { - int idxSave = idx; - skipWhiteSpace(); - try - { if (buf[idx] == '"') - { return scanQuotedString(); - } - else - { return scanNonWhiteSpaceString(); - } - } - catch (StringScanException e) - { idx = idxSave; - throw e; - } - } - - public String getString () - throws StringScanException - { - StringBuffer sbuf = new StringBuffer(len); - while (idx < len) - { sbuf.append (buf[idx++]); - } - return sbuf.toString(); - } - - public long scanInt () - throws StringScanException - { - int idxSave = idx; - char c; - int sign = 1; - - skipWhiteSpace(); - if ((c=buf[idx]) == '-' || c == '+') - { sign = (c == '-' ? -1 : 1); - idx++; - } - try - { if (idx==len) - { throw new StringScanException (len, "end of input"); - } - else if ((c=buf[idx]) == '0') - { if ((c=buf[idx+1]) == 'x' || c == 'X') - { idx += 2; - return sign*scanInt (16, false); - } - else - { return sign*scanInt (8, false); - } - } - else - { return sign*scanInt (10, false); - } - } - catch (StringScanException e) - { idx = idxSave; - throw e; - } - } - - public long scanInt (int radix) - throws StringScanException - { - return scanInt (radix, /*skipWhite=*/true); - } - - private String baseDesc (int radix) - { - switch (radix) - { case 10: - { return "decimal"; - } - case 8: - { return "octal"; - } - case 16: - { return "hex"; - } - default: - { return "base " + radix; - } - } - } - - public long scanInt (int radix, boolean skipWhite) - throws StringScanException - { - StringScanException exception = null; - int charval, idxSave = idx; - char c; - long val = 0; - boolean negate = false; - - if (skipWhite) - { skipWhiteSpace(); - } - if ((c=buf[idx]) == '-' || c == '+') - { negate = (c == '-'); - idx++; - } - if (idx >= len) - { exception = new StringScanException (len, "end of input"); - } - else if ((charval=Character.digit(buf[idx++],radix)) == -1) - { exception = new StringScanException - (idx-1, "malformed " + baseDesc(radix) + " integer"); - } - else - { val = charval; - while ((charval=Character.digit(buf[idx],radix)) != -1) - { val = val*radix + charval; - idx++; - } - if (Character.isLetter(c=buf[idx]) || - Character.isDigit(c) || c == '_') - { exception = new StringScanException - (idx, "malformed " + baseDesc(radix) + " integer"); - } - } - if (exception != null) - { idx = idxSave; - throw exception; - } - return negate ? -val : val; - } - - public double scanDouble () - throws StringScanException - { - StringScanException exception = null; - int idxSave = idx; - char c; - // parse [-][0-9]*[.][0-9]*[eE][-][0-9]* - boolean hasDigits = false; - boolean signed; - double value = 0; - - skipWhiteSpace(); - if (idx == len) - { exception = new StringScanException ("end of input"); - } - else - { - if ((c=buf[idx]) == '-' || c == '+') - { signed = true; - idx++; - } - if (matchDigits()) - { hasDigits = true; - } - if (buf[idx] == '.') - { idx++; - } - if (!hasDigits && (buf[idx] < '0' || buf[idx] > '9')) - { if (idx==len) - { exception = new StringScanException (idx, "end of input"); - } - else - { exception = new StringScanException ( - idx, "malformed floating number: no digits"); - } - } - else - { matchDigits(); - - if ((c=buf[idx]) == 'e' || c == 'E') - { idx++; - if ((c=buf[idx]) == '-' || c == '+') - { signed = true; - idx++; - } - if (buf[idx] < '0' || buf[idx] > '9') - { if (idx==len) - { exception = new StringScanException( - idx, "end of input"); - } - else - { exception = new StringScanException (idx, -"malformed floating number: no digits in exponent"); - } - } - else - { matchDigits(); - } - } - } - } - if (exception == null) - { -// if (Character.isLetterOrDigit(c=buf[idx]) || c == '_') -// { exception = new StringScanException (idx, -//"malformed floating number"); -// } -// else - { - try - { value = Double.parseDouble(new String(buf, idxSave, - idx-idxSave)); - } - catch (NumberFormatException e) - { exception = new StringScanException ( - idx, "malformed floating number"); - } - } - } - if (exception != null) - { idx = idxSave; - throw exception; - } - return value; - } - - public boolean scanBoolean () - throws StringScanException - { - StringScanException exception = null; - int idxSave = idx; - String testStr = "false"; - boolean testval = false; - char c; - - skipWhiteSpace(); - if (buf[idx] == 't') - { testStr = "true"; - testval = true; - } - else - { testval = false; - } - int i = 0; - for (i=0; i<testStr.length(); i++) - { if (testStr.charAt(i) != buf[idx]) - { if (idx==len) - { exception = new StringScanException (idx, "end of input"); - } - break; - } - idx++; - } - if (exception==null) - { if (i<testStr.length() || - Character.isLetterOrDigit(c=buf[idx]) || c == '_') - { exception = new StringScanException (idx, "illegal boolean"); - } - } - if (exception != null) - { idx = idxSave; - throw exception; - } - return testval; - } - - public boolean matchString (String s) - { - int k = idx; - for (int i=0; i<s.length(); i++) - { if (k >= len || s.charAt(i) != buf[k++]) - { return false; - } - } - idx = k; - return true; - } - - public boolean matchDigits () - { - int k = idx; - char c; - - while ((c=buf[k]) >= '0' && c <= '9') - { k++; - } - if (k > idx) - { idx = k; - return true; - } - else - { return false; - } - } - - public void skipWhiteSpace() - { - while (Character.isWhitespace(buf[idx])) - { idx++; - } - } - - private int skipWhiteSpace(int k) - { - while (Character.isWhitespace(buf[k])) - { k++; - } - return k; - } - - public boolean atEnd() - { - return idx == len; - } - - public boolean atBeginning() - { - return idx == 0; - } - - public void ungetc() - { - if (idx > 0) - { idx--; - } - } - - public char getc() - { - char c = buf[idx]; - if (idx < len) - { idx++; - } - return c; - } - - public char peekc() - { - return buf[idx]; - } - - public String substring (int i0, int i1) - { - if (i0 < 0) - { i0 = 0; - } - else if (i0 >= len) - { i0= len-1; - } - if (i1 < 0) - { i1 = 0; - } - else if (i1 > len) - { i1= len; - } - if (i1 <= i0) - { return ""; - } - return new String (buf, i0, i1-i0); - } - - public String substring (int i0) - { - if (i0 < 0) - { i0 = 0; - } - if (i0 >= len) - { return ""; - } - else - { return new String (buf, i0, len-i0); - } - } + * 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. + */ + +class StringScanner { + private char[] buf; + private int idx; + private int len; + private String stringDelimiters = ""; + + public StringScanner(String s) { + buf = new char[s.length() + 1]; + s.getChars(0, s.length(), buf, 0); + len = s.length(); + buf[len] = 0; + idx = 0; + } + + public int getIndex() { + return idx; + } + + public void setIndex(int i) { + if (i < 0) { + idx = 0; + } else if (i > len) { + idx = len; + } else { + idx = i; + } + } + + public void setStringDelimiters(String s) { + stringDelimiters = s; + } + + public String getStringDelimiters() { + return stringDelimiters; + } + + public char scanChar() throws StringScanException { + int idxSave = idx; + skipWhiteSpace(); + try { + if (buf[idx] == '\'') { + return scanQuotedChar(); + } else { + return scanUnquotedChar(); + } + } catch (StringScanException e) { + idx = idxSave; + throw e; + } + } + + public char scanQuotedChar() throws StringScanException { + StringScanException exception = null; + char retval = 0; + int idxSave = idx; + + skipWhiteSpace(); + if (idx == len) { + exception = new StringScanException(idx, "end of input"); + } else if (buf[idx++] == '\'') { + try { + retval = scanUnquotedChar(); + } catch (StringScanException e) { + exception = e; + } + if (exception == null) { + if (idx == len) { + exception = new StringScanException(idx, "end of input"); + } else if (buf[idx++] != '\'') { + exception = new StringScanException(idx - 1, + "unclosed quoted character"); + } + } + } else { + exception = new StringScanException(idx - 1, + "uninitialized quoted character"); + } + if (exception != null) { + idx = idxSave; + throw exception; + } + return retval; + } + + public char scanUnquotedChar() throws StringScanException { + StringScanException exception = null; + char c, retval = 0; + int idxSave = idx; + + if (idx == len) { + exception = new StringScanException(idx, "end of input"); + } else if ((c = buf[idx++]) == '\\') { + if (idx == len) { + exception = new StringScanException(idx, "end of input"); + } else { + c = buf[idx++]; + if (c == '"') { + retval = '"'; + } else if (c == '\'') { + retval = '\''; + } else if (c == '\\') { + retval = '\\'; + } else if (c == 'n') { + retval = '\n'; + } else if (c == 't') { + retval = '\t'; + } else if (c == 'b') { + retval = '\b'; + } else if (c == 'r') { + retval = '\r'; + } else if (c == 'f') { + retval = '\f'; + } else if ('0' <= c && c < '8') { + int v = c - '0'; + for (int j = 0; j < 2; j++) { + if (idx == len) { + break; + } + c = buf[idx]; + if ('0' <= c && c < '8' && (v * 8 + (c - '0')) <= 255) { + v = v * 8 + (c - '0'); + idx++; + } else { + break; + } + } + retval = (char) v; + } else { + exception = new StringScanException(idx - 1, + "illegal escape character '" + c + "'"); + } + } + } else { + retval = c; + } + if (exception != null) { + idx = idxSave; + throw exception; + } + return retval; + } + + public String scanQuotedString() throws StringScanException { + StringScanException exception = null; + StringBuffer sbuf = new StringBuffer(len); + char c; + int idxSave = idx; + + skipWhiteSpace(); + if (idx == len) { + exception = new StringScanException(idx, "end of input"); + } else if ((c = buf[idx++]) == '"') { + while (idx < len && (c = buf[idx]) != '"' && c != '\n') { + if (c == '\\') { + try { + c = scanUnquotedChar(); + } catch (StringScanException e) { + exception = e; + break; + } + } else { + idx++; + } + sbuf.append(c); + } + if (exception == null && idx >= len) { + exception = new StringScanException(len, "end of input"); + } else if (exception == null && c == '\n') { + exception = new StringScanException(idx, + "unclosed quoted string"); + } else { + idx++; + } + } else { + exception = new StringScanException(idx - 1, + "quoted string must start with \""); + } + if (exception != null) { + idx = idxSave; + throw exception; + } + return sbuf.toString(); + } + + public String scanNonWhiteSpaceString() throws StringScanException { + StringBuffer sbuf = new StringBuffer(len); + int idxSave = idx; + char c; + + skipWhiteSpace(); + if (idx == len) { + StringScanException e = new StringScanException(idx, "end of input"); + idx = idxSave; + throw e; + } else { + c = buf[idx++]; + while (idx < len && !Character.isWhitespace(c) + && stringDelimiters.indexOf(c) == -1) { + sbuf.append(c); + c = buf[idx++]; + } + if (Character.isWhitespace(c) || stringDelimiters.indexOf(c) != -1) { + idx--; + } else { + sbuf.append(c); + } + } + return sbuf.toString(); + } + + public String scanString() throws StringScanException { + int idxSave = idx; + skipWhiteSpace(); + try { + if (buf[idx] == '"') { + return scanQuotedString(); + } else { + return scanNonWhiteSpaceString(); + } + } catch (StringScanException e) { + idx = idxSave; + throw e; + } + } + + public String getString() throws StringScanException { + StringBuffer sbuf = new StringBuffer(len); + while (idx < len) { + sbuf.append(buf[idx++]); + } + return sbuf.toString(); + } + + public long scanInt() throws StringScanException { + int idxSave = idx; + char c; + int sign = 1; + + skipWhiteSpace(); + if ((c = buf[idx]) == '-' || c == '+') { + sign = (c == '-' ? -1 : 1); + idx++; + } + try { + if (idx == len) { + throw new StringScanException(len, "end of input"); + } else if ((c = buf[idx]) == '0') { + if ((c = buf[idx + 1]) == 'x' || c == 'X') { + idx += 2; + return sign * scanInt(16, false); + } else { + return sign * scanInt(8, false); + } + } else { + return sign * scanInt(10, false); + } + } catch (StringScanException e) { + idx = idxSave; + throw e; + } + } + + public long scanInt(int radix) throws StringScanException { + return scanInt(radix, /* skipWhite= */true); + } + + private String baseDesc(int radix) { + switch (radix) { + case 10: { + return "decimal"; + } + case 8: { + return "octal"; + } + case 16: { + return "hex"; + } + default: { + return "base " + radix; + } + } + } + + public long scanInt(int radix, boolean skipWhite) + throws StringScanException { + StringScanException exception = null; + int charval, idxSave = idx; + char c; + long val = 0; + boolean negate = false; + + if (skipWhite) { + skipWhiteSpace(); + } + if ((c = buf[idx]) == '-' || c == '+') { + negate = (c == '-'); + idx++; + } + if (idx >= len) { + exception = new StringScanException(len, "end of input"); + } else if ((charval = Character.digit(buf[idx++], radix)) == -1) { + exception = new StringScanException(idx - 1, "malformed " + + baseDesc(radix) + " integer"); + } else { + val = charval; + while ((charval = Character.digit(buf[idx], radix)) != -1) { + val = val * radix + charval; + idx++; + } + if (Character.isLetter(c = buf[idx]) || Character.isDigit(c) + || c == '_') { + exception = new StringScanException(idx, "malformed " + + baseDesc(radix) + " integer"); + } + } + if (exception != null) { + idx = idxSave; + throw exception; + } + return negate ? -val : val; + } + + public double scanDouble() throws StringScanException { + StringScanException exception = null; + int idxSave = idx; + char c; + // parse [-][0-9]*[.][0-9]*[eE][-][0-9]* + boolean hasDigits = false; + boolean signed; + double value = 0; + + skipWhiteSpace(); + if (idx == len) { + exception = new StringScanException("end of input"); + } else { + if ((c = buf[idx]) == '-' || c == '+') { + signed = true; + idx++; + } + if (matchDigits()) { + hasDigits = true; + } + if (buf[idx] == '.') { + idx++; + } + if (!hasDigits && (buf[idx] < '0' || buf[idx] > '9')) { + if (idx == len) { + exception = new StringScanException(idx, "end of input"); + } else { + exception = new StringScanException(idx, + "malformed floating number: no digits"); + } + } else { + matchDigits(); + + if ((c = buf[idx]) == 'e' || c == 'E') { + idx++; + if ((c = buf[idx]) == '-' || c == '+') { + signed = true; + idx++; + } + if (buf[idx] < '0' || buf[idx] > '9') { + if (idx == len) { + exception = new StringScanException(idx, + "end of input"); + } else { + exception = new StringScanException(idx, + "malformed floating number: no digits in exponent"); + } + } else { + matchDigits(); + } + } + } + } + if (exception == null) { + // if (Character.isLetterOrDigit(c=buf[idx]) || c == '_') + // { exception = new StringScanException (idx, + // "malformed floating number"); + // } + // else + { + try { + value = Double.parseDouble(new String(buf, idxSave, idx + - idxSave)); + } catch (NumberFormatException e) { + exception = new StringScanException(idx, + "malformed floating number"); + } + } + } + if (exception != null) { + idx = idxSave; + throw exception; + } + return value; + } + + public boolean scanBoolean() throws StringScanException { + StringScanException exception = null; + int idxSave = idx; + String testStr = "false"; + boolean testval = false; + char c; + + skipWhiteSpace(); + if (buf[idx] == 't') { + testStr = "true"; + testval = true; + } else { + testval = false; + } + int i = 0; + for (i = 0; i < testStr.length(); i++) { + if (testStr.charAt(i) != buf[idx]) { + if (idx == len) { + exception = new StringScanException(idx, "end of input"); + } + break; + } + idx++; + } + if (exception == null) { + if (i < testStr.length() || Character.isLetterOrDigit(c = buf[idx]) + || c == '_') { + exception = new StringScanException(idx, "illegal boolean"); + } + } + if (exception != null) { + idx = idxSave; + throw exception; + } + return testval; + } + + public boolean matchString(String s) { + int k = idx; + for (int i = 0; i < s.length(); i++) { + if (k >= len || s.charAt(i) != buf[k++]) { + return false; + } + } + idx = k; + return true; + } + + public boolean matchDigits() { + int k = idx; + char c; + + while ((c = buf[k]) >= '0' && c <= '9') { + k++; + } + if (k > idx) { + idx = k; + return true; + } else { + return false; + } + } + + public void skipWhiteSpace() { + while (Character.isWhitespace(buf[idx])) { + idx++; + } + } + + private int skipWhiteSpace(int k) { + while (Character.isWhitespace(buf[k])) { + k++; + } + return k; + } + + public boolean atEnd() { + return idx == len; + } + + public boolean atBeginning() { + return idx == 0; + } + + public void ungetc() { + if (idx > 0) { + idx--; + } + } + + public char getc() { + char c = buf[idx]; + if (idx < len) { + idx++; + } + return c; + } + + public char peekc() { + return buf[idx]; + } + + public String substring(int i0, int i1) { + if (i0 < 0) { + i0 = 0; + } else if (i0 >= len) { + i0 = len - 1; + } + if (i1 < 0) { + i1 = 0; + } else if (i1 > len) { + i1 = len; + } + if (i1 <= i0) { + return ""; + } + return new String(buf, i0, i1 - i0); + } + + public String substring(int i0) { + if (i0 < 0) { + i0 = 0; + } + if (i0 >= len) { + return ""; + } else { + return new String(buf, i0, len - i0); + } + } } |