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 018 package org.apache.commons.cli; 019 020 import java.util.ArrayList; 021 import java.util.Arrays; 022 import java.util.Enumeration; 023 import java.util.Iterator; 024 import java.util.List; 025 import java.util.ListIterator; 026 import java.util.Properties; 027 028 /** 029 * <code>Parser</code> creates {@link CommandLine}s. 030 * 031 * @author John Keyes (john at integralsource.com) 032 * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $ 033 */ 034 public 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(final 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 */ 071 protected abstract String[] flatten(Options opts, String[] arguments, boolean stopAtNonOption); 072 073 /** 074 * Parses the specified <code>arguments</code> based 075 * on the specifed {@link Options}. 076 * 077 * @param options the <code>Options</code> 078 * @param arguments the <code>arguments</code> 079 * @return the <code>CommandLine</code> 080 * @throws ParseException if an error occurs when parsing the 081 * arguments. 082 */ 083 public CommandLine parse(Options options, String[] arguments) throws ParseException 084 { 085 return parse(options, arguments, null, false); 086 } 087 088 /** 089 * Parse the arguments according to the specified options and properties. 090 * 091 * @param options the specified Options 092 * @param arguments the command line arguments 093 * @param properties command line option name-value pairs 094 * @return the list of atomic option and value tokens 095 * @throws ParseException if there are any problems encountered 096 * while parsing the command line tokens. 097 * 098 * @since 1.1 099 */ 100 public CommandLine parse(Options options, String[] arguments, Properties properties) throws ParseException 101 { 102 return parse(options, arguments, properties, false); 103 } 104 105 /** 106 * Parses the specified <code>arguments</code> 107 * based on the specifed {@link Options}. 108 * 109 * @param options the <code>Options</code> 110 * @param arguments the <code>arguments</code> 111 * @param stopAtNonOption specifies whether to stop interpreting the 112 * arguments when a non option has been encountered 113 * and to add them to the CommandLines args list. 114 * @return the <code>CommandLine</code> 115 * @throws ParseException if an error occurs when parsing the arguments. 116 */ 117 public CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException 118 { 119 return parse(options, arguments, null, stopAtNonOption); 120 } 121 122 /** 123 * Parse the arguments according to the specified options and 124 * properties. 125 * 126 * @param options the specified Options 127 * @param arguments the command line arguments 128 * @param properties command line option name-value pairs 129 * @param stopAtNonOption stop parsing the arguments when the first 130 * non option is encountered. 131 * 132 * @return the list of atomic option and value tokens 133 * 134 * @throws ParseException if there are any problems encountered 135 * while parsing the command line tokens. 136 * 137 * @since 1.1 138 */ 139 public CommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption) 140 throws ParseException 141 { 142 // clear out the data in options in case it's been used before (CLI-71) 143 for (Iterator it = options.helpOptions().iterator(); it.hasNext();) 144 { 145 Option opt = (Option) it.next(); 146 opt.clearValues(); 147 } 148 149 // initialise members 150 setOptions(options); 151 152 cmd = new CommandLine(); 153 154 boolean eatTheRest = false; 155 156 if (arguments == null) 157 { 158 arguments = new String[0]; 159 } 160 161 List tokenList = Arrays.asList(flatten(getOptions(), arguments, stopAtNonOption)); 162 163 ListIterator iterator = tokenList.listIterator(); 164 165 // process each flattened token 166 while (iterator.hasNext()) 167 { 168 String t = (String) iterator.next(); 169 170 // the value is the double-dash 171 if ("--".equals(t)) 172 { 173 eatTheRest = true; 174 } 175 176 // the value is a single dash 177 else if ("-".equals(t)) 178 { 179 if (stopAtNonOption) 180 { 181 eatTheRest = true; 182 } 183 else 184 { 185 cmd.addArg(t); 186 } 187 } 188 189 // the value is an option 190 else if (t.startsWith("-")) 191 { 192 if (stopAtNonOption && !getOptions().hasOption(t)) 193 { 194 eatTheRest = true; 195 cmd.addArg(t); 196 } 197 else 198 { 199 processOption(t, iterator); 200 } 201 } 202 203 // the value is an argument 204 else 205 { 206 cmd.addArg(t); 207 208 if (stopAtNonOption) 209 { 210 eatTheRest = true; 211 } 212 } 213 214 // eat the remaining tokens 215 if (eatTheRest) 216 { 217 while (iterator.hasNext()) 218 { 219 String str = (String) iterator.next(); 220 221 // ensure only one double-dash is added 222 if (!"--".equals(str)) 223 { 224 cmd.addArg(str); 225 } 226 } 227 } 228 } 229 230 processProperties(properties); 231 checkRequiredOptions(); 232 233 return cmd; 234 } 235 236 /** 237 * Sets the values of Options using the values in <code>properties</code>. 238 * 239 * @param properties The value properties to be processed. 240 */ 241 protected void processProperties(Properties properties) 242 { 243 if (properties == null) 244 { 245 return; 246 } 247 248 for (Enumeration e = properties.propertyNames(); e.hasMoreElements();) 249 { 250 String option = e.nextElement().toString(); 251 252 if (!cmd.hasOption(option)) 253 { 254 Option opt = getOptions().getOption(option); 255 256 // get the value from the properties instance 257 String value = properties.getProperty(option); 258 259 if (opt.hasArg()) 260 { 261 if (opt.getValues() == null || opt.getValues().length == 0) 262 { 263 try 264 { 265 opt.addValueForProcessing(value); 266 } 267 catch (RuntimeException exp) 268 { 269 // if we cannot add the value don't worry about it 270 } 271 } 272 } 273 else if (!("yes".equalsIgnoreCase(value) 274 || "true".equalsIgnoreCase(value) 275 || "1".equalsIgnoreCase(value))) 276 { 277 // if the value is not yes, true or 1 then don't add the 278 // option to the CommandLine 279 break; 280 } 281 282 cmd.addOption(opt); 283 } 284 } 285 } 286 287 /** 288 * Throws a {@link MissingOptionException} if all of the required options 289 * are not present. 290 * 291 * @throws MissingOptionException if any of the required Options 292 * are not present. 293 */ 294 protected void checkRequiredOptions() throws MissingOptionException 295 { 296 // if there are required options that have not been processsed 297 if (!getRequiredOptions().isEmpty()) 298 { 299 throw new MissingOptionException(getRequiredOptions()); 300 } 301 } 302 303 /** 304 * <p>Process the argument values for the specified Option 305 * <code>opt</code> using the values retrieved from the 306 * specified iterator <code>iter</code>. 307 * 308 * @param opt The current Option 309 * @param iter The iterator over the flattened command line 310 * Options. 311 * 312 * @throws ParseException if an argument value is required 313 * and it is has not been found. 314 */ 315 public void processArgs(Option opt, ListIterator iter) throws ParseException 316 { 317 // loop until an option is found 318 while (iter.hasNext()) 319 { 320 String str = (String) iter.next(); 321 322 // found an Option, not an argument 323 if (getOptions().hasOption(str) && str.startsWith("-")) 324 { 325 iter.previous(); 326 break; 327 } 328 329 // found a value 330 try 331 { 332 opt.addValueForProcessing(Util.stripLeadingAndTrailingQuotes(str)); 333 } 334 catch (RuntimeException exp) 335 { 336 iter.previous(); 337 break; 338 } 339 } 340 341 if (opt.getValues() == null && !opt.hasOptionalArg()) 342 { 343 throw new MissingArgumentException(opt); 344 } 345 } 346 347 /** 348 * Process the Option specified by <code>arg</code> using the values 349 * retrieved from the specfied iterator <code>iter</code>. 350 * 351 * @param arg The String value representing an Option 352 * @param iter The iterator over the flattened command line arguments. 353 * 354 * @throws ParseException if <code>arg</code> does not represent an Option 355 */ 356 protected void processOption(String arg, ListIterator iter) throws ParseException 357 { 358 boolean hasOption = getOptions().hasOption(arg); 359 360 // if there is no option throw an UnrecognisedOptionException 361 if (!hasOption) 362 { 363 throw new UnrecognizedOptionException("Unrecognized option: " + arg, arg); 364 } 365 366 // get the option represented by arg 367 Option opt = (Option) getOptions().getOption(arg).clone(); 368 369 // if the option is a required option remove the option from 370 // the requiredOptions list 371 if (opt.isRequired()) 372 { 373 getRequiredOptions().remove(opt.getKey()); 374 } 375 376 // if the option is in an OptionGroup make that option the selected 377 // option of the group 378 if (getOptions().getOptionGroup(opt) != null) 379 { 380 OptionGroup group = getOptions().getOptionGroup(opt); 381 382 if (group.isRequired()) 383 { 384 getRequiredOptions().remove(group); 385 } 386 387 group.setSelected(opt); 388 } 389 390 // if the option takes an argument value 391 if (opt.hasArg()) 392 { 393 processArgs(opt, iter); 394 } 395 396 // set the option on the command line 397 cmd.addOption(opt); 398 } 399 }