001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.cli; 019 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Enumeration; 023import java.util.List; 024import java.util.ListIterator; 025import java.util.Properties; 026 027/** 028 * <code>Parser</code> creates {@link CommandLine}s. 029 * 030 * @version $Id: Parser.java 1677406 2015-05-03 14:27:31Z britter $ 031 * @deprecated since 1.3, the two-pass parsing with the flatten method is not enough flexible to handle complex cases 032 */ 033@Deprecated 034public abstract class Parser implements CommandLineParser 035{ 036 /** commandline instance */ 037 protected CommandLine cmd; 038 039 /** current Options */ 040 private Options options; 041 042 /** list of required options strings */ 043 private List requiredOptions; 044 045 protected void setOptions(Options options) 046 { 047 this.options = options; 048 this.requiredOptions = new ArrayList(options.getRequiredOptions()); 049 } 050 051 protected Options getOptions() 052 { 053 return options; 054 } 055 056 protected List getRequiredOptions() 057 { 058 return requiredOptions; 059 } 060 061 /** 062 * Subclasses must implement this method to reduce 063 * the <code>arguments</code> that have been passed to the parse method. 064 * 065 * @param opts The Options to parse the arguments by. 066 * @param arguments The arguments that have to be flattened. 067 * @param stopAtNonOption specifies whether to stop 068 * flattening when a non option has been encountered 069 * @return a String array of the flattened arguments 070 * @throws ParseException if there are any problems encountered 071 * while parsing the command line tokens. 072 */ 073 protected abstract String[] flatten(Options opts, String[] arguments, boolean stopAtNonOption) 074 throws ParseException; 075 076 /** 077 * Parses the specified <code>arguments</code> based 078 * on the specified {@link Options}. 079 * 080 * @param options the <code>Options</code> 081 * @param arguments the <code>arguments</code> 082 * @return the <code>CommandLine</code> 083 * @throws ParseException if there are any problems encountered 084 * while parsing the command line tokens. 085 */ 086 public CommandLine parse(Options options, String[] arguments) throws ParseException 087 { 088 return parse(options, arguments, null, false); 089 } 090 091 /** 092 * Parse the arguments according to the specified options and properties. 093 * 094 * @param options the specified Options 095 * @param arguments the command line arguments 096 * @param properties command line option name-value pairs 097 * @return the list of atomic option and value tokens 098 * @throws ParseException if there are any problems encountered 099 * while parsing the command line tokens. 100 * 101 * @since 1.1 102 */ 103 public CommandLine parse(Options options, String[] arguments, Properties properties) throws ParseException 104 { 105 return parse(options, arguments, properties, false); 106 } 107 108 /** 109 * Parses the specified <code>arguments</code> 110 * based on the specified {@link Options}. 111 * 112 * @param options the <code>Options</code> 113 * @param arguments the <code>arguments</code> 114 * @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops 115 * the parsing and the remaining arguments are added to the 116 * {@link CommandLine}s args list. If <tt>false</tt> an unrecognized 117 * argument triggers a ParseException. 118 * @return the <code>CommandLine</code> 119 * @throws ParseException if an error occurs when parsing the arguments. 120 */ 121 public CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException 122 { 123 return parse(options, arguments, null, stopAtNonOption); 124 } 125 126 /** 127 * Parse the arguments according to the specified options and 128 * properties. 129 * 130 * @param options the specified Options 131 * @param arguments the command line arguments 132 * @param properties command line option name-value pairs 133 * @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops 134 * the parsing and the remaining arguments are added to the 135 * {@link CommandLine}s args list. If <tt>false</tt> an unrecognized 136 * argument triggers a ParseException. 137 * 138 * @return the list of atomic option and value tokens 139 * 140 * @throws ParseException if there are any problems encountered 141 * while parsing the command line tokens. 142 * 143 * @since 1.1 144 */ 145 public CommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption) 146 throws ParseException 147 { 148 // clear out the data in options in case it's been used before (CLI-71) 149 for (Option opt : options.helpOptions()) 150 { 151 opt.clearValues(); 152 } 153 154 // clear the data from the groups 155 for (OptionGroup group : options.getOptionGroups()) 156 { 157 group.setSelected(null); 158 } 159 160 // initialise members 161 setOptions(options); 162 163 cmd = new CommandLine(); 164 165 boolean eatTheRest = false; 166 167 if (arguments == null) 168 { 169 arguments = new String[0]; 170 } 171 172 List<String> tokenList = Arrays.asList(flatten(getOptions(), arguments, stopAtNonOption)); 173 174 ListIterator<String> iterator = tokenList.listIterator(); 175 176 // process each flattened token 177 while (iterator.hasNext()) 178 { 179 String t = iterator.next(); 180 181 // the value is the double-dash 182 if ("--".equals(t)) 183 { 184 eatTheRest = true; 185 } 186 187 // the value is a single dash 188 else if ("-".equals(t)) 189 { 190 if (stopAtNonOption) 191 { 192 eatTheRest = true; 193 } 194 else 195 { 196 cmd.addArg(t); 197 } 198 } 199 200 // the value is an option 201 else if (t.startsWith("-")) 202 { 203 if (stopAtNonOption && !getOptions().hasOption(t)) 204 { 205 eatTheRest = true; 206 cmd.addArg(t); 207 } 208 else 209 { 210 processOption(t, iterator); 211 } 212 } 213 214 // the value is an argument 215 else 216 { 217 cmd.addArg(t); 218 219 if (stopAtNonOption) 220 { 221 eatTheRest = true; 222 } 223 } 224 225 // eat the remaining tokens 226 if (eatTheRest) 227 { 228 while (iterator.hasNext()) 229 { 230 String str = iterator.next(); 231 232 // ensure only one double-dash is added 233 if (!"--".equals(str)) 234 { 235 cmd.addArg(str); 236 } 237 } 238 } 239 } 240 241 processProperties(properties); 242 checkRequiredOptions(); 243 244 return cmd; 245 } 246 247 /** 248 * Sets the values of Options using the values in <code>properties</code>. 249 * 250 * @param properties The value properties to be processed. 251 * @throws ParseException if there are any problems encountered 252 * while processing the properties. 253 */ 254 protected void processProperties(Properties properties) throws ParseException 255 { 256 if (properties == null) 257 { 258 return; 259 } 260 261 for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements();) 262 { 263 String option = e.nextElement().toString(); 264 265 Option opt = options.getOption(option); 266 if (opt == null) 267 { 268 throw new UnrecognizedOptionException("Default option wasn't defined", option); 269 } 270 271 // if the option is part of a group, check if another option of the group has been selected 272 OptionGroup group = options.getOptionGroup(opt); 273 boolean selected = group != null && group.getSelected() != null; 274 275 if (!cmd.hasOption(option) && !selected) 276 { 277 // get the value from the properties instance 278 String value = properties.getProperty(option); 279 280 if (opt.hasArg()) 281 { 282 if (opt.getValues() == null || opt.getValues().length == 0) 283 { 284 try 285 { 286 opt.addValueForProcessing(value); 287 } 288 catch (RuntimeException exp) //NOPMD 289 { 290 // if we cannot add the value don't worry about it 291 } 292 } 293 } 294 else if (!("yes".equalsIgnoreCase(value) 295 || "true".equalsIgnoreCase(value) 296 || "1".equalsIgnoreCase(value))) 297 { 298 // if the value is not yes, true or 1 then don't add the 299 // option to the CommandLine 300 continue; 301 } 302 303 cmd.addOption(opt); 304 updateRequiredOptions(opt); 305 } 306 } 307 } 308 309 /** 310 * Throws a {@link MissingOptionException} if all of the required options 311 * are not present. 312 * 313 * @throws MissingOptionException if any of the required Options are not present. 314 */ 315 protected void checkRequiredOptions() throws MissingOptionException 316 { 317 // if there are required options that have not been processed 318 if (!getRequiredOptions().isEmpty()) 319 { 320 throw new MissingOptionException(getRequiredOptions()); 321 } 322 } 323 324 /** 325 * Process the argument values for the specified Option 326 * <code>opt</code> using the values retrieved from the 327 * specified iterator <code>iter</code>. 328 * 329 * @param opt The current Option 330 * @param iter The iterator over the flattened command line Options. 331 * 332 * @throws ParseException if an argument value is required 333 * and it is has not been found. 334 */ 335 public void processArgs(Option opt, ListIterator<String> iter) throws ParseException 336 { 337 // loop until an option is found 338 while (iter.hasNext()) 339 { 340 String str = iter.next(); 341 342 // found an Option, not an argument 343 if (getOptions().hasOption(str) && str.startsWith("-")) 344 { 345 iter.previous(); 346 break; 347 } 348 349 // found a value 350 try 351 { 352 opt.addValueForProcessing(Util.stripLeadingAndTrailingQuotes(str)); 353 } 354 catch (RuntimeException exp) 355 { 356 iter.previous(); 357 break; 358 } 359 } 360 361 if (opt.getValues() == null && !opt.hasOptionalArg()) 362 { 363 throw new MissingArgumentException(opt); 364 } 365 } 366 367 /** 368 * Process the Option specified by <code>arg</code> using the values 369 * retrieved from the specified iterator <code>iter</code>. 370 * 371 * @param arg The String value representing an Option 372 * @param iter The iterator over the flattened command line arguments. 373 * 374 * @throws ParseException if <code>arg</code> does not represent an Option 375 */ 376 protected void processOption(String arg, ListIterator<String> iter) throws ParseException 377 { 378 boolean hasOption = getOptions().hasOption(arg); 379 380 // if there is no option throw an UnrecognisedOptionException 381 if (!hasOption) 382 { 383 throw new UnrecognizedOptionException("Unrecognized option: " + arg, arg); 384 } 385 386 // get the option represented by arg 387 Option opt = (Option) getOptions().getOption(arg).clone(); 388 389 // update the required options and groups 390 updateRequiredOptions(opt); 391 392 // if the option takes an argument value 393 if (opt.hasArg()) 394 { 395 processArgs(opt, iter); 396 } 397 398 // set the option on the command line 399 cmd.addOption(opt); 400 } 401 402 /** 403 * Removes the option or its group from the list of expected elements. 404 * 405 * @param opt 406 */ 407 private void updateRequiredOptions(Option opt) throws ParseException 408 { 409 // if the option is a required option remove the option from 410 // the requiredOptions list 411 if (opt.isRequired()) 412 { 413 getRequiredOptions().remove(opt.getKey()); 414 } 415 416 // if the option is in an OptionGroup make that option the selected 417 // option of the group 418 if (getOptions().getOptionGroup(opt) != null) 419 { 420 OptionGroup group = getOptions().getOptionGroup(opt); 421 422 if (group.isRequired()) 423 { 424 getRequiredOptions().remove(group); 425 } 426 427 group.setSelected(opt); 428 } 429 } 430}