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  
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.Iterator;
24  import java.util.List;
25  import java.util.ListIterator;
26  import java.util.Properties;
27  
28  /**
29   * <code>Parser</code> creates {@link CommandLine}s.
30   *
31   * @author John Keyes (john at integralsource.com)
32   * @version $Revision: 680644 $, $Date: 2008-07-29 09:13:48 +0100 (Tue, 29 Jul 2008) $
33   */
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(final 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  
73      /**
74       * Parses the specified <code>arguments</code> based
75       * on the specifed {@link Options}.
76       *
77       * @param options the <code>Options</code>
78       * @param arguments the <code>arguments</code>
79       * @return the <code>CommandLine</code>
80       * @throws ParseException if an error occurs when parsing the
81       * arguments.
82       */
83      public CommandLine parse(Options options, String[] arguments) throws ParseException
84      {
85          return parse(options, arguments, null, false);
86      }
87  
88      /**
89       * Parse the arguments according to the specified options and properties.
90       *
91       * @param options    the specified Options
92       * @param arguments  the command line arguments
93       * @param properties command line option name-value pairs
94       * @return the list of atomic option and value tokens
95       * @throws ParseException if there are any problems encountered
96       *                        while parsing the command line tokens.
97       *
98       * @since 1.1
99       */
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 }