| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.cli; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| |
| /** |
| * The class PosixParser provides an implementation of the |
| * {@link Parser#flatten(Options,String[],boolean) flatten} method. |
| * |
| * @author John Keyes (john at integralsource.com) |
| * @see Parser |
| * @version $Revision$ |
| */ |
| public class PosixParser extends Parser { |
| |
| /** holder for flattened tokens */ |
| private ArrayList tokens = new ArrayList(); |
| |
| /** specifies if bursting should continue */ |
| private boolean eatTheRest; |
| |
| /** holder for the current option */ |
| private Option currentOption; |
| |
| /** the command line Options */ |
| private Options options; |
| |
| /** |
| * <p>Resets the members to their original state i.e. remove |
| * all of <code>tokens</code> entries, set <code>eatTheRest</code> |
| * to false and set <code>currentOption</code> to null.</p> |
| */ |
| private void init() |
| { |
| eatTheRest = false; |
| tokens.clear(); |
| currentOption = null; |
| } |
| |
| /** |
| * <p>An implementation of {@link Parser}'s abstract |
| * {@link Parser#flatten(Options,String[],boolean) flatten} method.</p> |
| * |
| * <p>The following are the rules used by this flatten method. |
| * <ol> |
| * <li>if <code>stopAtNonOption</code> is <b>true</b> then do not |
| * burst anymore of <code>arguments</code> entries, just add each |
| * successive entry without further processing. Otherwise, ignore |
| * <code>stopAtNonOption</code>.</li> |
| * <li>if the current <code>arguments</code> entry is "<b>--</b>" |
| * just add the entry to the list of processed tokens</li> |
| * <li>if the current <code>arguments</code> entry is "<b>-</b>" |
| * just add the entry to the list of processed tokens</li> |
| * <li>if the current <code>arguments</code> entry is two characters |
| * in length and the first character is "<b>-</b>" then check if this |
| * is a valid {@link Option} id. If it is a valid id, then add the |
| * entry to the list of processed tokens and set the current {@link Option} |
| * member. If it is not a valid id and <code>stopAtNonOption</code> |
| * is true, then the remaining entries are copied to the list of |
| * processed tokens. Otherwise, the current entry is ignored.</li> |
| * <li>if the current <code>arguments</code> entry is more than two |
| * characters in length and the first character is "<b>-</b>" then |
| * we need to burst the entry to determine its constituents. For more |
| * information on the bursting algorithm see |
| * {@link PosixParser#burstToken(String, boolean) burstToken}.</li> |
| * <li>if the current <code>arguments</code> entry is not handled |
| * by any of the previous rules, then the entry is added to the list |
| * of processed tokens.</li> |
| * </ol> |
| * </p> |
| * |
| * @param options The command line {@link Options} |
| * @param arguments The command line arguments to be parsed |
| * @param stopAtNonOption Specifies whether to stop flattening |
| * when an non option is found. |
| * @return The flattened <code>arguments</code> String array. |
| */ |
| protected String[] flatten(Options options, String[] arguments, |
| boolean stopAtNonOption) |
| { |
| init(); |
| this.options = options; |
| |
| // an iterator for the command line tokens |
| Iterator iter = Arrays.asList(arguments).iterator(); |
| String token; |
| |
| // process each command line token |
| while (iter.hasNext()) |
| { |
| // get the next command line token |
| token = (String) iter.next(); |
| |
| // handle SPECIAL TOKEN |
| if (token.startsWith("--")) |
| { |
| if (token.indexOf('=') != -1) |
| { |
| tokens.add(token.substring(0, token.indexOf('='))); |
| tokens.add(token.substring(token.indexOf('=') + 1, |
| token.length())); |
| } |
| else |
| { |
| tokens.add(token); |
| } |
| } |
| |
| // single hyphen |
| else if ("-".equals(token)) |
| { |
| processSingleHyphen(token); |
| } |
| else if (token.startsWith("-")) |
| { |
| int tokenLength = token.length(); |
| |
| if (tokenLength == 2) |
| { |
| processOptionToken(token, stopAtNonOption); |
| } |
| else if (options.hasOption(token)) { |
| tokens.add(token); |
| } |
| // requires bursting |
| else |
| { |
| burstToken(token, stopAtNonOption); |
| } |
| } |
| else |
| { |
| if (stopAtNonOption) |
| { |
| process(token); |
| } |
| else |
| { |
| tokens.add(token); |
| } |
| } |
| |
| gobble(iter); |
| } |
| |
| return (String[]) tokens.toArray(new String[tokens.size()]); |
| } |
| |
| /** |
| * <p>Adds the remaining tokens to the processed tokens list.</p> |
| * |
| * @param iter An iterator over the remaining tokens |
| */ |
| private void gobble(Iterator iter) |
| { |
| if (eatTheRest) |
| { |
| while (iter.hasNext()) |
| { |
| tokens.add(iter.next()); |
| } |
| } |
| } |
| |
| /** |
| * <p>If there is a current option and it can have an argument |
| * value then add the token to the processed tokens list and |
| * set the current option to null.</p> |
| * <p>If there is a current option and it can have argument |
| * values then add the token to the processed tokens list.</p> |
| * <p>If there is not a current option add the special token |
| * "<b>--</b>" and the current <code>value</code> to the processed |
| * tokens list. The add all the remaining <code>argument</code> |
| * values to the processed tokens list.</p> |
| * |
| * @param value The current token |
| */ |
| private void process(String value) |
| { |
| if ((currentOption != null) && currentOption.hasArg()) |
| { |
| if (currentOption.hasArg()) |
| { |
| tokens.add(value); |
| currentOption = null; |
| } |
| else if (currentOption.hasArgs()) |
| { |
| tokens.add(value); |
| } |
| } |
| else |
| { |
| eatTheRest = true; |
| tokens.add("--"); |
| tokens.add(value); |
| } |
| } |
| |
| /** |
| * <p>If it is a hyphen then add the hyphen directly to |
| * the processed tokens list.</p> |
| * |
| * @param hyphen The hyphen token |
| */ |
| private void processSingleHyphen(String hyphen) |
| { |
| tokens.add(hyphen); |
| } |
| |
| /** |
| * <p>If an {@link Option} exists for <code>token</code> then |
| * set the current option and add the token to the processed |
| * list.</p> |
| * <p>If an {@link Option} does not exist and <code>stopAtNonOption</code> |
| * is set then ignore the current token and add the remaining tokens |
| * to the processed tokens list directly.</p> |
| * |
| * @param token The current option token |
| * @param stopAtNonOption Specifies whether flattening should halt |
| * at the first non option. |
| */ |
| private void processOptionToken(String token, boolean stopAtNonOption) |
| { |
| if (this.options.hasOption(token)) |
| { |
| currentOption = this.options.getOption(token); |
| tokens.add(token); |
| } |
| else if (stopAtNonOption) |
| { |
| eatTheRest = true; |
| } |
| } |
| |
| /** |
| * <p>Breaks <code>token</code> into its constituent parts |
| * using the following algorithm. |
| * <ul> |
| * <li>ignore the first character ("<b>-</b>")</li> |
| * <li>foreach remaining character check if an {@link Option} |
| * exists with that id.</li> |
| * <li>if an {@link Option} does exist then add that character |
| * prepended with "<b>-</b>" to the list of processed tokens.</li> |
| * <li>if the {@link Option} can have an argument value and there |
| * are remaining characters in the token then add the remaining |
| * characters as a token to the list of processed tokens.</li> |
| * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> |
| * <code>stopAtNonOption</code> <b>IS</b> set then add the special token |
| * "<b>--</b>" followed by the remaining characters and also |
| * the remaining tokens directly to the processed tokens list.</li> |
| * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> |
| * <code>stopAtNonOption</code> <b>IS NOT</b> set then add that |
| * character prepended with "<b>-</b>".</li> |
| * </ul> |
| * </p> |
| * |
| * @param token The current token to be <b>burst</b> |
| * @param stopAtNonOption Specifies whether to stop processing |
| * at the first non-Option encountered. |
| */ |
| protected void burstToken(String token, boolean stopAtNonOption) |
| { |
| int tokenLength = token.length(); |
| |
| for (int i = 1; i < tokenLength; i++) |
| { |
| String ch = String.valueOf(token.charAt(i)); |
| boolean hasOption = options.hasOption(ch); |
| |
| if (hasOption) |
| { |
| tokens.add("-" + ch); |
| currentOption = options.getOption(ch); |
| |
| if (currentOption.hasArg() && (token.length() != (i + 1))) |
| { |
| tokens.add(token.substring(i + 1)); |
| |
| break; |
| } |
| } |
| else if (stopAtNonOption) |
| { |
| process(token.substring(i)); |
| } |
| else |
| { |
| tokens.add(token); |
| break; |
| } |
| } |
| } |
| } |