/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed 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 com.alibaba.antx.util.cli;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* <p>
* <code>Parser</code> creates {@link CommandLine}s.
* </p>
*
* @author John Keyes (john at integralsource.com)
* @see Parser
*/
public abstract class Parser implements CommandLineParser {
/** commandline instance */
private CommandLine cmd;
/** current Options */
private Options options;
/** list of required options strings */
private List requiredOptions;
/**
* <p>
* Subclasses must implement this method to reduce the
* <code>arguments</code> that have been passed to the parse method.
* </p>
*
* @param opts The Options to parse the arguments by.
* @param args The arguments that have to be flattened.
* @param stopAtNonOption specifies whether to stop flattening when a non
* option has been encountered
* @return a String array of the flattened arguments
*/
protected abstract String[] flatten(Options opts, String[] arguments, boolean stopAtNonOption);
/**
* <p>
* Parses the specified <code>arguments</code> based on the specifed
* {@link Options}.
* </p>
*
* @param options the <code>Options</code>
* @param arguments the <code>arguments</code>
* @return the <code>CommandLine</code>
* @throws ParseException if an error occurs when parsing the arguments.
*/
public CommandLine parse(Options options, String[] arguments) throws ParseException {
return parse(options, arguments, false);
}
/**
* <p>
* Parses the specified <code>arguments</code> based on the specifed
* {@link Options}.
* </p>
*
* @param options the <code>Options</code>
* @param arguments the <code>arguments</code>
* @param stopAtNonOption specifies whether to stop interpreting the
* arguments when a non option has been encountered and to add
* them to the CommandLines args list.
* @return the <code>CommandLine</code>
* @throws ParseException if an error occurs when parsing the arguments.
*/
public CommandLine parse(Options opts, String[] arguments, boolean stopAtNonOption) throws ParseException {
// initialise members
options = opts;
requiredOptions = options.getRequiredOptions();
cmd = new CommandLine();
boolean eatTheRest = false;
List tokenList = Arrays.asList(flatten(opts, arguments, stopAtNonOption));
ListIterator iterator = tokenList.listIterator();
// process each flattened token
while (iterator.hasNext()) {
String t = (String) iterator.next();
// the value is the double-dash
if ("--".equals(t)) {
eatTheRest = true;
}
// the value is a single dash
else if ("-".equals(t)) {
if (stopAtNonOption) {
eatTheRest = true;
} else {
cmd.addArg(t);
}
}
// the value is an option
else if (t.startsWith("-")) {
if (stopAtNonOption && !options.hasOption(t)) {
eatTheRest = true;
cmd.addArg(t);
} else {
processOption(t, iterator);
}
}
// the value is an argument
else {
cmd.addArg(t);
if (stopAtNonOption) {
eatTheRest = true;
}
}
// eat the remaining tokens
if (eatTheRest) {
while (iterator.hasNext()) {
String str = (String) iterator.next();
// ensure only one double-dash is added
if (!"--".equals(str)) {
cmd.addArg(str);
}
}
}
}
checkRequiredOptions();
return cmd;
}
/**
* <p>
* Throws a {@link MissingOptionException} if all of the required options
* are no present.
* </p>
*/
private void checkRequiredOptions() throws MissingOptionException {
// if there are required options that have not been
// processsed
if (requiredOptions.size() > 0) {
Iterator iter = requiredOptions.iterator();
StringBuffer buff = new StringBuffer();
// loop through the required options
while (iter.hasNext()) {
buff.append(iter.next());
}
throw new MissingOptionException(buff.toString());
}
}
public void processArgs(Option opt, ListIterator iter) throws ParseException {
// loop until an option is found
while (iter.hasNext()) {
String var = (String) iter.next();
// found an Option
if (options.hasOption(var)) {
iter.previous();
break;
}
// found a value
else if (!opt.addValue(var)) {
iter.previous();
break;
}
}
if (opt.getValues() == null && !opt.hasOptionalArg()) {
throw new MissingArgumentException("no argument for:" + opt.getOpt());
}
}
private void processOption(String arg, ListIterator iter) throws ParseException {
// get the option represented by arg
Option opt = null;
boolean hasOption = options.hasOption(arg);
// if there is no option throw an UnrecognisedOptionException
if (!hasOption) {
throw new UnrecognizedOptionException("Unrecognized option: " + arg);
} else {
opt = options.getOption(arg);
}
// if the option is a required option remove the option from
// the requiredOptions list
if (opt.isRequired()) {
requiredOptions.remove("-" + opt.getOpt());
}
// if the option is in an OptionGroup make that option the selected
// option of the group
if (options.getOptionGroup(opt) != null) {
OptionGroup group = options.getOptionGroup(opt);
if (group.isRequired()) {
requiredOptions.remove(group);
}
group.setSelected(opt);
}
// if the option takes an argument value
if (opt.hasArg()) {
processArgs(opt, iter);
}
// set the option on the command line
cmd.addOption(opt);
}
}