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: */package org.apache.commons.cli;
017:
018: import java.util.ArrayList;
019: import java.util.Arrays;
020: import java.util.Iterator;
021:
022: /**
023: * The class PosixParser provides an implementation of the
024: * {@link Parser#flatten(Options,String[],boolean) flatten} method.
025: *
026: * @author John Keyes (john at integralsource.com)
027: * @see Parser
028: * @version $Revision: 542151 $
029: */
030: public class PosixParser extends Parser {
031:
032: /** holder for flattened tokens */
033: private ArrayList tokens = new ArrayList();
034:
035: /** specifies if bursting should continue */
036: private boolean eatTheRest;
037:
038: /** holder for the current option */
039: private Option currentOption;
040:
041: /** the command line Options */
042: private Options options;
043:
044: /**
045: * <p>Resets the members to their original state i.e. remove
046: * all of <code>tokens</code> entries, set <code>eatTheRest</code>
047: * to false and set <code>currentOption</code> to null.</p>
048: */
049: private void init() {
050: eatTheRest = false;
051: tokens.clear();
052: currentOption = null;
053: }
054:
055: /**
056: * <p>An implementation of {@link Parser}'s abstract
057: * {@link Parser#flatten(Options,String[],boolean) flatten} method.</p>
058: *
059: * <p>The following are the rules used by this flatten method.
060: * <ol>
061: * <li>if <code>stopAtNonOption</code> is <b>true</b> then do not
062: * burst anymore of <code>arguments</code> entries, just add each
063: * successive entry without further processing. Otherwise, ignore
064: * <code>stopAtNonOption</code>.</li>
065: * <li>if the current <code>arguments</code> entry is "<b>--</b>"
066: * just add the entry to the list of processed tokens</li>
067: * <li>if the current <code>arguments</code> entry is "<b>-</b>"
068: * just add the entry to the list of processed tokens</li>
069: * <li>if the current <code>arguments</code> entry is two characters
070: * in length and the first character is "<b>-</b>" then check if this
071: * is a valid {@link Option} id. If it is a valid id, then add the
072: * entry to the list of processed tokens and set the current {@link Option}
073: * member. If it is not a valid id and <code>stopAtNonOption</code>
074: * is true, then the remaining entries are copied to the list of
075: * processed tokens. Otherwise, the current entry is ignored.</li>
076: * <li>if the current <code>arguments</code> entry is more than two
077: * characters in length and the first character is "<b>-</b>" then
078: * we need to burst the entry to determine its constituents. For more
079: * information on the bursting algorithm see
080: * {@link PosixParser#burstToken(String, boolean) burstToken}.</li>
081: * <li>if the current <code>arguments</code> entry is not handled
082: * by any of the previous rules, then the entry is added to the list
083: * of processed tokens.</li>
084: * </ol>
085: * </p>
086: *
087: * @param options The command line {@link Options}
088: * @param arguments The command line arguments to be parsed
089: * @param stopAtNonOption Specifies whether to stop flattening
090: * when an non option is found.
091: * @return The flattened <code>arguments</code> String array.
092: */
093: protected String[] flatten(Options options, String[] arguments,
094: boolean stopAtNonOption) {
095: init();
096: this .options = options;
097:
098: // an iterator for the command line tokens
099: Iterator iter = Arrays.asList(arguments).iterator();
100: String token;
101:
102: // process each command line token
103: while (iter.hasNext()) {
104: // get the next command line token
105: token = (String) iter.next();
106:
107: // handle SPECIAL TOKEN
108: if (token.startsWith("--")) {
109: if (token.indexOf('=') != -1) {
110: tokens.add(token.substring(0, token.indexOf('=')));
111: tokens.add(token.substring(token.indexOf('=') + 1,
112: token.length()));
113: } else {
114: tokens.add(token);
115: }
116: }
117:
118: // single hyphen
119: else if ("-".equals(token)) {
120: processSingleHyphen(token);
121: } else if (token.startsWith("-")) {
122: int tokenLength = token.length();
123:
124: if (tokenLength == 2) {
125: processOptionToken(token, stopAtNonOption);
126: } else if (options.hasOption(token)) {
127: tokens.add(token);
128: }
129: // requires bursting
130: else {
131: burstToken(token, stopAtNonOption);
132: }
133: } else {
134: if (stopAtNonOption) {
135: process(token);
136: } else {
137: tokens.add(token);
138: }
139: }
140:
141: gobble(iter);
142: }
143:
144: return (String[]) tokens.toArray(new String[tokens.size()]);
145: }
146:
147: /**
148: * <p>Adds the remaining tokens to the processed tokens list.</p>
149: *
150: * @param iter An iterator over the remaining tokens
151: */
152: private void gobble(Iterator iter) {
153: if (eatTheRest) {
154: while (iter.hasNext()) {
155: tokens.add(iter.next());
156: }
157: }
158: }
159:
160: /**
161: * <p>If there is a current option and it can have an argument
162: * value then add the token to the processed tokens list and
163: * set the current option to null.</p>
164: * <p>If there is a current option and it can have argument
165: * values then add the token to the processed tokens list.</p>
166: * <p>If there is not a current option add the special token
167: * "<b>--</b>" and the current <code>value</code> to the processed
168: * tokens list. The add all the remaining <code>argument</code>
169: * values to the processed tokens list.</p>
170: *
171: * @param value The current token
172: */
173: private void process(String value) {
174: if ((currentOption != null) && currentOption.hasArg()) {
175: if (currentOption.hasArg()) {
176: tokens.add(value);
177: currentOption = null;
178: } else if (currentOption.hasArgs()) {
179: tokens.add(value);
180: }
181: } else {
182: eatTheRest = true;
183: tokens.add("--");
184: tokens.add(value);
185: }
186: }
187:
188: /**
189: * <p>If it is a hyphen then add the hyphen directly to
190: * the processed tokens list.</p>
191: *
192: * @param hyphen The hyphen token
193: */
194: private void processSingleHyphen(String hyphen) {
195: tokens.add(hyphen);
196: }
197:
198: /**
199: * <p>If an {@link Option} exists for <code>token</code> then
200: * set the current option and add the token to the processed
201: * list.</p>
202: * <p>If an {@link Option} does not exist and <code>stopAtNonOption</code>
203: * is set then ignore the current token and add the remaining tokens
204: * to the processed tokens list directly.</p>
205: *
206: * @param token The current option token
207: * @param stopAtNonOption Specifies whether flattening should halt
208: * at the first non option.
209: */
210: private void processOptionToken(String token,
211: boolean stopAtNonOption) {
212: if (this .options.hasOption(token)) {
213: currentOption = this .options.getOption(token);
214: tokens.add(token);
215: } else if (stopAtNonOption) {
216: eatTheRest = true;
217: }
218: }
219:
220: /**
221: * <p>Breaks <code>token</code> into its constituent parts
222: * using the following algorithm.
223: * <ul>
224: * <li>ignore the first character ("<b>-</b>")</li>
225: * <li>foreach remaining character check if an {@link Option}
226: * exists with that id.</li>
227: * <li>if an {@link Option} does exist then add that character
228: * prepended with "<b>-</b>" to the list of processed tokens.</li>
229: * <li>if the {@link Option} can have an argument value and there
230: * are remaining characters in the token then add the remaining
231: * characters as a token to the list of processed tokens.</li>
232: * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
233: * <code>stopAtNonOption</code> <b>IS</b> set then add the special token
234: * "<b>--</b>" followed by the remaining characters and also
235: * the remaining tokens directly to the processed tokens list.</li>
236: * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
237: * <code>stopAtNonOption</code> <b>IS NOT</b> set then add that
238: * character prepended with "<b>-</b>".</li>
239: * </ul>
240: * </p>
241: *
242: * @param token The current token to be <b>burst</b>
243: * @param stopAtNonOption Specifies whether to stop processing
244: * at the first non-Option encountered.
245: */
246: protected void burstToken(String token, boolean stopAtNonOption) {
247: int tokenLength = token.length();
248:
249: for (int i = 1; i < tokenLength; i++) {
250: String ch = String.valueOf(token.charAt(i));
251: boolean hasOption = options.hasOption(ch);
252:
253: if (hasOption) {
254: tokens.add("-" + ch);
255: currentOption = options.getOption(ch);
256:
257: if (currentOption.hasArg()
258: && (token.length() != (i + 1))) {
259: tokens.add(token.substring(i + 1));
260:
261: break;
262: }
263: } else if (stopAtNonOption) {
264: process(token.substring(i));
265: } else {
266: tokens.add(token);
267: break;
268: }
269: }
270: }
271: }
|