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.ArrayList;
20  import java.util.Arrays;
21  import java.util.Iterator;
22  
23  /***
24   * The class PosixParser provides an implementation of the 
25   * {@link Parser#flatten(Options,String[],boolean) flatten} method.
26   *
27   * @author John Keyes (john at integralsource.com)
28   * @see Parser
29   * @version $Revision: 542151 $
30   */
31  public class PosixParser extends Parser {
32  
33      /*** holder for flattened tokens */
34      private ArrayList tokens = new ArrayList();
35  
36      /*** specifies if bursting should continue */
37      private boolean eatTheRest;
38  
39      /*** holder for the current option */
40      private Option currentOption;
41  
42      /*** the command line Options */
43      private Options options;
44  
45      /***
46       * <p>Resets the members to their original state i.e. remove
47       * all of <code>tokens</code> entries, set <code>eatTheRest</code>
48       * to false and set <code>currentOption</code> to null.</p>
49       */
50      private void init()
51      {
52          eatTheRest = false;
53          tokens.clear();
54          currentOption = null;
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.
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       * </p>
88       *
89       * @param options The command line {@link Options}
90       * @param arguments The command line arguments to be parsed
91       * @param stopAtNonOption Specifies whether to stop flattening
92       * when an non option is found.
93       * @return The flattened <code>arguments</code> String array.
94       */
95      protected String[] flatten(Options options, String[] arguments, 
96                                 boolean stopAtNonOption)
97      {
98          init();
99          this.options = options;
100 
101         // an iterator for the command line tokens
102         Iterator iter = Arrays.asList(arguments).iterator();
103         String token;
104 
105         // process each command line token
106         while (iter.hasNext())
107         {
108             // get the next command line token
109             token = (String) iter.next();
110 
111             // handle SPECIAL TOKEN
112             if (token.startsWith("--"))
113             {
114                 if (token.indexOf('=') != -1)
115                 {
116                     tokens.add(token.substring(0, token.indexOf('=')));
117                     tokens.add(token.substring(token.indexOf('=') + 1, 
118                                                token.length()));
119                 }
120                 else
121                 {
122                     tokens.add(token);
123                 }
124             }
125 
126             // single hyphen
127             else if ("-".equals(token))
128             {
129                 processSingleHyphen(token);
130             }
131             else if (token.startsWith("-"))
132             {
133                 int tokenLength = token.length();
134 
135                 if (tokenLength == 2)
136                 {
137                     processOptionToken(token, stopAtNonOption);
138                 }
139                 else if (options.hasOption(token)) {
140                 	tokens.add(token);
141                 }
142                 // requires bursting
143                 else
144                 {
145                     burstToken(token, stopAtNonOption);
146                 }
147             }
148             else
149             {
150                 if (stopAtNonOption)
151                 {
152                     process(token);
153                 }
154                 else
155                 {
156                     tokens.add(token);
157                 }
158             }
159 
160             gobble(iter);
161         }
162 
163         return (String[]) tokens.toArray(new String[tokens.size()]);
164     }
165 
166     /***
167      * <p>Adds the remaining tokens to the processed tokens list.</p>
168      *
169      * @param iter An iterator over the remaining tokens
170      */
171     private void gobble(Iterator iter)
172     {
173         if (eatTheRest)
174         {
175             while (iter.hasNext())
176             {
177                 tokens.add(iter.next());
178             }
179         }
180     }
181 
182     /***
183      * <p>If there is a current option and it can have an argument
184      * value then add the token to the processed tokens list and 
185      * set the current option to null.</p>
186      * <p>If there is a current option and it can have argument
187      * values then add the token to the processed tokens list.</p>
188      * <p>If there is not a current option add the special token
189      * "<b>--</b>" and the current <code>value</code> to the processed
190      * tokens list.  The add all the remaining <code>argument</code>
191      * values to the processed tokens list.</p>
192      *
193      * @param value The current token
194      */
195     private void process(String value)
196     {
197         if ((currentOption != null) && currentOption.hasArg())
198         {
199             if (currentOption.hasArg())
200             {
201                 tokens.add(value);
202                 currentOption = null;
203             }
204             else if (currentOption.hasArgs())
205             {
206                 tokens.add(value);
207             }
208         }
209         else
210         {
211             eatTheRest = true;
212             tokens.add("--");
213             tokens.add(value);
214         }
215     }
216 
217     /***
218      * <p>If it is a hyphen then add the hyphen directly to
219      * the processed tokens list.</p>
220      *
221      * @param hyphen The hyphen token
222      */
223     private void processSingleHyphen(String hyphen)
224     {
225         tokens.add(hyphen);
226     }
227 
228     /***
229      * <p>If an {@link Option} exists for <code>token</code> then
230      * set the current option and add the token to the processed 
231      * list.</p>
232      * <p>If an {@link Option} does not exist and <code>stopAtNonOption</code>
233      * is set then ignore the current token and add the remaining tokens
234      * to the processed tokens list directly.</p>
235      *
236      * @param token The current option token
237      * @param stopAtNonOption Specifies whether flattening should halt
238      * at the first non option.
239      */
240     private void processOptionToken(String token, boolean stopAtNonOption)
241     {
242         if (this.options.hasOption(token))
243         {
244             currentOption = this.options.getOption(token);
245             tokens.add(token);
246         }
247         else if (stopAtNonOption)
248         {
249             eatTheRest = true;
250         }
251     }
252 
253     /***
254      * <p>Breaks <code>token</code> into its constituent parts
255      * using the following algorithm.
256      * <ul>
257      *  <li>ignore the first character ("<b>-</b>")</li>
258      *  <li>foreach remaining character check if an {@link Option}
259      *  exists with that id.</li>
260      *  <li>if an {@link Option} does exist then add that character
261      *  prepended with "<b>-</b>" to the list of processed tokens.</li>
262      *  <li>if the {@link Option} can have an argument value and there 
263      *  are remaining characters in the token then add the remaining 
264      *  characters as a token to the list of processed tokens.</li>
265      *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> 
266      *  <code>stopAtNonOption</code> <b>IS</b> set then add the special token
267      *  "<b>--</b>" followed by the remaining characters and also 
268      *  the remaining tokens directly to the processed tokens list.</li>
269      *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
270      *  <code>stopAtNonOption</code> <b>IS NOT</b> set then add that
271      *  character prepended with "<b>-</b>".</li>
272      * </ul>
273      * </p>
274      *
275      * @param token The current token to be <b>burst</b>
276      * @param stopAtNonOption Specifies whether to stop processing
277      * at the first non-Option encountered.
278      */
279     protected void burstToken(String token, boolean stopAtNonOption)
280     {
281         int tokenLength = token.length();
282 
283         for (int i = 1; i < tokenLength; i++)
284         {
285             String ch = String.valueOf(token.charAt(i));
286             boolean hasOption = options.hasOption(ch);
287 
288             if (hasOption)
289             {
290                 tokens.add("-" + ch);
291                 currentOption = options.getOption(ch);
292 
293                 if (currentOption.hasArg() && (token.length() != (i + 1)))
294                 {
295                     tokens.add(token.substring(i + 1));
296 
297                     break;
298                 }
299             }
300             else if (stopAtNonOption)
301             {
302                 process(token.substring(i));
303             }
304             else
305             {
306                 tokens.add(token);
307                 break;
308             }
309         }
310     }
311 }