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.
/** * 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.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Properties;
Default parser.
Version:$Id: DefaultParser.java 1783175 2017-02-16 07:52:05Z britter $
Since:1.3
/** * Default parser. * * @version $Id: DefaultParser.java 1783175 2017-02-16 07:52:05Z britter $ * @since 1.3 */
public class DefaultParser implements CommandLineParser {
The command-line instance.
/** The command-line instance. */
protected CommandLine cmd;
The current options.
/** The current options. */
protected Options options;
Flag indicating how unrecognized tokens are handled. true to stop the parsing and add the remaining tokens to the args list. false to throw an exception.
/** * Flag indicating how unrecognized tokens are handled. <tt>true</tt> to stop * the parsing and add the remaining tokens to the args list. * <tt>false</tt> to throw an exception. */
protected boolean stopAtNonOption;
The token currently processed.
/** The token currently processed. */
protected String currentToken;
The last option parsed.
/** The last option parsed. */
protected Option currentOption;
Flag indicating if tokens should no longer be analyzed and simply added as arguments of the command line.
/** Flag indicating if tokens should no longer be analyzed and simply added as arguments of the command line. */
protected boolean skipParsing;
The required options and groups expected to be found when parsing the command line.
/** The required options and groups expected to be found when parsing the command line. */
protected List expectedOpts; public CommandLine parse(Options options, String[] arguments) throws ParseException { return parse(options, arguments, null); }
Parse the arguments according to the specified options and properties.
Params:
  • options – the specified Options
  • arguments – the command line arguments
  • properties – command line option name-value pairs
Throws:
  • ParseException – if there are any problems encountered while parsing the command line tokens.
Returns:the list of atomic option and value tokens
/** * 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); } 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.
Params:
  • options – the specified Options
  • arguments – the command line arguments
  • properties – command line option name-value pairs
  • stopAtNonOption – if true an unrecognized argument stops the parsing and the remaining arguments are added to the CommandLines args list. If false an unrecognized argument triggers a ParseException.
Throws:
  • ParseException – if there are any problems encountered while parsing the command line tokens.
Returns:the list of atomic option and value tokens
/** * 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 if <tt>true</tt> an unrecognized argument stops * the parsing and the remaining arguments are added to the * {@link CommandLine}s args list. If <tt>false</tt> an unrecognized * argument triggers a ParseException. * * @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 { this.options = options; this.stopAtNonOption = stopAtNonOption; skipParsing = false; currentOption = null; expectedOpts = new ArrayList(options.getRequiredOptions()); // clear the data from the groups for (OptionGroup group : options.getOptionGroups()) { group.setSelected(null); } cmd = new CommandLine(); if (arguments != null) { for (String argument : arguments) { handleToken(argument); } } // check the arguments of the last option checkRequiredArgs(); // add the default options handleProperties(properties); checkRequiredOptions(); return cmd; }
Sets the values of Options using the values in properties.
Params:
  • properties – The value properties to be processed.
/** * Sets the values of Options using the values in <code>properties</code>. * * @param properties The value properties to be processed. */
private void handleProperties(Properties properties) throws ParseException { if (properties == null) { return; } for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements();) { String option = e.nextElement().toString(); Option opt = options.getOption(option); if (opt == null) { throw new UnrecognizedOptionException("Default option wasn't defined", option); } // if the option is part of a group, check if another option of the group has been selected OptionGroup group = options.getOptionGroup(opt); boolean selected = group != null && group.getSelected() != null; if (!cmd.hasOption(option) && !selected) { // get the value from the properties String value = properties.getProperty(option); if (opt.hasArg()) { if (opt.getValues() == null || opt.getValues().length == 0) { opt.addValueForProcessing(value); } } 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 continue; } handleOption(opt); currentOption = null; } } }
Throws a MissingOptionException if all of the required options are not present.
Throws:
/** * Throws a {@link MissingOptionException} if all of the required options * are not present. * * @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 processed if (!expectedOpts.isEmpty()) { throw new MissingOptionException(expectedOpts); } }
Throw a MissingArgumentException if the current option didn't receive the number of arguments expected.
/** * Throw a {@link MissingArgumentException} if the current option * didn't receive the number of arguments expected. */
private void checkRequiredArgs() throws ParseException { if (currentOption != null && currentOption.requiresArg()) { throw new MissingArgumentException(currentOption); } }
Handle any command line token.
Params:
  • token – the command line token to handle
Throws:
/** * Handle any command line token. * * @param token the command line token to handle * @throws ParseException */
private void handleToken(String token) throws ParseException { currentToken = token; if (skipParsing) { cmd.addArg(token); } else if ("--".equals(token)) { skipParsing = true; } else if (currentOption != null && currentOption.acceptsArg() && isArgument(token)) { currentOption.addValueForProcessing(Util.stripLeadingAndTrailingQuotes(token)); } else if (token.startsWith("--")) { handleLongOption(token); } else if (token.startsWith("-") && !"-".equals(token)) { handleShortAndLongOption(token); } else { handleUnknownToken(token); } if (currentOption != null && !currentOption.acceptsArg()) { currentOption = null; } }
Returns true is the token is a valid argument.
Params:
  • token –
/** * Returns true is the token is a valid argument. * * @param token */
private boolean isArgument(String token) { return !isOption(token) || isNegativeNumber(token); }
Check if the token is a negative number.
Params:
  • token –
/** * Check if the token is a negative number. * * @param token */
private boolean isNegativeNumber(String token) { try { Double.parseDouble(token); return true; } catch (NumberFormatException e) { return false; } }
Tells if the token looks like an option.
Params:
  • token –
/** * Tells if the token looks like an option. * * @param token */
private boolean isOption(String token) { return isLongOption(token) || isShortOption(token); }
Tells if the token looks like a short option.
Params:
  • token –
/** * Tells if the token looks like a short option. * * @param token */
private boolean isShortOption(String token) { // short options (-S, -SV, -S=V, -SV1=V2, -S1S2) if (!token.startsWith("-") || token.length() == 1) { return false; } // remove leading "-" and "=value" int pos = token.indexOf("="); String optName = pos == -1 ? token.substring(1) : token.substring(1, pos); if (options.hasShortOption(optName)) { return true; } // check for several concatenated short options return optName.length() > 0 && options.hasShortOption(String.valueOf(optName.charAt(0))); }
Tells if the token looks like a long option.
Params:
  • token –
/** * Tells if the token looks like a long option. * * @param token */
private boolean isLongOption(String token) { if (!token.startsWith("-") || token.length() == 1) { return false; } int pos = token.indexOf("="); String t = pos == -1 ? token : token.substring(0, pos); if (!options.getMatchingOptions(t).isEmpty()) { // long or partial long options (--L, -L, --L=V, -L=V, --l, --l=V) return true; } else if (getLongPrefix(token) != null && !token.startsWith("--")) { // -LV return true; } return false; }
Handles an unknown token. If the token starts with a dash an UnrecognizedOptionException is thrown. Otherwise the token is added to the arguments of the command line. If the stopAtNonOption flag is set, this stops the parsing and the remaining tokens are added as-is in the arguments of the command line.
Params:
  • token – the command line token to handle
/** * Handles an unknown token. If the token starts with a dash an * UnrecognizedOptionException is thrown. Otherwise the token is added * to the arguments of the command line. If the stopAtNonOption flag * is set, this stops the parsing and the remaining tokens are added * as-is in the arguments of the command line. * * @param token the command line token to handle */
private void handleUnknownToken(String token) throws ParseException { if (token.startsWith("-") && token.length() > 1 && !stopAtNonOption) { throw new UnrecognizedOptionException("Unrecognized option: " + token, token); } cmd.addArg(token); if (stopAtNonOption) { skipParsing = true; } }
Handles the following tokens: --L --L=V --L V --l
Params:
  • token – the command line token to handle
/** * Handles the following tokens: * * --L * --L=V * --L V * --l * * @param token the command line token to handle */
private void handleLongOption(String token) throws ParseException { if (token.indexOf('=') == -1) { handleLongOptionWithoutEqual(token); } else { handleLongOptionWithEqual(token); } }
Handles the following tokens: --L -L --l -l
Params:
  • token – the command line token to handle
/** * Handles the following tokens: * * --L * -L * --l * -l * * @param token the command line token to handle */
private void handleLongOptionWithoutEqual(String token) throws ParseException { List<String> matchingOpts = options.getMatchingOptions(token); if (matchingOpts.isEmpty()) { handleUnknownToken(currentToken); } else if (matchingOpts.size() > 1) { throw new AmbiguousOptionException(token, matchingOpts); } else { handleOption(options.getOption(matchingOpts.get(0))); } }
Handles the following tokens: --L=V -L=V --l=V -l=V
Params:
  • token – the command line token to handle
/** * Handles the following tokens: * * --L=V * -L=V * --l=V * -l=V * * @param token the command line token to handle */
private void handleLongOptionWithEqual(String token) throws ParseException { int pos = token.indexOf('='); String value = token.substring(pos + 1); String opt = token.substring(0, pos); List<String> matchingOpts = options.getMatchingOptions(opt); if (matchingOpts.isEmpty()) { handleUnknownToken(currentToken); } else if (matchingOpts.size() > 1) { throw new AmbiguousOptionException(opt, matchingOpts); } else { Option option = options.getOption(matchingOpts.get(0)); if (option.acceptsArg()) { handleOption(option); currentOption.addValueForProcessing(value); currentOption = null; } else { handleUnknownToken(currentToken); } } }
Handles the following tokens: -S -SV -S V -S=V -S1S2 -S1S2 V -SV1=V2 -L -LV -L V -L=V -l
Params:
  • token – the command line token to handle
/** * Handles the following tokens: * * -S * -SV * -S V * -S=V * -S1S2 * -S1S2 V * -SV1=V2 * * -L * -LV * -L V * -L=V * -l * * @param token the command line token to handle */
private void handleShortAndLongOption(String token) throws ParseException { String t = Util.stripLeadingHyphens(token); int pos = t.indexOf('='); if (t.length() == 1) { // -S if (options.hasShortOption(t)) { handleOption(options.getOption(t)); } else { handleUnknownToken(token); } } else if (pos == -1) { // no equal sign found (-xxx) if (options.hasShortOption(t)) { handleOption(options.getOption(t)); } else if (!options.getMatchingOptions(t).isEmpty()) { // -L or -l handleLongOptionWithoutEqual(token); } else { // look for a long prefix (-Xmx512m) String opt = getLongPrefix(t); if (opt != null && options.getOption(opt).acceptsArg()) { handleOption(options.getOption(opt)); currentOption.addValueForProcessing(t.substring(opt.length())); currentOption = null; } else if (isJavaProperty(t)) { // -SV1 (-Dflag) handleOption(options.getOption(t.substring(0, 1))); currentOption.addValueForProcessing(t.substring(1)); currentOption = null; } else { // -S1S2S3 or -S1S2V handleConcatenatedOptions(token); } } } else { // equal sign found (-xxx=yyy) String opt = t.substring(0, pos); String value = t.substring(pos + 1); if (opt.length() == 1) { // -S=V Option option = options.getOption(opt); if (option != null && option.acceptsArg()) { handleOption(option); currentOption.addValueForProcessing(value); currentOption = null; } else { handleUnknownToken(token); } } else if (isJavaProperty(opt)) { // -SV1=V2 (-Dkey=value) handleOption(options.getOption(opt.substring(0, 1))); currentOption.addValueForProcessing(opt.substring(1)); currentOption.addValueForProcessing(value); currentOption = null; } else { // -L=V or -l=V handleLongOptionWithEqual(token); } } }
Search for a prefix that is the long name of an option (-Xmx512m)
Params:
  • token –
/** * Search for a prefix that is the long name of an option (-Xmx512m) * * @param token */
private String getLongPrefix(String token) { String t = Util.stripLeadingHyphens(token); int i; String opt = null; for (i = t.length() - 2; i > 1; i--) { String prefix = t.substring(0, i); if (options.hasLongOption(prefix)) { opt = prefix; break; } } return opt; }
Check if the specified token is a Java-like property (-Dkey=value).
/** * Check if the specified token is a Java-like property (-Dkey=value). */
private boolean isJavaProperty(String token) { String opt = token.substring(0, 1); Option option = options.getOption(opt); return option != null && (option.getArgs() >= 2 || option.getArgs() == Option.UNLIMITED_VALUES); } private void handleOption(Option option) throws ParseException { // check the previous option before handling the next one checkRequiredArgs(); option = (Option) option.clone(); updateRequiredOptions(option); cmd.addOption(option); if (option.hasArg()) { currentOption = option; } else { currentOption = null; } }
Removes the option or its group from the list of expected elements.
Params:
  • option –
/** * Removes the option or its group from the list of expected elements. * * @param option */
private void updateRequiredOptions(Option option) throws AlreadySelectedException { if (option.isRequired()) { expectedOpts.remove(option.getKey()); } // if the option is in an OptionGroup make that option the selected option of the group if (options.getOptionGroup(option) != null) { OptionGroup group = options.getOptionGroup(option); if (group.isRequired()) { expectedOpts.remove(group); } group.setSelected(option); } }
Breaks token into its constituent parts using the following algorithm.
  • ignore the first character ("-")
  • for each remaining character check if an Option exists with that id.
  • if an Option does exist then add that character prepended with "-" to the list of processed tokens.
  • if the Option can have an argument value and there are remaining characters in the token then add the remaining characters as a token to the list of processed tokens.
  • if an Option does NOT exist AND stopAtNonOption IS set then add the special token "--" followed by the remaining characters and also the remaining tokens directly to the processed tokens list.
  • if an Option does NOT exist AND stopAtNonOption IS NOT set then add that character prepended with "-".
Params:
  • token – The current token to be burst at the first non-Option encountered.
Throws:
  • ParseException – if there are any problems encountered while parsing the command line token.
/** * Breaks <code>token</code> into its constituent parts * using the following algorithm. * * <ul> * <li>ignore the first character ("<b>-</b>")</li> * <li>for each remaining character check if an {@link Option} * exists with that id.</li> * <li>if an {@link Option} does exist then add that character * prepended with "<b>-</b>" to the list of processed tokens.</li> * <li>if the {@link Option} can have an argument value and there * are remaining characters in the token then add the remaining * characters as a token to the list of processed tokens.</li> * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> * <code>stopAtNonOption</code> <b>IS</b> set then add the special token * "<b>--</b>" followed by the remaining characters and also * the remaining tokens directly to the processed tokens list.</li> * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> * <code>stopAtNonOption</code> <b>IS NOT</b> set then add that * character prepended with "<b>-</b>".</li> * </ul> * * @param token The current token to be <b>burst</b> * at the first non-Option encountered. * @throws ParseException if there are any problems encountered * while parsing the command line token. */
protected void handleConcatenatedOptions(String token) throws ParseException { for (int i = 1; i < token.length(); i++) { String ch = String.valueOf(token.charAt(i)); if (options.hasOption(ch)) { handleOption(options.getOption(ch)); if (currentOption != null && token.length() != i + 1) { // add the trail as an argument of the option currentOption.addValueForProcessing(token.substring(i + 1)); break; } } else { handleUnknownToken(stopAtNonOption && i > 1 ? token.substring(i) : token); break; } } } }