/* ********************************************************************** * Copyright (c) 2002-2004, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * Author: Alan Liu * Created: November 15 2002 * Since: ICU 2.4 ********************************************************************** */ package com.ibm.icu.dev.tool; /** * A command-line option. A UOption specifies the name of an option * and whether or not it takes an argument. It is a mutable object * that later contains the option argument, if any, and a boolean * flag stating whether the option was seen or not. * * The static method parseArgs() takes an array of command-line * arguments and an array of UOptions and parses the command-line * arguments. * * This deliberately resembles the icu4c file uoption.[ch]. */ public class UOption { // Deliberated public data members public String longName; public String value; public Fn optionFn; public Object context; public char shortName; public int hasArg; public boolean doesOccur; // Values of hasArg public static final int NO_ARG = 0; public static final int REQUIRES_ARG = 1; public static final int OPTIONAL_ARG = 2; // Analog of UOptionFn. We don't pass in the context because the // functor can get it from the UOption. public interface Fn { int handle(UOption option); } /** * Create a UOption with the given attributes. */ public static UOption create(String aLongName, char aShortName, int hasArgument) { return new UOption(aLongName, aShortName, hasArgument); } /** * Create a UOption with the given attributes. * Synonym for create(), for C compatibility. */ public static UOption DEF(String aLongName, char aShortName, int hasArgument) { return create(aLongName, aShortName, hasArgument); } // Standard canned options. These create a new object when // called. Since the UOption object is mutable, we cannot use // static final instances. public static UOption HELP_H() { return create("help", 'h', NO_ARG); } public static UOption HELP_QUESTION_MARK() { return create("help", '?', NO_ARG); } public static UOption VERBOSE() { return create("verbose", 'v', NO_ARG); } public static UOption QUIET() { return create("quiet", 'q', NO_ARG); } public static UOption VERSION() { return create("version", 'V', NO_ARG); } public static UOption COPYRIGHT() { return create("copyright", 'c', NO_ARG); } public static UOption DESTDIR() { return create("destdir", 'd', REQUIRES_ARG); } public static UOption SOURCEDIR() { return create("sourcedir", 's', REQUIRES_ARG); } public static UOption ENCODING() { return create("encoding", 'e', REQUIRES_ARG); } public static UOption ICUDATADIR() { return create("icudatadir", 'i', REQUIRES_ARG); } public static UOption PACKAGE_NAME() { return create("package-name", 'p', REQUIRES_ARG); } public static UOption BUNDLE_NAME() { return create("bundle-name", 'b', REQUIRES_ARG); } /** * Java Command line argument parser. * * This function takes the argv[] command line and a description of * the program's options in form of an array of UOption structures. * Each UOption defines a long and a short name (a string and a character) * for options like "--foo" and "-f". * * Each option is marked with whether it does not take an argument, * requires one, or optionally takes one. The argument may follow in * the same argv[] entry for short options, or it may always follow * in the next argv[] entry. * * An argument is in the next argv[] entry for both long and short name * options, except it is taken from directly behind the short name in * its own argv[] entry if there are characters following the option letter. * An argument in its own argv[] entry must not begin with a '-' * unless it is only the '-' itself. There is no restriction of the * argument format if it is part of the short name options's argv[] entry. * * The argument is stored in the value field of the corresponding * UOption entry, and the doesOccur field is set to 1 if the option * is found at all. * * Short name options without arguments can be collapsed into a single * argv[] entry. After an option letter takes an argument, following * letters will be taken as its argument. * * If the same option is found several times, then the last * argument value will be stored in the value field. * * For each option, a function can be called. This could be used * for options that occur multiple times and all arguments are to * be collected. * * All options are removed from the argv[] array itself. If the parser * is successful, then it returns the number of remaining non-option * strings. (Unlike C, the Java argv[] array does NOT contain * the program name in argv[0].) * * An option "--" ends option processing; everything after this * remains in the argv[] array. * * An option string "-" alone is treated as a non-option. * * If an option is not recognized or an argument missing, then * the parser returns with the negative index of the argv[] entry * where the error was detected. * * @param argv this parameter is modified * @param start the first argument in argv[] to examine. Must be * 0..argv.length-1. Arguments from 0..start-1 are ignored. * @param options this parameter is modified * @return the number of unprocessed arguments in argv[], including * arguments 0..start-1. */ public static int parseArgs(String argv[], int start, UOption options[]) { String arg; int i=start, remaining=start; char c; boolean stopOptions=false; while(i1 && arg.charAt(0)=='-') { /* process an option */ c=arg.charAt(1); UOption option=null; arg=arg.substring(2); if(c=='-') { /* process a long option */ if(arg.length()==0) { /* stop processing options after "--" */ stopOptions=true; } else { /* search for the option string */ int j; for(j=0; j1 && argv[i+1].charAt(0)=='-')) { /* argument in the next argv[], and there is not an option in there */ option.value=argv[++i]; } else if(option.hasArg==REQUIRES_ARG) { /* there is no argument, but one is required: return with error */ syntaxError("Option " + argv[i] + " lacks required argument"); } } } } else { /* process one or more short options */ for (;;) { /* search for the option letter */ int j; for(j=0; j1 && argv[i+1].charAt(0)=='-')) { /* argument in the next argv[], and there is not an option in there */ option.value=argv[++i]; /* this break is redundant because we know that *arg==0 */ break; } else if(option.hasArg==REQUIRES_ARG) { /* there is no argument, but one is required: return with error */ syntaxError("Option -" + c + " lacks required argument"); } } /* get the next option letter */ option=null; if (arg.length()==0) break; c=arg.charAt(0); arg=arg.substring(1); } } if(option!=null && option.optionFn!=null && option.optionFn.handle(option)<0) { /* the option function was called and returned an error */ syntaxError("Option handler failed for " + argv[i]); } /* go to next argv[] */ ++i; } else { /* move a non-option up in argv[] */ argv[remaining++]=arg; ++i; } } return remaining; } /** * Allows the default to be set in an option list. * @param s * @return this */public UOption setDefault(String s) { value = s; return this; } /** * Convenient method. */ public static int parseArgs(String argv[], UOption options[]) { return parseArgs(argv, 0, options); } /** * Constructor. */ private UOption(String aLongName, char aShortName, int hasArgument) { longName = aLongName; shortName = aShortName; hasArg = hasArgument; } /** * Throw an exception indicating a syntax error. */ private static void syntaxError(String message) { throw new IllegalArgumentException("Error in argument list: " + message); } }