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 1677406 2015-05-03 14:27:31Z britter $
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       * @throws ParseException if there are any problems encountered
71       *                        while parsing the command line tokens.
72       */
73      protected abstract String[] flatten(Options opts, String[] arguments, boolean stopAtNonOption)
74              throws ParseException;
75  
76      /**
77       * Parses the specified <code>arguments</code> based
78       * on the specified {@link Options}.
79       *
80       * @param options the <code>Options</code>
81       * @param arguments the <code>arguments</code>
82       * @return the <code>CommandLine</code>
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) throws ParseException
87      {
88          return parse(options, arguments, null, false);
89      }
90  
91      /**
92       * Parse the arguments according to the specified options and properties.
93       *
94       * @param options    the specified Options
95       * @param arguments  the command line arguments
96       * @param properties command line option name-value pairs
97       * @return the list of atomic option and value tokens
98       * @throws ParseException if there are any problems encountered
99       *                        while parsing the command line tokens.
100      *
101      * @since 1.1
102      */
103     public CommandLine parse(Options options, String[] arguments, Properties properties) throws ParseException
104     {
105         return parse(options, arguments, properties, false);
106     }
107 
108     /**
109      * Parses the specified <code>arguments</code>
110      * based on the specified {@link Options}.
111      *
112      * @param options         the <code>Options</code>
113      * @param arguments       the <code>arguments</code>
114      * @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops
115      *     the parsing and the remaining arguments are added to the 
116      *     {@link CommandLine}s args list. If <tt>false</tt> an unrecognized
117      *     argument triggers a ParseException.
118      * @return the <code>CommandLine</code>
119      * @throws ParseException if an error occurs when parsing the arguments.
120      */
121     public CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException
122     {
123         return parse(options, arguments, null, stopAtNonOption);
124     }
125 
126     /**
127      * Parse the arguments according to the specified options and
128      * properties.
129      *
130      * @param options the specified Options
131      * @param arguments the command line arguments
132      * @param properties command line option name-value pairs
133      * @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops
134      *     the parsing and the remaining arguments are added to the 
135      *     {@link CommandLine}s args list. If <tt>false</tt> an unrecognized
136      *     argument triggers a ParseException.
137      *
138      * @return the list of atomic option and value tokens
139      *
140      * @throws ParseException if there are any problems encountered
141      * while parsing the command line tokens.
142      *
143      * @since 1.1
144      */
145     public CommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption)
146             throws ParseException
147     {
148         // clear out the data in options in case it's been used before (CLI-71)
149         for (Option opt : options.helpOptions())
150         {
151             opt.clearValues();
152         }
153         
154         // clear the data from the groups
155         for (OptionGroup group : options.getOptionGroups())
156         {
157             group.setSelected(null);
158         }        
159 
160         // initialise members
161         setOptions(options);
162 
163         cmd = new CommandLine();
164 
165         boolean eatTheRest = false;
166 
167         if (arguments == null)
168         {
169             arguments = new String[0];
170         }
171 
172         List<String> tokenList = Arrays.asList(flatten(getOptions(), arguments, stopAtNonOption));
173 
174         ListIterator<String> iterator = tokenList.listIterator();
175 
176         // process each flattened token
177         while (iterator.hasNext())
178         {
179             String t = iterator.next();
180 
181             // the value is the double-dash
182             if ("--".equals(t))
183             {
184                 eatTheRest = true;
185             }
186 
187             // the value is a single dash
188             else if ("-".equals(t))
189             {
190                 if (stopAtNonOption)
191                 {
192                     eatTheRest = true;
193                 }
194                 else
195                 {
196                     cmd.addArg(t);
197                 }
198             }
199 
200             // the value is an option
201             else if (t.startsWith("-"))
202             {
203                 if (stopAtNonOption && !getOptions().hasOption(t))
204                 {
205                     eatTheRest = true;
206                     cmd.addArg(t);
207                 }
208                 else
209                 {
210                     processOption(t, iterator);
211                 }
212             }
213 
214             // the value is an argument
215             else
216             {
217                 cmd.addArg(t);
218 
219                 if (stopAtNonOption)
220                 {
221                     eatTheRest = true;
222                 }
223             }
224 
225             // eat the remaining tokens
226             if (eatTheRest)
227             {
228                 while (iterator.hasNext())
229                 {
230                     String str = iterator.next();
231 
232                     // ensure only one double-dash is added
233                     if (!"--".equals(str))
234                     {
235                         cmd.addArg(str);
236                     }
237                 }
238             }
239         }
240 
241         processProperties(properties);
242         checkRequiredOptions();
243 
244         return cmd;
245     }
246 
247     /**
248      * Sets the values of Options using the values in <code>properties</code>.
249      *
250      * @param properties The value properties to be processed.
251      * @throws ParseException if there are any problems encountered
252      *                        while processing the properties.
253      */
254     protected void processProperties(Properties properties) throws ParseException
255     {
256         if (properties == null)
257         {
258             return;
259         }
260 
261         for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements();)
262         {
263             String option = e.nextElement().toString();
264             
265             Option opt = options.getOption(option);
266             if (opt == null)
267             {
268                 throw new UnrecognizedOptionException("Default option wasn't defined", option);
269             }
270             
271             // if the option is part of a group, check if another option of the group has been selected
272             OptionGroup group = options.getOptionGroup(opt);
273             boolean selected = group != null && group.getSelected() != null;
274             
275             if (!cmd.hasOption(option) && !selected)
276             {
277                 // get the value from the properties instance
278                 String value = properties.getProperty(option);
279 
280                 if (opt.hasArg())
281                 {
282                     if (opt.getValues() == null || opt.getValues().length == 0)
283                     {
284                         try
285                         {
286                             opt.addValueForProcessing(value);
287                         }
288                         catch (RuntimeException exp) //NOPMD
289                         {
290                             // if we cannot add the value don't worry about it
291                         }
292                     }
293                 }
294                 else if (!("yes".equalsIgnoreCase(value)
295                         || "true".equalsIgnoreCase(value)
296                         || "1".equalsIgnoreCase(value)))
297                 {
298                     // if the value is not yes, true or 1 then don't add the
299                     // option to the CommandLine
300                     continue;
301                 }
302 
303                 cmd.addOption(opt);
304                 updateRequiredOptions(opt);
305             }
306         }
307     }
308 
309     /**
310      * Throws a {@link MissingOptionException} if all of the required options
311      * are not present.
312      *
313      * @throws MissingOptionException if any of the required Options are not present.
314      */
315     protected void checkRequiredOptions() throws MissingOptionException
316     {
317         // if there are required options that have not been processed
318         if (!getRequiredOptions().isEmpty())
319         {
320             throw new MissingOptionException(getRequiredOptions());
321         }
322     }
323 
324     /**
325      * Process the argument values for the specified Option
326      * <code>opt</code> using the values retrieved from the
327      * specified iterator <code>iter</code>.
328      *
329      * @param opt The current Option
330      * @param iter The iterator over the flattened command line Options.
331      *
332      * @throws ParseException if an argument value is required
333      * and it is has not been found.
334      */
335     public void processArgs(Option opt, ListIterator<String> iter) throws ParseException
336     {
337         // loop until an option is found
338         while (iter.hasNext())
339         {
340             String str = iter.next();
341             
342             // found an Option, not an argument
343             if (getOptions().hasOption(str) && str.startsWith("-"))
344             {
345                 iter.previous();
346                 break;
347             }
348 
349             // found a value
350             try
351             {
352                 opt.addValueForProcessing(Util.stripLeadingAndTrailingQuotes(str));
353             }
354             catch (RuntimeException exp)
355             {
356                 iter.previous();
357                 break;
358             }
359         }
360 
361         if (opt.getValues() == null && !opt.hasOptionalArg())
362         {
363             throw new MissingArgumentException(opt);
364         }
365     }
366 
367     /**
368      * Process the Option specified by <code>arg</code> using the values
369      * retrieved from the specified iterator <code>iter</code>.
370      *
371      * @param arg The String value representing an Option
372      * @param iter The iterator over the flattened command line arguments.
373      *
374      * @throws ParseException if <code>arg</code> does not represent an Option
375      */
376     protected void processOption(String arg, ListIterator<String> iter) throws ParseException
377     {
378         boolean hasOption = getOptions().hasOption(arg);
379 
380         // if there is no option throw an UnrecognisedOptionException
381         if (!hasOption)
382         {
383             throw new UnrecognizedOptionException("Unrecognized option: " + arg, arg);
384         }
385 
386         // get the option represented by arg
387         Option opt = (Option) getOptions().getOption(arg).clone();
388         
389         // update the required options and groups
390         updateRequiredOptions(opt);
391         
392         // if the option takes an argument value
393         if (opt.hasArg())
394         {
395             processArgs(opt, iter);
396         }
397         
398         // set the option on the command line
399         cmd.addOption(opt);
400     }
401 
402     /**
403      * Removes the option or its group from the list of expected elements.
404      * 
405      * @param opt
406      */
407     private void updateRequiredOptions(Option opt) throws ParseException
408     {
409         // if the option is a required option remove the option from
410         // the requiredOptions list
411         if (opt.isRequired())
412         {
413             getRequiredOptions().remove(opt.getKey());
414         }
415 
416         // if the option is in an OptionGroup make that option the selected
417         // option of the group
418         if (getOptions().getOptionGroup(opt) != null)
419         {
420             OptionGroup group = getOptions().getOptionGroup(opt);
421 
422             if (group.isRequired())
423             {
424                 getRequiredOptions().remove(group);
425             }
426 
427             group.setSelected(opt);
428         }
429     }
430 }