Home | History | Annotate | Download | only in tool
      1 /*
      2 **********************************************************************
      3 * Copyright (c) 2002-2004, International Business Machines
      4 * Corporation and others.  All Rights Reserved.
      5 **********************************************************************
      6 * Author: Alan Liu
      7 * Created: November 15 2002
      8 * Since: ICU 2.4
      9 **********************************************************************
     10 */
     11 package com.ibm.icu.dev.tool;
     12 
     13 /**
     14  * A command-line option.  A UOption specifies the name of an option
     15  * and whether or not it takes an argument.  It is a mutable object
     16  * that later contains the option argument, if any, and a boolean
     17  * flag stating whether the option was seen or not.
     18  *
     19  * The static method parseArgs() takes an array of command-line
     20  * arguments and an array of UOptions and parses the command-line
     21  * arguments.
     22  *
     23  * This deliberately resembles the icu4c file uoption.[ch].
     24  */
     25 public class UOption {
     26 
     27     // Deliberated public data members
     28     public String  longName;
     29     public String  value;
     30     public Fn      optionFn;
     31     public Object  context;
     32     public char    shortName;
     33     public int     hasArg;
     34     public boolean doesOccur;
     35 
     36     // Values of hasArg
     37     public static final int NO_ARG       = 0;
     38     public static final int REQUIRES_ARG = 1;
     39     public static final int OPTIONAL_ARG = 2;
     40 
     41     // Analog of UOptionFn.  We don't pass in the context because the
     42     // functor can get it from the UOption.
     43     public interface Fn {
     44         int handle(UOption option);
     45     }
     46 
     47     /**
     48      * Create a UOption with the given attributes.
     49      */
     50     public static UOption create(String aLongName,
     51                                 char aShortName,
     52                                 int hasArgument) {
     53         return new UOption(aLongName, aShortName, hasArgument);
     54     }
     55 
     56     /**
     57      * Create a UOption with the given attributes.
     58      * Synonym for create(), for C compatibility.
     59      */
     60     public static UOption DEF(String aLongName,
     61                               char aShortName,
     62                               int hasArgument) {
     63         return create(aLongName, aShortName, hasArgument);
     64     }
     65 
     66     // Standard canned options.  These create a new object when
     67     // called.  Since the UOption object is mutable, we cannot use
     68     // static final instances.
     69     public static UOption HELP_H()             { return create("help", 'h', NO_ARG); }
     70     public static UOption HELP_QUESTION_MARK() { return create("help", '?', NO_ARG); }
     71     public static UOption VERBOSE()            { return create("verbose", 'v', NO_ARG); }
     72     public static UOption QUIET()              { return create("quiet", 'q', NO_ARG); }
     73     public static UOption VERSION()            { return create("version", 'V', NO_ARG); }
     74     public static UOption COPYRIGHT()          { return create("copyright", 'c', NO_ARG); }
     75 
     76     public static UOption DESTDIR()            { return create("destdir", 'd', REQUIRES_ARG); }
     77     public static UOption SOURCEDIR()          { return create("sourcedir", 's', REQUIRES_ARG); }
     78     public static UOption ENCODING()           { return create("encoding", 'e', REQUIRES_ARG); }
     79     public static UOption ICUDATADIR()         { return create("icudatadir", 'i', REQUIRES_ARG); }
     80     public static UOption PACKAGE_NAME()       { return create("package-name", 'p', REQUIRES_ARG); }
     81     public static UOption BUNDLE_NAME()        { return create("bundle-name", 'b', REQUIRES_ARG); }
     82 
     83     /**
     84      * Java Command line argument parser.
     85      *
     86      * This function takes the argv[] command line and a description of
     87      * the program's options in form of an array of UOption structures.
     88      * Each UOption defines a long and a short name (a string and a character)
     89      * for options like "--foo" and "-f".
     90      *
     91      * Each option is marked with whether it does not take an argument,
     92      * requires one, or optionally takes one. The argument may follow in
     93      * the same argv[] entry for short options, or it may always follow
     94      * in the next argv[] entry.
     95      *
     96      * An argument is in the next argv[] entry for both long and short name
     97      * options, except it is taken from directly behind the short name in
     98      * its own argv[] entry if there are characters following the option letter.
     99      * An argument in its own argv[] entry must not begin with a '-'
    100      * unless it is only the '-' itself. There is no restriction of the
    101      * argument format if it is part of the short name options's argv[] entry.
    102      *
    103      * The argument is stored in the value field of the corresponding
    104      * UOption entry, and the doesOccur field is set to 1 if the option
    105      * is found at all.
    106      *
    107      * Short name options without arguments can be collapsed into a single
    108      * argv[] entry. After an option letter takes an argument, following
    109      * letters will be taken as its argument.
    110      *
    111      * If the same option is found several times, then the last
    112      * argument value will be stored in the value field.
    113      *
    114      * For each option, a function can be called. This could be used
    115      * for options that occur multiple times and all arguments are to
    116      * be collected.
    117      *
    118      * All options are removed from the argv[] array itself. If the parser
    119      * is successful, then it returns the number of remaining non-option
    120      * strings.  (Unlike C, the Java argv[] array does NOT contain
    121      * the program name in argv[0].)
    122      *
    123      * An option "--" ends option processing; everything after this
    124      * remains in the argv[] array.
    125      *
    126      * An option string "-" alone is treated as a non-option.
    127      *
    128      * If an option is not recognized or an argument missing, then
    129      * the parser returns with the negative index of the argv[] entry
    130      * where the error was detected.
    131      *
    132      * @param argv this parameter is modified
    133      * @param start the first argument in argv[] to examine.  Must be
    134      * 0..argv.length-1.  Arguments from 0..start-1 are ignored.
    135      * @param options this parameter is modified
    136      * @return the number of unprocessed arguments in argv[], including
    137      * arguments 0..start-1.
    138      */
    139     public static int parseArgs(String argv[], int start, UOption options[]) {
    140         String arg;
    141         int i=start, remaining=start;
    142         char c;
    143         boolean stopOptions=false;
    144 
    145         while(i<argv.length) {
    146             arg=argv[i];
    147             if(!stopOptions && arg.length()>1 && arg.charAt(0)=='-') {
    148                 /* process an option */
    149                 c=arg.charAt(1);
    150                 UOption option=null;
    151                 arg=arg.substring(2);
    152                 if(c=='-') {
    153                     /* process a long option */
    154                     if(arg.length()==0) {
    155                         /* stop processing options after "--" */
    156                         stopOptions=true;
    157                     } else {
    158                         /* search for the option string */
    159                         int j;
    160                         for(j=0; j<options.length; ++j) {
    161                             if(options[j].longName != null && arg.equals(options[j].longName)) {
    162                                 option=options[j];
    163                                 break;
    164                             }
    165                         }
    166                         if(option==null) {
    167                             /* no option matches */
    168                             syntaxError("Unknown option " + argv[i]);
    169                         }
    170                         option.doesOccur=true;
    171 
    172                         if(option.hasArg!=NO_ARG) {
    173                             /* parse the argument for the option, if any */
    174                             if(i+1<argv.length && !(argv[i+1].length()>1 && argv[i+1].charAt(0)=='-')) {
    175                                 /* argument in the next argv[], and there is not an option in there */
    176                                 option.value=argv[++i];
    177                             } else if(option.hasArg==REQUIRES_ARG) {
    178                                 /* there is no argument, but one is required: return with error */
    179                                 syntaxError("Option " + argv[i] + " lacks required argument");
    180                             }
    181                         }
    182                     }
    183                 } else {
    184                     /* process one or more short options */
    185                     for (;;) {
    186                         /* search for the option letter */
    187                         int j;
    188                         for(j=0; j<options.length; ++j) {
    189                             if(c==options[j].shortName) {
    190                                 option=options[j];
    191                                 break;
    192                             }
    193                         }
    194                         if(option==null) {
    195                             /* no option matches */
    196                             syntaxError("Unknown option '" + c + "' in " + argv[i]);
    197                         }
    198                         option.doesOccur=true;
    199 
    200                         if(option.hasArg!=NO_ARG) {
    201                             /* parse the argument for the option, if any */
    202                             if(arg.length()!=0) {
    203                                 /* argument following in the same argv[] */
    204                                 option.value=arg;
    205                                 /* do not process the rest of this arg as option letters */
    206                                 break;
    207                             } else if(i+1<argv.length && !(argv[i+1].length()>1 && argv[i+1].charAt(0)=='-')) {
    208                                 /* argument in the next argv[], and there is not an option in there */
    209                                 option.value=argv[++i];
    210                                 /* this break is redundant because we know that *arg==0 */
    211                                 break;
    212                             } else if(option.hasArg==REQUIRES_ARG) {
    213                                 /* there is no argument, but one is required: return with error */
    214                                 syntaxError("Option -" + c + " lacks required argument");
    215                             }
    216                         }
    217 
    218                         /* get the next option letter */
    219                         option=null;
    220                         if (arg.length()==0) break;
    221                         c=arg.charAt(0);
    222                         arg=arg.substring(1);
    223                     }
    224                 }
    225 
    226                 if(option!=null && option.optionFn!=null && option.optionFn.handle(option)<0) {
    227                     /* the option function was called and returned an error */
    228                     syntaxError("Option handler failed for " + argv[i]);
    229                 }
    230 
    231                 /* go to next argv[] */
    232                 ++i;
    233             } else {
    234                 /* move a non-option up in argv[] */
    235                 argv[remaining++]=arg;
    236                 ++i;
    237             }
    238         }
    239         return remaining;
    240     }
    241 
    242     /**
    243      * Allows the default to be set in an option list.
    244      * @param s
    245      * @return this
    246      */public UOption setDefault(String s) {
    247         value = s;
    248         return this;
    249     }
    250 
    251     /**
    252      * Convenient method.
    253      */
    254     public static int parseArgs(String argv[], UOption options[]) {
    255         return parseArgs(argv, 0, options);
    256     }
    257 
    258     /**
    259      * Constructor.
    260      */
    261     private UOption(String aLongName,
    262                     char aShortName,
    263                     int hasArgument) {
    264         longName = aLongName;
    265         shortName = aShortName;
    266         hasArg = hasArgument;
    267     }
    268 
    269     /**
    270      * Throw an exception indicating a syntax error.
    271      */
    272     private static void syntaxError(String message) {
    273         throw new IllegalArgumentException("Error in argument list: " + message);
    274     }
    275 }
    276