From ed76e714f94e8aa60aa5c4b4a8cf386a582bf02d Mon Sep 17 00:00:00 2001 From: admiyo Date: Thu, 17 Nov 2011 00:02:59 +0000 Subject: PKISilent in single tree PKISilent was a series of classes, each in the Common namespace. Now it is in a single tree and fronted by a single Java class that multiplexes between them. git-svn-id: svn+ssh://svn.fedorahosted.org/svn/pki/trunk@2295 c9f7a03b-bd48-0410-a16d-cbbf54688b0b --- pki/base/silent/src/argparser/ArgParser.java | 2302 -------------------------- 1 file changed, 2302 deletions(-) delete mode 100755 pki/base/silent/src/argparser/ArgParser.java (limited to 'pki/base/silent/src/argparser/ArgParser.java') diff --git a/pki/base/silent/src/argparser/ArgParser.java b/pki/base/silent/src/argparser/ArgParser.java deleted file mode 100755 index e7bd7e741..000000000 --- a/pki/base/silent/src/argparser/ArgParser.java +++ /dev/null @@ -1,2302 +0,0 @@ -// --- 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.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.LineNumberReader; -import java.io.PrintStream; -import java.io.Reader; -import java.lang.reflect.Array; -import java.util.Vector; - -/** - * ArgParser is used to parse the command line arguments for a java - * application program. It provides a compact way to specify options and match - * them against command line arguments, with support for - * range checking, - * multiple option names (aliases), - * single word options, - * multiple values associated with an option, - * multiple option invocation, - * generating help information, - * custom argument parsing, and - * reading arguments from a file. The - * last feature is particularly useful and makes it - * easy to create ad-hoc configuration files for an application. - * - *

Basic Example

- * - *

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

- *
- * static public void main (String[] args)
- *  {
- *    // create holder objects for storing results ...
- * 
- *    DoubleHolder theta = new DoubleHolder();
- *    StringHolder fileName = new StringHolder();
- *    BooleanHolder debug = new BooleanHolder();
- * 
- *    // create the parser and specify the allowed options ...
- * 
- *    ArgParser parser = new ArgParser("java argparser.SimpleExample");
- *    parser.addOption ("-theta %f #theta value (in degrees)", theta); 
- *    parser.addOption ("-file %s #name of the operating file", fileName);
- *    parser.addOption ("-debug %v #enables display of debugging info", debug);
- *
- *    // match the arguments ...
- * 
- *    parser.matchAllArgs (args);
- *
- *    // and print out the values
- *
- *    System.out.println ("theta=" + theta.value);
- *    System.out.println ("fileName=" + fileName.value);
- *    System.out.println ("debug=" + debug.value);
- *  }
- * 
- *

A command line specifying all three options might look like this: - *

- * java argparser.SimpleExample -theta 7.8 -debug -file /ai/lloyd/bar 
- * 
- * - *

