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 package org.apache.commons.cli;
18
19 import java.util.Arrays;
20 import java.util.Enumeration;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.ListIterator;
24 import java.util.Properties;
25
26 /***
27 * <p><code>Parser</code> creates {@link CommandLine}s.</p>
28 *
29 * @author John Keyes (john at integralsource.com)
30 * @see Parser
31 * @version $Revision: 551815 $
32 */
33 public abstract class Parser implements CommandLineParser {
34
35 /*** commandline instance */
36 private CommandLine cmd;
37
38 /*** current Options */
39 private Options options;
40
41 /*** list of required options strings */
42 private List requiredOptions;
43
44 /***
45 * <p>Subclasses must implement this method to reduce
46 * the <code>arguments</code> that have been passed to the parse
47 * method.</p>
48 *
49 * @param opts The Options to parse the arguments by.
50 * @param arguments The arguments that have to be flattened.
51 * @param stopAtNonOption specifies whether to stop
52 * flattening when a non option has been encountered
53 * @return a String array of the flattened arguments
54 */
55 protected abstract String[] flatten(Options opts, String[] arguments,
56 boolean stopAtNonOption);
57
58 /***
59 * <p>Parses the specified <code>arguments</code>
60 * based on the specifed {@link Options}.</p>
61 *
62 * @param options the <code>Options</code>
63 * @param arguments the <code>arguments</code>
64 * @return the <code>CommandLine</code>
65 * @throws ParseException if an error occurs when parsing the
66 * arguments.
67 */
68 public CommandLine parse(Options options, String[] arguments)
69 throws ParseException
70 {
71 return parse(options, arguments, null, false);
72 }
73
74 /***
75 * Parse the arguments according to the specified options and
76 * properties.
77 *
78 * @param options the specified Options
79 * @param arguments the command line arguments
80 * @param properties command line option name-value pairs
81 * @return the list of atomic option and value tokens
82 *
83 * @throws ParseException if there are any problems encountered
84 * while parsing the command line tokens.
85 */
86 public CommandLine parse(Options options, String[] arguments,
87 Properties properties)
88 throws ParseException
89 {
90 return parse(options, arguments, properties, false);
91 }
92
93 /***
94 * <p>Parses the specified <code>arguments</code>
95 * based on the specifed {@link Options}.</p>
96 *
97 * @param options the <code>Options</code>
98 * @param arguments the <code>arguments</code>
99 * @param stopAtNonOption specifies whether to stop
100 * interpreting the arguments when a non option has
101 * been encountered and to add them to the CommandLines
102 * args list.
103 *
104 * @return the <code>CommandLine</code>
105 * @throws ParseException if an error occurs when parsing the
106 * arguments.
107 */
108 public CommandLine parse(Options options, String[] arguments,
109 boolean stopAtNonOption)
110 throws ParseException
111 {
112 return parse(options, arguments, null, stopAtNonOption);
113 }
114
115 /***
116 * Parse the arguments according to the specified options and
117 * properties.
118 *
119 * @param options the specified Options
120 * @param arguments the command line arguments
121 * @param properties command line option name-value pairs
122 * @param stopAtNonOption stop parsing the arguments when the first
123 * non option is encountered.
124 *
125 * @return the list of atomic option and value tokens
126 *
127 * @throws ParseException if there are any problems encountered
128 * while parsing the command line tokens.
129 */
130 public CommandLine parse(Options options, String[] arguments,
131 Properties properties, boolean stopAtNonOption)
132 throws ParseException
133 {
134
135 this.options = options;
136
137
138 for (Iterator it = options.helpOptions().iterator(); it.hasNext();) {
139 Option opt = (Option) it.next();
140 opt.clearValues();
141 }
142
143 requiredOptions = options.getRequiredOptions();
144 cmd = new CommandLine();
145
146 boolean eatTheRest = false;
147
148 if (arguments == null)
149 {
150 arguments = new String[0];
151 }
152
153 List tokenList = Arrays.asList(flatten(this.options,
154 arguments,
155 stopAtNonOption));
156
157 ListIterator iterator = tokenList.listIterator();
158
159
160 while (iterator.hasNext())
161 {
162 String t = (String) iterator.next();
163
164
165 if ("--".equals(t))
166 {
167 eatTheRest = true;
168 }
169
170
171 else if ("-".equals(t))
172 {
173 if (stopAtNonOption)
174 {
175 eatTheRest = true;
176 }
177 else
178 {
179 cmd.addArg(t);
180 }
181 }
182
183
184 else if (t.startsWith("-"))
185 {
186 if (stopAtNonOption && !options.hasOption(t))
187 {
188 eatTheRest = true;
189 cmd.addArg(t);
190 }
191 else
192 {
193 processOption(t, iterator);
194 }
195 }
196
197
198 else
199 {
200 cmd.addArg(t);
201
202 if (stopAtNonOption)
203 {
204 eatTheRest = true;
205 }
206 }
207
208
209 if (eatTheRest)
210 {
211 while (iterator.hasNext())
212 {
213 String str = (String) iterator.next();
214
215
216 if (!"--".equals(str))
217 {
218 cmd.addArg(str);
219 }
220 }
221 }
222 }
223
224 processProperties(properties);
225 checkRequiredOptions();
226
227 return cmd;
228 }
229
230 /***
231 * <p>Sets the values of Options using the values in
232 * <code>properties</code>.</p>
233 *
234 * @param properties The value properties to be processed.
235 */
236 private void processProperties(Properties properties)
237 {
238 if (properties == null)
239 {
240 return;
241 }
242
243 for (Enumeration e = properties.propertyNames(); e.hasMoreElements();)
244 {
245 String option = e.nextElement().toString();
246
247 if (!cmd.hasOption(option))
248 {
249 Option opt = options.getOption(option);
250
251
252 String value = properties.getProperty(option);
253
254 if (opt.hasArg())
255 {
256 if ((opt.getValues() == null)
257 || (opt.getValues().length == 0))
258 {
259 try
260 {
261 opt.addValueForProcessing(value);
262 }
263 catch (RuntimeException exp)
264 {
265
266 }
267 }
268 }
269 else if (!("yes".equalsIgnoreCase(value)
270 || "true".equalsIgnoreCase(value)
271 || "1".equalsIgnoreCase(value)))
272 {
273
274
275 break;
276 }
277
278 cmd.addOption(opt);
279 }
280 }
281 }
282
283 /***
284 * <p>Throws a {@link MissingOptionException} if all of the
285 * required options are no present.</p>
286 *
287 * @throws MissingOptionException if any of the required Options
288 * are not present.
289 */
290 private void checkRequiredOptions()
291 throws MissingOptionException
292 {
293
294
295 if (requiredOptions.size() > 0)
296 {
297 Iterator iter = requiredOptions.iterator();
298 StringBuffer buff = new StringBuffer("Missing required option");
299 buff.append(requiredOptions.size() == 1 ? "" : "s");
300 buff.append(": ");
301
302
303
304 while (iter.hasNext())
305 {
306 buff.append(iter.next());
307 }
308
309 throw new MissingOptionException(buff.toString());
310 }
311 }
312
313 /***
314 * <p>Process the argument values for the specified Option
315 * <code>opt</code> using the values retrieved from the
316 * specified iterator <code>iter</code>.
317 *
318 * @param opt The current Option
319 * @param iter The iterator over the flattened command line
320 * Options.
321 *
322 * @throws ParseException if an argument value is required
323 * and it is has not been found.
324 */
325 public void processArgs(Option opt, ListIterator iter)
326 throws ParseException
327 {
328
329 while (iter.hasNext())
330 {
331 String str = (String) iter.next();
332
333
334 if (options.hasOption(str) && str.startsWith("-"))
335 {
336 iter.previous();
337 break;
338 }
339
340
341 try
342 {
343 opt.addValueForProcessing( Util.stripLeadingAndTrailingQuotes(str) );
344 }
345 catch (RuntimeException exp)
346 {
347 iter.previous();
348 break;
349 }
350 }
351
352 if ((opt.getValues() == null) && !opt.hasOptionalArg())
353 {
354 throw new MissingArgumentException("Missing argument for option:"
355 + opt.getKey());
356 }
357 }
358
359 /***
360 * <p>Process the Option specified by <code>arg</code>
361 * using the values retrieved from the specfied iterator
362 * <code>iter</code>.
363 *
364 * @param arg The String value representing an Option
365 * @param iter The iterator over the flattened command
366 * line arguments.
367 *
368 * @throws ParseException if <code>arg</code> does not
369 * represent an Option
370 */
371 private void processOption(String arg, ListIterator iter)
372 throws ParseException
373 {
374 boolean hasOption = options.hasOption(arg);
375
376
377 if (!hasOption)
378 {
379 throw new UnrecognizedOptionException("Unrecognized option: "
380 + arg);
381 }
382
383
384 final Option opt = options.getOption(arg);
385
386
387
388 if (opt.isRequired())
389 {
390 requiredOptions.remove(opt.getKey());
391 }
392
393
394
395 if (options.getOptionGroup(opt) != null)
396 {
397 OptionGroup group = options.getOptionGroup(opt);
398
399 if (group.isRequired())
400 {
401 requiredOptions.remove(group);
402 }
403
404 group.setSelected(opt);
405 }
406
407
408 if (opt.hasArg())
409 {
410 processArgs(opt, iter);
411 }
412
413
414
415 cmd.addOption(opt);
416 }
417 }