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.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 }