The application creates an instance of ArgParser and then adds - * descriptions of the allowed options using {@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 value field of special - * ``holder'' classes (e.g., {@link argparser.DoubleHolder DoubleHolder}, - * {@link argparser.StringHolder StringHolder}, etc.). - * - *

The first argument to {@link #addOption addOption} is a string that - * specifies (1) the option's name, (2) a conversion code for its associated - * value (e.g., %f for floating point, %s for a - * string, %v for a boolean flag), and (3) an optional description - * (following the # character) which is used for generating help - * messages. The second argument is the holder object through which the value - * is returned. This may be either a type-specific object (such as {@link - * argparser.DoubleHolder DoubleHolder} or {@link argparser.StringHolder - * StringHolder}), an array of the appropriate type, or - * an instance of - * java.util.Vector. - * - *

By default, arguments that don't match the specified options, are out of range, or are otherwise formatted incorrectly, - * will cause matchAllArgs to print a message and exit the - * program. Alternatively, an application can use {@link - * #matchAllArgs(String[],int,int) matchAllArgs(args,idx,exitFlags)} to obtain - * an array of unmatched arguments which can then be - * processed separately - * - *

Range Specification

- * - * The values associated with options can also be given range specifications. A - * range specification appears in curly braces immediately following the - * conversion code. In the code fragment below, we show how to specify an - * option -name that expects to be provided with one of three - * string values (john, mary, or jane), - * an option -index that expects to be supplied with a integer - * value in the range 1 to 256, an option -size that expects to be - * supplied with integer values of either 1, 2, 4, 8, or 16, and an option - * -foo that expects to be supplied with floating point values in - * the ranges -99 < foo <= -50, or 50 <= foo < 99. - * - *
- *    StringHolder name = new StringHolder();
- *    IntHolder index = new IntHolder();
- *    IntHolder size = new IntHolder();
- *    DoubleHolder foo = new DoubleHolder();
- * 
- *    parser.addOption ("-name %s {john,mary,jane}", name);
- *    parser.addOption ("-index %d {[1,256]}", index);
- *    parser.addOption ("-size %d {1,2,4,8,16}", size);
- *    parser.addOption ("-foo %f {(-99,-50],[50,99)}", foo);
- * 
- * - * If an argument value does not lie within a specified range, an error is - * generated. - * - *

Multiple Option Names

- * - * An option may be given several names, or aliases, in the form of - * a comma seperated list: - * - *
- *    parser.addOption ("-v,--verbose %v #print lots of info");
- *    parser.addOption ("-of,-outfile,-outputFile %s #output file");
- * 
- * - *

Single Word Options

- * - * Normally, options are assumed to be "multi-word", meaning - * that any associated value must follow the option as a - * separate argument string. For - * example, - *
- *    parser.addOption ("-file %s #file name");
- * 
- * will cause the parser to look for two strings in the argument list - * of the form - *
- *    -file someFileName
- * 
- * However, if there is no white space separting the option's name from - * it's conversion code, then values associated with that - * option will be assumed to be part of the same argument - * string as the option itself. For example, - *
- *    parser.addOption ("-file=%s #file name");
- * 
- * will cause the parser to look for a single string in the argument - * list of the form - *
- *    -file=someFileName
- * 
- * Such an option is called a "single word" option. - * - *

- * In cases where an option has multiple names, then this single - * word behavior is invoked if there is no white space between - * the last indicated name and the conversion code. However, previous - * names in the list will still be given multi-word behavior - * if there is white space between the name and the - * following comma. For example, - *

- *    parser.addOption ("-nb=,-number ,-n%d #number of blocks");
- * 
- * will cause the parser to look for one, two, and one word constructions - * of the forms - *
- *    -nb=N
- *    -number N
- *    -nN
- * 
- * - *

Multiple Option Values

- * - * If may be useful for an option to be followed by several values. - * For instance, we might have an option -velocity - * which should be followed by three numbers denoting - * the x, y, and z components of a velocity vector. - * We can require multiple values for an option - * by placing a multiplier specification, - * of the form XN, where N is an integer, - * after the conversion code (or range specification, if present). - * For example, - * - *
- *    double[] pos = new double[3];
- *
- *    addOption ("-position %fX3 #position of the object", pos);
- * 
- * will cause the parser to look for - *
- *    -position xx yy zz
- * 
- * - * in the argument list, where xx, yy, and - * zz are numbers. The values are stored in the array - * pos. - * - * Options requiring multiple values must use arrays to - * return their values, and cannot be used in single word format. - * - *

Multiple Option Invocation

- * - * Normally, if an option appears twice in the command list, the - * value associated with the second instance simply overwrites the - * value associated with the first instance. - * - * However, the application can instead arrange for the storage of all - * values associated with multiple option invocation, by supplying a instance - * of java.util.Vector to serve as the value holder. Then every - * time the option appears in the argument list, the parser will create a value - * holder of appropriate type, set it to the current value, and store the - * holder in the vector. For example, the construction - * - *
- *    Vector vec = new Vector(10);
- *
- *    parser.addOption ("-foo %f", vec);
- *    parser.matchAllArgs(args);
- * 
- * when supplied with an argument list that contains - *
- *    -foo 1.2 -foo 1000 -foo -78
- * 
- * - * will create three instances of {@link argparser.DoubleHolder DoubleHolder}, - * initialized to 1.2, 1000, and -78, - * and store them in vec. - * - *

Generating help information

- * - * ArgParser automatically generates help information for the options, and this - * information may be printed in response to a help option, or may be - * queried by the application using {@link #getHelpMessage getHelpMessage}. - * The information for each option consists of the option's name(s), it's - * required value(s), and an application-supplied description. Value - * information is generated automaticlly from the conversion code, range, and - * multiplier specifications (although this can be overriden, as - * described below). - * The application-supplied description is whatever - * appears in the specification string after the optional # - * character. The string returned by {@link #getHelpMessage getHelpMessage} for - * the first example above would be - * - *
- * Usage: java argparser.SimpleExample
- * Options include:
- * 
- * -help,-?                displays help information
- * -theta <float>          theta value (in degrees)
- * -file <string>          name of the operating file
- * -debug                  enables display of debugging info
- * 
- * - * The options -help and -? are including in the - * parser by default as help options, and they automatically cause the help - * message to be printed. To exclude these - * options, one should use the constructor {@link #ArgParser(String,boolean) - * ArgParser(synopsis,false)}. - * Help options can also be specified by the application using {@link - * #addOption addOption} and the conversion code %h. Help options - * can be disabled using {@link #setHelpOptionsEnabled - * setHelpOptionsEnabled(false)}. - * - *

- * A description of the required values for an option can be - * specified explicitly - * by placing a second # character in the specification - * string. Everything between the first and second # - * characters then becomes the value description, and everything - * after the second # character becomes the option - * description. - * For example, if the -theta option - * above was specified with - *

- *    parser.addOption ("-theta %f #NUMBER#theta value (in degrees)",theta);
- * 
- * instead of - *
- *    parser.addOption ("-theta %f #theta value (in degrees)", theta);
- * 
- * then the corresponding entry in the help message would look - * like - *
- * -theta NUMBER          theta value (in degrees)
- * 
- * - *

Custom Argument Parsing

- * - * An application may find it necessary to handle arguments that - * don't fit into the framework of this class. There are a couple - * of ways to do this. - * - *

- * First, the method {@link #matchAllArgs(String[],int,int) - * matchAllArgs(args,idx,exitFlags)} returns an array of - * all unmatched arguments, which can then be handled - * specially: - *

- *    String[] unmatched =
- *       parser.matchAllArgs (args, 0, parser.EXIT_ON_ERROR);
- *    for (int i = 0; i < unmatched.length; i++)
- *     { ... handle unmatched arguments ...
- *     }
- * 
- * - * For instance, this would be useful for an applicatoon that accepts an - * arbitrary number of input file names. The options can be parsed using - * matchAllArgs, and the remaining unmatched arguments - * give the file names. - * - *

If we need more control over the parsing, we can parse arguments one at - * a time using {@link #matchArg matchArg}: - * - *

- *    int idx = 0;
- *    while (idx < args.length)
- *     { try
- *        { idx = parser.matchArg (args, idx);
- *          if (parser.getUnmatchedArgument() != null)
- *           {
- *             ... handle this unmatched argument ourselves ...
- *           }
- *        }
- *       catch (ArgParserException e) 
- *        { // malformed or erroneous argument
- *          parser.printErrorAndExit (e.getMessage());
- *        }
- *     }
- * 
- * - * {@link #matchArg matchArg(args,idx)} matches one option at location - * idx in the argument list, and then returns the location value - * that should be used for the next match. If an argument does - * not match any option, - * {@link #getUnmatchedArgument getUnmatchedArgument} will return a copy of the - * unmatched argument. - * - *

Reading Arguments From a File

- * - * 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 # (which - * comments out everything to the end of the current line). A typical usage - * looks like this: - * - *
- *    ... create parser and add options ...
- * 
- *    args = parser.prependArgs (new File(".configFile"), args);
- *
- *    parser.matchAllArgs (args);
- * 
- * - * This makes it easy to generate simple configuration files for an - * application. - * - * @author John E. Lloyd, Fall 2004 - */ -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 % character in - * the specification string of {@link #addOption addOption}. - * - * @return Valid conversion codes - * @see #addOption - */ - public static String getValidConversionCodes() - { - return validConversionCodes; - } - - static class NameDesc - { - String name; - // oneWord implies that any value associated with - // option is concatenated onto the argument string itself - boolean oneWord; - NameDesc next = null; - } - - static class RangePnt - { - double dval = 0; - long lval = 0; - String sval = null; - boolean bval = true; - boolean closed = true; - - RangePnt (String s, boolean closed) - { sval = s; - this.closed = closed; - } - - RangePnt (double d, boolean closed) - { dval = d; - this.closed = closed; - } - - RangePnt (long l, boolean closed) - { lval = l; - this.closed = closed; - } - - RangePnt (boolean b, boolean closed) - { bval = b; - this.closed = closed; - } - - RangePnt (StringScanner scanner, int type) - throws IllegalArgumentException - { - String typeName = null; - try - { switch (type) - { - case Record.CHAR: - { typeName = "character"; - lval = scanner.scanChar(); - break; - } - case Record.INT: - case Record.LONG: - { typeName = "integer"; - lval = scanner.scanInt(); - break; - } - case Record.FLOAT: - case Record.DOUBLE: - { typeName = "float"; - dval = scanner.scanDouble(); - break; - } - case Record.STRING: - { typeName = "string"; - sval = scanner.scanString(); - break; - } - case Record.BOOLEAN: - { typeName = "boolean"; - bval = scanner.scanBoolean(); - break; - } - } - } - catch (StringScanException e) - { throw new IllegalArgumentException ( - "Malformed " + typeName + " '" + - scanner.substring(scanner.getIndex(), - e.getFailIndex()+1) + - "' in range spec"); - } -// this.closed = closed; - } - - void setClosed (boolean closed) - { this.closed = closed; - } - - boolean getClosed() - { return closed; - } - - int compareTo (double d) - { if (dval < d) - { return -1; - } - else if (d == dval) - { return 0; - } - else - { return 1; - } - } - - int compareTo (long l) - { if (lval < l) - { return -1; - } - else if (l == lval) - { return 0; - } - else - { return 1; - } - } - - int compareTo (String s) - { return sval.compareTo (s); - } - - int compareTo (boolean b) - { if (b == bval) - { return 0; - } - else - { return 1; - } - } - - public String toString() - { return "{ dval=" + dval + ", lval=" + lval + - ", sval=" + sval + ", bval=" + bval + - ", closed=" + closed + "}"; - } - } - - class RangeAtom - { - RangePnt low = null; - RangePnt high = null; - RangeAtom next = null; - - RangeAtom (RangePnt p0, RangePnt p1, int type) - throws IllegalArgumentException - { - int cmp = 0; - switch (type) - { - case Record.CHAR: - case Record.INT: - case Record.LONG: - { cmp = p0.compareTo (p1.lval); - break; - } - case Record.FLOAT: - case Record.DOUBLE: - { cmp = p0.compareTo (p1.dval); - break; - } - case Record.STRING: - { cmp = p0.compareTo (p1.sval); - break; - } - } - if (cmp > 0) - { // then switch high and low - low = p1; - high = p0; - } - else - { low = p0; - high = p1; - } - } - - RangeAtom (RangePnt p0) - throws IllegalArgumentException - { - low = p0; - } - - boolean match (double d) - { int lc = low.compareTo(d); - if (high != null) - { int hc = high.compareTo(d); - return (lc*hc < 0 || - (low.closed && lc==0) || - (high.closed && hc==0)); - } - else - { return lc == 0; - } - } - - boolean match (long l) - { int lc = low.compareTo(l); - if (high != null) - { int hc = high.compareTo(l); - return (lc*hc < 0 || - (low.closed && lc==0) || - (high.closed && hc==0)); - } - else - { return lc == 0; - } - } - - boolean match (String s) - { int lc = low.compareTo(s); - if (high != null) - { int hc = high.compareTo(s); - return (lc*hc < 0 || - (low.closed && lc==0) || - (high.closed && hc==0)); - } - else - { return lc == 0; - } - } - - boolean match (boolean b) - { return low.compareTo(b) == 0; - } - - public String toString() - { return "low=" + (low==null ? "null" : low.toString()) + - ", high=" + (high==null ? "null" : high.toString()); - } - } - - class Record - { - NameDesc nameList; - static final int NOTYPE = 0; - static final int BOOLEAN = 1; - static final int CHAR = 2; - static final int INT = 3; - static final int LONG = 4; - static final int FLOAT = 5; - static final int DOUBLE = 6; - static final int STRING = 7; - int type; - int numValues; - boolean vectorResult = false; - boolean required = true; - - String helpMsg = null; - String valueDesc = null; - String rangeDesc = null; - Object resHolder = null; - RangeAtom rangeList = null; - RangeAtom rangeTail = null; - char convertCode; - boolean vval = true; // default value for now - - NameDesc firstNameDesc() - { - return nameList; - } - - RangeAtom firstRangeAtom() - { - return rangeList; - } - - int numRangeAtoms() - { int cnt = 0; - for (RangeAtom ra=rangeList; ra!=null; ra=ra.next) - { cnt++; - } - return cnt; - } - - void addRangeAtom (RangeAtom ra) - { if (rangeList == null) - { rangeList = ra; - } - else - { rangeTail.next = ra; - } - rangeTail = ra; - } - - boolean withinRange (double d) - { - if (rangeList == null) - { return true; - } - for (RangeAtom ra=rangeList; ra!=null; ra=ra.next) - { if (ra.match (d)) - { return true; - } - } - return false; - } - - boolean withinRange (long l) - { - if (rangeList == null) - { return true; - } - for (RangeAtom ra=rangeList; ra!=null; ra=ra.next) - { if (ra.match (l)) - { return true; - } - } - return false; - } - - boolean withinRange (String s) - { - if (rangeList == null) - { return true; - } - for (RangeAtom ra=rangeList; ra!=null; ra=ra.next) - { if (ra.match (s)) - { return true; - } - } - return false; - } - - boolean withinRange (boolean b) - { - if (rangeList == null) - { return true; - } - for (RangeAtom ra=rangeList; ra!=null; ra=ra.next) - { if (ra.match (b)) - { return true; - } - } - return false; - } - - String valTypeName() - { - switch (convertCode) - { - case 'i': - { return ("integer"); - } - case 'o': - { return ("octal integer"); - } - case 'd': - { return ("decimal integer"); - } - case 'x': - { return ("hex integer"); - } - case 'c': - { return ("char"); - } - case 'b': - { return ("boolean"); - } - case 'f': - { return ("float"); - } - case 's': - { return ("string"); - } - } - return ("unknown"); - } - - void scanValue (Object result, String name, String s, int resultIdx) - throws ArgParseException - { - double dval = 0; - String sval = null; - long lval = 0; - boolean bval = false; - - if (s.length()==0) - { throw new ArgParseException - (name, "requires a contiguous value"); - } - StringScanner scanner = new StringScanner(s); - try - { - switch (convertCode) - { - case 'i': - { lval = scanner.scanInt(); - break; - } - case 'o': - { lval = scanner.scanInt (8, false); - break; - } - case 'd': - { lval = scanner.scanInt (10, false); - break; - } - case 'x': - { lval = scanner.scanInt (16, false); - break; - } - case 'c': - { lval = scanner.scanChar(); - break; - } - case 'b': - { bval = scanner.scanBoolean(); - break; - } - case 'f': - { dval = scanner.scanDouble(); - break; - } - case 's': - { sval = scanner.getString(); - break; - } - } - } - catch (StringScanException e) - { throw new ArgParseException ( - name, "malformed " + valTypeName() + " '" + s + "'"); - } - scanner.skipWhiteSpace(); - if (!scanner.atEnd()) - { throw new ArgParseException ( - name, "malformed " + valTypeName() + " '" + s + "'"); - } - boolean outOfRange = false; - switch (type) - { - case CHAR: - case INT: - case LONG: - { outOfRange = !withinRange (lval); - break; - } - case FLOAT: - case DOUBLE: - { outOfRange = !withinRange (dval); - break; - } - case STRING: - { outOfRange = !withinRange (sval); - break; - } - case BOOLEAN: - { outOfRange = !withinRange (bval); - break; - } - } - if (outOfRange) - { String errmsg = "value " + s + " not in range "; - throw new ArgParseException ( - name, "value '" + s + "' not in range " + rangeDesc); - } - if (result.getClass().isArray()) - { - switch (type) - { - case BOOLEAN: - { ((boolean[])result)[resultIdx] = bval; - break; - } - case CHAR: - { ((char[])result)[resultIdx] = (char)lval; - break; - } - case INT: - { ((int[])result)[resultIdx] = (int)lval; - break; - } - case LONG: - { ((long[])result)[resultIdx] = lval; - break; - } - case FLOAT: - { ((float[])result)[resultIdx] = (float)dval; - break; - } - case DOUBLE: - { ((double[])result)[resultIdx] = dval; - break; - } - case STRING: - { ((String[])result)[resultIdx] = sval; - break; - } - } - } - else - { - switch (type) - { - case BOOLEAN: - { ((BooleanHolder)result).value = bval; - break; - } - case CHAR: - { ((CharHolder)result).value = (char)lval; - break; - } - case INT: - { ((IntHolder)result).value = (int)lval; - break; - } - case LONG: - { ((LongHolder)result).value = lval; - break; - } - case FLOAT: - { ((FloatHolder)result).value = (float)dval; - break; - } - case DOUBLE: - { ((DoubleHolder)result).value = dval; - break; - } - case STRING: - { ((StringHolder)result).value = sval; - break; - } - } - } - } - } - - private String firstHelpOptionName() - { - if (firstHelpOption != null) - { return firstHelpOption.nameList.name; - } - else - { return null; - } - } - - /** - * Creates an ArgParser with a synopsis - * string, and the default help options -help and - * -?. - * - * @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 ArgParser with a synopsis - * string. The help options -help and - * -? are added if defaultHelp - * 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 - *

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

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 %h. 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 true. - * @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. - * - *

The specification string has the general form - * - *

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

- * where - *

- * - *

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

If the result holder is not a Vector, then - * it must correspond as follows to the conversion code: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
%i, %d, %x, - * %o{@link argparser.IntHolder IntHolder}, - * {@link argparser.LongHolder LongHolder}, int[], or - * long[]
%f{@link argparser.FloatHolder FloatHolder}, - * {@link argparser.DoubleHolder DoubleHolder}, - * float[], or - * double[]
%b, %v{@link argparser.BooleanHolder BooleanHolder} or - * boolean[]
%s{@link argparser.StringHolder StringHolder} or - * String[]
%c{@link argparser.CharHolder CharHolder} or - * char[]
- * - *

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

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

The object allocated by the system to store the result - * will correspond to the conversion code as follows: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
%i, %d, %x, - * %o{@link argparser.LongHolder LongHolder}, or - * long[] if the multiplier value exceeds 1
%f{@link argparser.DoubleHolder DoubleHolder}, or - * double[] if the multiplier value exceeds 1
%b, %v{@link argparser.BooleanHolder BooleanHolder}, or - * boolean[] - * if the multiplier value exceeds 1
%s{@link argparser.StringHolder StringHolder}, or - * String[] - * if the multiplier value exceeds 1
%c{@link argparser.CharHolder CharHolder}, or char[] - * if the multiplier value exceeds 1
- * - * @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 = ""; - } - - // parse helpMsg for required/optional information if present - // default to required - if (rec.helpMsg.indexOf("(optional") != -1) { - rec.required = false; - } - - // add option information to match list - if (rec.convertCode == 'h' && firstHelpOption == defaultHelpOption) - { matchList.remove (defaultHelpOption); - firstHelpOption = rec; - } - matchList.add (rec); - } - - Record lastMatchRecord () - { return (Record)matchList.lastElement(); - } - - private Record getRecord (String arg, ObjectHolder ndescHolder) - { - NameDesc ndesc; - for (int i=0; i". The character # 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 null. - * @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". The character # 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 null. - * @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. - * - *

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

If help options are enabled and one of the arguments matches a - * help option, then the result of {@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 args specified by idx, and - * unmatched arguments are returned in a String array. - * - *

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

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

If help options are enabled and one of the arguments matches a - * help option, then the result of {@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 - * null 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. - * - *

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}. - * - *

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 - * null. - * - *

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 0) -// { ps.print (spaceString(initialIndent)); -// } -// for (int i=0; i"; -// 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"; - } - 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 matchArg - * or matchAllArgs, and is automatically set to - * null 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, - * null is returned. - * - * @return unmatched argument - */ - public String getUnmatchedArgument() - { - return unmatchedArg; - } -} - - -- cgit