View Javadoc

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         // initialise members
135         this.options = options;
136 
137         // clear out the data in options in case it's been used before (CLI-71)
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         // process each flattened token
160         while (iterator.hasNext())
161         {
162             String t = (String) iterator.next();
163 
164             // the value is the double-dash
165             if ("--".equals(t))
166             {
167                 eatTheRest = true;
168             }
169 
170             // the value is a single dash
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             // the value is an option
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             // the value is an argument
198             else
199             {
200                 cmd.addArg(t);
201 
202                 if (stopAtNonOption)
203                 {
204                     eatTheRest = true;
205                 }
206             }
207 
208             // eat the remaining tokens
209             if (eatTheRest)
210             {
211                 while (iterator.hasNext())
212                 {
213                     String str = (String) iterator.next();
214 
215                     // ensure only one double-dash is added
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                 // get the value from the properties instance
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                             // if we cannot add the value don't worry about it
266                         }
267                     }
268                 }
269                 else if (!("yes".equalsIgnoreCase(value) 
270                            || "true".equalsIgnoreCase(value)
271                            || "1".equalsIgnoreCase(value)))
272                 {
273                     // if the value is not yes, true or 1 then don't add the
274                     // option to the CommandLine
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         // if there are required options that have not been
294         // processsed
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             // loop through the required options
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         // loop until an option is found
329         while (iter.hasNext())
330         {
331             String str = (String) iter.next();
332 
333             // found an Option, not an argument
334             if (options.hasOption(str) && str.startsWith("-"))
335             {
336                 iter.previous();
337                 break;
338             }
339 
340             // found a value
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         // if there is no option throw an UnrecognisedOptionException
377         if (!hasOption)
378         {
379             throw new UnrecognizedOptionException("Unrecognized option: " 
380                                                   + arg);
381         }
382         
383         // get the option represented by arg
384         final Option opt = options.getOption(arg);
385 
386         // if the option is a required option remove the option from
387         // the requiredOptions list
388         if (opt.isRequired())
389         {
390             requiredOptions.remove(opt.getKey());
391         }
392 
393         // if the option is in an OptionGroup make that option the selected
394         // option of the group
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         // if the option takes an argument value
408         if (opt.hasArg())
409         {
410             processArgs(opt, iter);
411         }
412 
413 
414         // set the option on the command line
415         cmd.addOption(opt);
416     }
417 }