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