001: /*
002: **********************************************************************
003: * Copyright (c) 2002-2004, International Business Machines
004: * Corporation and others. All Rights Reserved.
005: **********************************************************************
006: * Author: Alan Liu
007: * Created: November 15 2002
008: * Since: ICU 2.4
009: **********************************************************************
010: */
011: package com.ibm.icu.dev.tool;
012:
013: /**
014: * A command-line option. A UOption specifies the name of an option
015: * and whether or not it takes an argument. It is a mutable object
016: * that later contains the option argument, if any, and a boolean
017: * flag stating whether the option was seen or not.
018: *
019: * The static method parseArgs() takes an array of command-line
020: * arguments and an array of UOptions and parses the command-line
021: * arguments.
022: *
023: * This deliberately resembles the icu4c file uoption.[ch].
024: */
025: public class UOption {
026:
027: // Deliberated public data members
028: public String longName;
029: public String value;
030: public Fn optionFn;
031: public Object context;
032: public char shortName;
033: public int hasArg;
034: public boolean doesOccur;
035:
036: // Values of hasArg
037: public static final int NO_ARG = 0;
038: public static final int REQUIRES_ARG = 1;
039: public static final int OPTIONAL_ARG = 2;
040:
041: // Analog of UOptionFn. We don't pass in the context because the
042: // functor can get it from the UOption.
043: public interface Fn {
044: int handle(UOption option);
045: }
046:
047: /**
048: * Create a UOption with the given attributes.
049: */
050: public static UOption create(String aLongName, char aShortName,
051: int hasArgument) {
052: return new UOption(aLongName, aShortName, hasArgument);
053: }
054:
055: /**
056: * Create a UOption with the given attributes.
057: * Synonym for create(), for C compatibility.
058: */
059: public static UOption DEF(String aLongName, char aShortName,
060: int hasArgument) {
061: return create(aLongName, aShortName, hasArgument);
062: }
063:
064: // Standard canned options. These create a new object when
065: // called. Since the UOption object is mutable, we cannot use
066: // static final instances.
067: public static UOption HELP_H() {
068: return create("help", 'h', NO_ARG);
069: }
070:
071: public static UOption HELP_QUESTION_MARK() {
072: return create("help", '?', NO_ARG);
073: }
074:
075: public static UOption VERBOSE() {
076: return create("verbose", 'v', NO_ARG);
077: }
078:
079: public static UOption QUIET() {
080: return create("quiet", 'q', NO_ARG);
081: }
082:
083: public static UOption VERSION() {
084: return create("version", 'V', NO_ARG);
085: }
086:
087: public static UOption COPYRIGHT() {
088: return create("copyright", 'c', NO_ARG);
089: }
090:
091: public static UOption DESTDIR() {
092: return create("destdir", 'd', REQUIRES_ARG);
093: }
094:
095: public static UOption SOURCEDIR() {
096: return create("sourcedir", 's', REQUIRES_ARG);
097: }
098:
099: public static UOption ENCODING() {
100: return create("encoding", 'e', REQUIRES_ARG);
101: }
102:
103: public static UOption ICUDATADIR() {
104: return create("icudatadir", 'i', REQUIRES_ARG);
105: }
106:
107: public static UOption PACKAGE_NAME() {
108: return create("package-name", 'p', REQUIRES_ARG);
109: }
110:
111: public static UOption BUNDLE_NAME() {
112: return create("bundle-name", 'b', REQUIRES_ARG);
113: }
114:
115: /**
116: * Java Command line argument parser.
117: *
118: * This function takes the argv[] command line and a description of
119: * the program's options in form of an array of UOption structures.
120: * Each UOption defines a long and a short name (a string and a character)
121: * for options like "--foo" and "-f".
122: *
123: * Each option is marked with whether it does not take an argument,
124: * requires one, or optionally takes one. The argument may follow in
125: * the same argv[] entry for short options, or it may always follow
126: * in the next argv[] entry.
127: *
128: * An argument is in the next argv[] entry for both long and short name
129: * options, except it is taken from directly behind the short name in
130: * its own argv[] entry if there are characters following the option letter.
131: * An argument in its own argv[] entry must not begin with a '-'
132: * unless it is only the '-' itself. There is no restriction of the
133: * argument format if it is part of the short name options's argv[] entry.
134: *
135: * The argument is stored in the value field of the corresponding
136: * UOption entry, and the doesOccur field is set to 1 if the option
137: * is found at all.
138: *
139: * Short name options without arguments can be collapsed into a single
140: * argv[] entry. After an option letter takes an argument, following
141: * letters will be taken as its argument.
142: *
143: * If the same option is found several times, then the last
144: * argument value will be stored in the value field.
145: *
146: * For each option, a function can be called. This could be used
147: * for options that occur multiple times and all arguments are to
148: * be collected.
149: *
150: * All options are removed from the argv[] array itself. If the parser
151: * is successful, then it returns the number of remaining non-option
152: * strings. (Unlike C, the Java argv[] array does NOT contain
153: * the program name in argv[0].)
154: *
155: * An option "--" ends option processing; everything after this
156: * remains in the argv[] array.
157: *
158: * An option string "-" alone is treated as a non-option.
159: *
160: * If an option is not recognized or an argument missing, then
161: * the parser returns with the negative index of the argv[] entry
162: * where the error was detected.
163: *
164: * @param argv this parameter is modified
165: * @param start the first argument in argv[] to examine. Must be
166: * 0..argv.length-1. Arguments from 0..start-1 are ignored.
167: * @param options this parameter is modified
168: * @return the number of unprocessed arguments in argv[], including
169: * arguments 0..start-1.
170: */
171: public static int parseArgs(String argv[], int start,
172: UOption options[]) {
173: String arg;
174: int i = start, remaining = start;
175: char c;
176: boolean stopOptions = false;
177:
178: while (i < argv.length) {
179: arg = argv[i];
180: if (!stopOptions && arg.length() > 1
181: && arg.charAt(0) == '-') {
182: /* process an option */
183: c = arg.charAt(1);
184: UOption option = null;
185: arg = arg.substring(2);
186: if (c == '-') {
187: /* process a long option */
188: if (arg.length() == 0) {
189: /* stop processing options after "--" */
190: stopOptions = true;
191: } else {
192: /* search for the option string */
193: int j;
194: for (j = 0; j < options.length; ++j) {
195: if (options[j].longName != null
196: && arg.equals(options[j].longName)) {
197: option = options[j];
198: break;
199: }
200: }
201: if (option == null) {
202: /* no option matches */
203: syntaxError("Unknown option " + argv[i]);
204: }
205: option.doesOccur = true;
206:
207: if (option.hasArg != NO_ARG) {
208: /* parse the argument for the option, if any */
209: if (i + 1 < argv.length
210: && !(argv[i + 1].length() > 1 && argv[i + 1]
211: .charAt(0) == '-')) {
212: /* argument in the next argv[], and there is not an option in there */
213: option.value = argv[++i];
214: } else if (option.hasArg == REQUIRES_ARG) {
215: /* there is no argument, but one is required: return with error */
216: syntaxError("Option " + argv[i]
217: + " lacks required argument");
218: }
219: }
220: }
221: } else {
222: /* process one or more short options */
223: for (;;) {
224: /* search for the option letter */
225: int j;
226: for (j = 0; j < options.length; ++j) {
227: if (c == options[j].shortName) {
228: option = options[j];
229: break;
230: }
231: }
232: if (option == null) {
233: /* no option matches */
234: syntaxError("Unknown option '" + c
235: + "' in " + argv[i]);
236: }
237: option.doesOccur = true;
238:
239: if (option.hasArg != NO_ARG) {
240: /* parse the argument for the option, if any */
241: if (arg.length() != 0) {
242: /* argument following in the same argv[] */
243: option.value = arg;
244: /* do not process the rest of this arg as option letters */
245: break;
246: } else if (i + 1 < argv.length
247: && !(argv[i + 1].length() > 1 && argv[i + 1]
248: .charAt(0) == '-')) {
249: /* argument in the next argv[], and there is not an option in there */
250: option.value = argv[++i];
251: /* this break is redundant because we know that *arg==0 */
252: break;
253: } else if (option.hasArg == REQUIRES_ARG) {
254: /* there is no argument, but one is required: return with error */
255: syntaxError("Option -" + c
256: + " lacks required argument");
257: }
258: }
259:
260: /* get the next option letter */
261: option = null;
262: if (arg.length() == 0)
263: break;
264: c = arg.charAt(0);
265: arg = arg.substring(1);
266: }
267: }
268:
269: if (option != null && option.optionFn != null
270: && option.optionFn.handle(option) < 0) {
271: /* the option function was called and returned an error */
272: syntaxError("Option handler failed for " + argv[i]);
273: }
274:
275: /* go to next argv[] */
276: ++i;
277: } else {
278: /* move a non-option up in argv[] */
279: argv[remaining++] = arg;
280: ++i;
281: }
282: }
283: return remaining;
284: }
285:
286: /**
287: * Allows the default to be set in an option list.
288: * @param s
289: * @return this
290: */
291: public UOption setDefault(String s) {
292: value = s;
293: return this ;
294: }
295:
296: /**
297: * Convenient method.
298: */
299: public static int parseArgs(String argv[], UOption options[]) {
300: return parseArgs(argv, 0, options);
301: }
302:
303: /**
304: * Constructor.
305: */
306: private UOption(String aLongName, char aShortName, int hasArgument) {
307: longName = aLongName;
308: shortName = aShortName;
309: hasArg = hasArgument;
310: }
311:
312: /**
313: * Throw an exception indicating a syntax error.
314: */
315: private static void syntaxError(String message) {
316: throw new IllegalArgumentException("Error in argument list: "
317: + message);
318: }
319: }
|