blob: 96e20a0c813ec931446e77d7484f18b304e7a79d [file] [log] [blame]
/**
* 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.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Properties;
/**
* <p><code>Parser</code> creates {@link CommandLine}s.</p>
*
* @author John Keyes (john at integralsource.com)
* @see Parser
* @version $Revision$
*/
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 arguments 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, null, false);
}
/**
* Parse the arguments according to the specified options and
* properties.
*
* @param options the specified Options
* @param arguments the command line arguments
* @param properties command line option name-value pairs
* @return the list of atomic option and value tokens
*
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*/
public CommandLine parse(Options options, String[] arguments,
Properties properties)
throws ParseException
{
return parse(options, arguments, properties, 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 options, String[] arguments,
boolean stopAtNonOption)
throws ParseException
{
return parse(options, arguments, null, stopAtNonOption);
}
/**
* Parse the arguments according to the specified options and
* properties.
*
* @param options the specified Options
* @param arguments the command line arguments
* @param properties command line option name-value pairs
* @param stopAtNonOption stop parsing the arguments when the first
* non option is encountered.
*
* @return the list of atomic option and value tokens
*
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*/
public CommandLine parse(Options options, String[] arguments,
Properties properties, boolean stopAtNonOption)
throws ParseException
{
// initialise members
this.options = options;
// clear out the data in options in case it's been used before (CLI-71)
for (Iterator it = options.helpOptions().iterator(); it.hasNext();) {
Option opt = (Option) it.next();
opt.clearValues();
}
requiredOptions = options.getRequiredOptions();
cmd = new CommandLine();
boolean eatTheRest = false;
if (arguments == null)
{
arguments = new String[0];
}
List tokenList = Arrays.asList(flatten(this.options,
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);
}
}
}
}
processProperties(properties);
checkRequiredOptions();
return cmd;
}
/**
* <p>Sets the values of Options using the values in
* <code>properties</code>.</p>
*
* @param properties The value properties to be processed.
*/
private void processProperties(Properties properties)
{
if (properties == null)
{
return;
}
for (Enumeration e = properties.propertyNames(); e.hasMoreElements();)
{
String option = e.nextElement().toString();
if (!cmd.hasOption(option))
{
Option opt = options.getOption(option);
// get the value from the properties instance
String value = properties.getProperty(option);
if (opt.hasArg())
{
if ((opt.getValues() == null)
|| (opt.getValues().length == 0))
{
try
{
opt.addValueForProcessing(value);
}
catch (RuntimeException exp)
{
// if we cannot add the value don't worry about it
}
}
}
else if (!("yes".equalsIgnoreCase(value)
|| "true".equalsIgnoreCase(value)
|| "1".equalsIgnoreCase(value)))
{
// if the value is not yes, true or 1 then don't add the
// option to the CommandLine
break;
}
cmd.addOption(opt);
}
}
}
/**
* <p>Throws a {@link MissingOptionException} if all of the
* required options are no present.</p>
*
* @throws MissingOptionException if any of the required Options
* are not present.
*/
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("Missing required option");
buff.append(requiredOptions.size() == 1 ? "" : "s");
buff.append(": ");
// loop through the required options
while (iter.hasNext())
{
buff.append(iter.next());
}
throw new MissingOptionException(buff.toString());
}
}
/**
* <p>Process the argument values for the specified Option
* <code>opt</code> using the values retrieved from the
* specified iterator <code>iter</code>.
*
* @param opt The current Option
* @param iter The iterator over the flattened command line
* Options.
*
* @throws ParseException if an argument value is required
* and it is has not been found.
*/
public void processArgs(Option opt, ListIterator iter)
throws ParseException
{
// loop until an option is found
while (iter.hasNext())
{
String str = (String) iter.next();
// found an Option, not an argument
if (options.hasOption(str) && str.startsWith("-"))
{
iter.previous();
break;
}
// found a value
try
{
opt.addValueForProcessing( Util.stripLeadingAndTrailingQuotes(str) );
}
catch (RuntimeException exp)
{
iter.previous();
break;
}
}
if ((opt.getValues() == null) && !opt.hasOptionalArg())
{
throw new MissingArgumentException("Missing argument for option:"
+ opt.getKey());
}
}
/**
* <p>Process the Option specified by <code>arg</code>
* using the values retrieved from the specfied iterator
* <code>iter</code>.
*
* @param arg The String value representing an Option
* @param iter The iterator over the flattened command
* line arguments.
*
* @throws ParseException if <code>arg</code> does not
* represent an Option
*/
private void processOption(String arg, ListIterator iter)
throws ParseException
{
boolean hasOption = options.hasOption(arg);
// if there is no option throw an UnrecognisedOptionException
if (!hasOption)
{
throw new UnrecognizedOptionException("Unrecognized option: "
+ arg);
}
// get the option represented by arg
final Option opt = options.getOption(arg);
// if the option is a required option remove the option from
// the requiredOptions list
if (opt.isRequired())
{
requiredOptions.remove(opt.getKey());
}
// 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);
}
}