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.Arrays;
019: import java.util.Enumeration;
020: import java.util.Iterator;
021: import java.util.List;
022: import java.util.ListIterator;
023: import java.util.Properties;
024:
025: /**
026: * <p><code>Parser</code> creates {@link CommandLine}s.</p>
027: *
028: * @author John Keyes (john at integralsource.com)
029: * @see Parser
030: * @version $Revision: 551815 $
031: */
032: public abstract class Parser implements CommandLineParser {
033:
034: /** commandline instance */
035: private CommandLine cmd;
036:
037: /** current Options */
038: private Options options;
039:
040: /** list of required options strings */
041: private List requiredOptions;
042:
043: /**
044: * <p>Subclasses must implement this method to reduce
045: * the <code>arguments</code> that have been passed to the parse
046: * method.</p>
047: *
048: * @param opts The Options to parse the arguments by.
049: * @param arguments The arguments that have to be flattened.
050: * @param stopAtNonOption specifies whether to stop
051: * flattening when a non option has been encountered
052: * @return a String array of the flattened arguments
053: */
054: protected abstract String[] flatten(Options opts,
055: String[] arguments, boolean stopAtNonOption);
056:
057: /**
058: * <p>Parses the specified <code>arguments</code>
059: * based on the specifed {@link Options}.</p>
060: *
061: * @param options the <code>Options</code>
062: * @param arguments the <code>arguments</code>
063: * @return the <code>CommandLine</code>
064: * @throws ParseException if an error occurs when parsing the
065: * arguments.
066: */
067: public CommandLine parse(Options options, String[] arguments)
068: throws ParseException {
069: return parse(options, arguments, null, false);
070: }
071:
072: /**
073: * Parse the arguments according to the specified options and
074: * properties.
075: *
076: * @param options the specified Options
077: * @param arguments the command line arguments
078: * @param properties command line option name-value pairs
079: * @return the list of atomic option and value tokens
080: *
081: * @throws ParseException if there are any problems encountered
082: * while parsing the command line tokens.
083: */
084: public CommandLine parse(Options options, String[] arguments,
085: Properties properties) throws ParseException {
086: return parse(options, arguments, properties, false);
087: }
088:
089: /**
090: * <p>Parses the specified <code>arguments</code>
091: * based on the specifed {@link Options}.</p>
092: *
093: * @param options the <code>Options</code>
094: * @param arguments the <code>arguments</code>
095: * @param stopAtNonOption specifies whether to stop
096: * interpreting the arguments when a non option has
097: * been encountered and to add them to the CommandLines
098: * args list.
099: *
100: * @return the <code>CommandLine</code>
101: * @throws ParseException if an error occurs when parsing the
102: * arguments.
103: */
104: public CommandLine parse(Options options, String[] arguments,
105: boolean stopAtNonOption) throws ParseException {
106: return parse(options, arguments, null, stopAtNonOption);
107: }
108:
109: /**
110: * Parse the arguments according to the specified options and
111: * properties.
112: *
113: * @param options the specified Options
114: * @param arguments the command line arguments
115: * @param properties command line option name-value pairs
116: * @param stopAtNonOption stop parsing the arguments when the first
117: * non option is encountered.
118: *
119: * @return the list of atomic option and value tokens
120: *
121: * @throws ParseException if there are any problems encountered
122: * while parsing the command line tokens.
123: */
124: public CommandLine parse(Options options, String[] arguments,
125: Properties properties, boolean stopAtNonOption)
126: throws ParseException {
127: // initialise members
128: this .options = options;
129:
130: // clear out the data in options in case it's been used before (CLI-71)
131: for (Iterator it = options.helpOptions().iterator(); it
132: .hasNext();) {
133: Option opt = (Option) it.next();
134: opt.clearValues();
135: }
136:
137: requiredOptions = options.getRequiredOptions();
138: cmd = new CommandLine();
139:
140: boolean eatTheRest = false;
141:
142: if (arguments == null) {
143: arguments = new String[0];
144: }
145:
146: List tokenList = Arrays.asList(flatten(this .options, arguments,
147: stopAtNonOption));
148:
149: ListIterator iterator = tokenList.listIterator();
150:
151: // process each flattened token
152: while (iterator.hasNext()) {
153: String t = (String) iterator.next();
154:
155: // the value is the double-dash
156: if ("--".equals(t)) {
157: eatTheRest = true;
158: }
159:
160: // the value is a single dash
161: else if ("-".equals(t)) {
162: if (stopAtNonOption) {
163: eatTheRest = true;
164: } else {
165: cmd.addArg(t);
166: }
167: }
168:
169: // the value is an option
170: else if (t.startsWith("-")) {
171: if (stopAtNonOption && !options.hasOption(t)) {
172: eatTheRest = true;
173: cmd.addArg(t);
174: } else {
175: processOption(t, iterator);
176: }
177: }
178:
179: // the value is an argument
180: else {
181: cmd.addArg(t);
182:
183: if (stopAtNonOption) {
184: eatTheRest = true;
185: }
186: }
187:
188: // eat the remaining tokens
189: if (eatTheRest) {
190: while (iterator.hasNext()) {
191: String str = (String) iterator.next();
192:
193: // ensure only one double-dash is added
194: if (!"--".equals(str)) {
195: cmd.addArg(str);
196: }
197: }
198: }
199: }
200:
201: processProperties(properties);
202: checkRequiredOptions();
203:
204: return cmd;
205: }
206:
207: /**
208: * <p>Sets the values of Options using the values in
209: * <code>properties</code>.</p>
210: *
211: * @param properties The value properties to be processed.
212: */
213: private void processProperties(Properties properties) {
214: if (properties == null) {
215: return;
216: }
217:
218: for (Enumeration e = properties.propertyNames(); e
219: .hasMoreElements();) {
220: String option = e.nextElement().toString();
221:
222: if (!cmd.hasOption(option)) {
223: Option opt = options.getOption(option);
224:
225: // get the value from the properties instance
226: String value = properties.getProperty(option);
227:
228: if (opt.hasArg()) {
229: if ((opt.getValues() == null)
230: || (opt.getValues().length == 0)) {
231: try {
232: opt.addValueForProcessing(value);
233: } catch (RuntimeException exp) {
234: // if we cannot add the value don't worry about it
235: }
236: }
237: } else if (!("yes".equalsIgnoreCase(value)
238: || "true".equalsIgnoreCase(value) || "1"
239: .equalsIgnoreCase(value))) {
240: // if the value is not yes, true or 1 then don't add the
241: // option to the CommandLine
242: break;
243: }
244:
245: cmd.addOption(opt);
246: }
247: }
248: }
249:
250: /**
251: * <p>Throws a {@link MissingOptionException} if all of the
252: * required options are no present.</p>
253: *
254: * @throws MissingOptionException if any of the required Options
255: * are not present.
256: */
257: private void checkRequiredOptions() throws MissingOptionException {
258: // if there are required options that have not been
259: // processsed
260: if (requiredOptions.size() > 0) {
261: Iterator iter = requiredOptions.iterator();
262: StringBuffer buff = new StringBuffer(
263: "Missing required option");
264: buff.append(requiredOptions.size() == 1 ? "" : "s");
265: buff.append(": ");
266:
267: // loop through the required options
268: while (iter.hasNext()) {
269: buff.append(iter.next());
270: }
271:
272: throw new MissingOptionException(buff.toString());
273: }
274: }
275:
276: /**
277: * <p>Process the argument values for the specified Option
278: * <code>opt</code> using the values retrieved from the
279: * specified iterator <code>iter</code>.
280: *
281: * @param opt The current Option
282: * @param iter The iterator over the flattened command line
283: * Options.
284: *
285: * @throws ParseException if an argument value is required
286: * and it is has not been found.
287: */
288: public void processArgs(Option opt, ListIterator iter)
289: throws ParseException {
290: // loop until an option is found
291: while (iter.hasNext()) {
292: String str = (String) iter.next();
293:
294: // found an Option, not an argument
295: if (options.hasOption(str) && str.startsWith("-")) {
296: iter.previous();
297: break;
298: }
299:
300: // found a value
301: try {
302: opt.addValueForProcessing(Util
303: .stripLeadingAndTrailingQuotes(str));
304: } catch (RuntimeException exp) {
305: iter.previous();
306: break;
307: }
308: }
309:
310: if ((opt.getValues() == null) && !opt.hasOptionalArg()) {
311: throw new MissingArgumentException(
312: "Missing argument for option:" + opt.getKey());
313: }
314: }
315:
316: /**
317: * <p>Process the Option specified by <code>arg</code>
318: * using the values retrieved from the specfied iterator
319: * <code>iter</code>.
320: *
321: * @param arg The String value representing an Option
322: * @param iter The iterator over the flattened command
323: * line arguments.
324: *
325: * @throws ParseException if <code>arg</code> does not
326: * represent an Option
327: */
328: private void processOption(String arg, ListIterator iter)
329: throws ParseException {
330: boolean hasOption = options.hasOption(arg);
331:
332: // if there is no option throw an UnrecognisedOptionException
333: if (!hasOption) {
334: throw new UnrecognizedOptionException(
335: "Unrecognized option: " + arg);
336: }
337:
338: // get the option represented by arg
339: final Option opt = options.getOption(arg);
340:
341: // if the option is a required option remove the option from
342: // the requiredOptions list
343: if (opt.isRequired()) {
344: requiredOptions.remove(opt.getKey());
345: }
346:
347: // if the option is in an OptionGroup make that option the selected
348: // option of the group
349: if (options.getOptionGroup(opt) != null) {
350: OptionGroup group = options.getOptionGroup(opt);
351:
352: if (group.isRequired()) {
353: requiredOptions.remove(group);
354: }
355:
356: group.setSelected(opt);
357: }
358:
359: // if the option takes an argument value
360: if (opt.hasArg()) {
361: processArgs(opt, iter);
362: }
363:
364: // set the option on the command line
365: cmd.addOption(opt);
366: }
367: }
|