Source Code Cross Referenced for ArgParser.java in  » Development » ArgParser » argparser » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Development » ArgParser » argparser 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**
0002:         * Copyright John E. Lloyd, 2004. All rights reserved. Permission to use,
0003:         * copy, modify and redistribute is granted, provided that this copyright
0004:         * notice is retained and the author is given credit whenever appropriate.
0005:         *
0006:         * This  software is distributed "as is", without any warranty, including 
0007:         * any implied warranty of merchantability or fitness for a particular
0008:         * use. The author assumes no responsibility for, and shall not be liable
0009:         * for, any special, indirect, or consequential damages, or any damages
0010:         * whatsoever, arising out of or in connection with the use of this
0011:         * software.
0012:         */package argparser;
0013:
0014:        import java.io.PrintStream;
0015:        import java.io.IOException;
0016:        import java.io.LineNumberReader;
0017:        import java.io.File;
0018:        import java.io.FileReader;
0019:        import java.io.Reader;
0020:        import java.util.Vector;
0021:
0022:        import java.lang.reflect.Array;
0023:
0024:        /**
0025:         * ArgParser is used to parse the command line arguments for a java
0026:         * application program. It provides a compact way to specify options and match
0027:         * them against command line arguments, with support for
0028:         * <a href=#rangespec>range checking</a>,
0029:         * <a href=#multipleOptionNames>multiple option names</a> (aliases),
0030:         * <a href=#singleWordOptions>single word options</a>,
0031:         * <a href=#multipleOptionValues>multiple values associated with an option</a>,
0032:         * <a href=#multipleOptionInvocation>multiple option invocation</a>,
0033:         * <a href=#helpInfo>generating help information</a>,
0034:         * <a href=#customArgParsing>custom argument parsing</a>, and
0035:         * <a href=#argsFromAFile>reading arguments from a file</a>. The
0036:         * last feature is particularly useful and makes it
0037:         * easy to create ad-hoc configuration files for an application.
0038:         *
0039:         * <h3><a name="example">Basic Example</a></h3>
0040:         *
0041:         * <p>Here is a simple example in which an application has three
0042:         * command line options:
0043:         * <code>-theta</code> (followed by a floating point value),
0044:         * <code>-file</code> (followed by a string value), and 
0045:         * <code>-debug</code>, which causes a boolean value to be set.
0046:         * 
0047:         * <pre>
0048:         *
0049:         * static public void main (String[] args)
0050:         *  {
0051:         *    // create holder objects for storing results ...
0052:         * 
0053:         *    DoubleHolder theta = new DoubleHolder();
0054:         *    StringHolder fileName = new StringHolder();
0055:         *    BooleanHolder debug = new BooleanHolder();
0056:         * 
0057:         *    // create the parser and specify the allowed options ...
0058:         * 
0059:         *    ArgParser parser = new ArgParser("java argparser.SimpleExample");
0060:         *    parser.addOption ("-theta %f #theta value (in degrees)", theta); 
0061:         *    parser.addOption ("-file %s #name of the operating file", fileName);
0062:         *    parser.addOption ("-debug %v #enables display of debugging info", debug);
0063:         *
0064:         *    // match the arguments ...
0065:         * 
0066:         *    parser.matchAllArgs (args);
0067:         *
0068:         *    // and print out the values
0069:         *
0070:         *    System.out.println ("theta=" + theta.value);
0071:         *    System.out.println ("fileName=" + fileName.value);
0072:         *    System.out.println ("debug=" + debug.value);
0073:         *  }
0074:         * </pre>
0075:         * <p>A command line specifying all three options might look like this:
0076:         * <pre>
0077:         * java argparser.SimpleExample -theta 7.8 -debug -file /ai/lloyd/bar 
0078:         * </pre>
0079:         * 
0080:         * <p>The application creates an instance of ArgParser and then adds
0081:         * descriptions of the allowed options using {@link #addOption addOption}.  The
0082:         * method {@link #matchAllArgs(String[]) matchAllArgs} is then used to match
0083:         * these options against the command line arguments. Values associated with
0084:         * each option are returned in the <code>value</code> field of special
0085:         * ``holder'' classes (e.g., {@link argparser.DoubleHolder DoubleHolder},
0086:         * {@link argparser.StringHolder StringHolder}, etc.).
0087:         *
0088:         * <p> The first argument to {@link #addOption addOption} is a string that
0089:         * specifies (1) the option's name, (2) a conversion code for its associated
0090:         * value (e.g., <code>%f</code> for floating point, <code>%s</code> for a
0091:         * string, <code>%v</code> for a boolean flag), and (3) an optional description
0092:         * (following the <code>#</code> character) which is used for generating help
0093:         * messages. The second argument is the holder object through which the value
0094:         * is returned. This may be either a type-specific object (such as {@link
0095:         * argparser.DoubleHolder DoubleHolder} or {@link argparser.StringHolder
0096:         * StringHolder}), an array of the appropriate type, or
0097:         * <a href=#multipleOptionInvocation> an instance of 
0098:         * <code>java.util.Vector</code></a>.
0099:         *
0100:         * <p>By default, arguments that don't match the specified options, are <a
0101:         * href=#rangespec>out of range</a>, or are otherwise formatted incorrectly,
0102:         * will cause <code>matchAllArgs</code> to print a message and exit the
0103:         * program. Alternatively, an application can use {@link
0104:         * #matchAllArgs(String[],int,int) matchAllArgs(args,idx,exitFlags)} to obtain
0105:         * an array of unmatched arguments which can then be
0106:         * <a href=#customArgParsing>processed separately</a>
0107:         *
0108:         * <h3><a name="rangespec">Range Specification</a></h3>
0109:         *
0110:         * The values associated with options can also be given range specifications. A
0111:         * range specification appears in curly braces immediately following the
0112:         * conversion code. In the code fragment below, we show how to specify an
0113:         * option <code>-name</code> that expects to be provided with one of three
0114:         * string values (<code>john</code>, <code>mary</code>, or <code>jane</code>),
0115:         * an option <code>-index</code> that expects to be supplied with a integer
0116:         * value in the range 1 to 256, an option <code>-size</code> that expects to be
0117:         * supplied with integer values of either 1, 2, 4, 8, or 16, and an option
0118:         * <code>-foo</code> that expects to be supplied with floating point values in
0119:         * the ranges -99 < foo <= -50, or 50 <= foo < 99.
0120:         *
0121:         * <pre>
0122:         *    StringHolder name = new StringHolder();
0123:         *    IntHolder index = new IntHolder();
0124:         *    IntHolder size = new IntHolder();
0125:         *    DoubleHolder foo = new DoubleHolder();
0126:         * 
0127:         *    parser.addOption ("-name %s {john,mary,jane}", name);
0128:         *    parser.addOption ("-index %d {[1,256]}", index);
0129:         *    parser.addOption ("-size %d {1,2,4,8,16}", size);
0130:         *    parser.addOption ("-foo %f {(-99,-50],[50,99)}", foo);
0131:         * </pre>
0132:         *
0133:         * If an argument value does not lie within a specified range, an error is
0134:         * generated.
0135:         *
0136:         * <h3><a name="multipleOptionNames">Multiple Option Names</a></h3>
0137:         *
0138:         * An option may be given several names, or aliases, in the form of
0139:         * a comma seperated list:
0140:         *
0141:         * <pre>
0142:         *    parser.addOption ("-v,--verbose %v #print lots of info");
0143:         *    parser.addOption ("-of,-outfile,-outputFile %s #output file");
0144:         * </pre>
0145:         *
0146:         * <h3><a name="singleWordOptions">Single Word Options</a></h3>
0147:         *
0148:         * Normally, options are assumed to be "multi-word", meaning
0149:         * that any associated value must follow the option as a
0150:         * separate argument string. For 
0151:         * example,
0152:         * <pre>
0153:         *    parser.addOption ("-file %s #file name");
0154:         * </pre>
0155:         * will cause the parser to look for two strings in the argument list
0156:         * of the form
0157:         * <pre>
0158:         *    -file someFileName
0159:         * </pre>
0160:         * However, if there is no white space separting the option's name from
0161:         * it's conversion code, then values associated with that
0162:         * option will be assumed to be part of the same argument
0163:         * string as the option itself. For example,
0164:         * <pre>
0165:         *    parser.addOption ("-file=%s #file name");
0166:         * </pre>
0167:         * will cause the parser to look for a single string in the argument
0168:         * list of the form
0169:         * <pre>
0170:         *    -file=someFileName
0171:         * </pre>
0172:         * Such an option is called a "single word" option.
0173:         *
0174:         * <p>
0175:         * In cases where an option has multiple names, then this single
0176:         * word behavior is invoked if there is no white space between
0177:         * the last indicated name and the conversion code. However, previous
0178:         * names in the list will still be given multi-word behavior
0179:         * if there is white space between the name and the
0180:         * following comma. For example,
0181:         * <pre>
0182:         *    parser.addOption ("-nb=,-number ,-n%d #number of blocks");
0183:         * </pre>
0184:         * will cause the parser to look for one, two, and one word constructions
0185:         * of the forms
0186:         * <pre>
0187:         *    -nb=N
0188:         *    -number N
0189:         *    -nN
0190:         * </pre>
0191:         *
0192:         * <h3><a name="multipleOptionValues">Multiple Option Values</a></h3>
0193:         *
0194:         * If may be useful for an option to be followed by several values.
0195:         * For instance, we might have an option <code>-velocity</code>
0196:         * which should be followed by three numbers denoting
0197:         * the x, y, and z components of a velocity vector.
0198:         * We can require multiple values for an option
0199:         * by placing a <i>multiplier</i> specification,
0200:         * of the form <code>X</code>N, where N is an integer,
0201:         * after the conversion code (or range specification, if present).
0202:         * For example,
0203:         * 
0204:         * <pre>
0205:         *    double[] pos = new double[3];
0206:         *
0207:         *    addOption ("-position %fX3 #position of the object", pos);
0208:         * </pre>
0209:         * will cause the parser to look for
0210:         * <pre>
0211:         *    -position xx yy zz
0212:         * </pre>
0213:         * 
0214:         * in the argument list, where <code>xx</code>, <code>yy</code>, and
0215:         * <code>zz</code> are numbers. The values are stored in the array
0216:         * <code>pos</code>.
0217:         *
0218:         * Options requiring multiple values must use arrays to
0219:         * return their values, and cannot be used in single word format.
0220:         *
0221:         * <h3><a name="multipleOptionInvocation">Multiple Option Invocation</a></h3>
0222:         *
0223:         * Normally, if an option appears twice in the command list, the
0224:         * value associated with the second instance simply overwrites the
0225:         * value associated with the first instance.
0226:         *
0227:         * However, the application can instead arrange for the storage of <i>all</i>
0228:         * values associated with multiple option invocation, by supplying a instance
0229:         * of <code>java.util.Vector</code> to serve as the value holder. Then every
0230:         * time the option appears in the argument list, the parser will create a value
0231:         * holder of appropriate type, set it to the current value, and store the
0232:         * holder in the vector. For example, the construction
0233:         *
0234:         * <pre>
0235:         *    Vector vec = new Vector(10);
0236:         *
0237:         *    parser.addOption ("-foo %f", vec);
0238:         *    parser.matchAllArgs(args);
0239:         * </pre>
0240:         * when supplied with an argument list that contains
0241:         * <pre>
0242:         *    -foo 1.2 -foo 1000 -foo -78
0243:         * </pre>
0244:         * 
0245:         * will create three instances of {@link argparser.DoubleHolder DoubleHolder},
0246:         * initialized to <code>1.2</code>, <code>1000</code>, and <code>-78</code>,
0247:         * and store them in <code>vec</code>.
0248:         *
0249:         * <h3><a name="helpInfo">Generating help information</a></h3>
0250:         *
0251:         * ArgParser automatically generates help information for the options, and this
0252:         * information may be printed in response to a <i>help</i> option, or may be
0253:         * queried by the application using {@link #getHelpMessage getHelpMessage}.
0254:         * The information for each option consists of the option's name(s), it's
0255:         * required value(s), and an application-supplied description.  Value
0256:         * information is generated automaticlly from the conversion code, range, and
0257:         * multiplier specifications (although this can be overriden, as
0258:         * <a href=#valueInfo>described below</a>).
0259:         * The application-supplied description is whatever
0260:         * appears in the specification string after the optional <code>#</code>
0261:         * character. The string returned by {@link #getHelpMessage getHelpMessage} for
0262:         * the <a href=#example>first example above</a> would be
0263:         * 
0264:         * <pre>
0265:         * Usage: java argparser.SimpleExample
0266:         * Options include:
0267:         * 
0268:         * -help,-?                displays help information
0269:         * -theta &lt;float&gt;          theta value (in degrees)
0270:         * -file &lt;string&gt;          name of the operating file
0271:         * -debug                  enables display of debugging info
0272:         * </pre>
0273:         * 
0274:         * The options <code>-help</code> and <code>-?</code> are including in the
0275:         * parser by default as help options, and they automatically cause the help
0276:         * message to be printed. To exclude these
0277:         * options, one should use the constructor {@link #ArgParser(String,boolean)
0278:         * ArgParser(synopsis,false)}.
0279:         * Help options can also be specified by the application using {@link
0280:         * #addOption addOption} and the conversion code <code>%h</code>.  Help options
0281:         * can be disabled using {@link #setHelpOptionsEnabled
0282:         * setHelpOptionsEnabled(false)}.
0283:         *
0284:         * <p><a name=valueInfo>
0285:         * A description of the required values for an option can be
0286:         * specified explicitly 
0287:         * by placing a second <code>#</code> character in the specification
0288:         * string. Everything between the first and second <code>#</code>
0289:         * characters then becomes the value description, and everything
0290:         * after the second <code>#</code> character becomes the option
0291:         * description.
0292:         * For example, if the <code>-theta</code> option
0293:         * above was specified with
0294:         * <pre>
0295:         *    parser.addOption ("-theta %f #NUMBER#theta value (in degrees)",theta);
0296:         * </pre>
0297:         * instead of
0298:         * <pre>
0299:         *    parser.addOption ("-theta %f #theta value (in degrees)", theta);
0300:         * </pre>
0301:         * then the corresponding entry in the help message would look
0302:         * like
0303:         * <pre>
0304:         * -theta NUMBER          theta value (in degrees)
0305:         * </pre>
0306:         *
0307:         * <h3><a name="customArgParsing">Custom Argument Parsing</a></h3>
0308:         * 
0309:         * An application may find it necessary to handle arguments that
0310:         * don't fit into the framework of this class. There are a couple
0311:         * of ways to do this.
0312:         *
0313:         * <p>
0314:         * First, the method {@link #matchAllArgs(String[],int,int)
0315:         * matchAllArgs(args,idx,exitFlags)} returns an array of
0316:         * all unmatched arguments, which can then be handled
0317:         * specially:
0318:         * <pre>
0319:         *    String[] unmatched =
0320:         *       parser.matchAllArgs (args, 0, parser.EXIT_ON_ERROR);
0321:         *    for (int i = 0; i < unmatched.length; i++)
0322:         *     { ... handle unmatched arguments ...
0323:         *     }
0324:         * </pre>
0325:         * 
0326:         * For instance, this would be useful for an applicatoon that accepts an
0327:         * arbitrary number of input file names.  The options can be parsed using
0328:         * <code>matchAllArgs</code>, and the remaining unmatched arguments
0329:         * give the file names.
0330:         *
0331:         * <p> If we need more control over the parsing, we can parse arguments one at
0332:         * a time using {@link #matchArg matchArg}:
0333:         * 
0334:         * <pre>
0335:         *    int idx = 0;
0336:         *    while (idx < args.length)
0337:         *     { try
0338:         *        { idx = parser.matchArg (args, idx);
0339:         *          if (parser.getUnmatchedArgument() != null)
0340:         *           {
0341:         *             ... handle this unmatched argument ourselves ...
0342:         *           }
0343:         *        }
0344:         *       catch (ArgParserException e) 
0345:         *        { // malformed or erroneous argument
0346:         *          parser.printErrorAndExit (e.getMessage());
0347:         *        }
0348:         *     }
0349:         * </pre>
0350:         *
0351:         * {@link #matchArg matchArg(args,idx)} matches one option at location
0352:         * <code>idx</code> in the argument list, and then returns the location value
0353:         * that should be used for the next match.  If an argument does
0354:         * not match any option,
0355:         * {@link #getUnmatchedArgument getUnmatchedArgument} will return a copy of the
0356:         * unmatched argument.
0357:         *
0358:         * <h3><a name="argsFromAFile">Reading Arguments From a File</a></h3>
0359:         *
0360:         * The method {@link #prependArgs prependArgs} can be used to automatically
0361:         * read in a set of arguments from a file and prepend them onto an existing
0362:         * argument list. Argument words correspond to white-space-delimited strings,
0363:         * and the file may contain the comment character <code>#</code> (which
0364:         * comments out everything to the end of the current line). A typical usage
0365:         * looks like this:
0366:         *
0367:         * <pre>
0368:         *    ... create parser and add options ...
0369:         * 
0370:         *    args = parser.prependArgs (new File(".configFile"), args);
0371:         *
0372:         *    parser.matchAllArgs (args);
0373:         * </pre>
0374:         *
0375:         * This makes it easy to generate simple configuration files for an
0376:         * application.
0377:         *
0378:         * @author John E. Lloyd, Fall 2004
0379:         */
0380:        public class ArgParser {
0381:            Vector matchList;
0382:            //	int tabSpacing = 8;
0383:            String synopsisString;
0384:            boolean helpOptionsEnabled = true;
0385:            Record defaultHelpOption = null;
0386:            Record firstHelpOption = null;
0387:            PrintStream printStream = System.out;
0388:            int helpIndent = 24;
0389:            String errMsg = null;
0390:            String unmatchedArg = null;
0391:
0392:            static String validConversionCodes = "iodxcbfsvh";
0393:
0394:            /**
0395:             * Indicates that the program should exit with an appropriate message
0396:             * in the event of an erroneous or malformed argument.*/
0397:            public static int EXIT_ON_ERROR = 1;
0398:
0399:            /**
0400:             * Indicates that the program should exit with an appropriate message
0401:             * in the event of an unmatched argument.*/
0402:            public static int EXIT_ON_UNMATCHED = 2;
0403:
0404:            /**
0405:             * Returns a string containing the valid conversion codes. These
0406:             * are the characters which may follow the <code>%</code> character in
0407:             * the specification string of {@link #addOption addOption}.
0408:             *
0409:             * @return Valid conversion codes
0410:             * @see #addOption
0411:             */
0412:            public static String getValidConversionCodes() {
0413:                return validConversionCodes;
0414:            }
0415:
0416:            static class NameDesc {
0417:                String name;
0418:                // oneWord implies that any value associated with
0419:                // option is concatenated onto the argument string itself
0420:                boolean oneWord;
0421:                NameDesc next = null;
0422:            }
0423:
0424:            static class RangePnt {
0425:                double dval = 0;
0426:                long lval = 0;
0427:                String sval = null;
0428:                boolean bval = true;
0429:                boolean closed = true;
0430:
0431:                RangePnt(String s, boolean closed) {
0432:                    sval = s;
0433:                    this .closed = closed;
0434:                }
0435:
0436:                RangePnt(double d, boolean closed) {
0437:                    dval = d;
0438:                    this .closed = closed;
0439:                }
0440:
0441:                RangePnt(long l, boolean closed) {
0442:                    lval = l;
0443:                    this .closed = closed;
0444:                }
0445:
0446:                RangePnt(boolean b, boolean closed) {
0447:                    bval = b;
0448:                    this .closed = closed;
0449:                }
0450:
0451:                RangePnt(StringScanner scanner, int type)
0452:                        throws IllegalArgumentException {
0453:                    String typeName = null;
0454:                    try {
0455:                        switch (type) {
0456:                        case Record.CHAR: {
0457:                            typeName = "character";
0458:                            lval = scanner.scanChar();
0459:                            break;
0460:                        }
0461:                        case Record.INT:
0462:                        case Record.LONG: {
0463:                            typeName = "integer";
0464:                            lval = scanner.scanInt();
0465:                            break;
0466:                        }
0467:                        case Record.FLOAT:
0468:                        case Record.DOUBLE: {
0469:                            typeName = "float";
0470:                            dval = scanner.scanDouble();
0471:                            break;
0472:                        }
0473:                        case Record.STRING: {
0474:                            typeName = "string";
0475:                            sval = scanner.scanString();
0476:                            break;
0477:                        }
0478:                        case Record.BOOLEAN: {
0479:                            typeName = "boolean";
0480:                            bval = scanner.scanBoolean();
0481:                            break;
0482:                        }
0483:                        }
0484:                    } catch (StringScanException e) {
0485:                        throw new IllegalArgumentException("Malformed "
0486:                                + typeName
0487:                                + " '"
0488:                                + scanner.substring(scanner.getIndex(), e
0489:                                        .getFailIndex() + 1)
0490:                                + "' in range spec");
0491:                    }
0492:                    //	      this.closed = closed;
0493:                }
0494:
0495:                void setClosed(boolean closed) {
0496:                    this .closed = closed;
0497:                }
0498:
0499:                boolean getClosed() {
0500:                    return closed;
0501:                }
0502:
0503:                int compareTo(double d) {
0504:                    if (dval < d) {
0505:                        return -1;
0506:                    } else if (d == dval) {
0507:                        return 0;
0508:                    } else {
0509:                        return 1;
0510:                    }
0511:                }
0512:
0513:                int compareTo(long l) {
0514:                    if (lval < l) {
0515:                        return -1;
0516:                    } else if (l == lval) {
0517:                        return 0;
0518:                    } else {
0519:                        return 1;
0520:                    }
0521:                }
0522:
0523:                int compareTo(String s) {
0524:                    return sval.compareTo(s);
0525:                }
0526:
0527:                int compareTo(boolean b) {
0528:                    if (b == bval) {
0529:                        return 0;
0530:                    } else {
0531:                        return 1;
0532:                    }
0533:                }
0534:
0535:                public String toString() {
0536:                    return "{ dval=" + dval + ", lval=" + lval + ", sval="
0537:                            + sval + ", bval=" + bval + ", closed=" + closed
0538:                            + "}";
0539:                }
0540:            }
0541:
0542:            class RangeAtom {
0543:                RangePnt low = null;
0544:                RangePnt high = null;
0545:                RangeAtom next = null;
0546:
0547:                RangeAtom(RangePnt p0, RangePnt p1, int type)
0548:                        throws IllegalArgumentException {
0549:                    int cmp = 0;
0550:                    switch (type) {
0551:                    case Record.CHAR:
0552:                    case Record.INT:
0553:                    case Record.LONG: {
0554:                        cmp = p0.compareTo(p1.lval);
0555:                        break;
0556:                    }
0557:                    case Record.FLOAT:
0558:                    case Record.DOUBLE: {
0559:                        cmp = p0.compareTo(p1.dval);
0560:                        break;
0561:                    }
0562:                    case Record.STRING: {
0563:                        cmp = p0.compareTo(p1.sval);
0564:                        break;
0565:                    }
0566:                    }
0567:                    if (cmp > 0) { // then switch high and low
0568:                        low = p1;
0569:                        high = p0;
0570:                    } else {
0571:                        low = p0;
0572:                        high = p1;
0573:                    }
0574:                }
0575:
0576:                RangeAtom(RangePnt p0) throws IllegalArgumentException {
0577:                    low = p0;
0578:                }
0579:
0580:                boolean match(double d) {
0581:                    int lc = low.compareTo(d);
0582:                    if (high != null) {
0583:                        int hc = high.compareTo(d);
0584:                        return (lc * hc < 0 || (low.closed && lc == 0) || (high.closed && hc == 0));
0585:                    } else {
0586:                        return lc == 0;
0587:                    }
0588:                }
0589:
0590:                boolean match(long l) {
0591:                    int lc = low.compareTo(l);
0592:                    if (high != null) {
0593:                        int hc = high.compareTo(l);
0594:                        return (lc * hc < 0 || (low.closed && lc == 0) || (high.closed && hc == 0));
0595:                    } else {
0596:                        return lc == 0;
0597:                    }
0598:                }
0599:
0600:                boolean match(String s) {
0601:                    int lc = low.compareTo(s);
0602:                    if (high != null) {
0603:                        int hc = high.compareTo(s);
0604:                        return (lc * hc < 0 || (low.closed && lc == 0) || (high.closed && hc == 0));
0605:                    } else {
0606:                        return lc == 0;
0607:                    }
0608:                }
0609:
0610:                boolean match(boolean b) {
0611:                    return low.compareTo(b) == 0;
0612:                }
0613:
0614:                public String toString() {
0615:                    return "low=" + (low == null ? "null" : low.toString())
0616:                            + ", high="
0617:                            + (high == null ? "null" : high.toString());
0618:                }
0619:            }
0620:
0621:            class Record {
0622:                NameDesc nameList;
0623:                static final int NOTYPE = 0;
0624:                static final int BOOLEAN = 1;
0625:                static final int CHAR = 2;
0626:                static final int INT = 3;
0627:                static final int LONG = 4;
0628:                static final int FLOAT = 5;
0629:                static final int DOUBLE = 6;
0630:                static final int STRING = 7;
0631:                int type;
0632:                int numValues;
0633:                boolean vectorResult = false;
0634:
0635:                String helpMsg = null;
0636:                String valueDesc = null;
0637:                String rangeDesc = null;
0638:                Object resHolder = null;
0639:                RangeAtom rangeList = null;
0640:                RangeAtom rangeTail = null;
0641:                char convertCode;
0642:                boolean vval = true; // default value for now
0643:
0644:                NameDesc firstNameDesc() {
0645:                    return nameList;
0646:                }
0647:
0648:                RangeAtom firstRangeAtom() {
0649:                    return rangeList;
0650:                }
0651:
0652:                int numRangeAtoms() {
0653:                    int cnt = 0;
0654:                    for (RangeAtom ra = rangeList; ra != null; ra = ra.next) {
0655:                        cnt++;
0656:                    }
0657:                    return cnt;
0658:                }
0659:
0660:                void addRangeAtom(RangeAtom ra) {
0661:                    if (rangeList == null) {
0662:                        rangeList = ra;
0663:                    } else {
0664:                        rangeTail.next = ra;
0665:                    }
0666:                    rangeTail = ra;
0667:                }
0668:
0669:                boolean withinRange(double d) {
0670:                    if (rangeList == null) {
0671:                        return true;
0672:                    }
0673:                    for (RangeAtom ra = rangeList; ra != null; ra = ra.next) {
0674:                        if (ra.match(d)) {
0675:                            return true;
0676:                        }
0677:                    }
0678:                    return false;
0679:                }
0680:
0681:                boolean withinRange(long l) {
0682:                    if (rangeList == null) {
0683:                        return true;
0684:                    }
0685:                    for (RangeAtom ra = rangeList; ra != null; ra = ra.next) {
0686:                        if (ra.match(l)) {
0687:                            return true;
0688:                        }
0689:                    }
0690:                    return false;
0691:                }
0692:
0693:                boolean withinRange(String s) {
0694:                    if (rangeList == null) {
0695:                        return true;
0696:                    }
0697:                    for (RangeAtom ra = rangeList; ra != null; ra = ra.next) {
0698:                        if (ra.match(s)) {
0699:                            return true;
0700:                        }
0701:                    }
0702:                    return false;
0703:                }
0704:
0705:                boolean withinRange(boolean b) {
0706:                    if (rangeList == null) {
0707:                        return true;
0708:                    }
0709:                    for (RangeAtom ra = rangeList; ra != null; ra = ra.next) {
0710:                        if (ra.match(b)) {
0711:                            return true;
0712:                        }
0713:                    }
0714:                    return false;
0715:                }
0716:
0717:                String valTypeName() {
0718:                    switch (convertCode) {
0719:                    case 'i': {
0720:                        return ("integer");
0721:                    }
0722:                    case 'o': {
0723:                        return ("octal integer");
0724:                    }
0725:                    case 'd': {
0726:                        return ("decimal integer");
0727:                    }
0728:                    case 'x': {
0729:                        return ("hex integer");
0730:                    }
0731:                    case 'c': {
0732:                        return ("char");
0733:                    }
0734:                    case 'b': {
0735:                        return ("boolean");
0736:                    }
0737:                    case 'f': {
0738:                        return ("float");
0739:                    }
0740:                    case 's': {
0741:                        return ("string");
0742:                    }
0743:                    }
0744:                    return ("unknown");
0745:                }
0746:
0747:                void scanValue(Object result, String name, String s,
0748:                        int resultIdx) throws ArgParseException {
0749:                    double dval = 0;
0750:                    String sval = null;
0751:                    long lval = 0;
0752:                    boolean bval = false;
0753:
0754:                    if (s.length() == 0) {
0755:                        throw new ArgParseException(name,
0756:                                "requires a contiguous value");
0757:                    }
0758:                    StringScanner scanner = new StringScanner(s);
0759:                    try {
0760:                        switch (convertCode) {
0761:                        case 'i': {
0762:                            lval = scanner.scanInt();
0763:                            break;
0764:                        }
0765:                        case 'o': {
0766:                            lval = scanner.scanInt(8, false);
0767:                            break;
0768:                        }
0769:                        case 'd': {
0770:                            lval = scanner.scanInt(10, false);
0771:                            break;
0772:                        }
0773:                        case 'x': {
0774:                            lval = scanner.scanInt(16, false);
0775:                            break;
0776:                        }
0777:                        case 'c': {
0778:                            lval = scanner.scanChar();
0779:                            break;
0780:                        }
0781:                        case 'b': {
0782:                            bval = scanner.scanBoolean();
0783:                            break;
0784:                        }
0785:                        case 'f': {
0786:                            dval = scanner.scanDouble();
0787:                            break;
0788:                        }
0789:                        case 's': {
0790:                            sval = scanner.getString();
0791:                            break;
0792:                        }
0793:                        }
0794:                    } catch (StringScanException e) {
0795:                        throw new ArgParseException(name, "malformed "
0796:                                + valTypeName() + " '" + s + "'");
0797:                    }
0798:                    scanner.skipWhiteSpace();
0799:                    if (!scanner.atEnd()) {
0800:                        throw new ArgParseException(name, "malformed "
0801:                                + valTypeName() + " '" + s + "'");
0802:                    }
0803:                    boolean outOfRange = false;
0804:                    switch (type) {
0805:                    case CHAR:
0806:                    case INT:
0807:                    case LONG: {
0808:                        outOfRange = !withinRange(lval);
0809:                        break;
0810:                    }
0811:                    case FLOAT:
0812:                    case DOUBLE: {
0813:                        outOfRange = !withinRange(dval);
0814:                        break;
0815:                    }
0816:                    case STRING: {
0817:                        outOfRange = !withinRange(sval);
0818:                        break;
0819:                    }
0820:                    case BOOLEAN: {
0821:                        outOfRange = !withinRange(bval);
0822:                        break;
0823:                    }
0824:                    }
0825:                    if (outOfRange) {
0826:                        String errmsg = "value " + s + " not in range ";
0827:                        throw new ArgParseException(name, "value '" + s
0828:                                + "' not in range " + rangeDesc);
0829:                    }
0830:                    if (result.getClass().isArray()) {
0831:                        switch (type) {
0832:                        case BOOLEAN: {
0833:                            ((boolean[]) result)[resultIdx] = bval;
0834:                            break;
0835:                        }
0836:                        case CHAR: {
0837:                            ((char[]) result)[resultIdx] = (char) lval;
0838:                            break;
0839:                        }
0840:                        case INT: {
0841:                            ((int[]) result)[resultIdx] = (int) lval;
0842:                            break;
0843:                        }
0844:                        case LONG: {
0845:                            ((long[]) result)[resultIdx] = lval;
0846:                            break;
0847:                        }
0848:                        case FLOAT: {
0849:                            ((float[]) result)[resultIdx] = (float) dval;
0850:                            break;
0851:                        }
0852:                        case DOUBLE: {
0853:                            ((double[]) result)[resultIdx] = dval;
0854:                            break;
0855:                        }
0856:                        case STRING: {
0857:                            ((String[]) result)[resultIdx] = sval;
0858:                            break;
0859:                        }
0860:                        }
0861:                    } else {
0862:                        switch (type) {
0863:                        case BOOLEAN: {
0864:                            ((BooleanHolder) result).value = bval;
0865:                            break;
0866:                        }
0867:                        case CHAR: {
0868:                            ((CharHolder) result).value = (char) lval;
0869:                            break;
0870:                        }
0871:                        case INT: {
0872:                            ((IntHolder) result).value = (int) lval;
0873:                            break;
0874:                        }
0875:                        case LONG: {
0876:                            ((LongHolder) result).value = lval;
0877:                            break;
0878:                        }
0879:                        case FLOAT: {
0880:                            ((FloatHolder) result).value = (float) dval;
0881:                            break;
0882:                        }
0883:                        case DOUBLE: {
0884:                            ((DoubleHolder) result).value = dval;
0885:                            break;
0886:                        }
0887:                        case STRING: {
0888:                            ((StringHolder) result).value = sval;
0889:                            break;
0890:                        }
0891:                        }
0892:                    }
0893:                }
0894:            }
0895:
0896:            private String firstHelpOptionName() {
0897:                if (firstHelpOption != null) {
0898:                    return firstHelpOption.nameList.name;
0899:                } else {
0900:                    return null;
0901:                }
0902:            }
0903:
0904:            /**
0905:             * Creates an <code>ArgParser</code> with a synopsis
0906:             * string, and the default help options <code>-help</code> and
0907:             * <code>-&#063;</code>.
0908:             *
0909:             * @param synopsisString string that briefly describes program usage,
0910:             * for use by {@link #getHelpMessage getHelpMessage}.
0911:             * @see ArgParser#getSynopsisString
0912:             * @see ArgParser#getHelpMessage
0913:             */
0914:            public ArgParser(String synopsisString) {
0915:                this (synopsisString, true);
0916:            }
0917:
0918:            /**
0919:             * Creates an <code>ArgParser</code> with a synopsis
0920:             * string. The help options <code>-help</code> and
0921:             * <code>-?</code> are added if <code>defaultHelp</code>
0922:             * is true.
0923:             *
0924:             * @param synopsisString string that briefly describes program usage,
0925:             * for use by {@link #getHelpMessage getHelpMessage}.
0926:             * @param defaultHelp if true, adds the default help options
0927:             * @see ArgParser#getSynopsisString
0928:             * @see ArgParser#getHelpMessage
0929:             */
0930:            public ArgParser(String synopsisString, boolean defaultHelp) {
0931:                matchList = new Vector(128);
0932:                this .synopsisString = synopsisString;
0933:                if (defaultHelp) {
0934:                    addOption("-help,-? %h #displays help information", null);
0935:                    defaultHelpOption = firstHelpOption = (Record) matchList
0936:                            .get(0);
0937:                }
0938:            }
0939:
0940:            /**
0941:             * Returns the synopsis string used by the parser.
0942:             * The synopsis string is a short description of how to invoke
0943:             * the program, and usually looks something like
0944:             * <p>
0945:             * <prec>
0946:             * "java somepackage.SomeClass [options] files ..."
0947:             * </prec>
0948:             * 
0949:             * <p> It is used in help and error messages.
0950:             *
0951:             * @return synopsis string
0952:             * @see ArgParser#setSynopsisString
0953:             * @see ArgParser#getHelpMessage
0954:             */
0955:            public String getSynopsisString() {
0956:                return synopsisString;
0957:            }
0958:
0959:            /**
0960:             * Sets the synopsis string used by the parser.
0961:             *
0962:             * @param s new synopsis string
0963:             * @see ArgParser#getSynopsisString
0964:             * @see ArgParser#getHelpMessage
0965:             */
0966:            public void setSynopsisString(String s) {
0967:                synopsisString = s;
0968:            }
0969:
0970:            /**
0971:             * Indicates whether or not help options are enabled.
0972:             *
0973:             * @return true if help options are enabled
0974:             * @see ArgParser#setHelpOptionsEnabled 
0975:             * @see ArgParser#addOption
0976:             */
0977:            public boolean getHelpOptionsEnabled() {
0978:                return helpOptionsEnabled;
0979:            }
0980:
0981:            /**
0982:             * Enables or disables help options. Help options are those
0983:             * associated with a conversion code of <code>%h</code>. If
0984:             * help options are enabled, and a help option is matched,
0985:             * then the string produced by
0986:             * {@link #getHelpMessage getHelpMessage}
0987:             * is printed to the default print stream and the program
0988:             * exits with code 0. Otherwise, arguments which match help
0989:             * options are ignored.
0990:             *
0991:             * @param enable enables help options if <code>true</code>.
0992:             * @see ArgParser#getHelpOptionsEnabled 
0993:             * @see ArgParser#addOption
0994:             * @see ArgParser#setDefaultPrintStream */
0995:            public void setHelpOptionsEnabled(boolean enable) {
0996:                helpOptionsEnabled = enable;
0997:            }
0998:
0999:            /**
1000:             * Returns the default print stream used for outputting help
1001:             * and error information.
1002:             *
1003:             * @return default print stream
1004:             * @see ArgParser#setDefaultPrintStream
1005:             */
1006:            public PrintStream getDefaultPrintStream() {
1007:                return printStream;
1008:            }
1009:
1010:            /**
1011:             * Sets the default print stream used for outputting help
1012:             * and error information.
1013:             *
1014:             * @param stream new default print stream
1015:             * @see ArgParser#getDefaultPrintStream
1016:             */
1017:            public void setDefaultPrintStream(PrintStream stream) {
1018:                printStream = stream;
1019:            }
1020:
1021:            /**
1022:             * Gets the indentation used by {@link #getHelpMessage
1023:             * getHelpMessage}.
1024:             *
1025:             * @return number of indentation columns
1026:             * @see ArgParser#setHelpIndentation
1027:             * @see ArgParser#getHelpMessage
1028:             */
1029:            public int getHelpIndentation() {
1030:                return helpIndent;
1031:            }
1032:
1033:            /**
1034:             * Sets the indentation used by {@link #getHelpMessage
1035:             * getHelpMessage}. This is the number of columns that an option's help
1036:             * information is indented. If the option's name and value information
1037:             * can fit within this number of columns, then all information about
1038:             * the option is placed on one line.  Otherwise, the indented help
1039:             * information is placed on a separate line.
1040:             *
1041:             * @param indent number of indentation columns
1042:             * @see ArgParser#getHelpIndentation
1043:             * @see ArgParser#getHelpMessage
1044:             */
1045:            public void setHelpIndentation(int indent) {
1046:                helpIndent = indent;
1047:            }
1048:
1049:            //  	public void setTabSpacing (int n)
1050:            //  	 { tabSpacing = n;
1051:            //  	 }
1052:
1053:            //  	public int getTabSpacing ()
1054:            //  	 { return tabSpacing;
1055:            //  	 }
1056:
1057:            private void scanRangeSpec(Record rec, String s)
1058:                    throws IllegalArgumentException {
1059:                StringScanner scanner = new StringScanner(s);
1060:                int i0, i = 1;
1061:                char c, c0, c1;
1062:
1063:                scanner.setStringDelimiters(")],}");
1064:                c = scanner.getc(); // swallow the first '{'
1065:                scanner.skipWhiteSpace();
1066:                while ((c = scanner.peekc()) != '}') {
1067:                    RangePnt p0, p1;
1068:
1069:                    if (c == '[' || c == '(') {
1070:                        if (rec.convertCode == 'v' || rec.convertCode == 'b') {
1071:                            throw new IllegalArgumentException(
1072:                                    "Sub ranges not supported for %b or %v");
1073:                        }
1074:                        c0 = scanner.getc(); // record & swallow character
1075:                        scanner.skipWhiteSpace();
1076:                        p0 = new RangePnt(scanner, rec.type);
1077:                        scanner.skipWhiteSpace();
1078:                        if (scanner.getc() != ',') {
1079:                            throw new IllegalArgumentException(
1080:                                    "Missing ',' in subrange specification");
1081:                        }
1082:                        p1 = new RangePnt(scanner, rec.type);
1083:                        scanner.skipWhiteSpace();
1084:                        if ((c1 = scanner.getc()) != ']' && c1 != ')') {
1085:                            throw new IllegalArgumentException(
1086:                                    "Unterminated subrange");
1087:                        }
1088:                        if (c0 == '(') {
1089:                            p0.setClosed(false);
1090:                        }
1091:                        if (c1 == ')') {
1092:                            p1.setClosed(false);
1093:                        }
1094:                        rec.addRangeAtom(new RangeAtom(p0, p1, rec.type));
1095:                    } else {
1096:                        scanner.skipWhiteSpace();
1097:                        p0 = new RangePnt(scanner, rec.type);
1098:                        rec.addRangeAtom(new RangeAtom(p0));
1099:                    }
1100:                    scanner.skipWhiteSpace();
1101:                    if ((c = scanner.peekc()) == ',') {
1102:                        scanner.getc();
1103:                        scanner.skipWhiteSpace();
1104:                    } else if (c != '}') {
1105:                        throw new IllegalArgumentException(
1106:                                "Range spec: ',' or '}' expected");
1107:                    }
1108:                }
1109:                if (rec.numRangeAtoms() == 1) {
1110:                    rec.rangeDesc = s.substring(1, s.length() - 1);
1111:                } else {
1112:                    rec.rangeDesc = s;
1113:                }
1114:            }
1115:
1116:            private int defaultResultType(char convertCode) {
1117:                switch (convertCode) {
1118:                case 'i':
1119:                case 'o':
1120:                case 'd':
1121:                case 'x': {
1122:                    return Record.LONG;
1123:                }
1124:                case 'c': {
1125:                    return Record.CHAR;
1126:                }
1127:                case 'v':
1128:                case 'b': {
1129:                    return Record.BOOLEAN;
1130:                }
1131:                case 'f': {
1132:                    return Record.DOUBLE;
1133:                }
1134:                case 's': {
1135:                    return Record.STRING;
1136:                }
1137:                }
1138:                return Record.NOTYPE;
1139:            }
1140:
1141:            /**
1142:             * Adds a new option description to the parser. The method takes two
1143:             * arguments: a specification string, and a result holder in which to
1144:             * store the associated value.
1145:             *
1146:             * <p>The specification string has the general form
1147:             *
1148:             * <p> <var>optionNames</var>
1149:             * <code>%</code><var>conversionCode</var>
1150:             * [<code>{</code><var>rangeSpec</var><code>}</code>]
1151:             * [<code>X</code><var>multiplier</var>]
1152:             * [<code>#</code><var>valueDescription</var>]
1153:             * [<code>#</code><var>optionDescription</var>] </code>
1154:             * 
1155:             * <p>
1156:             * where
1157:             * <ul> <p><li><var>optionNames</var> is a
1158:             * comma-separated list of names for the option
1159:             * (such as <code>-f, --file</code>).
1160:             * 
1161:             * <p><li><var>conversionCode</var> is a single letter,
1162:             * following a <code>%</code> character, specifying
1163:             * information about what value the option requires:
1164:             *
1165:             * <table>
1166:             * <tr><td><code>%f</code></td><td>a floating point number</td>
1167:             * <tr><td><code>%i</code></td><td>an integer, in either decimal,
1168:             * hex (if preceeded by <code>0x</code>), or
1169:             * octal (if preceeded by <code>0</code>)</td>
1170:             * <tr valign=top>
1171:             * <td><code>%d</code></td><td>a decimal integer</td>
1172:             * <tr valign=top>
1173:             * <td><code>%o</code></td><td>an octal integer</td>
1174:             * <tr valign=top>
1175:             * <td><code>%h</code></td><td>a hex integer (without the
1176:             * preceeding <code>0x</code>)</td>
1177:             * <tr valign=top>
1178:             * <td><code>%c</code></td><td>a single character, including
1179:             * escape sequences (such as <code>\n</code> or <code>\007</code>),
1180:             * and optionally enclosed in single quotes
1181:             * <tr valign=top>
1182:             * <td><code>%b</code></td><td>a boolean value (<code>true</code>
1183:             * or <code>false</code>)</td>
1184:             * <tr valign=top>
1185:             * <td><code>%s</code></td><td>a string. This will
1186:             * be the argument string itself (or its remainder, in
1187:             * the case of a single word option)</td>
1188:             * <tr valign=top>
1189:             * <td><code>%v</code></td><td>no explicit value is expected,
1190:             * but a boolean value of <code>true</code> (by default)
1191:             * will be stored into the associated result holder if this
1192:             * option is matched. If one wishes to have a value of
1193:             * <code>false</code> stored instead, then the <code>%v</code>
1194:             * should be followed by a "range spec" containing
1195:             * <code>false</code>, as in <code>%v{false}</code>.
1196:             * </table>
1197:             *
1198:             * <p><li><var>rangeSpec</var> is an optional range specification,
1199:             * placed inside curly braces, consisting of a
1200:             * comma-separated list of range items each specifying
1201:             * permissible values for the option. A range item may be an
1202:             * individual value, or it may itself be a subrange,
1203:             * consisting of two individual values, separated by a comma,
1204:             * and enclosed in square or round brackets. Square and round
1205:             * brackets denote closed and open endpoints of a subrange, indicating
1206:             * that the associated endpoint value is included or excluded
1207:             * from the subrange.
1208:             * The values specified in the range spec need to be
1209:             * consistent with the type of value expected by the option.
1210:             *
1211:             * <p><b>Examples:</b>
1212:             *
1213:             * <p>A range spec of <code>{2,4,8,16}</code> for an integer
1214:             * value will allow the integers 2, 4, 8, or 16.
1215:             *
1216:             * <p>A range spec of <code>{[-1.0,1.0]}</code> for a floating
1217:             * point value will allow any floating point number in the
1218:             * range -1.0 to 1.0.
1219:             * 
1220:             * <p>A range spec of <code>{(-88,100],1000}</code> for an integer
1221:             * value will allow values > -88 and <= 100, as well as 1000.
1222:             *
1223:             * <p>A range spec of <code>{"foo", "bar", ["aaa","zzz")} </code> for a
1224:             * string value will allow strings equal to <code>"foo"</code> or
1225:             * <code>"bar"</code>, plus any string lexically greater than or equal
1226:             * to <code>"aaa"</code> but less then <code>"zzz"</code>.
1227:             *
1228:             * <p><li><var>multiplier</var> is an optional integer,
1229:             * following a <code>X</code> character, 
1230:             * indicating the number of values which the option expects.
1231:             * If the multiplier is not specified, it is assumed to be
1232:             * 1. If the multiplier value is greater than 1, then the
1233:             * result holder should be either an array (of appropriate
1234:             * type) with a length greater than or equal to the multiplier
1235:             * value, or a <code>java.util.Vector</code>
1236:             * <a href=#vectorHolder>as discussed below</a>.
1237:             *
1238:             * <p><li><var>valueDescription</var> is an optional
1239:             * description of the option's value requirements,
1240:             * and consists of all
1241:             * characters between two <code>#</code> characters.
1242:             * The final <code>#</code> character initiates the
1243:             * <i>option description</i>, which may be empty.
1244:             * The value description is used in
1245:             * <a href=#helpInfo>generating help messages</a>.
1246:             *
1247:             * <p><li><var>optionDescription</var> is an optional
1248:             * description of the option itself, consisting of all
1249:             * characters between a <code>#</code> character
1250:             * and the end of the specification string.
1251:             * The option description is used in 
1252:             * <a href=#helpInfo>generating help messages</a>.
1253:             * </ul>
1254:             *
1255:             * <p>The result holder must be an object capable of holding
1256:             * a value compatible with the conversion code,
1257:             * or it must be a <code>java.util.Vector</code>.
1258:             * When the option is matched, its associated value is
1259:             * placed in the result holder. If the same option is
1260:             * matched repeatedly, the result holder value will be overwritten,
1261:             * unless the result holder is a <code>java.util.Vector</code>,
1262:             * in which
1263:             * case new holder objects for each match will be allocated
1264:             * and added to the vector. Thus if
1265:             * multiple instances of an option are desired by the
1266:             * program, the result holder should be a
1267:             * <code>java.util.Vector</code>.
1268:             *
1269:             * <p>If the result holder is not a <code>Vector</code>, then
1270:             * it must correspond as follows to the conversion code:
1271:             *
1272:             * <table>
1273:             * <tr valign=top>
1274:             * <td><code>%i</code>, <code>%d</code>, <code>%x</code>,
1275:             * <code>%o</code></td>
1276:             * <td>{@link argparser.IntHolder IntHolder},
1277:             * {@link argparser.LongHolder LongHolder}, <code>int[]</code>, or
1278:             * <code>long[]</code></td>
1279:             * </tr>
1280:             * 
1281:             * <tr valign=top>
1282:             * <td><code>%f</code></td>
1283:             * <td>{@link argparser.FloatHolder FloatHolder},
1284:             * {@link argparser.DoubleHolder DoubleHolder},
1285:             * <code>float[]</code>, or
1286:             * <code>double[]</code></td>
1287:             * </tr>
1288:             * 
1289:             * <tr valign=top>
1290:             * <td><code>%b</code>, <code>%v</code></td>
1291:             * <td>{@link argparser.BooleanHolder BooleanHolder} or
1292:             * <code>boolean[]</code></td>
1293:             * </tr>
1294:             * 
1295:             * <tr valign=top>
1296:             * <td><code>%s</code></td>
1297:             * <td>{@link argparser.StringHolder StringHolder} or
1298:             * <code>String[]</code></td>
1299:             * </tr>
1300:             * 
1301:             * <tr valign=top>
1302:             * <td><code>%c</code></td>
1303:             * <td>{@link argparser.CharHolder CharHolder} or
1304:             * <code>char[]</code></td>
1305:             * </tr>
1306:             * </table>
1307:             *
1308:             * <p>In addition, if the multiplier is greater than 1,
1309:             * then only the array type indicated above may be used,
1310:             * and the array must be at least as long as the multiplier.
1311:             *
1312:             * <p><a name=vectorHolder>If the result holder is a
1313:             * <code>Vector</code>, then the system will create an appropriate
1314:             * result holder object and add it to the vector. Multiple occurances
1315:             * of the option will cause multiple results to be added to the vector.
1316:             *
1317:             * <p>The object allocated by the system to store the result
1318:             * will correspond to the conversion code as follows:
1319:             * 
1320:             * <table>
1321:             * <tr valign=top>
1322:             * <td><code>%i</code>, <code>%d</code>, <code>%x</code>,
1323:             * <code>%o</code></td>
1324:             * <td>{@link argparser.LongHolder LongHolder}, or
1325:             * <code>long[]</code> if the multiplier value exceeds 1</td>
1326:             * </tr>
1327:             * 
1328:             * <tr valign=top>
1329:             * <td><code>%f</code></td>
1330:             * <td>{@link argparser.DoubleHolder DoubleHolder}, or
1331:             * <code>double[]</code> if the multiplier value exceeds 1</td>
1332:             * </tr>
1333:             * 
1334:             * <tr valign=top>
1335:             * <td><code>%b</code>, <code>%v</code></td>
1336:             * <td>{@link argparser.BooleanHolder BooleanHolder}, or
1337:             * <code>boolean[]</code>
1338:             * if the multiplier value exceeds 1</td>
1339:             * </tr>
1340:             * 
1341:             * <tr valign=top>
1342:             * <td><code>%s</code></td>
1343:             * <td>{@link argparser.StringHolder StringHolder}, or
1344:             * <code>String[]</code>
1345:             * if the multiplier value exceeds 1</td>
1346:             * </tr>
1347:             * 
1348:             * <tr valign=top>
1349:             * <td><code>%c</code></td>
1350:             * <td>{@link argparser.CharHolder CharHolder}, or <code>char[]</code>
1351:             * if the multiplier value exceeds 1</td>
1352:             * </tr>
1353:             * </table>
1354:             *
1355:             * @param spec the specification string
1356:             * @param resHolder object in which to store the associated
1357:             * value
1358:             * @throws IllegalArgumentException if there is an error in
1359:             * the specification or if the result holder is of an invalid
1360:             * type.  */
1361:            public void addOption(String spec, Object resHolder)
1362:                    throws IllegalArgumentException {
1363:                // null terminated string is easier to parse
1364:                StringScanner scanner = new StringScanner(spec);
1365:                Record rec = null;
1366:                NameDesc nameTail = null;
1367:                NameDesc ndesc;
1368:                int i0, i1;
1369:                char c;
1370:
1371:                do {
1372:                    ndesc = new NameDesc();
1373:                    boolean nameEndsInWhiteSpace = false;
1374:
1375:                    scanner.skipWhiteSpace();
1376:                    i0 = scanner.getIndex();
1377:                    while (!Character.isWhitespace(c = scanner.getc())
1378:                            && c != ',' && c != '%' && c != '\000')
1379:                        ;
1380:                    i1 = scanner.getIndex();
1381:                    if (c != '\000') {
1382:                        i1--;
1383:                    }
1384:                    if (i0 == i1) { // then c is one of ',' '%' or '\000'
1385:                        throw new IllegalArgumentException(
1386:                                "Null option name given");
1387:                    }
1388:                    if (Character.isWhitespace(c)) {
1389:                        nameEndsInWhiteSpace = true;
1390:                        scanner.skipWhiteSpace();
1391:                        c = scanner.getc();
1392:                    }
1393:                    if (c == '\000') {
1394:                        throw new IllegalArgumentException(
1395:                                "No conversion character given");
1396:                    }
1397:                    if (c != ',' && c != '%') {
1398:                        throw new IllegalArgumentException(
1399:                                "Names not separated by ','");
1400:                    }
1401:                    ndesc.name = scanner.substring(i0, i1);
1402:                    if (rec == null) {
1403:                        rec = new Record();
1404:                        rec.nameList = ndesc;
1405:                    } else {
1406:                        nameTail.next = ndesc;
1407:                    }
1408:                    nameTail = ndesc;
1409:                    ndesc.oneWord = !nameEndsInWhiteSpace;
1410:                } while (c != '%');
1411:
1412:                if (nameTail == null) {
1413:                    throw new IllegalArgumentException("Null option name given");
1414:                }
1415:                if (!nameTail.oneWord) {
1416:                    for (ndesc = rec.nameList; ndesc != null; ndesc = ndesc.next) {
1417:                        ndesc.oneWord = false;
1418:                    }
1419:                }
1420:                c = scanner.getc();
1421:                if (c == '\000') {
1422:                    throw new IllegalArgumentException(
1423:                            "No conversion character given");
1424:                }
1425:                if (validConversionCodes.indexOf(c) == -1) {
1426:                    throw new IllegalArgumentException("Conversion code '" + c
1427:                            + "' not one of '" + validConversionCodes + "'");
1428:                }
1429:                rec.convertCode = c;
1430:
1431:                if (resHolder instanceof  Vector) {
1432:                    rec.vectorResult = true;
1433:                    rec.type = defaultResultType(rec.convertCode);
1434:                } else {
1435:                    switch (rec.convertCode) {
1436:                    case 'i':
1437:                    case 'o':
1438:                    case 'd':
1439:                    case 'x': {
1440:                        if (resHolder instanceof  LongHolder
1441:                                || resHolder instanceof  long[]) {
1442:                            rec.type = Record.LONG;
1443:                        } else if (resHolder instanceof  IntHolder
1444:                                || resHolder instanceof  int[]) {
1445:                            rec.type = Record.INT;
1446:                        } else {
1447:                            throw new IllegalArgumentException(
1448:                                    "Invalid result holder for %" + c);
1449:                        }
1450:                        break;
1451:                    }
1452:                    case 'c': {
1453:                        if (!(resHolder instanceof  CharHolder)
1454:                                && !(resHolder instanceof  char[])) {
1455:                            throw new IllegalArgumentException(
1456:                                    "Invalid result holder for %c");
1457:                        }
1458:                        rec.type = Record.CHAR;
1459:                        break;
1460:                    }
1461:                    case 'v':
1462:                    case 'b': {
1463:                        if (!(resHolder instanceof  BooleanHolder)
1464:                                && !(resHolder instanceof  boolean[])) {
1465:                            throw new IllegalArgumentException(
1466:                                    "Invalid result holder for %" + c);
1467:                        }
1468:                        rec.type = Record.BOOLEAN;
1469:                        break;
1470:                    }
1471:                    case 'f': {
1472:                        if (resHolder instanceof  DoubleHolder
1473:                                || resHolder instanceof  double[]) {
1474:                            rec.type = Record.DOUBLE;
1475:                        } else if (resHolder instanceof  FloatHolder
1476:                                || resHolder instanceof  float[]) {
1477:                            rec.type = Record.FLOAT;
1478:                        } else {
1479:                            throw new IllegalArgumentException(
1480:                                    "Invalid result holder for %f");
1481:                        }
1482:                        break;
1483:                    }
1484:                    case 's': {
1485:                        if (!(resHolder instanceof  StringHolder)
1486:                                && !(resHolder instanceof  String[])) {
1487:                            throw new IllegalArgumentException(
1488:                                    "Invalid result holder for %s");
1489:                        }
1490:                        rec.type = Record.STRING;
1491:                        break;
1492:                    }
1493:                    case 'h': { // resHolder is ignored for this type
1494:                        break;
1495:                    }
1496:                    }
1497:                }
1498:                if (rec.convertCode == 'h') {
1499:                    rec.resHolder = null;
1500:                } else {
1501:                    rec.resHolder = resHolder;
1502:                }
1503:
1504:                scanner.skipWhiteSpace();
1505:                // get the range specification, if any
1506:                if (scanner.peekc() == '{') {
1507:                    if (rec.convertCode == 'h') {
1508:                        throw new IllegalArgumentException(
1509:                                "Ranges not supported for %h");
1510:                    }
1511:                    //	      int bcnt = 0;
1512:                    i0 = scanner.getIndex(); // beginning of range spec
1513:                    do {
1514:                        c = scanner.getc();
1515:                        if (c == '\000') {
1516:                            throw new IllegalArgumentException(
1517:                                    "Unterminated range specification");
1518:                        }
1519:                        //  		 else if (c=='[' || c=='(')
1520:                        //  		  { bcnt++;
1521:                        //  		  }
1522:                        //  		 else if (c==']' || c==')')
1523:                        //  		  { bcnt--;
1524:                        //  		  }
1525:                        //  		 if ((rec.convertCode=='v'||rec.convertCode=='b') && bcnt>1)
1526:                        //  		  { throw new IllegalArgumentException
1527:                        //  		      ("Sub ranges not supported for %b or %v");
1528:                        //  		  }
1529:                    } while (c != '}');
1530:                    //  	      if (c != ']')
1531:                    //  	       { throw new IllegalArgumentException
1532:                    //  		    ("Range specification must end with ']'");
1533:                    //  	       }
1534:                    i1 = scanner.getIndex(); // end of range spec
1535:                    scanRangeSpec(rec, scanner.substring(i0, i1));
1536:                    if (rec.convertCode == 'v' && rec.rangeList != null) {
1537:                        rec.vval = rec.rangeList.low.bval;
1538:                    }
1539:                }
1540:                // check for value multiplicity information, if any 
1541:                if (scanner.peekc() == 'X') {
1542:                    if (rec.convertCode == 'h') {
1543:                        throw new IllegalArgumentException(
1544:                                "Multipliers not supported for %h");
1545:                    }
1546:                    scanner.getc();
1547:                    try {
1548:                        rec.numValues = (int) scanner.scanInt();
1549:                    } catch (StringScanException e) {
1550:                        throw new IllegalArgumentException(
1551:                                "Malformed value multiplier");
1552:                    }
1553:                    if (rec.numValues <= 0) {
1554:                        throw new IllegalArgumentException(
1555:                                "Value multiplier number must be > 0");
1556:                    }
1557:                } else {
1558:                    rec.numValues = 1;
1559:                }
1560:                if (rec.numValues > 1) {
1561:                    for (ndesc = rec.nameList; ndesc != null; ndesc = ndesc.next) {
1562:                        if (ndesc.oneWord) {
1563:                            throw new IllegalArgumentException(
1564:                                    "Multiplier value incompatible with one word option "
1565:                                            + ndesc.name);
1566:                        }
1567:                    }
1568:                }
1569:                if (resHolder != null && resHolder.getClass().isArray()) {
1570:                    if (Array.getLength(resHolder) < rec.numValues) {
1571:                        throw new IllegalArgumentException(
1572:                                "Result holder array must have a length >= "
1573:                                        + rec.numValues);
1574:                    }
1575:                } else {
1576:                    if (rec.numValues > 1 && !(resHolder instanceof  Vector)) {
1577:                        throw new IllegalArgumentException(
1578:                                "Multiplier requires result holder to be an array of length >= "
1579:                                        + rec.numValues);
1580:                    }
1581:                }
1582:
1583:                // skip white space following conversion information
1584:                scanner.skipWhiteSpace();
1585:
1586:                // get the help message, if any
1587:
1588:                if (!scanner.atEnd()) {
1589:                    if (scanner.getc() != '#') {
1590:                        throw new IllegalArgumentException(
1591:                                "Illegal character(s), expecting '#'");
1592:                    }
1593:                    String helpInfo = scanner.substring(scanner.getIndex());
1594:                    // look for second '#'. If there is one, then info
1595:                    // between the first and second '#' is the value descriptor.
1596:                    int k = helpInfo.indexOf("#");
1597:                    if (k != -1) {
1598:                        rec.valueDesc = helpInfo.substring(0, k);
1599:                        rec.helpMsg = helpInfo.substring(k + 1);
1600:                    } else {
1601:                        rec.helpMsg = helpInfo;
1602:                    }
1603:                } else {
1604:                    rec.helpMsg = "";
1605:                }
1606:                // add option information to match list
1607:                if (rec.convertCode == 'h'
1608:                        && firstHelpOption == defaultHelpOption) {
1609:                    matchList.remove(defaultHelpOption);
1610:                    firstHelpOption = rec;
1611:                }
1612:                matchList.add(rec);
1613:            }
1614:
1615:            Record lastMatchRecord() {
1616:                return (Record) matchList.lastElement();
1617:            }
1618:
1619:            private Record getRecord(String arg, ObjectHolder ndescHolder) {
1620:                NameDesc ndesc;
1621:                for (int i = 0; i < matchList.size(); i++) {
1622:                    Record rec = (Record) matchList.get(i);
1623:                    for (ndesc = rec.nameList; ndesc != null; ndesc = ndesc.next) {
1624:                        if (rec.convertCode != 'v' && ndesc.oneWord) {
1625:                            if (arg.startsWith(ndesc.name)) {
1626:                                if (ndescHolder != null) {
1627:                                    ndescHolder.value = ndesc;
1628:                                }
1629:                                return rec;
1630:                            }
1631:                        } else {
1632:                            if (arg.equals(ndesc.name)) {
1633:                                if (ndescHolder != null) {
1634:                                    ndescHolder.value = ndesc;
1635:                                }
1636:                                return rec;
1637:                            }
1638:                        }
1639:                    }
1640:                }
1641:                return null;
1642:            }
1643:
1644:            Object getResultHolder(String arg) {
1645:                Record rec = getRecord(arg, null);
1646:                return (rec != null) ? rec.resHolder : null;
1647:            }
1648:
1649:            String getOptionName(String arg) {
1650:                ObjectHolder ndescHolder = new ObjectHolder();
1651:                Record rec = getRecord(arg, ndescHolder);
1652:                return (rec != null) ? ((NameDesc) ndescHolder.value).name
1653:                        : null;
1654:            }
1655:
1656:            String getOptionRangeDesc(String arg) {
1657:                Record rec = getRecord(arg, null);
1658:                return (rec != null) ? rec.rangeDesc : null;
1659:            }
1660:
1661:            String getOptionTypeName(String arg) {
1662:                Record rec = getRecord(arg, null);
1663:                return (rec != null) ? rec.valTypeName() : null;
1664:            }
1665:
1666:            private Object createResultHolder(Record rec) {
1667:                if (rec.numValues == 1) {
1668:                    switch (rec.type) {
1669:                    case Record.LONG: {
1670:                        return new LongHolder();
1671:                    }
1672:                    case Record.CHAR: {
1673:                        return new CharHolder();
1674:                    }
1675:                    case Record.BOOLEAN: {
1676:                        return new BooleanHolder();
1677:                    }
1678:                    case Record.DOUBLE: {
1679:                        return new DoubleHolder();
1680:                    }
1681:                    case Record.STRING: {
1682:                        return new StringHolder();
1683:                    }
1684:                    }
1685:                } else {
1686:                    switch (rec.type) {
1687:                    case Record.LONG: {
1688:                        return new long[rec.numValues];
1689:                    }
1690:                    case Record.CHAR: {
1691:                        return new char[rec.numValues];
1692:                    }
1693:                    case Record.BOOLEAN: {
1694:                        return new boolean[rec.numValues];
1695:                    }
1696:                    case Record.DOUBLE: {
1697:                        return new double[rec.numValues];
1698:                    }
1699:                    case Record.STRING: {
1700:                        return new String[rec.numValues];
1701:                    }
1702:                    }
1703:                }
1704:                return null; // can't happen
1705:            }
1706:
1707:            static void stringToArgs(Vector vec, String s,
1708:                    boolean allowQuotedStrings) throws StringScanException {
1709:                StringScanner scanner = new StringScanner(s);
1710:                scanner.skipWhiteSpace();
1711:                while (!scanner.atEnd()) {
1712:                    if (allowQuotedStrings) {
1713:                        vec.add(scanner.scanString());
1714:                    } else {
1715:                        vec.add(scanner.scanNonWhiteSpaceString());
1716:                    }
1717:                    scanner.skipWhiteSpace();
1718:                }
1719:            }
1720:
1721:            /**
1722:             * Reads in a set of strings from a reader and prepends them to an
1723:             * argument list.  Strings are delimited by either whitespace or
1724:             * double quotes <code>"</code>.  The character <code>#</code> acts as
1725:             * a comment character, causing input to the end of the current line to
1726:             * be ignored.
1727:             *
1728:             * @param reader Reader from which to read the strings
1729:             * @param args Initial set of argument values. Can be
1730:             * specified as <code>null</code>.
1731:             * @throws IOException if an error occured while reading.
1732:             */
1733:            public static String[] prependArgs(Reader reader, String[] args)
1734:                    throws IOException {
1735:                if (args == null) {
1736:                    args = new String[0];
1737:                }
1738:                LineNumberReader lineReader = new LineNumberReader(reader);
1739:                Vector vec = new Vector(100, 100);
1740:                String line;
1741:                int i, k;
1742:
1743:                while ((line = lineReader.readLine()) != null) {
1744:                    int commentIdx = line.indexOf("#");
1745:                    if (commentIdx != -1) {
1746:                        line = line.substring(0, commentIdx);
1747:                    }
1748:                    try {
1749:                        stringToArgs(vec, line, /*allowQuotedStings=*/true);
1750:                    } catch (StringScanException e) {
1751:                        throw new IOException("malformed string, line "
1752:                                + lineReader.getLineNumber());
1753:                    }
1754:                }
1755:                String[] result = new String[vec.size() + args.length];
1756:                for (i = 0; i < vec.size(); i++) {
1757:                    result[i] = (String) vec.get(i);
1758:                }
1759:                for (k = 0; k < args.length; k++) {
1760:                    result[i++] = args[k];
1761:                }
1762:                return result;
1763:            }
1764:
1765:            /**
1766:             * Reads in a set of strings from a file and prepends them to an
1767:             * argument list.  Strings are delimited by either whitespace or double
1768:             * quotes <code>"</code>.  The character <code>#</code> acts as a
1769:             * comment character, causing input to the end of the current line to
1770:             * be ignored.
1771:             *
1772:             * @param file File to be read
1773:             * @param args Initial set of argument values. Can be
1774:             * specified as <code>null</code>.
1775:             * @throws IOException if an error occured while reading the file.
1776:             */
1777:            public static String[] prependArgs(File file, String[] args)
1778:                    throws IOException {
1779:                if (args == null) {
1780:                    args = new String[0];
1781:                }
1782:                if (!file.canRead()) {
1783:                    return args;
1784:                }
1785:                try {
1786:                    return prependArgs(new FileReader(file), args);
1787:                } catch (IOException e) {
1788:                    throw new IOException("File " + file.getName() + ": "
1789:                            + e.getMessage());
1790:                }
1791:            }
1792:
1793:            /**
1794:             * Sets the parser's error message.
1795:             *
1796:             * @param s Error message
1797:             */
1798:            protected void setError(String msg) {
1799:                errMsg = msg;
1800:            }
1801:
1802:            /**
1803:             * Prints an error message, along with a pointer to help options,
1804:             * if available, and causes the program to exit with code 1.
1805:             */
1806:            public void printErrorAndExit(String msg) {
1807:                if (helpOptionsEnabled && firstHelpOptionName() != null) {
1808:                    msg += "\nUse " + firstHelpOptionName()
1809:                            + " for help information";
1810:                }
1811:                if (printStream != null) {
1812:                    printStream.println(msg);
1813:                }
1814:                System.exit(1);
1815:            }
1816:
1817:            /**
1818:             * Matches arguments within an argument list.
1819:             *
1820:             * <p>In the event of an erroneous or unmatched argument, the method
1821:             * prints a message and exits the program with code 1.
1822:             *
1823:             * <p>If help options are enabled and one of the arguments matches a
1824:             * help option, then the result of {@link #getHelpMessage
1825:             * getHelpMessage} is printed to the default print stream and the
1826:             * program exits with code 0.  If help options are not enabled, they
1827:             * are ignored.
1828:             *
1829:             * @param args argument list
1830:             * @see ArgParser#getDefaultPrintStream
1831:             */
1832:            public void matchAllArgs(String[] args) {
1833:                matchAllArgs(args, 0, EXIT_ON_UNMATCHED | EXIT_ON_ERROR);
1834:            }
1835:
1836:            /**
1837:             * Matches arguments within an argument list and returns
1838:             * those which were not matched. The matching starts at a location
1839:             * in <code>args</code> specified by <code>idx</code>, and
1840:             * unmatched arguments are returned in a String array.
1841:             *
1842:             * <p>In the event of an erroneous argument, the method either prints a
1843:             * message and exits the program (if {@link #EXIT_ON_ERROR} is
1844:             * set in <code>exitFlags</code>)
1845:             * or terminates the matching and creates a error message that
1846:             * can be retrieved by {@link #getErrorMessage}.
1847:             *
1848:             * <p>In the event of an umatched argument, the method will print a
1849:             * message and exit if {@link #EXIT_ON_UNMATCHED} is set
1850:             * in <code>errorFlags</code>.
1851:             * Otherwise, the unmatched argument will be appended to the returned
1852:             * array of unmatched values, and the matching will continue at the
1853:             * next location.
1854:             *
1855:             * <p>If help options are enabled and one of the arguments matches a
1856:             * help option, then the result of {@link #getHelpMessage
1857:             * getHelpMessage} is printed to the the default print stream and the
1858:             * program exits with code 0.  If help options are not enabled, then
1859:             * they will not be matched.
1860:             *
1861:             * @param args argument list
1862:             * @param idx starting location in list
1863:             * @param exitFlags conditions causing the program to exit.  Should be
1864:             * an or-ed combintion of {@link #EXIT_ON_ERROR} or {@link
1865:             * #EXIT_ON_UNMATCHED}.
1866:             * @return array of arguments that were not matched, or
1867:             * <code>null</code> if all arguments were successfully matched
1868:             * @see ArgParser#getErrorMessage
1869:             * @see ArgParser#getDefaultPrintStream
1870:             */
1871:            public String[] matchAllArgs(String[] args, int idx, int exitFlags) {
1872:                Vector unmatched = new Vector(10);
1873:
1874:                while (idx < args.length) {
1875:                    try {
1876:                        idx = matchArg(args, idx);
1877:                        if (unmatchedArg != null) {
1878:                            if ((exitFlags & EXIT_ON_UNMATCHED) != 0) {
1879:                                printErrorAndExit("Unrecognized argument: "
1880:                                        + unmatchedArg);
1881:                            } else {
1882:                                unmatched.add(unmatchedArg);
1883:                            }
1884:                        }
1885:                    } catch (ArgParseException e) {
1886:                        if ((exitFlags & EXIT_ON_ERROR) != 0) {
1887:                            printErrorAndExit(e.getMessage());
1888:                        }
1889:                        break;
1890:                    }
1891:                }
1892:                if (unmatched.size() == 0) {
1893:                    return null;
1894:                } else {
1895:                    return (String[]) unmatched.toArray(new String[0]);
1896:                }
1897:            }
1898:
1899:            /**
1900:             * Matches one option starting at a specified location in an argument
1901:             * list. The method returns the location in the list where the next
1902:             * match should begin.
1903:             *
1904:             * <p>In the event of an erroneous argument, the method throws
1905:             * an {@link argparser.ArgParseException ArgParseException}
1906:             * with an appropriate error message. This error
1907:             * message can also be retrieved using
1908:             * {@link #getErrorMessage getErrorMessage}.
1909:             *
1910:             * <p>In the event of an umatched argument, the method will return idx
1911:             * + 1, and {@link #getUnmatchedArgument getUnmatchedArgument} will
1912:             * return a copy of the unmatched argument. If an argument is matched,
1913:             * {@link #getUnmatchedArgument getUnmatchedArgument} will return
1914:             * <code>null</code>.
1915:             *
1916:             * <p>If help options are enabled and the argument matches a help
1917:             * option, then the result of {@link #getHelpMessage getHelpMessage} is printed to
1918:             * the the default print stream and the program exits with code 0.  If
1919:             * help options are not enabled, then they are ignored.
1920:             *
1921:             * @param args argument list
1922:             * @param idx location in list where match should start
1923:             * @return location in list where next match should start
1924:             * @throws ArgParseException if there was an error performing
1925:             * the match (such as improper or insufficient values).
1926:             * @see ArgParser#setDefaultPrintStream
1927:             * @see ArgParser#getHelpOptionsEnabled
1928:             * @see ArgParser#getErrorMessage
1929:             * @see ArgParser#getUnmatchedArgument
1930:             */
1931:            public int matchArg(String[] args, int idx)
1932:                    throws ArgParseException {
1933:                unmatchedArg = null;
1934:                setError(null);
1935:                try {
1936:                    ObjectHolder ndescHolder = new ObjectHolder();
1937:                    Record rec = getRecord(args[idx], ndescHolder);
1938:                    if (rec == null
1939:                            || (rec.convertCode == 'h' && !helpOptionsEnabled)) { // didn't match
1940:                        unmatchedArg = new String(args[idx]);
1941:                        return idx + 1;
1942:                    }
1943:                    NameDesc ndesc = (NameDesc) ndescHolder.value;
1944:                    Object result;
1945:                    if (rec.resHolder instanceof  Vector) {
1946:                        result = createResultHolder(rec);
1947:                    } else {
1948:                        result = rec.resHolder;
1949:                    }
1950:                    if (rec.convertCode == 'h') {
1951:                        if (helpOptionsEnabled) {
1952:                            printStream.println(getHelpMessage());
1953:                            System.exit(0);
1954:                        } else {
1955:                            return idx + 1;
1956:                        }
1957:                    } else if (rec.convertCode != 'v') {
1958:                        if (ndesc.oneWord) {
1959:                            rec.scanValue(result, ndesc.name, args[idx]
1960:                                    .substring(ndesc.name.length()), 0);
1961:                        } else {
1962:                            if (idx + rec.numValues >= args.length) {
1963:                                throw new ArgParseException(
1964:                                        ndesc.name,
1965:                                        "requires "
1966:                                                + rec.numValues
1967:                                                + " value"
1968:                                                + (rec.numValues > 1 ? "s" : ""));
1969:                            }
1970:                            for (int k = 0; k < rec.numValues; k++) {
1971:                                rec.scanValue(result, ndesc.name, args[++idx],
1972:                                        k);
1973:                            }
1974:                        }
1975:                    } else {
1976:                        if (rec.resHolder instanceof  BooleanHolder) {
1977:                            ((BooleanHolder) result).value = rec.vval;
1978:                        } else {
1979:                            for (int k = 0; k < rec.numValues; k++) {
1980:                                ((boolean[]) result)[k] = rec.vval;
1981:                            }
1982:                        }
1983:                    }
1984:                    if (rec.resHolder instanceof  Vector) {
1985:                        ((Vector) rec.resHolder).add(result);
1986:                    }
1987:                } catch (ArgParseException e) {
1988:                    setError(e.getMessage());
1989:                    throw e;
1990:                }
1991:                return idx + 1;
1992:            }
1993:
1994:            private String spaceString(int n) {
1995:                StringBuffer sbuf = new StringBuffer(n);
1996:                for (int i = 0; i < n; i++) {
1997:                    sbuf.append(' ');
1998:                }
1999:                return sbuf.toString();
2000:            }
2001:
2002:            // 	public String getShortHelpMessage ()
2003:            // 	 {
2004:            // 	   String s;
2005:            // 	   Record rec;
2006:            // 	   NameDesc ndesc;
2007:            // 	   int initialIndent = 8;
2008:            // 	   int col = initialIndent;
2009:
2010:            // 	   if (maxcols <= 0)
2011:            // 	    { maxcols = 80; 
2012:            // 	    }
2013:            // 	   if (matchList.size() > 0)
2014:            // 	    { ps.print (spaceString(initialIndent));
2015:            // 	    }
2016:            // 	   for (int i=0; i<matchList.size(); i++)
2017:            // 	    { rec = (Record)matchList.get(i);
2018:            // 	      s = "[";
2019:            // 	      for (ndesc=rec.nameList; ndesc!=null; ndesc=ndesc.next)
2020:            // 	       { s = s + ndesc.name;
2021:            // 		 if (ndesc.oneWord == false)
2022:            // 		  { s = s + " ";
2023:            // 		  }
2024:            // 		 if (ndesc.next != null)
2025:            // 		  { s = s + ",";
2026:            // 		  }
2027:            // 	       }
2028:            // 	      if (rec.convertCode != 'v' && rec.convertCode != 'h')
2029:            // 	       { if (rec.valueDesc != null)
2030:            // 		  { s += rec.valueDesc; 
2031:            // 		  }
2032:            // 		 else
2033:            // 		  { s = s + "<" + rec.valTypeName() + ">";
2034:            // 		    if (rec.numValues > 1)
2035:            // 		     { s += "X" + rec.numValues;
2036:            // 		     }
2037:            // 		  }
2038:            // 	       }
2039:            // 	      s = s + "]";
2040:            // 	      /* 
2041:            // 		 (col+=s.length()) > (maxcols-1) => we will spill over edge. 
2042:            // 			 we use (maxcols-1) because if we go right to the edge
2043:            // 			 (maxcols), we get wrap new line inserted "for us".
2044:            // 		 i != 0 means we print the first entry, no matter
2045:            // 			 how long it is. Subsequent entries are printed
2046:            // 			 full length anyway. */		     
2047:
2048:            // 	      if ((col+=s.length()) > (maxcols-1) && i != 0)
2049:            // 	       { col = initialIndent+s.length();
2050:            // 		 ps.print ("\n" + spaceString(initialIndent));
2051:            // 	       }
2052:            // 	      ps.print (s);
2053:            // 	    }
2054:            // 	   if (matchList.size() > 0)
2055:            // 	    { ps.print ('\n');
2056:            // 	      ps.flush();
2057:            // 	    }
2058:            // 	 }
2059:
2060:            /**
2061:             * Returns a string describing the allowed options
2062:             * in detail.
2063:             *
2064:             * @return help information string.
2065:             */
2066:            public String getHelpMessage() {
2067:                Record rec;
2068:                NameDesc ndesc;
2069:                boolean hasOneWordAlias = false;
2070:                String s;
2071:
2072:                s = "Usage: " + synopsisString + "\n";
2073:                s += "Options include:\n\n";
2074:                for (int i = 0; i < matchList.size(); i++) {
2075:                    String optionInfo = "";
2076:                    rec = (Record) matchList.get(i);
2077:                    if (rec.convertCode == 'h' && !helpOptionsEnabled) {
2078:                        continue;
2079:                    }
2080:                    for (ndesc = rec.nameList; ndesc != null; ndesc = ndesc.next) {
2081:                        if (ndesc.oneWord) {
2082:                            hasOneWordAlias = true;
2083:                            break;
2084:                        }
2085:                    }
2086:                    for (ndesc = rec.nameList; ndesc != null; ndesc = ndesc.next) {
2087:                        optionInfo += ndesc.name;
2088:                        if (hasOneWordAlias && !ndesc.oneWord) {
2089:                            optionInfo += " ";
2090:                        }
2091:                        if (ndesc.next != null) {
2092:                            optionInfo += ",";
2093:                        }
2094:                    }
2095:                    if (!hasOneWordAlias) {
2096:                        optionInfo += " ";
2097:                    }
2098:                    if (rec.convertCode != 'v' && rec.convertCode != 'h') {
2099:                        if (rec.valueDesc != null) {
2100:                            optionInfo += rec.valueDesc;
2101:                        } else {
2102:                            if (rec.rangeDesc != null) {
2103:                                optionInfo += "<" + rec.valTypeName() + " "
2104:                                        + rec.rangeDesc + ">";
2105:                            } else {
2106:                                optionInfo += "<" + rec.valTypeName() + ">";
2107:                            }
2108:                        }
2109:                    }
2110:                    if (rec.numValues > 1) {
2111:                        optionInfo += "X" + rec.numValues;
2112:                    }
2113:                    s += optionInfo;
2114:                    if (rec.helpMsg.length() > 0) {
2115:                        int pad = helpIndent - optionInfo.length();
2116:                        if (pad < 2) {
2117:                            s += '\n';
2118:                            pad = helpIndent;
2119:                        }
2120:                        s += spaceString(pad) + rec.helpMsg;
2121:                    }
2122:                    s += '\n';
2123:                }
2124:                return s;
2125:            }
2126:
2127:            /**
2128:             * Returns the parser's error message. This is automatically
2129:             * set whenever an error is encountered in <code>matchArg</code>
2130:             * or <code>matchAllArgs</code>, and is automatically set to
2131:             * <code>null</code> at the beginning of these methods.
2132:             *
2133:             * @return error message
2134:             */
2135:            public String getErrorMessage() {
2136:                return errMsg;
2137:            }
2138:
2139:            /**
2140:             * Returns the value of an unmatched argument discovered {@link
2141:             * #matchArg matchArg} or {@link #matchAllArgs(String[],int,int)
2142:             * matchAllArgs}.  If there was no unmatched argument,
2143:             * <code>null</code> is returned.
2144:             *
2145:             * @return unmatched argument
2146:             */
2147:            public String getUnmatchedArgument() {
2148:                return unmatchedArg;
2149:            }
2150:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.