diff options
Diffstat (limited to 'pki/base/silent/src/argparser')
-rw-r--r-- | pki/base/silent/src/argparser/ArgParseException.java | 48 | ||||
-rwxr-xr-x | pki/base/silent/src/argparser/ArgParser.java | 2284 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/ArgParserTest.java | 1571 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/BooleanHolder.java | 49 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/CharHolder.java | 51 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/DoubleHolder.java | 50 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/FloatHolder.java | 51 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/IntHolder.java | 50 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/LongHolder.java | 50 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/ObjectHolder.java | 49 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/SimpleExample.java | 55 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/StringHolder.java | 50 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/StringScanException.java | 53 | ||||
-rw-r--r-- | pki/base/silent/src/argparser/StringScanner.java | 650 |
14 files changed, 5061 insertions, 0 deletions
diff --git a/pki/base/silent/src/argparser/ArgParseException.java b/pki/base/silent/src/argparser/ArgParseException.java new file mode 100644 index 000000000..9253ca24f --- /dev/null +++ b/pki/base/silent/src/argparser/ArgParseException.java @@ -0,0 +1,48 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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 +{ + /** + * 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); + } +} diff --git a/pki/base/silent/src/argparser/ArgParser.java b/pki/base/silent/src/argparser/ArgParser.java new file mode 100755 index 000000000..cd1b777de --- /dev/null +++ b/pki/base/silent/src/argparser/ArgParser.java @@ -0,0 +1,2284 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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. + */ + +import java.io.PrintStream; +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.File; +import java.io.FileReader; +import java.io.Reader; +import java.util.Vector; + +import java.lang.reflect.Array; + +/** + * 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. + * + * <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(); + * + * // create the parser and specify the allowed options ... + * + * ArgParser parser = new ArgParser("java argparser.SimpleExample"); + * parser.addOption ("-theta %f #theta value (in degrees)", theta); + * parser.addOption ("-file %s #name of the operating file", fileName); + * parser.addOption ("-debug %v #enables display of debugging info", debug); + * + * // match the arguments ... + * + * parser.matchAllArgs (args); + * + * // and print out the values + * + * System.out.println ("theta=" + theta.value); + * System.out.println ("fileName=" + fileName.value); + * System.out.println ("debug=" + debug.value); + * } + * </pre> + * <p>A command line specifying all three options might look like this: + * <pre> + * 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}, + * {@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 + * (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 + * 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> + * + * <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 + * 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); + * </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: + * + * <pre> + * 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, + * <pre> + * parser.addOption ("-file %s #file name"); + * </pre> + * 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, + * <pre> + * parser.addOption ("-file=%s #file name"); + * </pre> + * 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 + * following comma. For example, + * <pre> + * 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 + * <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, + * + * <pre> + * 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> + * + * 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. + * + * <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. + * + * 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 + * + * <pre> + * 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>. + * + * <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 + * + * <pre> + * Usage: java argparser.SimpleExample + * Options include: + * + * -help,-? displays help information + * -theta <float> theta value (in degrees) + * -file <string> name of the operating file + * -debug enables display of debugging info + * </pre> + * + * 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 + * 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 + * <pre> + * parser.addOption ("-theta %f #NUMBER#theta value (in degrees)",theta); + * </pre> + * instead of + * <pre> + * parser.addOption ("-theta %f #theta value (in degrees)", theta); + * </pre> + * 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. + * + * <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: + * <pre> + * String[] unmatched = + * parser.matchAllArgs (args, 0, parser.EXIT_ON_ERROR); + * for (int i = 0; i < unmatched.length; i++) + * { ... handle unmatched arguments ... + * } + * </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}: + * + * <pre> + * int idx = 0; + * while (idx < args.length) + * { try + * { idx = parser.matchArg (args, idx); + * if (parser.getUnmatchedArgument() != null) + * { + * ... handle this unmatched argument ourselves ... + * } + * } + * catch (ArgParserException e) + * { // malformed or erroneous argument + * parser.printErrorAndExit (e.getMessage()); + * } + * } + * </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. + * + * <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: + * + * <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. + * + * @author John E. Lloyd, Fall 2004 + */ +public class ArgParser +{ + Vector 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; + + 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(128); + this.synopsisString = synopsisString; + if (defaultHelp) + { addOption ("-help,-? %h #displays help information", null); + defaultHelpOption = firstHelpOption = (Record)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 == null) + { throw new IllegalArgumentException + ("Null option name given"); + } + 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 = ""; + } + // 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; + } + + 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 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 vec = new Vector(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 unmatched = new Vector(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 + */ + 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)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/argparser/ArgParserTest.java b/pki/base/silent/src/argparser/ArgParserTest.java new file mode 100644 index 000000000..74b4c03e1 --- /dev/null +++ b/pki/base/silent/src/argparser/ArgParserTest.java @@ -0,0 +1,1571 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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. + */ + +import java.io.*; + +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. + * + * @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 vec = new Vector(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 vec = new Vector(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/argparser/BooleanHolder.java b/pki/base/silent/src/argparser/BooleanHolder.java new file mode 100644 index 000000000..18a35c058 --- /dev/null +++ b/pki/base/silent/src/argparser/BooleanHolder.java @@ -0,0 +1,49 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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 +{ + /** + * 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 a + * specific initial value. + * + * @param b Initial boolean value. + */ + public BooleanHolder (boolean b) + { value = b; + } +} diff --git a/pki/base/silent/src/argparser/CharHolder.java b/pki/base/silent/src/argparser/CharHolder.java new file mode 100644 index 000000000..b096df9ca --- /dev/null +++ b/pki/base/silent/src/argparser/CharHolder.java @@ -0,0 +1,51 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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 +{ + /** + * 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 a + * specific initial value. + * + * @param c Initial character value. + */ + public CharHolder (char c) + { value = c; + } +} + + diff --git a/pki/base/silent/src/argparser/DoubleHolder.java b/pki/base/silent/src/argparser/DoubleHolder.java new file mode 100644 index 000000000..3728ebf2e --- /dev/null +++ b/pki/base/silent/src/argparser/DoubleHolder.java @@ -0,0 +1,50 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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 +{ + /** + * 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 a + * specific initial value. + * + * @param d Initial double value. + */ + public DoubleHolder (double d) + { value = d; + } +} + diff --git a/pki/base/silent/src/argparser/FloatHolder.java b/pki/base/silent/src/argparser/FloatHolder.java new file mode 100644 index 000000000..4cded9aa4 --- /dev/null +++ b/pki/base/silent/src/argparser/FloatHolder.java @@ -0,0 +1,51 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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 +{ + /** + * 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 a + * specific initial value. + * + * @param f Initial float value. + */ + public FloatHolder (float f) + { value = f; + } +} + + diff --git a/pki/base/silent/src/argparser/IntHolder.java b/pki/base/silent/src/argparser/IntHolder.java new file mode 100644 index 000000000..fd8403bd1 --- /dev/null +++ b/pki/base/silent/src/argparser/IntHolder.java @@ -0,0 +1,50 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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 +{ + /** + * 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 a + * specific initial value. + * + * @param i Initial integer value. + */ + public IntHolder (int i) + { value = i; + } +} + diff --git a/pki/base/silent/src/argparser/LongHolder.java b/pki/base/silent/src/argparser/LongHolder.java new file mode 100644 index 000000000..13a84008b --- /dev/null +++ b/pki/base/silent/src/argparser/LongHolder.java @@ -0,0 +1,50 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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 +{ + /** + * 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 a + * specific initial value. + * + * @param l Initial long value. + */ + public LongHolder (long l) + { value = l; + } +} + diff --git a/pki/base/silent/src/argparser/ObjectHolder.java b/pki/base/silent/src/argparser/ObjectHolder.java new file mode 100644 index 000000000..8e3493e1d --- /dev/null +++ b/pki/base/silent/src/argparser/ObjectHolder.java @@ -0,0 +1,49 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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 +{ + /** + * 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 a + * specific initial value. + * + * @param o Initial Object reference. + */ + public ObjectHolder (Object o) + { value = o; + } +} diff --git a/pki/base/silent/src/argparser/SimpleExample.java b/pki/base/silent/src/argparser/SimpleExample.java new file mode 100644 index 000000000..58e4d4656 --- /dev/null +++ b/pki/base/silent/src/argparser/SimpleExample.java @@ -0,0 +1,55 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +/** + * 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); + } +} + diff --git a/pki/base/silent/src/argparser/StringHolder.java b/pki/base/silent/src/argparser/StringHolder.java new file mode 100644 index 000000000..7737fa4cd --- /dev/null +++ b/pki/base/silent/src/argparser/StringHolder.java @@ -0,0 +1,50 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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 +{ + /** + * 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 a + * specific initial value. + * + * @param s Initial String reference. + */ + public StringHolder (String s) + { value = s; + } +} + diff --git a/pki/base/silent/src/argparser/StringScanException.java b/pki/base/silent/src/argparser/StringScanException.java new file mode 100644 index 000000000..b7b4c7d2d --- /dev/null +++ b/pki/base/silent/src/argparser/StringScanException.java @@ -0,0 +1,53 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +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 +{ + int failIdx; + + /** + * Creates a new StringScanException with the given message. + * + * @param msg Error message + * @see StringScanner + */ + + public StringScanException (String msg) + { super (msg); + } + + public StringScanException (int idx, String msg) + { + super (msg); + failIdx = idx; + } + + public int getFailIndex() + { + return failIdx; + } +} diff --git a/pki/base/silent/src/argparser/StringScanner.java b/pki/base/silent/src/argparser/StringScanner.java new file mode 100644 index 000000000..425ad3ac1 --- /dev/null +++ b/pki/base/silent/src/argparser/StringScanner.java @@ -0,0 +1,650 @@ +// --- 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 +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- 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); + } + } +} |