1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.cli;
19
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Enumeration;
23 import java.util.List;
24 import java.util.ListIterator;
25 import java.util.Properties;
26
27 /**
28 * <code>Parser</code> creates {@link CommandLine}s.
29 *
30 * @version $Id: Parser.java 1445352 2013-02-12 20:48:19Z tn $
31 * @deprecated since 1.3, the two-pass parsing with the flatten method is not enough flexible to handle complex cases
32 */
33 @Deprecated
34 public abstract class Parser implements CommandLineParser
35 {
36 /** commandline instance */
37 protected CommandLine cmd;
38
39 /** current Options */
40 private Options options;
41
42 /** list of required options strings */
43 private List requiredOptions;
44
45 protected void setOptions(Options options)
46 {
47 this.options = options;
48 this.requiredOptions = new ArrayList(options.getRequiredOptions());
49 }
50
51 protected Options getOptions()
52 {
53 return options;
54 }
55
56 protected List getRequiredOptions()
57 {
58 return requiredOptions;
59 }
60
61 /**
62 * Subclasses must implement this method to reduce
63 * the <code>arguments</code> that have been passed to the parse method.
64 *
65 * @param opts The Options to parse the arguments by.
66 * @param arguments The arguments that have to be flattened.
67 * @param stopAtNonOption specifies whether to stop
68 * flattening when a non option has been encountered
69 * @return a String array of the flattened arguments
70 */
71 protected abstract String[] flatten(Options opts, String[] arguments, boolean stopAtNonOption)
72 throws ParseException;
73
74 /**
75 * Parses the specified <code>arguments</code> based
76 * on the specified {@link Options}.
77 *
78 * @param options the <code>Options</code>
79 * @param arguments the <code>arguments</code>
80 * @return the <code>CommandLine</code>
81 * @throws ParseException if an error occurs when parsing the
82 * arguments.
83 */
84 public CommandLine parse(Options options, String[] arguments) throws ParseException
85 {
86 return parse(options, arguments, null, false);
87 }
88
89 /**
90 * Parse the arguments according to the specified options and properties.
91 *
92 * @param options the specified Options
93 * @param arguments the command line arguments
94 * @param properties command line option name-value pairs
95 * @return the list of atomic option and value tokens
96 * @throws ParseException if there are any problems encountered
97 * while parsing the command line tokens.
98 *
99 * @since 1.1
100 */
101 public CommandLine parse(Options options, String[] arguments, Properties properties) throws ParseException
102 {
103 return parse(options, arguments, properties, false);
104 }
105
106 /**
107 * Parses the specified <code>arguments</code>
108 * based on the specified {@link Options}.
109 *
110 * @param options the <code>Options</code>
111 * @param arguments the <code>arguments</code>
112 * @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops
113 * the parsing and the remaining arguments are added to the
114 * {@link CommandLine}s args list. If <tt>false</tt> an unrecognized
115 * argument triggers a ParseException.
116 * @return the <code>CommandLine</code>
117 * @throws ParseException if an error occurs when parsing the arguments.
118 */
119 public CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException
120 {
121 return parse(options, arguments, null, stopAtNonOption);
122 }
123
124 /**
125 * Parse the arguments according to the specified options and
126 * properties.
127 *
128 * @param options the specified Options
129 * @param arguments the command line arguments
130 * @param properties command line option name-value pairs
131 * @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops
132 * the parsing and the remaining arguments are added to the
133 * {@link CommandLine}s args list. If <tt>false</tt> an unrecognized
134 * argument triggers a ParseException.
135 *
136 * @return the list of atomic option and value tokens
137 *
138 * @throws ParseException if there are any problems encountered
139 * while parsing the command line tokens.
140 *
141 * @since 1.1
142 */
143 public CommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption)
144 throws ParseException
145 {
146 // clear out the data in options in case it's been used before (CLI-71)
147 for (Option opt : options.helpOptions())
148 {
149 opt.clearValues();
150 }
151
152 // clear the data from the groups
153 for (OptionGroup group : options.getOptionGroups())
154 {
155 group.setSelected(null);
156 }
157
158 // initialise members
159 setOptions(options);
160
161 cmd = new CommandLine();
162
163 boolean eatTheRest = false;
164
165 if (arguments == null)
166 {
167 arguments = new String[0];
168 }
169
170 List<String> tokenList = Arrays.asList(flatten(getOptions(), arguments, stopAtNonOption));
171
172 ListIterator<String> iterator = tokenList.listIterator();
173
174 // process each flattened token
175 while (iterator.hasNext())
176 {
177 String t = iterator.next();
178
179 // the value is the double-dash
180 if ("--".equals(t))
181 {
182 eatTheRest = true;
183 }
184
185 // the value is a single dash
186 else if ("-".equals(t))
187 {
188 if (stopAtNonOption)
189 {
190 eatTheRest = true;
191 }
192 else
193 {
194 cmd.addArg(t);
195 }
196 }
197
198 // the value is an option
199 else if (t.startsWith("-"))
200 {
201 if (stopAtNonOption && !getOptions().hasOption(t))
202 {
203 eatTheRest = true;
204 cmd.addArg(t);
205 }
206 else
207 {
208 processOption(t, iterator);
209 }
210 }
211
212 // the value is an argument
213 else
214 {
215 cmd.addArg(t);
216
217 if (stopAtNonOption)
218 {
219 eatTheRest = true;
220 }
221 }
222
223 // eat the remaining tokens
224 if (eatTheRest)
225 {
226 while (iterator.hasNext())
227 {
228 String str = iterator.next();
229
230 // ensure only one double-dash is added
231 if (!"--".equals(str))
232 {
233 cmd.addArg(str);
234 }
235 }
236 }
237 }
238
239 processProperties(properties);
240 checkRequiredOptions();
241
242 return cmd;
243 }
244
245 /**
246 * Sets the values of Options using the values in <code>properties</code>.
247 *
248 * @param properties The value properties to be processed.
249 */
250 protected void processProperties(Properties properties) throws ParseException
251 {
252 if (properties == null)
253 {
254 return;
255 }
256
257 for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements();)
258 {
259 String option = e.nextElement().toString();
260
261 Option opt = options.getOption(option);
262 if (opt == null)
263 {
264 throw new UnrecognizedOptionException("Default option wasn't defined", option);
265 }
266
267 // if the option is part of a group, check if another option of the group has been selected
268 OptionGroup group = options.getOptionGroup(opt);
269 boolean selected = group != null && group.getSelected() != null;
270
271 if (!cmd.hasOption(option) && !selected)
272 {
273 // get the value from the properties instance
274 String value = properties.getProperty(option);
275
276 if (opt.hasArg())
277 {
278 if (opt.getValues() == null || opt.getValues().length == 0)
279 {
280 try
281 {
282 opt.addValueForProcessing(value);
283 }
284 catch (RuntimeException exp) //NOPMD
285 {
286 // if we cannot add the value don't worry about it
287 }
288 }
289 }
290 else if (!("yes".equalsIgnoreCase(value)
291 || "true".equalsIgnoreCase(value)
292 || "1".equalsIgnoreCase(value)))
293 {
294 // if the value is not yes, true or 1 then don't add the
295 // option to the CommandLine
296 continue;
297 }
298
299 cmd.addOption(opt);
300 updateRequiredOptions(opt);
301 }
302 }
303 }
304
305 /**
306 * Throws a {@link MissingOptionException} if all of the required options
307 * are not present.
308 *
309 * @throws MissingOptionException if any of the required Options are not present.
310 */
311 protected void checkRequiredOptions() throws MissingOptionException
312 {
313 // if there are required options that have not been processed
314 if (!getRequiredOptions().isEmpty())
315 {
316 throw new MissingOptionException(getRequiredOptions());
317 }
318 }
319
320 /**
321 * Process the argument values for the specified Option
322 * <code>opt</code> using the values retrieved from the
323 * specified iterator <code>iter</code>.
324 *
325 * @param opt The current Option
326 * @param iter The iterator over the flattened command line Options.
327 *
328 * @throws ParseException if an argument value is required
329 * and it is has not been found.
330 */
331 public void processArgs(Option opt, ListIterator<String> iter) throws ParseException
332 {
333 // loop until an option is found
334 while (iter.hasNext())
335 {
336 String str = iter.next();
337
338 // found an Option, not an argument
339 if (getOptions().hasOption(str) && str.startsWith("-"))
340 {
341 iter.previous();
342 break;
343 }
344
345 // found a value
346 try
347 {
348 opt.addValueForProcessing(Util.stripLeadingAndTrailingQuotes(str));
349 }
350 catch (RuntimeException exp)
351 {
352 iter.previous();
353 break;
354 }
355 }
356
357 if (opt.getValues() == null && !opt.hasOptionalArg())
358 {
359 throw new MissingArgumentException(opt);
360 }
361 }
362
363 /**
364 * Process the Option specified by <code>arg</code> using the values
365 * retrieved from the specified iterator <code>iter</code>.
366 *
367 * @param arg The String value representing an Option
368 * @param iter The iterator over the flattened command line arguments.
369 *
370 * @throws ParseException if <code>arg</code> does not represent an Option
371 */
372 protected void processOption(String arg, ListIterator<String> iter) throws ParseException
373 {
374 boolean hasOption = getOptions().hasOption(arg);
375
376 // if there is no option throw an UnrecognisedOptionException
377 if (!hasOption)
378 {
379 throw new UnrecognizedOptionException("Unrecognized option: " + arg, arg);
380 }
381
382 // get the option represented by arg
383 Option opt = (Option) getOptions().getOption(arg).clone();
384
385 // update the required options and groups
386 updateRequiredOptions(opt);
387
388 // if the option takes an argument value
389 if (opt.hasArg())
390 {
391 processArgs(opt, iter);
392 }
393
394 // set the option on the command line
395 cmd.addOption(opt);
396 }
397
398 /**
399 * Removes the option or its group from the list of expected elements.
400 *
401 * @param opt
402 */
403 private void updateRequiredOptions(Option opt) throws ParseException
404 {
405 // if the option is a required option remove the option from
406 // the requiredOptions list
407 if (opt.isRequired())
408 {
409 getRequiredOptions().remove(opt.getKey());
410 }
411
412 // if the option is in an OptionGroup make that option the selected
413 // option of the group
414 if (getOptions().getOptionGroup(opt) != null)
415 {
416 OptionGroup group = getOptions().getOptionGroup(opt);
417
418 if (group.isRequired())
419 {
420 getRequiredOptions().remove(group);
421 }
422
423 group.setSelected(opt);
424 }
425 }
426 }