001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.cli;
019
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.Iterator;
023import java.util.List;
024
025/**
026 * The class PosixParser provides an implementation of the
027 * {@link Parser#flatten(Options,String[],boolean) flatten} method.
028 *
029 * @version $Id: PosixParser.java 1677406 2015-05-03 14:27:31Z britter $
030 * @deprecated since 1.3, use the {@link DefaultParser} instead
031 */
032@Deprecated
033public class PosixParser extends Parser
034{
035    /** holder for flattened tokens */
036    private final List<String> tokens = new ArrayList<String>();
037
038    /** specifies if bursting should continue */
039    private boolean eatTheRest;
040
041    /** holder for the current option */
042    private Option currentOption;
043
044    /** the command line Options */
045    private Options options;
046
047    /**
048     * Resets the members to their original state i.e. remove
049     * all of <code>tokens</code> entries and set <code>eatTheRest</code>
050     * to false.
051     */
052    private void init()
053    {
054        eatTheRest = false;
055        tokens.clear();
056    }
057
058    /**
059     * <p>An implementation of {@link Parser}'s abstract
060     * {@link Parser#flatten(Options,String[],boolean) flatten} method.</p>
061     *
062     * <p>The following are the rules used by this flatten method.</p>
063     * <ol>
064     *  <li>if <code>stopAtNonOption</code> is <b>true</b> then do not
065     *  burst anymore of <code>arguments</code> entries, just add each
066     *  successive entry without further processing.  Otherwise, ignore
067     *  <code>stopAtNonOption</code>.</li>
068     *  <li>if the current <code>arguments</code> entry is "<b>--</b>"
069     *  just add the entry to the list of processed tokens</li>
070     *  <li>if the current <code>arguments</code> entry is "<b>-</b>"
071     *  just add the entry to the list of processed tokens</li>
072     *  <li>if the current <code>arguments</code> entry is two characters
073     *  in length and the first character is "<b>-</b>" then check if this
074     *  is a valid {@link Option} id.  If it is a valid id, then add the
075     *  entry to the list of processed tokens and set the current {@link Option}
076     *  member.  If it is not a valid id and <code>stopAtNonOption</code>
077     *  is true, then the remaining entries are copied to the list of
078     *  processed tokens.  Otherwise, the current entry is ignored.</li>
079     *  <li>if the current <code>arguments</code> entry is more than two
080     *  characters in length and the first character is "<b>-</b>" then
081     *  we need to burst the entry to determine its constituents.  For more
082     *  information on the bursting algorithm see
083     *  {@link PosixParser#burstToken(String, boolean) burstToken}.</li>
084     *  <li>if the current <code>arguments</code> entry is not handled
085     *  by any of the previous rules, then the entry is added to the list
086     *  of processed tokens.</li>
087     * </ol>
088     *
089     * @param options The command line {@link Options}
090     * @param arguments The command line arguments to be parsed
091     * @param stopAtNonOption Specifies whether to stop flattening
092     * when an non option is found.
093     * @return The flattened <code>arguments</code> String array.
094     */
095    @Override
096    protected String[] flatten(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException
097    {
098        init();
099        this.options = options;
100
101        // an iterator for the command line tokens
102        Iterator<String> iter = Arrays.asList(arguments).iterator();
103
104        // process each command line token
105        while (iter.hasNext())
106        {
107            // get the next command line token
108            String token = iter.next();
109
110            // single or double hyphen
111            if ("-".equals(token) || "--".equals(token))
112            {
113                tokens.add(token);
114            }
115            
116            // handle long option --foo or --foo=bar
117            else if (token.startsWith("--"))
118            {
119                int pos = token.indexOf('=');
120                String opt = pos == -1 ? token : token.substring(0, pos); // --foo
121                
122                List<String> matchingOpts = options.getMatchingOptions(opt);
123
124                if (matchingOpts.isEmpty())
125                {
126                    processNonOptionToken(token, stopAtNonOption);
127                }
128                else if (matchingOpts.size() > 1)
129                {
130                    throw new AmbiguousOptionException(opt, matchingOpts);
131                }
132                else
133                {
134                    currentOption = options.getOption(matchingOpts.get(0));
135                    
136                    tokens.add("--" + currentOption.getLongOpt());
137                    if (pos != -1)
138                    {
139                        tokens.add(token.substring(pos + 1));
140                    }
141                }
142            }
143
144            else if (token.startsWith("-"))
145            {
146                if (token.length() == 2 || options.hasOption(token))
147                {
148                    processOptionToken(token, stopAtNonOption);
149                }
150                else if (!options.getMatchingOptions(token).isEmpty())
151                {
152                    List<String> matchingOpts = options.getMatchingOptions(token);
153                    if (matchingOpts.size() > 1)
154                    {
155                        throw new AmbiguousOptionException(token, matchingOpts);
156                    }
157                    else
158                    {
159                        Option opt = options.getOption(matchingOpts.get(0));
160                        processOptionToken("-" + opt.getLongOpt(), stopAtNonOption);
161                    }
162                }
163                // requires bursting
164                else
165                {
166                    burstToken(token, stopAtNonOption);
167                }
168            }
169            else
170            {
171                processNonOptionToken(token, stopAtNonOption);
172            }
173
174            gobble(iter);
175        }
176
177        return tokens.toArray(new String[tokens.size()]);
178    }
179
180    /**
181     * Adds the remaining tokens to the processed tokens list.
182     *
183     * @param iter An iterator over the remaining tokens
184     */
185    private void gobble(Iterator<String> iter)
186    {
187        if (eatTheRest)
188        {
189            while (iter.hasNext())
190            {
191                tokens.add(iter.next());
192            }
193        }
194    }
195
196    /**
197     * Add the special token "<b>--</b>" and the current <code>value</code>
198     * to the processed tokens list. Then add all the remaining
199     * <code>argument</code> values to the processed tokens list.
200     *
201     * @param value The current token
202     */
203    private void processNonOptionToken(String value, boolean stopAtNonOption)
204    {
205        if (stopAtNonOption && (currentOption == null || !currentOption.hasArg()))
206        {
207            eatTheRest = true;
208            tokens.add("--");
209        }
210
211        tokens.add(value);
212    }
213
214    /**
215     * <p>If an {@link Option} exists for <code>token</code> then
216     * add the token to the processed list.</p>
217     *
218     * <p>If an {@link Option} does not exist and <code>stopAtNonOption</code>
219     * is set then add the remaining tokens to the processed tokens list
220     * directly.</p>
221     *
222     * @param token The current option token
223     * @param stopAtNonOption Specifies whether flattening should halt
224     * at the first non option.
225     */
226    private void processOptionToken(String token, boolean stopAtNonOption)
227    {
228        if (stopAtNonOption && !options.hasOption(token))
229        {
230            eatTheRest = true;
231        }
232
233        if (options.hasOption(token))
234        {
235            currentOption = options.getOption(token);
236        }
237
238        tokens.add(token);
239    }
240
241    /**
242     * Breaks <code>token</code> into its constituent parts
243     * using the following algorithm.
244     *
245     * <ul>
246     *  <li>ignore the first character ("<b>-</b>")</li>
247     *  <li>foreach remaining character check if an {@link Option}
248     *  exists with that id.</li>
249     *  <li>if an {@link Option} does exist then add that character
250     *  prepended with "<b>-</b>" to the list of processed tokens.</li>
251     *  <li>if the {@link Option} can have an argument value and there
252     *  are remaining characters in the token then add the remaining
253     *  characters as a token to the list of processed tokens.</li>
254     *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
255     *  <code>stopAtNonOption</code> <b>IS</b> set then add the special token
256     *  "<b>--</b>" followed by the remaining characters and also
257     *  the remaining tokens directly to the processed tokens list.</li>
258     *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
259     *  <code>stopAtNonOption</code> <b>IS NOT</b> set then add that
260     *  character prepended with "<b>-</b>".</li>
261     * </ul>
262     *
263     * @param token The current token to be <b>burst</b>
264     * @param stopAtNonOption Specifies whether to stop processing
265     * at the first non-Option encountered.
266     */
267    protected void burstToken(String token, boolean stopAtNonOption)
268    {
269        for (int i = 1; i < token.length(); i++)
270        {
271            String ch = String.valueOf(token.charAt(i));
272
273            if (options.hasOption(ch))
274            {
275                tokens.add("-" + ch);
276                currentOption = options.getOption(ch);
277
278                if (currentOption.hasArg() && token.length() != i + 1)
279                {
280                    tokens.add(token.substring(i + 1));
281
282                    break;
283                }
284            }
285            else if (stopAtNonOption)
286            {
287                processNonOptionToken(token.substring(i), true);
288                break;
289            }
290            else
291            {
292                tokens.add(token);
293                break;
294            }
295        }
296    }
297}