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
102 Iterator iter = Arrays.asList(arguments).iterator();
103 String token;
104
105
106 while (iter.hasNext())
107 {
108
109 token = (String) iter.next();
110
111
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
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
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 }