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.Iterator;
23  import java.util.List;
24  
25  /**
26   * The class PosixParser provides an implementation of the
27   * {@link Parser#flatten(Options,String[],boolean) flatten} method.
28   *
29   * @deprecated since 1.3, use the {@link DefaultParser} instead
30   */
31  @Deprecated
32  public class PosixParser extends Parser
33  {
34      /** holder for flattened tokens */
35      private final List<String> tokens = new ArrayList<String>();
36  
37      /** specifies if bursting should continue */
38      private boolean eatTheRest;
39  
40      /** holder for the current option */
41      private Option currentOption;
42  
43      /** the command line Options */
44      private Options options;
45  
46      /**
47       * Resets the members to their original state i.e. remove
48       * all of <code>tokens</code> entries and set <code>eatTheRest</code>
49       * to false.
50       */
51      private void init()
52      {
53          eatTheRest = false;
54          tokens.clear();
55      }
56  
57      /**
58       * <p>An implementation of {@link Parser}'s abstract
59       * {@link Parser#flatten(Options,String[],boolean) flatten} method.</p>
60       *
61       * <p>The following are the rules used by this flatten method.</p>
62       * <ol>
63       *  <li>if <code>stopAtNonOption</code> is <b>true</b> then do not
64       *  burst anymore of <code>arguments</code> entries, just add each
65       *  successive entry without further processing.  Otherwise, ignore
66       *  <code>stopAtNonOption</code>.</li>
67       *  <li>if the current <code>arguments</code> entry is "<b>--</b>"
68       *  just add the entry to the list of processed tokens</li>
69       *  <li>if the current <code>arguments</code> entry is "<b>-</b>"
70       *  just add the entry to the list of processed tokens</li>
71       *  <li>if the current <code>arguments</code> entry is two characters
72       *  in length and the first character is "<b>-</b>" then check if this
73       *  is a valid {@link Option} id.  If it is a valid id, then add the
74       *  entry to the list of processed tokens and set the current {@link Option}
75       *  member.  If it is not a valid id and <code>stopAtNonOption</code>
76       *  is true, then the remaining entries are copied to the list of
77       *  processed tokens.  Otherwise, the current entry is ignored.</li>
78       *  <li>if the current <code>arguments</code> entry is more than two
79       *  characters in length and the first character is "<b>-</b>" then
80       *  we need to burst the entry to determine its constituents.  For more
81       *  information on the bursting algorithm see
82       *  {@link PosixParser#burstToken(String, boolean) burstToken}.</li>
83       *  <li>if the current <code>arguments</code> entry is not handled
84       *  by any of the previous rules, then the entry is added to the list
85       *  of processed tokens.</li>
86       * </ol>
87       *
88       * @param options The command line {@link Options}
89       * @param arguments The command line arguments to be parsed
90       * @param stopAtNonOption Specifies whether to stop flattening
91       * when an non option is found.
92       * @return The flattened <code>arguments</code> String array.
93       */
94      @Override
95      protected String[] flatten(final Options options, final String[] arguments, final boolean stopAtNonOption) throws ParseException
96      {
97          init();
98          this.options = options;
99  
100         // an iterator for the command line tokens
101         final Iterator<String> iter = Arrays.asList(arguments).iterator();
102 
103         // process each command line token
104         while (iter.hasNext())
105         {
106             // get the next command line token
107             final String token = iter.next();
108 
109             // single or double hyphen
110             if ("-".equals(token) || "--".equals(token))
111             {
112                 tokens.add(token);
113             }
114             
115             // handle long option --foo or --foo=bar
116             else if (token.startsWith("--"))
117             {
118                 final int pos = token.indexOf('=');
119                 final String opt = pos == -1 ? token : token.substring(0, pos); // --foo
120                 
121                 final List<String> matchingOpts = options.getMatchingOptions(opt);
122 
123                 if (matchingOpts.isEmpty())
124                 {
125                     processNonOptionToken(token, stopAtNonOption);
126                 }
127                 else if (matchingOpts.size() > 1)
128                 {
129                     throw new AmbiguousOptionException(opt, matchingOpts);
130                 }
131                 else
132                 {
133                     currentOption = options.getOption(matchingOpts.get(0));
134                     
135                     tokens.add("--" + currentOption.getLongOpt());
136                     if (pos != -1)
137                     {
138                         tokens.add(token.substring(pos + 1));
139                     }
140                 }
141             }
142 
143             else if (token.startsWith("-"))
144             {
145                 if (token.length() == 2 || options.hasOption(token))
146                 {
147                     processOptionToken(token, stopAtNonOption);
148                 }
149                 else if (!options.getMatchingOptions(token).isEmpty())
150                 {
151                     final List<String> matchingOpts = options.getMatchingOptions(token);
152                     if (matchingOpts.size() > 1)
153                     {
154                         throw new AmbiguousOptionException(token, matchingOpts);
155                     }
156                     final Option opt = options.getOption(matchingOpts.get(0));
157                     processOptionToken("-" + opt.getLongOpt(), stopAtNonOption);
158                 }
159                 // requires bursting
160                 else
161                 {
162                     burstToken(token, stopAtNonOption);
163                 }
164             }
165             else
166             {
167                 processNonOptionToken(token, stopAtNonOption);
168             }
169 
170             gobble(iter);
171         }
172 
173         return tokens.toArray(new String[tokens.size()]);
174     }
175 
176     /**
177      * Adds the remaining tokens to the processed tokens list.
178      *
179      * @param iter An iterator over the remaining tokens
180      */
181     private void gobble(final Iterator<String> iter)
182     {
183         if (eatTheRest)
184         {
185             while (iter.hasNext())
186             {
187                 tokens.add(iter.next());
188             }
189         }
190     }
191 
192     /**
193      * Add the special token "<b>--</b>" and the current <code>value</code>
194      * to the processed tokens list. Then add all the remaining
195      * <code>argument</code> values to the processed tokens list.
196      *
197      * @param value The current token
198      */
199     private void processNonOptionToken(final String value, final boolean stopAtNonOption)
200     {
201         if (stopAtNonOption && (currentOption == null || !currentOption.hasArg()))
202         {
203             eatTheRest = true;
204             tokens.add("--");
205         }
206 
207         tokens.add(value);
208     }
209 
210     /**
211      * <p>If an {@link Option} exists for <code>token</code> then
212      * add the token to the processed list.</p>
213      *
214      * <p>If an {@link Option} does not exist and <code>stopAtNonOption</code>
215      * is set then add the remaining tokens to the processed tokens list
216      * directly.</p>
217      *
218      * @param token The current option token
219      * @param stopAtNonOption Specifies whether flattening should halt
220      * at the first non option.
221      */
222     private void processOptionToken(final String token, final boolean stopAtNonOption)
223     {
224         if (stopAtNonOption && !options.hasOption(token))
225         {
226             eatTheRest = true;
227         }
228 
229         if (options.hasOption(token))
230         {
231             currentOption = options.getOption(token);
232         }
233 
234         tokens.add(token);
235     }
236 
237     /**
238      * Breaks <code>token</code> into its constituent parts
239      * using the following algorithm.
240      *
241      * <ul>
242      *  <li>ignore the first character ("<b>-</b>")</li>
243      *  <li>for each remaining character check if an {@link Option}
244      *  exists with that id.</li>
245      *  <li>if an {@link Option} does exist then add that character
246      *  prepended with "<b>-</b>" to the list of processed tokens.</li>
247      *  <li>if the {@link Option} can have an argument value and there
248      *  are remaining characters in the token then add the remaining
249      *  characters as a token to the list of processed tokens.</li>
250      *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
251      *  <code>stopAtNonOption</code> <b>IS</b> set then add the special token
252      *  "<b>--</b>" followed by the remaining characters and also
253      *  the remaining tokens directly to the processed tokens list.</li>
254      *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
255      *  <code>stopAtNonOption</code> <b>IS NOT</b> set then add that
256      *  character prepended with "<b>-</b>".</li>
257      * </ul>
258      *
259      * @param token The current token to be <b>burst</b>
260      * @param stopAtNonOption Specifies whether to stop processing
261      * at the first non-Option encountered.
262      */
263     protected void burstToken(final String token, final boolean stopAtNonOption)
264     {
265         for (int i = 1; i < token.length(); i++)
266         {
267             final String ch = String.valueOf(token.charAt(i));
268 
269             if (options.hasOption(ch))
270             {
271                 tokens.add("-" + ch);
272                 currentOption = options.getOption(ch);
273 
274                 if (currentOption.hasArg() && token.length() != i + 1)
275                 {
276                     tokens.add(token.substring(i + 1));
277 
278                     break;
279                 }
280             }
281             else if (stopAtNonOption)
282             {
283                 processNonOptionToken(token.substring(i), true);
284                 break;
285             }
286             else
287             {
288                 tokens.add(token);
289                 break;
290             }
291         }
292     }
293 }