optionGroups = new LinkedHashMap<>();
/**
- * Adds an option instance
+ * Constructs new instance.
+ */
+ public Options() {
+ // empty
+ }
+
+ /**
+ * Adds an option instance.
*
- * @param opt the option that is to be added
- * @return the resulting Options instance
+ * @param opt the option that is to be added.
+ * @return the resulting Options instance.
*/
public Options addOption(final Option opt) {
final String key = opt.getKey();
-
// add it to the long option list
if (opt.hasLongOpt()) {
longOpts.put(opt.getLongOpt(), opt);
}
-
// if the option is required add it to the required list
if (opt.isRequired()) {
if (requiredOpts.contains(key)) {
@@ -75,23 +83,20 @@ public Options addOption(final Option opt) {
}
requiredOpts.add(key);
}
-
shortOpts.put(key, opt);
-
return this;
}
/**
- * Add an option that only contains a short-name.
- *
+ * Adds an option that only contains a short-name.
*
* It may be specified as requiring an argument.
*
*
* @param opt Short single-character name of the option.
- * @param hasArg flag signalling if an argument is required after this option
- * @param description Self-documenting description
- * @return the resulting Options instance
+ * @param hasArg flag signaling if an argument is required after this option.
+ * @param description Self-documenting description.
+ * @return the resulting Options instance.
*/
public Options addOption(final String opt, final boolean hasArg, final String description) {
addOption(opt, null, hasArg, description);
@@ -99,15 +104,14 @@ public Options addOption(final String opt, final boolean hasArg, final String de
}
/**
- * Add an option that only contains a short name.
- *
+ * Adds an option that only contains a short name.
*
* The option does not take an argument.
*
*
* @param opt Short single-character name of the option.
- * @param description Self-documenting description
- * @return the resulting Options instance
+ * @param description Self-documenting description.
+ * @return the resulting Options instance.
* @since 1.3
*/
public Options addOption(final String opt, final String description) {
@@ -116,17 +120,16 @@ public Options addOption(final String opt, final String description) {
}
/**
- * Add an option that contains a short-name and a long-name.
- *
+ * Adds an option that contains a short-name and a long-name.
*
* It may be specified as requiring an argument.
*
*
* @param opt Short single-character name of the option.
* @param longOpt Long multi-character name of the option.
- * @param hasArg flag signalling if an argument is required after this option
- * @param description Self-documenting description
- * @return the resulting Options instance
+ * @param hasArg flag signaling if an argument is required after this option.
+ * @param description Self-documenting description.
+ * @return the resulting Options instance.
*/
public Options addOption(final String opt, final String longOpt, final boolean hasArg, final String description) {
addOption(new Option(opt, longOpt, hasArg, description));
@@ -134,36 +137,56 @@ public Options addOption(final String opt, final String longOpt, final boolean h
}
/**
- * Add the specified option group.
+ * Adds the specified option group.
+ *
+ * An Option cannot be required if it is in an {@link OptionGroup}, either the group is required or nothing is required. This means that {@link Option} in
+ * the given group are set to optional.
+ *
*
- * @param group the OptionGroup that is to be added
- * @return the resulting Options instance
+ * @param optionGroup the OptionGroup that is to be added.
+ * @return the resulting Options instance.
*/
- public Options addOptionGroup(final OptionGroup group) {
- if (group.isRequired()) {
- requiredOpts.add(group);
+ public Options addOptionGroup(final OptionGroup optionGroup) {
+ if (optionGroup.isRequired()) {
+ requiredOpts.add(optionGroup);
}
-
- for (final Option option : group.getOptions()) {
+ optionGroup.getOptions().forEach(option -> {
// an Option cannot be required if it is in an
// OptionGroup, either the group is required or
// nothing is required
option.setRequired(false);
+ final String key = option.getKey();
+ requiredOpts.remove(key);
addOption(option);
-
- optionGroups.put(option.getKey(), group);
- }
-
+ optionGroups.put(key, optionGroup);
+ });
return this;
}
/**
- * Add an option that contains a short-name and a long-name.
+ * Adds options to this option. If any Option in {@code options} already exists
+ * in this Options an IllegalArgumentException is thrown.
*
+ * @param options the options to add.
+ * @return The resulting Options instance.
+ * @since 1.7.0
+ */
+ public Options addOptions(final Options options) {
+ options.getOptions().forEach(opt -> {
+ if (hasOption(opt.getKey())) {
+ throw new IllegalArgumentException("Duplicate key: " + opt.getKey());
+ }
+ addOption(opt);
+ });
+ options.getOptionGroups().forEach(this::addOptionGroup);
+ return this;
+ }
+
+ /**
+ * Adds an option that contains a short-name and a long-name.
*
* The added option is set as required. It may be specified as requiring an argument. This method is a shortcut for:
*
- *
*
*
* Options option = new Option(opt, longOpt, hasArg, description);
@@ -174,9 +197,9 @@ public Options addOptionGroup(final OptionGroup group) {
*
* @param opt Short single-character name of the option.
* @param longOpt Long multi-character name of the option.
- * @param hasArg flag signalling if an argument is required after this option
- * @param description Self-documenting description
- * @return the resulting Options instance
+ * @param hasArg flag signaling if an argument is required after this option.
+ * @param description Self-documenting description.
+ * @return the resulting Options instance.
* @since 1.4
*/
public Options addRequiredOption(final String opt, final String longOpt, final boolean hasArg, final String description) {
@@ -189,57 +212,48 @@ public Options addRequiredOption(final String opt, final String longOpt, final b
/**
* Gets the options with a long name starting with the name specified.
*
- * @param opt the partial name of the option
- * @return the options matching the partial name specified, or an empty list if none matches
+ * @param opt the partial name of the option.
+ * @return the options matching the partial name specified, or an empty list if none matches.
* @since 1.3
*/
- public List getMatchingOptions(String opt) {
- opt = Util.stripLeadingHyphens(opt);
-
+ public List getMatchingOptions(final String opt) {
+ final String clean = Util.stripLeadingHyphens(opt);
final List matchingOpts = new ArrayList<>();
-
// for a perfect match return the single option only
- if (longOpts.containsKey(opt)) {
- return Collections.singletonList(opt);
+ if (longOpts.containsKey(clean)) {
+ return Collections.singletonList(clean);
}
-
- for (final String longOpt : longOpts.keySet()) {
- if (longOpt.startsWith(opt)) {
+ longOpts.keySet().forEach(longOpt -> {
+ if (longOpt.startsWith(clean)) {
matchingOpts.add(longOpt);
}
- }
-
+ });
return matchingOpts;
}
/**
* Gets the {@link Option} matching the long or short name specified.
- *
*
* The leading hyphens in the name are ignored (up to 2).
*
*
- * @param opt short or long name of the {@link Option}
- * @return the option represented by opt
+ * @param opt short or long name of the {@link Option}.
+ * @return the option represented by opt.
*/
- public Option getOption(String opt) {
- opt = Util.stripLeadingHyphens(opt);
-
- if (shortOpts.containsKey(opt)) {
- return shortOpts.get(opt);
- }
-
- return longOpts.get(opt);
+ public Option getOption(final String opt) {
+ final String clean = Util.stripLeadingHyphens(opt);
+ final Option option = shortOpts.get(clean);
+ return option != null ? option : longOpts.get(clean);
}
/**
* Gets the OptionGroup the {@code opt} belongs to.
*
- * @param opt the option whose OptionGroup is being queried.
- * @return the OptionGroup if {@code opt} is part of an OptionGroup, otherwise return null
+ * @param option the option whose OptionGroup is being queried.
+ * @return the OptionGroup if {@code opt} is part of an OptionGroup, otherwise return null.
*/
- public OptionGroup getOptionGroup(final Option opt) {
- return optionGroups.get(opt.getKey());
+ public OptionGroup getOptionGroup(final Option option) {
+ return optionGroups.get(option.getKey());
}
/**
@@ -248,13 +262,18 @@ public OptionGroup getOptionGroup(final Option opt) {
* @return a Collection of OptionGroup instances.
*/
Collection getOptionGroups() {
+
+ // The optionGroups map will have duplicates in the values() results. We
+ // use the HashSet to filter out duplicates and return a collection of
+ // OpitonGroup. The decision to return a Collection rather than a set
+ // was probably to keep symmetry with the getOptions() method.
return new HashSet<>(optionGroups.values());
}
/**
- * Gets a read-only list of options in this set
+ * Gets a read-only list of options in this set.
*
- * @return read-only Collection of {@link Option} objects in this descriptor
+ * @return read-only Collection of {@link Option} objects in this descriptor.
*/
public Collection getOptions() {
return Collections.unmodifiableCollection(helpOptions());
@@ -263,54 +282,50 @@ public Collection getOptions() {
/**
* Gets the required options.
*
- * @return read-only List of required options
+ * @return read-only List of required options.
*/
- public List getRequiredOptions() {
+ public List> getRequiredOptions() {
return Collections.unmodifiableList(requiredOpts);
}
/**
- * Returns whether the named {@link Option} is a member of this {@link Options}.
+ * Tests whether the named {@link Option} is a member of this {@link Options}.
*
- * @param opt long name of the {@link Option}
- * @return true if the named {@link Option} is a member of this {@link Options}
+ * @param opt long name of the {@link Option}.
+ * @return true if the named {@link Option} is a member of this {@link Options}.
* @since 1.3
*/
- public boolean hasLongOption(String opt) {
- opt = Util.stripLeadingHyphens(opt);
-
- return longOpts.containsKey(opt);
+ public boolean hasLongOption(final String opt) {
+ return longOpts.containsKey(Util.stripLeadingHyphens(opt));
}
/**
- * Returns whether the named {@link Option} is a member of this {@link Options}.
+ * Tests whether the named {@link Option} is a member of this {@link Options}.
*
- * @param opt short or long name of the {@link Option}
- * @return true if the named {@link Option} is a member of this {@link Options}
+ * @param opt short or long name of the {@link Option}.
+ * @return true if the named {@link Option} is a member of this {@link Options}.
*/
- public boolean hasOption(String opt) {
- opt = Util.stripLeadingHyphens(opt);
-
- return shortOpts.containsKey(opt) || longOpts.containsKey(opt);
+ public boolean hasOption(final String opt) {
+ final String clean = Util.stripLeadingHyphens(opt);
+ return shortOpts.containsKey(clean) || longOpts.containsKey(clean);
}
/**
- * Returns whether the named {@link Option} is a member of this {@link Options}.
+ * Tests whether the named {@link Option} is a member of this {@link Options}.
*
- * @param opt short name of the {@link Option}
- * @return true if the named {@link Option} is a member of this {@link Options}
+ * @param opt short name of the {@link Option}.
+ * @return true if the named {@link Option} is a member of this {@link Options}.
* @since 1.3
*/
- public boolean hasShortOption(String opt) {
- opt = Util.stripLeadingHyphens(opt);
-
- return shortOpts.containsKey(opt);
+ public boolean hasShortOption(final String opt) {
+ final String clean = Util.stripLeadingHyphens(opt);
+ return shortOpts.containsKey(clean);
}
/**
* Returns the Options for use by the HelpFormatter.
*
- * @return the List of Options
+ * @return the List of Options.
*/
List helpOptions() {
return new ArrayList<>(shortOpts.values());
@@ -319,18 +334,16 @@ List helpOptions() {
/**
* Dump state, suitable for debugging.
*
- * @return Stringified form of this object
+ * @return Stringified form of this object.
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
-
buf.append("[ Options: [ short ");
buf.append(shortOpts.toString());
buf.append(" ] [ long ");
buf.append(longOpts);
buf.append(" ]");
-
return buf.toString();
}
}
diff --git a/src/main/java/org/apache/commons/cli/ParseException.java b/src/main/java/org/apache/commons/cli/ParseException.java
index 5d3acc6ca..a8a67f221 100644
--- a/src/main/java/org/apache/commons/cli/ParseException.java
+++ b/src/main/java/org/apache/commons/cli/ParseException.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -21,17 +21,50 @@ Licensed to the Apache Software Foundation (ASF) under one or more
* Base for Exceptions thrown during parsing of a command-line.
*/
public class ParseException extends Exception {
+
/**
* This exception {@code serialVersionUID}.
*/
private static final long serialVersionUID = 9112808380089253192L;
/**
- * Construct a new {@code ParseException} with the specified detail message.
+ * Converts any exception except {@code UnsupportedOperationException} to a {@code ParseException}. if {@code e} is an instance of {@code ParseException} it
+ * is returned, otherwise a {@code ParseException} is created that wraps it.
+ *
+ * Note: {@code UnsupportedOperationException} are not wrapped. This is to solve a legacy expected exception problem and will be removed in the future.
+ *
+ *
+ * @param e the exception to convert.
+ * @return the ParseException.
+ * @throws UnsupportedOperationException due to legacy expectations. Will be removed in the future.
+ * @since 1.7.0
+ */
+ public static ParseException wrap(final Throwable e) throws UnsupportedOperationException {
+ if (e instanceof UnsupportedOperationException) {
+ throw (UnsupportedOperationException) e;
+ }
+
+ if (e instanceof ParseException) {
+ return (ParseException) e;
+ }
+ return new ParseException(e);
+ }
+
+ /**
+ * Constructs a new {@code ParseException} with the specified detail message.
*
- * @param message the detail message
+ * @param message the detail message.
*/
public ParseException(final String message) {
super(message);
}
+
+ /**
+ * Constructs a new {@code ParseException} wrapping the specified exception.
+ *
+ * @param e the Exception to wrap.
+ */
+ public ParseException(final Throwable e) {
+ super(e);
+ }
}
diff --git a/src/main/java/org/apache/commons/cli/Parser.java b/src/main/java/org/apache/commons/cli/Parser.java
index 8543d9c12..2ef6e9c9d 100644
--- a/src/main/java/org/apache/commons/cli/Parser.java
+++ b/src/main/java/org/apache/commons/cli/Parser.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -24,22 +24,32 @@ Licensed to the Apache Software Foundation (ASF) under one or more
import java.util.ListIterator;
import java.util.Properties;
+import org.apache.commons.cli.help.OptionFormatter;
+
/**
- * {@code Parser} creates {@link CommandLine}s.
+ * Creates {@link CommandLine} instances.
*
- * @deprecated since 1.3, the two-pass parsing with the flatten method is not enough flexible to handle complex cases
+ * @deprecated Since 1.3, the two-pass parsing with the flatten method is not enough flexible to handle complex cases.
*/
@Deprecated
public abstract class Parser implements CommandLineParser {
+
/** CommandLine instance */
protected CommandLine cmd;
- /** current Options */
+ /** Current Options */
private Options options;
- /** list of required options strings */
+ /** List of required options strings */
private List requiredOptions;
+ /**
+ * Constructs a new instance.
+ */
+ public Parser() {
+ // empty
+ }
+
/**
* Throws a {@link MissingOptionException} if all of the required options are not present.
*
@@ -57,16 +67,26 @@ protected void checkRequiredOptions() throws MissingOptionException {
*
* @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
+ * @param stopAtNonOption specifies whether to stop flattening when a non option has been encountered.
+ * @return a String array of the flattened arguments.
* @throws ParseException if there are any problems encountered while parsing the command line tokens.
*/
protected abstract String[] flatten(Options opts, String[] arguments, boolean stopAtNonOption) throws ParseException;
+ /**
+ * Gets the options.
+ *
+ * @return the options.
+ */
protected Options getOptions() {
return options;
}
+ /**
+ * Gets the required options.
+ *
+ * @return the required options.
+ */
protected List getRequiredOptions() {
return requiredOptions;
}
@@ -74,9 +94,9 @@ protected List getRequiredOptions() {
/**
* Parses the specified {@code arguments} based on the specified {@link Options}.
*
- * @param options the {@code Options}
- * @param arguments the {@code arguments}
- * @return the {@code CommandLine}
+ * @param options the {@code Options}.
+ * @param arguments the {@code arguments}.
+ * @return the {@code CommandLine}.
* @throws ParseException if there are any problems encountered while parsing the command line tokens.
*/
@Override
@@ -87,12 +107,12 @@ public CommandLine parse(final Options options, final String[] arguments) throws
/**
* Parses the specified {@code arguments} based on the specified {@link Options}.
*
- * @param options the {@code Options}
- * @param arguments the {@code arguments}
+ * @param options the {@code Options}.
+ * @param arguments the {@code arguments}.
* @param stopAtNonOption if {@code true} an unrecognized argument stops the parsing and the remaining arguments
* are added to the {@link CommandLine}s args list. If {@code false} an unrecognized argument triggers a
* ParseException.
- * @return the {@code CommandLine}
+ * @return the {@code CommandLine}.
* @throws ParseException if an error occurs when parsing the arguments.
*/
@Override
@@ -101,14 +121,13 @@ public CommandLine parse(final Options options, final String[] arguments, final
}
/**
- * Parse the arguments according to the specified options and properties.
+ * Parses 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
+ * @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.
- *
* @since 1.1
*/
public CommandLine parse(final Options options, final String[] arguments, final Properties properties) throws ParseException {
@@ -116,165 +135,129 @@ public CommandLine parse(final Options options, final String[] arguments, final
}
/**
- * Parse the arguments according to the specified options and properties.
+ * Parses 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 options the specified Options.
+ * @param arguments the command line arguments.
+ * @param properties command line option name-value pairs.
* @param stopAtNonOption if {@code true} an unrecognized argument stops the parsing and the remaining arguments
* are added to the {@link CommandLine}s args list. If {@code false} an unrecognized argument triggers a
* ParseException.
- *
- * @return the list of atomic option and value tokens
- *
+ * @return the list of atomic option and value tokens.
* @throws ParseException if there are any problems encountered while parsing the command line tokens.
- *
* @since 1.1
*/
- public CommandLine parse(final Options options, String[] arguments, final Properties properties, final boolean stopAtNonOption) throws ParseException {
+ public CommandLine parse(final Options options, final String[] arguments, final Properties properties, final boolean stopAtNonOption)
+ throws ParseException {
// clear out the data in options in case it's been used before (CLI-71)
- for (final Option opt : options.helpOptions()) {
- opt.clearValues();
- }
-
+ options.helpOptions().forEach(Option::clearValues);
// clear the data from the groups
- for (final OptionGroup group : options.getOptionGroups()) {
- group.setSelected(null);
+ for (final OptionGroup optionGroup : options.getOptionGroups()) {
+ optionGroup.setSelected(null);
}
-
// initialize members
setOptions(options);
-
- cmd = new CommandLine();
-
+ cmd = CommandLine.builder().get();
boolean eatTheRest = false;
-
- if (arguments == null) {
- arguments = new String[0];
- }
-
- final List tokenList = Arrays.asList(flatten(getOptions(), arguments, stopAtNonOption));
-
+ final List tokenList = Arrays.asList(flatten(getOptions(), arguments == null ? Util.EMPTY_STRING_ARRAY : arguments, stopAtNonOption));
final ListIterator iterator = tokenList.listIterator();
-
// process each flattened token
while (iterator.hasNext()) {
- final String t = 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) {
+ final String token = iterator.next();
+ if (token != null) {
+ // the value is the double-dash
+ if (OptionFormatter.DEFAULT_LONG_OPT_PREFIX.equals(token)) {
eatTheRest = true;
+ } else if (OptionFormatter.DEFAULT_OPT_PREFIX.equals(token)) {
+ // the value is a single dash
+ if (stopAtNonOption) {
+ eatTheRest = true;
+ } else {
+ cmd.addArg(token);
+ }
+ } else if (token.startsWith(OptionFormatter.DEFAULT_OPT_PREFIX)) {
+ // the value is an option
+ if (stopAtNonOption && !getOptions().hasOption(token)) {
+ eatTheRest = true;
+ cmd.addArg(token);
+ } else {
+ processOption(token, iterator);
+ }
} else {
- cmd.addArg(t);
- }
- }
-
- // the value is an option
- else if (t.startsWith("-")) {
- if (stopAtNonOption && !getOptions().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()) {
- final String str = iterator.next();
-
- // ensure only one double-dash is added
- if (!"--".equals(str)) {
- cmd.addArg(str);
+ // the value is an argument
+ cmd.addArg(token);
+ if (stopAtNonOption) {
+ eatTheRest = true;
}
}
+ // eat the remaining tokens
+ if (eatTheRest) {
+ iterator.forEachRemaining(str -> {
+ // ensure only one double-dash is added
+ if (!OptionFormatter.DEFAULT_LONG_OPT_PREFIX.equals(str)) {
+ cmd.addArg(str);
+ }
+ });
+ }
}
}
-
processProperties(properties);
checkRequiredOptions();
-
return cmd;
}
/**
- * Process the argument values for the specified Option {@code opt} using the values retrieved from the specified
+ * Processes the argument values for the specified Option {@code opt} using the values retrieved from the specified
* iterator {@code iter}.
*
- * @param opt The current Option
+ * @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(final Option opt, final ListIterator iter) throws ParseException {
// loop until an option is found
while (iter.hasNext()) {
final String str = iter.next();
-
// found an Option, not an argument
- if (getOptions().hasOption(str) && str.startsWith("-")) {
+ if (getOptions().hasOption(str) && str.startsWith(OptionFormatter.DEFAULT_OPT_PREFIX)) {
iter.previous();
break;
}
-
// found a value
try {
- opt.addValueForProcessing(Util.stripLeadingAndTrailingQuotes(str));
+ opt.processValue(Util.stripLeadingAndTrailingQuotes(str));
} catch (final RuntimeException exp) {
iter.previous();
break;
}
}
-
- if (opt.getValues() == null && !opt.hasOptionalArg()) {
+ if (opt.isValuesEmpty() && !opt.hasOptionalArg()) {
throw new MissingArgumentException(opt);
}
}
/**
- * Process the Option specified by {@code arg} using the values retrieved from the specified iterator
+ * Processes the Option specified by {@code arg} using the values retrieved from the specified iterator
* {@code iter}.
*
- * @param arg The String value representing an Option
+ * @param arg The String value representing an Option.
* @param iter The iterator over the flattened command line arguments.
- *
- * @throws ParseException if {@code arg} does not represent an Option
+ * @throws ParseException if {@code arg} does not represent an Option.
*/
protected void processOption(final String arg, final ListIterator iter) throws ParseException {
final boolean hasOption = getOptions().hasOption(arg);
-
// if there is no option throw an UnrecognizedOptionException
if (!hasOption) {
throw new UnrecognizedOptionException("Unrecognized option: " + arg, arg);
}
-
// get the option represented by arg
final Option opt = (Option) getOptions().getOption(arg).clone();
-
// update the required options and groups
updateRequiredOptions(opt);
-
// if the option takes an argument value
if (opt.hasArg()) {
processArgs(opt, iter);
}
-
// set the option on the command line
cmd.addOption(opt);
}
@@ -289,27 +272,22 @@ protected void processProperties(final Properties properties) throws ParseExcept
if (properties == null) {
return;
}
-
for (final Enumeration> e = properties.propertyNames(); e.hasMoreElements();) {
final String option = e.nextElement().toString();
-
final 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
- final OptionGroup group = options.getOptionGroup(opt);
- final boolean selected = group != null && group.getSelected() != null;
-
+ final OptionGroup optionGroup = options.getOptionGroup(opt);
+ final boolean selected = optionGroup != null && optionGroup.isSelected();
if (!cmd.hasOption(option) && !selected) {
// get the value from the properties instance
final String value = properties.getProperty(option);
-
if (opt.hasArg()) {
- if (opt.getValues() == null || opt.getValues().length == 0) {
+ if (opt.isValuesEmpty()) {
try {
- opt.addValueForProcessing(value);
+ opt.processValue(value);
} catch (final RuntimeException exp) { // NOPMD
// if we cannot add the value don't worry about it
}
@@ -319,13 +297,17 @@ protected void processProperties(final Properties properties) throws ParseExcept
// option to the CommandLine
continue;
}
-
cmd.addOption(opt);
updateRequiredOptions(opt);
}
}
}
+ /**
+ * Sets the options.
+ *
+ * @param options the options.
+ */
protected void setOptions(final Options options) {
this.options = options;
this.requiredOptions = new ArrayList<>(options.getRequiredOptions());
@@ -334,7 +316,7 @@ protected void setOptions(final Options options) {
/**
* Removes the option or its group from the list of expected elements.
*
- * @param opt
+ * @param opt the option.
*/
private void updateRequiredOptions(final Option opt) throws ParseException {
// if the option is a required option remove the option from
@@ -342,17 +324,15 @@ private void updateRequiredOptions(final Option opt) throws ParseException {
if (opt.isRequired()) {
getRequiredOptions().remove(opt.getKey());
}
-
// if the option is in an OptionGroup make that option the selected
// option of the group
if (getOptions().getOptionGroup(opt) != null) {
- final OptionGroup group = getOptions().getOptionGroup(opt);
-
- if (group.isRequired()) {
- getRequiredOptions().remove(group);
+ final OptionGroup optionGroup = getOptions().getOptionGroup(opt);
+ if (optionGroup.isRequired()) {
+ getRequiredOptions().remove(optionGroup);
}
-
- group.setSelected(opt);
+ optionGroup.setSelected(opt);
}
}
+
}
diff --git a/src/main/java/org/apache/commons/cli/PatternOptionBuilder.java b/src/main/java/org/apache/commons/cli/PatternOptionBuilder.java
index bc203fe8a..43b36abfa 100644
--- a/src/main/java/org/apache/commons/cli/PatternOptionBuilder.java
+++ b/src/main/java/org/apache/commons/cli/PatternOptionBuilder.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -21,6 +21,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
import java.io.FileInputStream;
import java.net.URL;
import java.util.Date;
+import java.util.Map;
/**
* Allows Options to be created from a single String. The pattern contains various single character flags and via an
@@ -34,15 +35,15 @@ Licensed to the Apache Software Foundation (ASF) under one or more
*
*
* b@
- * -b [classname]
+ * -b [class name]
*
*
* c>
- * -c [filename]
+ * -c [file name]
*
*
* d+
- * -d [classname] (creates object via empty constructor)
+ * -d [class name] (creates object via empty constructor)
*
*
* e%
@@ -50,7 +51,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
*
*
* f/
- * -f [url]
+ * -f [URL]
*
*
* g:
@@ -72,6 +73,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
*
*/
public class PatternOptionBuilder {
+
/** String class */
public static final Class STRING_VALUE = String.class;
@@ -87,13 +89,13 @@ public class PatternOptionBuilder {
/** Class class */
public static final Class> CLASS_VALUE = Class.class;
+ /** FileInputStream class */
+ public static final Class EXISTING_FILE_VALUE = FileInputStream.class;
+
/// can we do this one??
// is meant to check that the file exists, else it errors.
// ie) it's for reading not writing.
- /** FileInputStream class */
- public static final Class EXISTING_FILE_VALUE = FileInputStream.class;
-
/** File class */
public static final Class FILE_VALUE = File.class;
@@ -103,32 +105,50 @@ public class PatternOptionBuilder {
/** URL class */
public static final Class URL_VALUE = URL.class;
+ /** The converter to use for Unimplemented data types */
+ private static final Converter, UnsupportedOperationException> UNSUPPORTED = s -> {
+ throw new UnsupportedOperationException("Not yet implemented");
+ };
+
+ /**
+ * Retrieve the class that {@code ch} represents.
+ *
+ * @param ch the specified character.
+ * @return The class that {@code ch} represents.
+ * @deprecated Use {@link #getValueType(char)}.
+ */
+ @Deprecated // since="1.7.0"
+ public static Object getValueClass(final char ch) {
+ return getValueType(ch);
+ }
+
/**
* Retrieve the class that {@code ch} represents.
*
* @param ch the specified character
* @return The class that {@code ch} represents
+ * @since 1.7.0
*/
- public static Object getValueClass(final char ch) {
+ public static Class> getValueType(final char ch) {
switch (ch) {
case '@':
- return PatternOptionBuilder.OBJECT_VALUE;
+ return OBJECT_VALUE;
case ':':
- return PatternOptionBuilder.STRING_VALUE;
+ return STRING_VALUE;
case '%':
- return PatternOptionBuilder.NUMBER_VALUE;
+ return NUMBER_VALUE;
case '+':
- return PatternOptionBuilder.CLASS_VALUE;
+ return CLASS_VALUE;
case '#':
- return PatternOptionBuilder.DATE_VALUE;
+ return DATE_VALUE;
case '<':
- return PatternOptionBuilder.EXISTING_FILE_VALUE;
+ return EXISTING_FILE_VALUE;
case '>':
- return PatternOptionBuilder.FILE_VALUE;
+ return FILE_VALUE;
case '*':
- return PatternOptionBuilder.FILES_VALUE;
+ return FILES_VALUE;
case '/':
- return PatternOptionBuilder.URL_VALUE;
+ return URL_VALUE;
}
return null;
@@ -151,9 +171,10 @@ public static boolean isValueCode(final char ch) {
* @return The {@link Options} instance
*/
public static Options parsePattern(final String pattern) {
- char opt = ' ';
+ char opt = Char.SP;
boolean required = false;
Class> type = null;
+ Converter, ?> converter = Converter.DEFAULT;
final Options options = new Options();
@@ -163,26 +184,36 @@ public static Options parsePattern(final String pattern) {
// a value code comes after an option and specifies
// details about it
if (!isValueCode(ch)) {
- if (opt != ' ') {
- final Option option = Option.builder(String.valueOf(opt)).hasArg(type != null).required(required).type(type).build();
-
+ if (opt != Char.SP) {
+ // @formatter:off
+ final Option option = Option.builder(String.valueOf(opt))
+ .hasArg(type != null)
+ .required(required)
+ .type(type)
+ .converter(converter)
+ .get();
+ // @formatter:on
// we have a previous one to deal with
options.addOption(option);
required = false;
type = null;
- opt = ' ';
+ converter = Converter.DEFAULT;
}
opt = ch;
} else if (ch == '!') {
required = true;
} else {
- type = (Class>) getValueClass(ch);
+ type = getValueType(ch);
+ final Map>, Converter, ? extends Throwable>> map = TypeHandler.createDefaultMap();
+ // Backward compatibility (probably).
+ map.put(FILES_VALUE, unsupported());
+ converter = new TypeHandler(map).getConverter(getValueType(ch));
}
}
- if (opt != ' ') {
- final Option option = Option.builder(String.valueOf(opt)).hasArg(type != null).required(required).type(type).build();
+ if (opt != Char.SP) {
+ final Option option = Option.builder(String.valueOf(opt)).hasArg(type != null).required(required).type(type).get();
// we have a final one to deal with
options.addOption(option);
@@ -190,4 +221,19 @@ public static Options parsePattern(final String pattern) {
return options;
}
+
+ @SuppressWarnings("unchecked")
+ static T unsupported() {
+ return (T) UNSUPPORTED;
+ }
+
+ /**
+ * Deprecated, only provides static methods.
+ *
+ * @deprecated Will be private or class will be final.
+ */
+ @Deprecated
+ public PatternOptionBuilder() {
+ // empty
+ }
}
diff --git a/src/main/java/org/apache/commons/cli/PosixParser.java b/src/main/java/org/apache/commons/cli/PosixParser.java
index b30466855..b4bf779c3 100644
--- a/src/main/java/org/apache/commons/cli/PosixParser.java
+++ b/src/main/java/org/apache/commons/cli/PosixParser.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -22,50 +22,70 @@ Licensed to the Apache Software Foundation (ASF) under one or more
import java.util.Iterator;
import java.util.List;
+import org.apache.commons.cli.help.OptionFormatter;
+
/**
* The class PosixParser provides an implementation of the {@link Parser#flatten(Options,String[],boolean) flatten}
* method.
*
- * @deprecated since 1.3, use the {@link DefaultParser} instead
+ * @deprecated Since 1.3, use the {@link DefaultParser} instead.
*/
@Deprecated
public class PosixParser extends Parser {
- /** holder for flattened tokens */
+
+ /** Holder for flattened tokens. */
private final List tokens = new ArrayList<>();
- /** specifies if bursting should continue */
+ /** Specifies if bursting should continue.. */
private boolean eatTheRest;
- /** holder for the current option */
+ /** Holder for the current option */
private Option currentOption;
- /** The command line Options */
+ /** The command line Options. */
private Options options;
+ /**
+ * Constructs a new instance.
+ */
+ public PosixParser() {
+ // empty
+ }
+
+ /**
+ * Adds the remaining tokens to the processed tokens list.
+ *
+ * @param iter An iterator over the remaining tokens.
+ */
+ private void addRemaining(final Iterator iter) {
+ if (eatTheRest) {
+ iter.forEachRemaining(tokens::add);
+ }
+ }
+
/**
* Breaks {@code token} into its constituent parts using the following algorithm.
*
*
- * ignore the first character ("- ")
+ * ignore the first character ("- ")
* for each remaining character check if an {@link Option} exists with that id.
- * if an {@link Option} does exist then add that character prepended with "- " to the list of processed
+ * if an {@link Option} does exist then add that character prepended with "- " to the list of processed
* tokens.
* 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.
- * if an {@link Option} does NOT exist AND {@code stopAtNonOption} IS set then add the
- * special token "-- " followed by the remaining characters and also the remaining tokens directly to the
+ * if an {@link Option} does NOT exist AND {@code 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 {@link Option} does NOT exist AND {@code stopAtNonOption} IS NOT set then add
- * that character prepended with "- ".
+ * if an {@link Option} does NOT exist AND {@code stopAtNonOption} IS NOT set then add
+ * that character prepended with "- ".
*
*
- * @param token The current token to be burst
+ * @param token The current token to be burst .
* @param stopAtNonOption Specifies whether to stop processing at the first non-Option encountered.
*/
protected void burstToken(final String token, final boolean stopAtNonOption) {
for (int i = 1; i < token.length(); i++) {
final String ch = String.valueOf(token.charAt(i));
-
if (!options.hasOption(ch)) {
if (stopAtNonOption) {
processNonOptionToken(token.substring(i), true);
@@ -74,12 +94,10 @@ protected void burstToken(final String token, final boolean stopAtNonOption) {
}
break;
}
- tokens.add("-" + ch);
+ tokens.add(OptionFormatter.DEFAULT_OPT_PREFIX + ch);
currentOption = options.getOption(ch);
-
if (currentOption.hasArg() && token.length() != i + 1) {
tokens.add(token.substring(i + 1));
-
break;
}
}
@@ -94,24 +112,24 @@ protected void burstToken(final String token, final boolean stopAtNonOption) {
* The following are the rules used by this flatten method.
*
*
- * if {@code stopAtNonOption} is true then do not burst anymore of {@code arguments} entries, just
+ * if {@code stopAtNonOption} is true then do not burst anymore of {@code arguments} entries, just
* add each successive entry without further processing. Otherwise, ignore {@code stopAtNonOption}.
- * if the current {@code arguments} entry is "-- " just add the entry to the list of processed
+ * if the current {@code arguments} entry is "-- " just add the entry to the list of processed
* tokens
- * if the current {@code arguments} entry is "- " just add the entry to the list of processed tokens
- * if the current {@code arguments} entry is two characters in length and the first character is "- "
+ * if the current {@code arguments} entry is "- " just add the entry to the list of processed tokens
+ * if the current {@code arguments} entry is two characters in length and the first character is "- "
* then check if this is a valid {@link Option} id. If it is a valid id, then add the entry to the list of processed
* tokens and set the current {@link Option} member. If it is not a valid id and {@code stopAtNonOption} is true,
* then the remaining entries are copied to the list of processed tokens. Otherwise, the current entry is ignored.
* if the current {@code arguments} entry is more than two characters in length and the first character is
- * "- " then we need to burst the entry to determine its constituents. For more information on the bursting
+ * "- " then we need to burst the entry to determine its constituents. For more information on the bursting
* algorithm see {@link PosixParser#burstToken(String, boolean) burstToken}.
* if the current {@code arguments} entry is not handled by any of the previous rules, then the entry is added
* to the list of processed tokens.
*
*
- * @param options The command line {@link Options}
- * @param arguments The command line arguments to be parsed
+ * @param options The command line {@link Options}.
+ * @param arguments The command line arguments to be parsed.
* @param stopAtNonOption Specifies whether to stop flattening when an non option is found.
* @return The flattened {@code arguments} String array.
*/
@@ -119,79 +137,56 @@ protected void burstToken(final String token, final boolean stopAtNonOption) {
protected String[] flatten(final Options options, final String[] arguments, final boolean stopAtNonOption) throws ParseException {
init();
this.options = options;
-
// an iterator for the command line tokens
final Iterator iter = Arrays.asList(arguments).iterator();
-
// process each command line token
while (iter.hasNext()) {
// get the next command line token
final String token = iter.next();
-
- // single or double hyphen
- if ("-".equals(token) || "--".equals(token)) {
- tokens.add(token);
- }
-
- // handle long option --foo or --foo=bar
- else if (token.startsWith("--")) {
- final int pos = token.indexOf('=');
- final String opt = pos == -1 ? token : token.substring(0, pos); // --foo
-
- final List matchingOpts = options.getMatchingOptions(opt);
-
- if (matchingOpts.isEmpty()) {
- processNonOptionToken(token, stopAtNonOption);
- } else if (matchingOpts.size() > 1) {
- throw new AmbiguousOptionException(opt, matchingOpts);
- } else {
- currentOption = options.getOption(matchingOpts.get(0));
-
- tokens.add("--" + currentOption.getLongOpt());
- if (pos != -1) {
- tokens.add(token.substring(pos + 1));
+ if (token != null) {
+ // single or double hyphen
+ if (OptionFormatter.DEFAULT_OPT_PREFIX.equals(token) || OptionFormatter.DEFAULT_LONG_OPT_PREFIX.equals(token)) {
+ tokens.add(token);
+ } else if (token.startsWith(OptionFormatter.DEFAULT_LONG_OPT_PREFIX)) {
+ // handle long option --foo or --foo=bar
+ final int pos = DefaultParser.indexOfEqual(token);
+ final String opt = pos == -1 ? token : token.substring(0, pos); // --foo
+ final List matchingOpts = options.getMatchingOptions(opt);
+ if (matchingOpts.isEmpty()) {
+ processNonOptionToken(token, stopAtNonOption);
+ } else if (matchingOpts.size() > 1) {
+ throw new AmbiguousOptionException(opt, matchingOpts);
+ } else {
+ currentOption = options.getOption(matchingOpts.get(0));
+ tokens.add(OptionFormatter.DEFAULT_LONG_OPT_PREFIX + currentOption.getLongOpt());
+ if (pos != -1) {
+ tokens.add(token.substring(pos + 1));
+ }
}
- }
- }
-
- else if (token.startsWith("-")) {
- if (token.length() == 2 || options.hasOption(token)) {
- processOptionToken(token, stopAtNonOption);
- } else if (!options.getMatchingOptions(token).isEmpty()) {
- final List matchingOpts = options.getMatchingOptions(token);
- if (matchingOpts.size() > 1) {
- throw new AmbiguousOptionException(token, matchingOpts);
+ } else if (token.startsWith(OptionFormatter.DEFAULT_OPT_PREFIX)) {
+ if (token.length() == 2 || options.hasOption(token)) {
+ processOptionToken(token, stopAtNonOption);
+ } else if (!options.getMatchingOptions(token).isEmpty()) {
+ final List matchingOpts = options.getMatchingOptions(token);
+ if (matchingOpts.size() > 1) {
+ throw new AmbiguousOptionException(token, matchingOpts);
+ }
+ final Option opt = options.getOption(matchingOpts.get(0));
+ processOptionToken(OptionFormatter.DEFAULT_OPT_PREFIX + opt.getLongOpt(), stopAtNonOption);
}
- final Option opt = options.getOption(matchingOpts.get(0));
- processOptionToken("-" + opt.getLongOpt(), stopAtNonOption);
- }
- // requires bursting
- else {
- burstToken(token, stopAtNonOption);
+ // requires bursting
+ else {
+ burstToken(token, stopAtNonOption);
+ }
+ } else {
+ processNonOptionToken(token, stopAtNonOption);
}
- } else {
- processNonOptionToken(token, stopAtNonOption);
}
-
- gobble(iter);
+ addRemaining(iter);
}
-
return tokens.toArray(Util.EMPTY_STRING_ARRAY);
}
- /**
- * Adds the remaining tokens to the processed tokens list.
- *
- * @param iter An iterator over the remaining tokens
- */
- private void gobble(final Iterator iter) {
- if (eatTheRest) {
- while (iter.hasNext()) {
- tokens.add(iter.next());
- }
- }
- }
-
/**
* Resets the members to their original state i.e. remove all of {@code tokens} entries and set
* {@code eatTheRest} to false.
@@ -202,17 +197,16 @@ private void init() {
}
/**
- * Add the special token "-- " and the current {@code value} to the processed tokens list. Then add all the
+ * Add the special token "-- " and the current {@code value} to the processed tokens list. Then add all the
* remaining {@code argument} values to the processed tokens list.
*
- * @param value The current token
+ * @param value The current token.
*/
private void processNonOptionToken(final String value, final boolean stopAtNonOption) {
if (stopAtNonOption && (currentOption == null || !currentOption.hasArg())) {
eatTheRest = true;
- tokens.add("--");
+ tokens.add(OptionFormatter.DEFAULT_LONG_OPT_PREFIX);
}
-
tokens.add(value);
}
@@ -226,18 +220,16 @@ private void processNonOptionToken(final String value, final boolean stopAtNonOp
* processed tokens list directly.
*
*
- * @param token The current option token
+ * @param token The current option token.
* @param stopAtNonOption Specifies whether flattening should halt at the first non option.
*/
private void processOptionToken(final String token, final boolean stopAtNonOption) {
if (stopAtNonOption && !options.hasOption(token)) {
eatTheRest = true;
}
-
if (options.hasOption(token)) {
currentOption = options.getOption(token);
}
-
tokens.add(token);
}
}
diff --git a/src/main/java/org/apache/commons/cli/TypeHandler.java b/src/main/java/org/apache/commons/cli/TypeHandler.java
index abb7016e8..9795de45b 100644
--- a/src/main/java/org/apache/commons/cli/TypeHandler.java
+++ b/src/main/java/org/apache/commons/cli/TypeHandler.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -19,190 +19,264 @@ Licensed to the Apache Software Foundation (ASF) under one or more
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.net.MalformedURLException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.net.URL;
+import java.nio.file.Path;
import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
/**
- * This is a temporary implementation. TypeHandler will handle the pluggableness of OptionTypes and it will direct all
- * of these types of conversion functionalities to ConvertUtils component in Commons already. BeanUtils I think.
+ * TypeHandler will handle the pluggable conversion and verification of Option types. It handles the mapping of classes to bot converters and verifiers. It
+ * provides the default conversion and verification methods when converters and verifiers are not explicitly set.
+ *
+ * If Options are serialized and deserialized their converters and verifiers will revert to the defaults defined in this class. To correctly de-serialize
+ * Options with custom converters and/or verifiers, using the default serialization methods, this class should be properly configured with the custom converters
+ * and verifiers for the specific class.
+ *
*/
public class TypeHandler {
+
+ /**
+ * The default TypeHandler.
+ */
+ private static final TypeHandler DEFAULT = new TypeHandler();
+
+ /** Value of hex conversion of strings. */
+ private static final int HEX_RADIX = 16;
+
/**
- * Returns the class whose name is {@code classname}.
+ * Returns the class whose name is {@code className}.
*
- * @param classname the class name
- * @return The class if it is found
- * @throws ParseException if the class could not be found
+ * @param className the class name.
+ * @return The class if it is found.
+ * @throws ParseException if the class could not be found.
*/
- public static Class> createClass(final String classname) throws ParseException {
- try {
- return Class.forName(classname);
- } catch (final ClassNotFoundException e) {
- throw new ParseException("Unable to find the class: " + classname);
- }
+ public static Class> createClass(final String className) throws ParseException {
+ return createValue(className, Class.class);
}
/**
- * Returns the date represented by {@code str}.
+ * Returns the date represented by {@code string}.
*
* This method is not yet implemented and always throws an {@link UnsupportedOperationException}.
+ *
*
- * @param str the date string
- * @return The date if {@code str} is a valid date string, otherwise return null.
- * @throws UnsupportedOperationException always
+ * @param string the date string.
+ * @return The date if {@code string} is a valid date string, otherwise return null.
*/
- public static Date createDate(final String str) {
- throw new UnsupportedOperationException("Not yet implemented");
+ public static Date createDate(final String string) {
+ return createValueUnchecked(string, Date.class);
}
/**
- * Returns the File represented by {@code str}.
+ * Creates a default converter map.
*
- * @param str the File location
- * @return The file represented by {@code str}.
+ * @return a default converter map.
+ * @since 1.7.0
*/
- public static File createFile(final String str) {
- return new File(str);
+ public static Map>, Converter, ? extends Throwable>> createDefaultMap() {
+ return putDefaultMap(new HashMap<>());
}
/**
- * Returns the File[] represented by {@code str}.
+ * Returns the File represented by {@code string}.
+ *
+ * @param string the File location.
+ * @return The file represented by {@code string}.
+ */
+ public static File createFile(final String string) {
+ return createValueUnchecked(string, File.class);
+ }
+
+ /**
+ * Creates the File[] represented by {@code string}.
+ *
*
* This method is not yet implemented and always throws an {@link UnsupportedOperationException}.
+ *
*
- * @param str the paths to the files
- * @return The File[] represented by {@code str}.
- * @throws UnsupportedOperationException always
+ * @param string the paths to the files.
+ * @return The File[] represented by {@code string}.
+ * @throws UnsupportedOperationException always.
+ * @deprecated Without replacement.
*/
- public static File[] createFiles(final String str) {
+ @Deprecated // since 1.7.0
+ public static File[] createFiles(final String string) {
// to implement/port:
- // return FileW.findFiles(str);
+ // return FileW.findFiles(string);
throw new UnsupportedOperationException("Not yet implemented");
}
/**
- * Create a number from a String. If a . is present, it creates a Double, otherwise a Long.
+ * Creates a number from a String. If a '.' is present, it creates a Double, otherwise a Long.
*
- * @param str the value
- * @return the number represented by {@code str}
- * @throws ParseException if {@code str} is not a number
+ * @param string the value.
+ * @return the number represented by {@code string}.
+ * @throws ParseException if {@code string} is not a number.
*/
- public static Number createNumber(final String str) throws ParseException {
- try {
- if (str.indexOf('.') != -1) {
- return Double.valueOf(str);
- }
- return Long.valueOf(str);
- } catch (final NumberFormatException e) {
- throw new ParseException(e.getMessage());
- }
+ @Deprecated // since 1.7.0
+ public static Number createNumber(final String string) throws ParseException {
+ return createValue(string, Number.class);
}
/**
- * Create an Object from the classname and empty constructor.
+ * Creates an Object from the class name and empty constructor.
*
- * @param classname the argument value
- * @return the initialized object
- * @throws ParseException if the class could not be found or the object could not be created
+ * @param className the argument value.
+ * @return the initialized object.
+ * @throws ParseException if the class could not be found or the object could not be created.
+ * @deprecated Use {@link #createValue(String, Class)}.
*/
- public static Object createObject(final String classname) throws ParseException {
- final Class> cl;
+ @Deprecated // since 1.7.0
+ public static Object createObject(final String className) throws ParseException {
+ return createValue(className, Object.class);
+ }
- try {
- cl = Class.forName(classname);
- } catch (final ClassNotFoundException cnfe) {
- throw new ParseException("Unable to find the class: " + classname);
- }
+ /**
+ * Creates the URL represented by {@code string}.
+ *
+ * @param string the URL string.
+ * @return The URL in {@code string} is well-formed.
+ * @throws ParseException if the URL in {@code string} is not well-formed.
+ */
+ public static URL createURL(final String string) throws ParseException {
+ return createValue(string, URL.class);
+ }
+ /**
+ * Creates the @code Object} of type {@code clazz} with the value of {@code string}.
+ *
+ * @param string the command line value.
+ * @param clazz the class representing the type of argument.
+ * @param type of argument.
+ * @return The instance of {@code clazz} initialized with the value of {@code string}.
+ * @throws ParseException if the value creation for the given class threw an exception.
+ */
+ public static T createValue(final String string, final Class clazz) throws ParseException {
try {
- return cl.newInstance();
+ return getDefault().getConverter(clazz).apply(string);
} catch (final Exception e) {
- throw new ParseException(e.getClass().getName() + "; Unable to create an instance of: " + classname);
+ throw ParseException.wrap(e);
}
}
/**
- * Returns the URL represented by {@code str}.
+ * Creates the {@code Object} of type {@code obj} with the value of {@code string}.
+ *
+ * @param string the command line value.
+ * @param obj the type of argument.
+ * @return The instance of {@code obj} initialized with the value of {@code string}.
+ * @throws ParseException if the value creation for the given object type failed.
+ * @deprecated Use {@link #createValue(String, Class)}.
+ */
+ @Deprecated // since 1.7.0
+ public static Object createValue(final String string, final Object obj) throws ParseException {
+ return createValue(string, (Class>) obj);
+ }
+
+ /**
+ * Delegates to {@link #createValue(String, Class)} throwing IllegalArgumentException instead of ParseException.
*
- * @param str the URL string
- * @return The URL in {@code str} is well-formed
- * @throws ParseException if the URL in {@code str} is not well-formed
+ * @param string the command line value.
+ * @param clazz the class representing the type of argument.
+ * @param type of argument.
+ * @return The instance of {@code clazz} initialized with the value of {@code string}.
+ * @throws IllegalArgumentException if the value creation for the given class threw an exception.
*/
- public static URL createURL(final String str) throws ParseException {
+ private static T createValueUnchecked(final String string, final Class clazz) {
try {
- return new URL(str);
- } catch (final MalformedURLException e) {
- throw new ParseException("Unable to parse the URL: " + str);
+ return createValue(string, clazz);
+ } catch (final ParseException e) {
+ throw new IllegalArgumentException(e);
}
}
/**
- * Returns the {@code Object} of type {@code clazz} with the value of {@code str}.
+ * Gets the default TypeHandler.
*
- * @param str the command line value
- * @param clazz the class representing the type of argument
- * @param type of argument
- * @return The instance of {@code clazz} initialized with the value of {@code str}.
- * @throws ParseException if the value creation for the given class failed
+ * @return the default TypeHandler.
+ * @since 1.7.0
*/
- @SuppressWarnings("unchecked") // returned value will have type T because it is fixed by clazz
- public static T createValue(final String str, final Class clazz) throws ParseException {
- if (PatternOptionBuilder.STRING_VALUE == clazz) {
- return (T) str;
- }
- if (PatternOptionBuilder.OBJECT_VALUE == clazz) {
- return (T) createObject(str);
- }
- if (PatternOptionBuilder.NUMBER_VALUE == clazz) {
- return (T) createNumber(str);
- }
- if (PatternOptionBuilder.DATE_VALUE == clazz) {
- return (T) createDate(str);
- }
- if (PatternOptionBuilder.CLASS_VALUE == clazz) {
- return (T) createClass(str);
- }
- if (PatternOptionBuilder.FILE_VALUE == clazz) {
- return (T) createFile(str);
- }
- if (PatternOptionBuilder.EXISTING_FILE_VALUE == clazz) {
- return (T) openFile(str);
- }
- if (PatternOptionBuilder.FILES_VALUE == clazz) {
- return (T) createFiles(str);
- }
- if (PatternOptionBuilder.URL_VALUE == clazz) {
- return (T) createURL(str);
- }
- throw new ParseException("Unable to handle the class: " + clazz);
+ public static TypeHandler getDefault() {
+ return DEFAULT;
+ }
+
+ /**
+ * Returns the opened FileInputStream represented by {@code string}.
+ *
+ * @param string the file location.
+ * @return The file input stream represented by {@code string}.
+ * @throws ParseException if the file is not exist or not readable.
+ * @deprecated Use {@link #createValue(String, Class)}.
+ */
+ @Deprecated // since 1.7.0
+ public static FileInputStream openFile(final String string) throws ParseException {
+ return createValue(string, FileInputStream.class);
}
+ private static Map>, Converter, ? extends Throwable>> putDefaultMap(final Map>, Converter, ? extends Throwable>> map) {
+ map.put(Object.class, Converter.OBJECT);
+ map.put(Class.class, Converter.CLASS);
+ map.put(Date.class, Converter.DATE);
+ map.put(File.class, Converter.FILE);
+ map.put(Path.class, Converter.PATH);
+ map.put(Number.class, Converter.NUMBER);
+ map.put(URL.class, Converter.URL);
+ map.put(FileInputStream.class, FileInputStream::new);
+ map.put(Long.class, Long::parseLong);
+ map.put(Integer.class, Integer::parseInt);
+ map.put(Short.class, Short::parseShort);
+ map.put(Byte.class, Byte::parseByte);
+ map.put(Character.class, s -> s.startsWith("\\u") ? Character.toChars(Integer.parseInt(s.substring(2), HEX_RADIX))[0] : s.charAt(0));
+ map.put(Double.class, Double::parseDouble);
+ map.put(Float.class, Float::parseFloat);
+ map.put(BigInteger.class, BigInteger::new);
+ map.put(BigDecimal.class, BigDecimal::new);
+ return map;
+ }
+
+ /**
+ * Map of Class to Converter.
+ *
+ * For each entry, that Class' type must match the Converter's first type.
+ *
+ */
+ private final Map>, Converter, ? extends Throwable>> converterMap;
+
/**
- * Returns the {@code Object} of type {@code obj} with the value of {@code str}.
+ * Constructs a default initialized instance.
+ */
+ public TypeHandler() {
+ this(createDefaultMap());
+ }
+
+ /**
+ * Constructs a default initialized instance.
+ *
+ * For each entry, that Class' type must match the Converter's first type.
+ *
*
- * @param str the command line value
- * @param obj the type of argument
- * @return The instance of {@code obj} initialized with the value of {@code str}.
- * @throws ParseException if the value creation for the given object type failed
+ * @param converterMap The converter map, not null.
+ * @since 1.7.0
*/
- public static Object createValue(final String str, final Object obj) throws ParseException {
- return createValue(str, (Class>) obj);
+ public TypeHandler(final Map>, Converter, ? extends Throwable>> converterMap) {
+ this.converterMap = Objects.requireNonNull(converterMap, "converterMap");
}
/**
- * Returns the opened FileInputStream represented by {@code str}.
+ * Gets the registered converter for the the Class, or {@link Converter#DEFAULT} if absent.
*
- * @param str the file location
- * @return The file input stream represented by {@code str}.
- * @throws ParseException if the file is not exist or not readable
+ * @param The Class parameter type.
+ * @param clazz The Class to get the Converter for.
+ * @return the registered converter if any, {@link Converter#DEFAULT} otherwise.
+ * @since 1.7.0
*/
- public static FileInputStream openFile(final String str) throws ParseException {
- try {
- return new FileInputStream(str);
- } catch (final FileNotFoundException e) {
- throw new ParseException("Unable to find file: " + str);
- }
+ @SuppressWarnings("unchecked") // returned value will have type T because it is fixed by clazz
+ public Converter getConverter(final Class clazz) {
+ return (Converter) converterMap.getOrDefault(clazz, Converter.DEFAULT);
}
+
}
diff --git a/src/main/java/org/apache/commons/cli/UnrecognizedOptionException.java b/src/main/java/org/apache/commons/cli/UnrecognizedOptionException.java
index df9bdf8fe..49f0ed29a 100644
--- a/src/main/java/org/apache/commons/cli/UnrecognizedOptionException.java
+++ b/src/main/java/org/apache/commons/cli/UnrecognizedOptionException.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -33,7 +33,7 @@ public class UnrecognizedOptionException extends ParseException {
/**
* Constructs a new {@code UnrecognizedArgumentException} with the specified detail message.
*
- * @param message the detail message
+ * @param message the detail message.
*/
public UnrecognizedOptionException(final String message) {
this(message, null);
@@ -42,8 +42,8 @@ public UnrecognizedOptionException(final String message) {
/**
* Constructs a new {@code UnrecognizedArgumentException} with the specified option and detail message.
*
- * @param message the detail message
- * @param option the unrecognized option
+ * @param message the detail message.
+ * @param option the unrecognized option.
* @since 1.2
*/
public UnrecognizedOptionException(final String message, final String option) {
@@ -54,7 +54,7 @@ public UnrecognizedOptionException(final String message, final String option) {
/**
* Gets the unrecognized option.
*
- * @return the related option
+ * @return the related option.
* @since 1.2
*/
public String getOption() {
diff --git a/src/main/java/org/apache/commons/cli/Util.java b/src/main/java/org/apache/commons/cli/Util.java
index ed47ba3fc..cca7369f7 100644
--- a/src/main/java/org/apache/commons/cli/Util.java
+++ b/src/main/java/org/apache/commons/cli/Util.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,6 +17,8 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
+import org.apache.commons.cli.help.OptionFormatter;
+
/**
* Contains useful helper methods for classes within this package.
*/
@@ -28,39 +30,62 @@ final class Util {
static final String[] EMPTY_STRING_ARRAY = {};
/**
- * Remove the leading and trailing quotes from {@code str}. E.g. if str is '"one two"', then 'one two' is returned.
+ * Tests whether the given array is null or empty.
*
- * @param str The string from which the leading and trailing quotes should be removed.
+ * @param array the array to test.
+ * @return the given array is null or empty.
+ */
+ static boolean isEmpty(final Object[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
+ * Tests whether the given string is null or empty.
+ *
+ * @param str The string to test.
+ * @return Whether the given string is null or empty.
+ */
+ static boolean isEmpty(final String str) {
+ return str == null || str.isEmpty();
+ }
+
+ /**
+ * Removes the leading and trailing quotes from {@code str}. E.g. if str is '"one two"', then 'one two' is returned.
*
+ * @param str The string from which the leading and trailing quotes should be removed.
* @return The string without the leading and trailing quotes.
*/
- static String stripLeadingAndTrailingQuotes(String str) {
+ static String stripLeadingAndTrailingQuotes(final String str) {
+ if (isEmpty(str)) {
+ return str;
+ }
final int length = str.length();
if (length > 1 && str.startsWith("\"") && str.endsWith("\"") && str.substring(1, length - 1).indexOf('"') == -1) {
- str = str.substring(1, length - 1);
+ return str.substring(1, length - 1);
}
-
return str;
}
/**
- * Remove the hyphens from the beginning of {@code str} and return the new String.
+ * Removes the hyphens from the beginning of {@code str} and return the new String.
*
* @param str The string from which the hyphens should be removed.
- *
* @return the new String.
*/
static String stripLeadingHyphens(final String str) {
- if (str == null) {
- return null;
+ if (isEmpty(str)) {
+ return str;
}
- if (str.startsWith("--")) {
+ if (str.startsWith(OptionFormatter.DEFAULT_LONG_OPT_PREFIX)) {
return str.substring(2);
}
- if (str.startsWith("-")) {
+ if (str.startsWith(OptionFormatter.DEFAULT_OPT_PREFIX)) {
return str.substring(1);
}
-
return str;
}
+
+ private Util() {
+ // no instances
+ }
}
diff --git a/src/main/java/org/apache/commons/cli/doc-files/leaf.svg b/src/main/java/org/apache/commons/cli/doc-files/leaf.svg
new file mode 100644
index 000000000..71de588c6
--- /dev/null
+++ b/src/main/java/org/apache/commons/cli/doc-files/leaf.svg
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/cli/doc-files/logo.png b/src/main/java/org/apache/commons/cli/doc-files/logo.png
new file mode 100644
index 000000000..d9d3358a7
Binary files /dev/null and b/src/main/java/org/apache/commons/cli/doc-files/logo.png differ
diff --git a/src/main/java/org/apache/commons/cli/help/AbstractHelpFormatter.java b/src/main/java/org/apache/commons/cli/help/AbstractHelpFormatter.java
new file mode 100644
index 000000000..03a7f40ff
--- /dev/null
+++ b/src/main/java/org/apache/commons/cli/help/AbstractHelpFormatter.java
@@ -0,0 +1,494 @@
+/*
+ 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
+
+ https://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.help;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionGroup;
+import org.apache.commons.cli.Options;
+
+/**
+ * Helps formatters provides the framework to link a {@link HelpAppendable} with a {@link OptionFormatter} and a default {@link TableDefinition} so to produce
+ * standardized format help output.
+ *
+ * @since 1.10.0
+ */
+public abstract class AbstractHelpFormatter {
+
+ /**
+ * Abstracts building instances for subclasses.
+ *
+ * helpAppendable = a {@link TextHelpAppendable} writing to {@code System.out}
+ * optionFormatter.Builder = the default {@link OptionFormatter.Builder}
+ *
+ *
+ * @param The builder type.
+ * @param The type to build.
+ */
+ public abstract static class Builder, T extends AbstractHelpFormatter> implements Supplier {
+
+ /** The comparator to sort lists of options */
+ private Comparator comparator = DEFAULT_COMPARATOR;
+
+ /** The {@link HelpAppendable}. */
+ private HelpAppendable helpAppendable = TextHelpAppendable.systemOut();
+
+ /** The {@link OptionFormatter.Builder} to use to format options in the table. */
+ private OptionFormatter.Builder optionFormatBuilder = OptionFormatter.builder();
+
+ /** The string to separate option groups. */
+ private String optionGroupSeparator = DEFAULT_OPTION_GROUP_SEPARATOR;
+
+ /**
+ * Constructs a new instance.
+ *
+ * Sets {@code showSince} to {@code true}.
+ *
+ */
+ protected Builder() {
+ // empty
+ }
+
+ /**
+ * Returns this instance cast to {@code B}.
+ *
+ * @return {@code this} instance cast to {@code B}.
+ */
+ @SuppressWarnings("unchecked")
+ protected B asThis() {
+ return (B) this;
+ }
+
+ /**
+ * Gets the comparator to sort lists of options.
+ *
+ * @return the comparator to sort lists of options.
+ */
+ protected Comparator getComparator() {
+ return comparator;
+ }
+
+ /**
+ * Gets {@link HelpAppendable}.
+ *
+ * @return the {@link HelpAppendable}.
+ */
+ protected HelpAppendable getHelpAppendable() {
+ return helpAppendable;
+ }
+
+ /**
+ * Gets {@link OptionFormatter.Builder} to use to format options in the table.
+ *
+ * @return the {@link OptionFormatter.Builder} to use to format options in the table.
+ */
+ protected OptionFormatter.Builder getOptionFormatBuilder() {
+ return optionFormatBuilder;
+ }
+
+ /**
+ * Gets string to separate option groups.
+ *
+ * @return the string to separate option groups.
+ */
+ protected String getOptionGroupSeparator() {
+ return optionGroupSeparator;
+ }
+
+ /**
+ * Sets the comparator to use for sorting options. If set to {@code null} no sorting is performed.
+ *
+ * @param comparator The comparator to use for sorting options.
+ * @return this
+ */
+ public B setComparator(final Comparator comparator) {
+ this.comparator = comparator;
+ return asThis();
+ }
+
+ /**
+ * Sets the {@link HelpAppendable}.
+ *
+ * @param helpAppendable the {@link HelpAppendable} to use.
+ * @return this
+ */
+ public B setHelpAppendable(final HelpAppendable helpAppendable) {
+ this.helpAppendable = helpAppendable != null ? helpAppendable : TextHelpAppendable.systemOut();
+ return asThis();
+ }
+
+ /**
+ * Sets the {@link OptionFormatter.Builder}.
+ *
+ * @param optionFormatBuilder the {@link OptionFormatter.Builder} to use.
+ * @return this
+ */
+ public B setOptionFormatBuilder(final OptionFormatter.Builder optionFormatBuilder) {
+ this.optionFormatBuilder = optionFormatBuilder != null ? optionFormatBuilder : OptionFormatter.builder();
+ return asThis();
+ }
+
+ /**
+ * Sets the OptionGroup separator. Normally " | " or something similar to denote that only one option may be chosen.
+ *
+ * @param optionGroupSeparator the string to separate option group elements with.
+ * @return this
+ */
+ public B setOptionGroupSeparator(final String optionGroupSeparator) {
+ this.optionGroupSeparator = Util.defaultValue(optionGroupSeparator, "");
+ return asThis();
+ }
+
+ }
+
+ /**
+ * The default comparator for {@link Option} implementations.
+ */
+ public static final Comparator DEFAULT_COMPARATOR = (opt1, opt2) -> opt1.getKey().compareToIgnoreCase(opt2.getKey());
+
+ /**
+ * The default separator between {@link OptionGroup} elements: {@value}.
+ */
+ public static final String DEFAULT_OPTION_GROUP_SEPARATOR = " | ";
+
+ /**
+ * The string to display at the beginning of the usage statement: {@value}.
+ */
+ public static final String DEFAULT_SYNTAX_PREFIX = "usage: ";
+
+ /** The comparator for sorting {@link Option} collections */
+ private final Comparator comparator;
+ /**
+ * The {@link HelpAppendable} that produces the final output.
+ */
+ private final HelpAppendable helpAppendable;
+
+ /**
+ * The OptionFormatter.Builder used to display options within the help page.
+ */
+ private final OptionFormatter.Builder optionFormatBuilder;
+
+ /** The separator between {@link OptionGroup} components. */
+ private final String optionGroupSeparator;
+
+ /**
+ * The phrase printed before the syntax line.
+ */
+ private String syntaxPrefix = DEFAULT_SYNTAX_PREFIX;
+
+ /**
+ * Constructs the base formatter.
+ *
+ * @param builder the builder.
+ */
+ protected AbstractHelpFormatter(final Builder, ?> builder) {
+ this.helpAppendable = Objects.requireNonNull(builder.getHelpAppendable(), "helpAppendable");
+ this.optionFormatBuilder = Objects.requireNonNull(builder.getOptionFormatBuilder(), "optionFormatBuilder");
+ this.comparator = Objects.requireNonNull(builder.getComparator(), "comparator");
+ this.optionGroupSeparator = Util.defaultValue(builder.getOptionGroupSeparator(), "");
+ }
+
+ /**
+ * Gets the comparator for sorting options.
+ *
+ * @return The comparator for sorting options.
+ */
+ protected Comparator getComparator() {
+ return comparator;
+ }
+
+ /**
+ * Gets the help appendable.
+ *
+ * @return The help appendable.
+ */
+ protected HelpAppendable getHelpAppendable() {
+ return helpAppendable;
+ }
+
+ /**
+ * Gets the option formatter builder.
+ *
+ * @return The option formatter builder.
+ */
+ protected OptionFormatter.Builder getOptionFormatBuilder() {
+ return optionFormatBuilder;
+ }
+
+ /**
+ * Constructs an {@link OptionFormatter} for the specified {@link Option}.
+ *
+ * @param option The Option to format.
+ * @return an {@link OptionFormatter} for the specified {@link Option}.
+ */
+ public final OptionFormatter getOptionFormatter(final Option option) {
+ return optionFormatBuilder.build(option);
+ }
+
+ /**
+ * Gets the option group separator.
+ *
+ * @return The option group separator.
+ */
+ protected String getOptionGroupSeparator() {
+ return optionGroupSeparator;
+ }
+
+ /**
+ * Gets the {@link HelpAppendable} associated with this help formatter.
+ *
+ * @return The {@link HelpAppendable} associated with this help formatter.
+ */
+ public final HelpAppendable getSerializer() {
+ return helpAppendable;
+ }
+
+ /**
+ * Gets the currently set syntax prefix.
+ *
+ * @return The currently set syntax prefix.
+ */
+ public final String getSyntaxPrefix() {
+ return syntaxPrefix;
+ }
+
+ /**
+ * Converts a collection of {@link Option}s into a {@link TableDefinition}.
+ *
+ * @param options The options to create a table for.
+ * @return the TableDefinition.
+ */
+ protected abstract TableDefinition getTableDefinition(Iterable options);
+
+ /**
+ * Prints the help for {@link Options} with the specified command line syntax.
+ *
+ * @param cmdLineSyntax the syntax for this application.
+ * @param header the banner to display at the beginning of the help.
+ * @param options the collection of {@link Option} objects to print.
+ * @param footer the banner to display at the end of the help.
+ * @param autoUsage whether to print an automatically generated usage statement.
+ * @throws IOException If the output could not be written to the {@link HelpAppendable}.
+ */
+ public void printHelp(final String cmdLineSyntax, final String header, final Iterable options, final String footer, final boolean autoUsage)
+ throws IOException {
+ Options optionsObject = new Options();
+ options.forEach(optionsObject::addOption);
+ printHelp(cmdLineSyntax, header, optionsObject, footer, autoUsage);
+ }
+
+ /**
+ * Prints the help for a collection of {@link Option}s with the specified command line syntax.
+ *
+ * @param cmdLineSyntax the syntax for this application.
+ * @param header the banner to display at the beginning of the help.
+ * @param options the collection of {@link Option} objects to print.
+ * @param footer the banner to display at the end of the help.
+ * @param autoUsage whether to print an automatically generated usage statement.
+ * @throws IOException If the output could not be written to the {@link HelpAppendable}.
+ */
+ public void printHelp(final String cmdLineSyntax, final String header, final Options options, final String footer, final boolean autoUsage)
+ throws IOException {
+ if (Util.isEmpty(cmdLineSyntax)) {
+ throw new IllegalArgumentException("cmdLineSyntax not provided");
+ }
+ if (autoUsage) {
+ helpAppendable.appendParagraphFormat("%s %s %s", syntaxPrefix, cmdLineSyntax, toSyntaxOptions(options));
+ } else {
+ helpAppendable.appendParagraphFormat("%s %s", syntaxPrefix, cmdLineSyntax);
+ }
+ if (!Util.isEmpty(header)) {
+ helpAppendable.appendParagraph(header);
+ }
+ helpAppendable.appendTable(getTableDefinition(options.getOptions()));
+ if (!Util.isEmpty(footer)) {
+ helpAppendable.appendParagraph(footer);
+ }
+ }
+
+ /**
+ * Prints the option table for a collection of {@link Option} objects to the {@link HelpAppendable}.
+ *
+ * @param options the collection of Option objects to print in the table.
+ * @throws IOException If the output could not be written to the {@link HelpAppendable}.
+ */
+ public final void printOptions(final Iterable options) throws IOException {
+ printOptions(getTableDefinition(options));
+ }
+
+ /**
+ * Prints the option table for the specified {@link Options} to the {@link HelpAppendable}.
+ *
+ * @param options the Options to print in the table.
+ * @throws IOException If the output could not be written to the {@link HelpAppendable}.
+ */
+ public final void printOptions(final Options options) throws IOException {
+ printOptions(options.getOptions());
+ }
+
+ /**
+ * Prints a {@link TableDefinition} to the {@link HelpAppendable}.
+ *
+ * @param tableDefinition the {@link TableDefinition} to print.
+ * @throws IOException If the output could not be written to the {@link HelpAppendable}.
+ */
+ public final void printOptions(final TableDefinition tableDefinition) throws IOException {
+ helpAppendable.appendTable(tableDefinition);
+ }
+
+ /**
+ * Sets the syntax prefix. This is the phrase that is printed before the syntax line.
+ *
+ * @param prefix the new value for the syntax prefix.
+ */
+ public final void setSyntaxPrefix(final String prefix) {
+ this.syntaxPrefix = prefix;
+ }
+
+ /**
+ * Creates a new list of options ordered by the comparator.
+ *
+ * @param options the Options to sort.
+ * @return a new list of options ordered by the comparator.
+ */
+ public List sort(final Iterable options) {
+ final List result = new ArrayList<>();
+ if (options != null) {
+ options.forEach(result::add);
+ result.sort(comparator);
+ }
+ return result;
+ }
+
+ /**
+ * Creates a new list of options ordered by the comparator.
+ *
+ * @param options the Options to sort.
+ * @return a new list of options ordered by the comparator.
+ */
+ public List sort(final Options options) {
+ return sort(options == null ? null : options.getOptions());
+ }
+
+ /**
+ * Formats the {@code argName} as an argument a defined in the enclosed {@link OptionFormatter.Builder}.
+ *
+ * @param argName the string to format as an argument.
+ * @return the {@code argName} formatted as an argument.
+ */
+ public final String toArgName(final String argName) {
+ return optionFormatBuilder.toArgName(argName);
+ }
+
+ /**
+ * Return the string representation of the options as used in the syntax display.
+ *
+ * This is probably not the method you want. This method does not track the presence
+ * of option groups. To display the option grouping use {@link #toSyntaxOptions(Options)} or
+ * {@link #toSyntaxOptions(OptionGroup)} for individual groups.
+ *
+ * @param options The collection of {@link Option} instances to create the string representation for.
+ * @return the string representation of the options as used in the syntax display.
+ */
+ public String toSyntaxOptions(final Iterable options) {
+ return toSyntaxOptions(options, o -> null);
+ }
+
+ /**
+ * Return the string representation of the options as used in the syntax display.
+ *
+ * @param options The options to create the string representation for.
+ * @param lookup a function to determine if the Option is part of an OptionGroup that has already been processed.
+ * @return the string representation of the options as used in the syntax display.
+ */
+ protected String toSyntaxOptions(final Iterable options, final Function lookup) {
+ // list of groups that have been processed.
+ final Collection processedGroups = new ArrayList<>();
+ final List optList = sort(options);
+ final StringBuilder buff = new StringBuilder();
+ String prefix = "";
+ // iterate over the options
+ for (final Option option : optList) {
+ // get the next Option
+ // check if the option is part of an OptionGroup
+ final OptionGroup optionGroup = lookup.apply(option);
+ // if the option is part of a group
+ if (optionGroup != null) {
+ // and if the group has not already been processed
+ if (!processedGroups.contains(optionGroup)) {
+ // add the group to the processed list
+ processedGroups.add(optionGroup);
+ // add the usage clause
+ buff.append(prefix).append(toSyntaxOptions(optionGroup));
+ prefix = " ";
+ }
+ // otherwise the option was displayed in the group previously so ignore it.
+ }
+ // if the Option is not part of an OptionGroup
+ else {
+ buff.append(prefix).append(optionFormatBuilder.build(option).toSyntaxOption());
+ prefix = " ";
+ }
+ }
+ return buff.toString();
+ }
+
+ /**
+ * Return the string representation of the options as used in the syntax display.
+ *
+ * @param group The OptionGroup to create the string representation for.
+ * @return the string representation of the options as used in the syntax display.
+ */
+ public String toSyntaxOptions(final OptionGroup group) {
+ final StringBuilder buff = new StringBuilder();
+ final List optList = sort(group.getOptions());
+ OptionFormatter formatter = null;
+ // for each option in the OptionGroup
+ final Iterator iter = optList.iterator();
+ while (iter.hasNext()) {
+ formatter = optionFormatBuilder.build(iter.next());
+ // whether the option is required or not is handled at group level
+ buff.append(formatter.toSyntaxOption(true));
+ if (iter.hasNext()) {
+ buff.append(optionGroupSeparator);
+ }
+ }
+ if (formatter != null) {
+ return group.isRequired() ? buff.toString() : formatter.toOptional(buff.toString());
+ }
+ return ""; // there were no entries in the group.
+ }
+
+ /**
+ * Return the string representation of the options as used in the syntax display.
+ *
+ * @param options The {@link Options} to create the string representation for.
+ * @return the string representation of the options as used in the syntax display.
+ */
+ public String toSyntaxOptions(final Options options) {
+ return toSyntaxOptions(options.getOptions(), options::getOptionGroup);
+ }
+}
diff --git a/src/main/java/org/apache/commons/cli/help/FilterHelpAppendable.java b/src/main/java/org/apache/commons/cli/help/FilterHelpAppendable.java
new file mode 100644
index 000000000..b583fff81
--- /dev/null
+++ b/src/main/java/org/apache/commons/cli/help/FilterHelpAppendable.java
@@ -0,0 +1,72 @@
+/*
+ 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
+
+ https://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.help;
+
+import java.io.IOException;
+
+/**
+ * An abstract implementation of {@link HelpAppendable} that writes output to an {@link Appendable} instance.
+ *
+ * This class is the superclass of all classes that filter help appendables. These appendable sit on top of an existing appendable (the underlying
+ * appendable) which it uses as its basic sink of data, but possibly transforming the data along the way or providing additional functionality.
+ *
+ *
+ * The class {@code FilterHelpAppendable} itself simply overrides all methods of {@code HelpAppendable} with versions that pass all requests to the underlying
+ * appendable. Subclasses of {@code FilterHelpAppendable} may further override some of these methods as well as provide additional methods and fields.
+ *
+ *
+ * Implementation Note : This class is similar to FilterOutputStream in relation to OutputStream. We could further split FilterHelpAppendable into a
+ * FilterAppendable but that seems like YAGNI.
+ *
+ *
+ * @since 1.10.0
+ */
+public abstract class FilterHelpAppendable implements HelpAppendable {
+
+ /**
+ * The underlying appendable to be filtered.
+ */
+ protected final Appendable output;
+
+ /**
+ * Constructs an appendable filter built on top of the specified underlying appendable.
+ *
+ * @param output the underlying appendable to be assigned to the field {@code this.output} for later use, or {@code null} if this instance is to be created
+ * without an underlying stream.
+ */
+ protected FilterHelpAppendable(final Appendable output) {
+ this.output = output;
+ }
+
+ @Override
+ public FilterHelpAppendable append(final char ch) throws IOException {
+ output.append(ch);
+ return this;
+ }
+
+ @Override
+ public FilterHelpAppendable append(final CharSequence text) throws IOException {
+ output.append(text);
+ return this;
+ }
+
+ @Override
+ public FilterHelpAppendable append(final CharSequence csq, final int start, final int end) throws IOException {
+ output.append(csq, start, end);
+ return this;
+ }
+}
diff --git a/src/main/java/org/apache/commons/cli/help/HelpAppendable.java b/src/main/java/org/apache/commons/cli/help/HelpAppendable.java
new file mode 100644
index 000000000..921a0be3c
--- /dev/null
+++ b/src/main/java/org/apache/commons/cli/help/HelpAppendable.java
@@ -0,0 +1,122 @@
+/*
+ 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
+
+ https://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.help;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Formatter;
+import java.util.IllegalFormatException;
+
+/**
+ * Defines a semantic scribe. The semantic scribe appends text to an output based on the semantic meaning of the type of string. For example, a Paragraph versus
+ * a Heading.
+ *
+ * The representation of the semantics is dependent upon the format being output. For example, the plain text output for a paragraph may print the text followed
+ * by two line breaks, while an XHTML output would print the text surrounded by <p> and </p>.
+ *
+ *
+ * Note the {@link Appendable} documentation on the topics of Unicode and threading, these comments also apply here.
+ *
+ *
+ * @since 1.10.0
+ */
+public interface HelpAppendable extends Appendable {
+
+ /**
+ * Appends a formatted string using the specified format string and arguments.
+ *
+ * Short-hand for calling:
+ *
+ *
+ *
+ * helpAppendable.{@link #append(CharSequence) append.}({@link String#format(String, Object...) String.format}(format, args));
+ *
+ *
+ * @param format The format string for {@link String#format(String, Object...)}.
+ * @param args Arguments to {@link String#format(String, Object...)}.
+ * @throws IllegalFormatException See {@link String#format(String, Object...)}.
+ * @throws IOException If an output error occurs.
+ * @see String#format(String, Object...)
+ * @see Formatter
+ * @see #append(CharSequence)
+ */
+ default void appendFormat(final String format, final Object... args) throws IOException {
+ append(String.format(format, args));
+ }
+
+ /**
+ * Appends a header.
+ *
+ * @param level the level of the header. This is equivalent to the "1", "2", or "3" in the HTML "h1", "h2", "h3" tags.
+ * @param text the text for the header, null is a noop.
+ * @throws IOException If an output error occurs.
+ */
+ void appendHeader(int level, CharSequence text) throws IOException;
+
+ /**
+ * Appends a list.
+ *
+ * @param ordered {@code true} if the list should be ordered.
+ * @param list the list to write, null is a noop.
+ * @throws IOException If an output error occurs.
+ */
+ void appendList(boolean ordered, Collection list) throws IOException;
+
+ /**
+ * Appends a paragraph.
+ *
+ * @param paragraph the paragraph to write, null is a noop.
+ * @throws IOException If an output error occurs.
+ */
+ void appendParagraph(CharSequence paragraph) throws IOException;
+
+ /**
+ * Appends a formatted string as a paragraph.
+ *
+ *
+ * helpAppendable.{@link #appendParagraph(CharSequence) appendParagraph.}({@link String#format(String, Object...) String.format}(format, args));
+ *
+ *
+ * @param format The format string for {@link String#format(String, Object...)}.
+ * @param args Arguments to {@link String#format(String, Object...)}.
+ * @throws IllegalFormatException See {@link String#format(String, Object...)}.
+ * @throws IOException If an output error occurs.
+ * @see String#format(String, Object...)
+ * @see Formatter
+ * @see #append(CharSequence)
+ */
+ default void appendParagraphFormat(final String format, final Object... args) throws IOException {
+ appendParagraph(String.format(format, args));
+ }
+
+ /**
+ * Appends a table.
+ *
+ * @param table the table definition to write, null is a noop.
+ * @throws IOException If an output error occurs.
+ */
+ void appendTable(TableDefinition table) throws IOException;
+
+ /**
+ * Appends a title.
+ *
+ * @param title the title to write, null is a noop.
+ * @throws IOException If an output error occurs.
+ */
+ void appendTitle(CharSequence title) throws IOException;
+
+}
diff --git a/src/main/java/org/apache/commons/cli/help/HelpFormatter.java b/src/main/java/org/apache/commons/cli/help/HelpFormatter.java
new file mode 100644
index 000000000..c4081ec4b
--- /dev/null
+++ b/src/main/java/org/apache/commons/cli/help/HelpFormatter.java
@@ -0,0 +1,189 @@
+/*
+ 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
+
+ https://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.help;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.cli.Option;
+
+/**
+ * A default formatter implementation for standard usage.
+ *
+ * Example:
+ *
+ *
+ *
+ * Options options = new Options();
+ * options.addOption(OptionBuilder.withLongOpt("file").withDescription("The file to be processed").hasArg().withArgName("FILE").isRequired().create('f'));
+ * options.addOption(OptionBuilder.withLongOpt("version").withDescription("Print the version of the application").create('v'));
+ * options.addOption(OptionBuilder.withLongOpt("help").create('h'));
+ *
+ * String header = "Do something useful with an input file";
+ * String footer = "Please report issues at https://example.com/issues";
+ *
+ * HelpFormatter formatter = new HelpFormatter();
+ * formatter.printHelp("myapp", header, options, footer, true);
+ *
+ *
+ * This produces the following output:
+ *
+ *
+ *
+ * {@code
+ * usage: myapp -f [-h] [-v]
+ * Do something useful with an input file
+ *
+ * -f,--file The file to be processed
+ * -h,--help
+ * -v,--version Print the version of the application
+ *
+ * Please report issues at https://example.com/issues
+ * }
+ *
+ *
+ * @since 1.10.0
+ */
+public class HelpFormatter extends AbstractHelpFormatter {
+
+ /**
+ * A builder for the HelpFormatter. Intended to make more complex uses of the HelpFormatter class easier. Default values are:
+ *
+ * showSince = true
+ * helpAppendable = a {@link TextHelpAppendable} writing to {@code System.out}
+ * optionFormatter.Builder = the default {@link OptionFormatter.Builder}
+ *
+ */
+ public static class Builder extends AbstractHelpFormatter.Builder {
+
+ /** If {@code true} show the "Since" column, otherwise ignore it. */
+ private boolean showSince = true;
+
+ /**
+ * Constructs a new instace.
+ *
+ * Sets {@code showSince} to {@code true}.
+ *
+ */
+ protected Builder() {
+ // empty
+ }
+
+ @Override
+ public HelpFormatter get() {
+ return new HelpFormatter(this);
+ }
+
+ /**
+ * Sets the showSince flag.
+ *
+ * @param showSince the desired value of the showSince flag.
+ * @return {@code this} instance.
+ */
+ public Builder setShowSince(final boolean showSince) {
+ this.showSince = showSince;
+ return this;
+ }
+ }
+
+ /**
+ * Default number of characters per line: {@value}.
+ */
+ public static final int DEFAULT_WIDTH = 74;
+
+ /**
+ * Default padding to the left of each line: {@value}.
+ */
+ public static final int DEFAULT_LEFT_PAD = 1;
+
+ /**
+ * The default number of spaces between columns in the options table: {@value}.
+ */
+ public static final int DEFAULT_COLUMN_SPACING = 5;
+
+ /**
+ * Constructs a new builder.
+ *
+ * @return a new builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** If {@code true} show the "Since" column, otherwise ignore it. */
+ private final boolean showSince;
+
+ /**
+ * Constructs the Help formatter.
+ *
+ * @param builder the Builder to build from.
+ */
+ protected HelpFormatter(final Builder builder) {
+ super(builder);
+ this.showSince = builder.showSince;
+ }
+
+ /**
+ * Gets the table definition for the options.
+ *
+ * @param options the collection of {@link Option} instances to create the table from.
+ * @return A {@link TableDefinition} to display the options.
+ */
+ @Override
+ public TableDefinition getTableDefinition(final Iterable options) {
+ // set up the base TextStyle for the columns configured for the Option opt and arg values.
+ final TextStyle.Builder builder = TextStyle.builder().setAlignment(TextStyle.Alignment.LEFT).setIndent(DEFAULT_LEFT_PAD).setScalable(false);
+ final List styles = new ArrayList<>();
+ styles.add(builder.get());
+ // set up showSince column
+ builder.setScalable(true).setLeftPad(DEFAULT_COLUMN_SPACING);
+ if (showSince) {
+ builder.setAlignment(TextStyle.Alignment.CENTER);
+ styles.add(builder.get());
+ }
+ // set up the description column.
+ builder.setAlignment(TextStyle.Alignment.LEFT);
+ styles.add(builder.get());
+ // setup the rows for the table.
+ final List> rows = new ArrayList<>();
+ final StringBuilder sb = new StringBuilder();
+ options.forEach(option -> {
+ final List row = new ArrayList<>();
+ // create an option formatter to correctly format the parts of the option
+ final OptionFormatter formatter = getOptionFormatBuilder().build(option);
+ sb.setLength(0);
+ // append the opt values.
+ sb.append(formatter.getBothOpt());
+ // append the arg name if it exists.
+ if (option.hasArg()) {
+ sb.append(" ").append(formatter.getArgName());
+ }
+ row.add(sb.toString());
+ // append the "since" value if desired.
+ if (showSince) {
+ row.add(formatter.getSince());
+ }
+ // add the option description
+ row.add(formatter.getDescription());
+ rows.add(row);
+ });
+ // return the TableDefinition with the proper column headers.
+ return TableDefinition.from("", styles, showSince ? Arrays.asList("Options", "Since", "Description") : Arrays.asList("Options", "Description"), rows);
+ }
+}
diff --git a/src/main/java/org/apache/commons/cli/help/OptionFormatter.java b/src/main/java/org/apache/commons/cli/help/OptionFormatter.java
new file mode 100644
index 000000000..54e292fff
--- /dev/null
+++ b/src/main/java/org/apache/commons/cli/help/OptionFormatter.java
@@ -0,0 +1,480 @@
+/*
+ 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
+
+ https://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.help;
+
+import java.util.Arrays;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.commons.cli.DeprecatedAttributes;
+import org.apache.commons.cli.Option;
+
+/**
+ * The definition of how to display Option attributes.
+ *
+ * @since 1.10.0
+ */
+public final class OptionFormatter {
+
+ /**
+ * Builds instances of {@link OptionFormatter}.
+ */
+ public static final class Builder implements Supplier {
+
+ /** The argument name delimiters */
+ private final String[] argNameDelimiters;
+
+ /** The default argument name */
+ private String defaultArgName;
+
+ /** The function to create the deprecated message for an option */
+ private Function deprecatedFormatFunction;
+
+ /** The long option prefix */
+ private String longOptPrefix;
+
+ /** The option prefix */
+ private String optPrefix;
+
+ /** The separator between long and short options */
+ private String optSeparator;
+
+ /** The separator between the opt and/or longOpt and the argument name */
+ private String optArgSeparator;
+
+ /** The delimiters surrounding optional {@link Option} instances. */
+ private final String[] optionalDelimiters;
+
+ /** A function to convert the {@link OptionFormatter} into an entry in the syntax description. */
+ private BiFunction syntaxFormatFunction;
+
+ /**
+ * Default constructor. Uses the defaults specified in {@link OptionFormatter}.
+ */
+ private Builder() {
+ argNameDelimiters = Arrays.copyOf(DEFAULT_ARG_NAME_DELIMITERS, 2);
+ defaultArgName = DEFAULT_ARG_NAME;
+ deprecatedFormatFunction = NO_DEPRECATED_FORMAT;
+ longOptPrefix = DEFAULT_LONG_OPT_PREFIX;
+ optPrefix = DEFAULT_OPT_PREFIX;
+ optSeparator = DEFAULT_OPT_SEPARATOR;
+ optArgSeparator = DEFAULT_OPT_ARG_SEPARATOR;
+ optionalDelimiters = Arrays.copyOf(DEFAULT_OPTIONAL_DELIMITERS, 2);
+ }
+
+ /**
+ * Constructor that takes the arguments from the supplied {@link OptionFormatter}
+ *
+ * @param optionFormatter The option formatter to provide values for the builder.
+ */
+ public Builder(final OptionFormatter optionFormatter) {
+ optionalDelimiters = Arrays.copyOf(optionFormatter.optionalDelimiters, 2);
+ argNameDelimiters = Arrays.copyOf(optionFormatter.argNameDelimiters, 2);
+ defaultArgName = optionFormatter.defaultArgName;
+ optPrefix = optionFormatter.optPrefix;
+ longOptPrefix = optionFormatter.longOptPrefix;
+ optSeparator = optionFormatter.optSeparator;
+ deprecatedFormatFunction = optionFormatter.deprecatedFormatFunction;
+ syntaxFormatFunction = optionFormatter.syntaxFormatFunction;
+ }
+
+ /**
+ * Build an OptionFormatter to format the specified option.
+ *
+ * @param option The Option to format.
+ * @return An OptionFormatter to format the specified option.
+ */
+ public OptionFormatter build(final Option option) {
+ return new OptionFormatter(option, this);
+ }
+
+ @Override
+ public OptionFormatter get() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * Specifies the starting and ending argument name delimiters for {@link Option} instances.
+ *
+ * @param begin the beginning delimiter.
+ * @param end the ending delimiter.
+ * @return {@code this} instance.
+ */
+ public Builder setArgumentNameDelimiters(final String begin, final String end) {
+ this.argNameDelimiters[0] = Util.defaultValue(begin, "");
+ this.argNameDelimiters[1] = Util.defaultValue(end, "");
+ return this;
+ }
+
+ /**
+ * Sets the default argument name.
+ *
+ * @param name the new value of default argument name.
+ * @return this
+ */
+ public Builder setDefaultArgName(final String name) {
+ this.defaultArgName = Util.defaultValue(name, DEFAULT_ARG_NAME);
+ return this;
+ }
+
+ /**
+ * Specifies the function to construct the deprecated massage for the Option. Should include the description text if desired.
+ *
+ * @param deprecatedFormatFunction the function to specify the deprecated message for the option.
+ * @return {@code this} instance.
+ */
+ public Builder setDeprecatedFormatFunction(final Function deprecatedFormatFunction) {
+ this.deprecatedFormatFunction = deprecatedFormatFunction;
+ return this;
+ }
+
+ /**
+ * Sets the long option prefix.
+ *
+ * @param prefix prefix for long options.
+ * @return this
+ */
+ public Builder setLongOptPrefix(final String prefix) {
+ this.longOptPrefix = Util.defaultValue(prefix, "");
+ return this;
+ }
+
+ /**
+ * Sets the separator displayed between a options and the argument name. Typically ' ' or '='.
+ *
+ * @param optArgSeparator the separator.
+ * @return this
+ * @since 1.3
+ */
+ public Builder setOptArgSeparator(final String optArgSeparator) {
+ this.optArgSeparator = Util.defaultValue(optArgSeparator, "");
+ return this;
+ }
+
+ /**
+ * Specifies the starting and ending delimiters for optional {@link Option} instances.
+ *
+ * @param begin the beginning delimiter.
+ * @param end the ending delimiter.
+ * @return {@code this} instance.
+ */
+ public Builder setOptionalDelimiters(final String begin, final String end) {
+ this.optionalDelimiters[0] = Util.defaultValue(begin, "");
+ this.optionalDelimiters[1] = Util.defaultValue(end, "");
+ return this;
+ }
+
+ /**
+ * Specifies the short option prefix.
+ *
+ * @param optPrefix the prefix for short options.
+ * @return {@code this} instance.
+ */
+ public Builder setOptPrefix(final String optPrefix) {
+ this.optPrefix = Util.defaultValue(optPrefix, "");
+ return this;
+ }
+
+ /**
+ * Sets the separator displayed between a long option and short options. Typically ',' or ' '.
+ *
+ * @param optSeparator the separator.
+ * @return this
+ * @since 1.3
+ */
+ public Builder setOptSeparator(final String optSeparator) {
+ this.optSeparator = Util.defaultValue(optSeparator, "");
+ return this;
+ }
+
+ /**
+ * Specifies the function to convert an {@link OptionFormatter} into the syntax format for the option.
+ *
+ * @param syntaxFormatFunction The function to convert an {@link OptionFormatter} into the syntax format for the option.
+ * @return this
+ */
+ public Builder setSyntaxFormatFunction(final BiFunction syntaxFormatFunction) {
+ this.syntaxFormatFunction = syntaxFormatFunction;
+ return this;
+ }
+
+ /**
+ * A helper method to format any string as an argument name based on this builder.
+ *
+ * @param argName the name of the argument.
+ * @return the formatted argument.
+ */
+ public String toArgName(final String argName) {
+ return argNameDelimiters[0] + Util.defaultValue(argName, "") + argNameDelimiters[1];
+ }
+ }
+
+ /** The default delimiters for optional arguments */
+ private static final String[] DEFAULT_OPTIONAL_DELIMITERS = { "[", "]" };
+
+ /** The default delimiters for an argument name */
+ private static final String[] DEFAULT_ARG_NAME_DELIMITERS = { "<", ">" };
+
+ /**
+ * The default argument name: {@value}.
+ */
+ public static final String DEFAULT_ARG_NAME = "arg";
+
+ /**
+ * A function to display a deprecated option with the "[Deprecated]" prefix.
+ */
+ public static final Function SIMPLE_DEPRECATED_FORMAT = o -> "[Deprecated] " + Util.defaultValue(o.getDescription(), "");
+
+ /**
+ * A function to display a deprecated option with a "Deprecated" prefix that displays all deprecation information.
+ */
+ public static final Function COMPLEX_DEPRECATED_FORMAT = o -> {
+ final StringBuilder sb = new StringBuilder("[Deprecated");
+ final DeprecatedAttributes attr = o.getDeprecated();
+ if (attr.isForRemoval()) {
+ sb.append(" for removal");
+ }
+ if (!Util.isEmpty(attr.getSince())) {
+ sb.append(" since ").append(attr.getSince());
+ }
+ if (!Util.isEmpty(attr.getDescription())) {
+ sb.append(". ").append(attr.getDescription());
+ }
+ sb.append("]");
+ if (!Util.isEmpty(o.getDescription())) {
+ sb.append(" ").append(o.getDescription());
+ }
+ return sb.toString();
+ };
+
+ /**
+ * A function to display a deprecated option with the "[Deprecated]" prefix.
+ */
+ public static final Function NO_DEPRECATED_FORMAT = o -> Util.defaultValue(o.getDescription(), "");
+
+ /**
+ * The string to display at the beginning of the usage statement: {@value}.
+ */
+ public static final String DEFAULT_SYNTAX_PREFIX = "usage: ";
+
+ /**
+ * Default prefix for short options: {@value}.
+ */
+ public static final String DEFAULT_OPT_PREFIX = "-";
+
+ /**
+ * Default prefix for long options: {@value}.
+ */
+ public static final String DEFAULT_LONG_OPT_PREFIX = "--";
+
+ /**
+ * The default separator between options: {@value}.
+ */
+ public static final String DEFAULT_OPT_SEPARATOR = ", ";
+
+ /**
+ * The default separator between the opt and/or longOpt and the argument name: {@value}.
+ */
+ public static final String DEFAULT_OPT_ARG_SEPARATOR = " ";
+
+ /**
+ * Creates a new builder.
+ *
+ * @return a new builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Construct the {@link OptionFormatter} from an {@link Option} using the default {@link OptionFormatter.Builder}.
+ *
+ * @param option the option to format.
+ * @return an OptionFormatter for the specified @{code option}.
+ */
+ public static OptionFormatter from(final Option option) {
+ return new Builder().build(option);
+ }
+
+ /**
+ * The delimiters around argument names.
+ */
+ private final String[] argNameDelimiters;
+
+ /** The default argument name. */
+ private final String defaultArgName;
+
+ /** The function to display the deprecated option message. */
+ private final Function deprecatedFormatFunction;
+
+ /** The prefix for the long option text. */
+ private final String longOptPrefix;
+
+ /** The prefix for the short option text. */
+ private final String optPrefix;
+
+ /** The separator between the options. */
+ private final String optSeparator;
+
+ /** the separator between the opt and/or longOpt and the argument name. */
+ private final String optArgSeparator;
+
+ /** The delimiters for optional {@link Option}s. */
+ private final String[] optionalDelimiters;
+
+ /** The method to convert an Option formatter into a syntax notation. */
+ private final BiFunction syntaxFormatFunction;
+
+ /** The {@link Option} being formatted. */
+ private final Option option;
+
+ /**
+ * An OptionFormatter applies formatting options to various {@link Option} attributes for textual display.
+ *
+ * @param option the Option to apply formatting to.
+ * @param builder The Builder that specifies the various formatting options.
+ */
+ private OptionFormatter(final Option option, final Builder builder) {
+ this.optionalDelimiters = builder.optionalDelimiters;
+ this.argNameDelimiters = builder.argNameDelimiters;
+ this.defaultArgName = builder.defaultArgName;
+ this.optPrefix = builder.optPrefix;
+ this.longOptPrefix = builder.longOptPrefix;
+ this.optSeparator = builder.optSeparator;
+ this.optArgSeparator = builder.optArgSeparator;
+ this.deprecatedFormatFunction = builder.deprecatedFormatFunction;
+ this.option = option;
+ this.syntaxFormatFunction = builder.syntaxFormatFunction != null ? builder.syntaxFormatFunction : (o, required) -> {
+ final StringBuilder buff = new StringBuilder();
+ final String argName = o.getArgName();
+ buff.append(Util.defaultValue(o.getOpt(), o.getLongOpt()));
+ if (!Util.isEmpty(argName)) {
+ buff.append(optArgSeparator).append(argName);
+ }
+ final boolean requiredFlg = required == null ? o.isRequired() : required;
+ return requiredFlg ? buff.toString() : o.toOptional(buff.toString());
+ };
+ }
+
+ /**
+ * Gets the argument name wrapped in the argument name delimiters.
+ *
+ * If option has no arguments an empty string is returned
+ * If the argument name is not set the default argument name is used.
+ *
+ *
+ * @return The argument name wrapped in the argument name delimiters or an empty string.
+ */
+ public String getArgName() {
+ return option.hasArg() ? argNameDelimiters[0] + Util.defaultValue(option.getArgName(), defaultArgName) + argNameDelimiters[1] : "";
+ }
+
+ /**
+ * Gets both options separated by the specified option separator. Correctly handles the case where one option is not specified.
+ *
+ * @return The one or both of the short and/or long Opt with the associate prefixes.
+ */
+ public String getBothOpt() {
+ final String lOpt = getLongOpt();
+
+ final StringBuilder sb = new StringBuilder(getOpt());
+ if (sb.length() > 0 && !Util.isEmpty(lOpt)) {
+ sb.append(optSeparator);
+ }
+ // sb will not be empty as Option requries at least one of opt or longOpt.
+ return sb.append(getLongOpt()).toString();
+ }
+
+ /**
+ * Gets the description for the option. This will include any deprecation notices if the deprecated format function has been set.
+ *
+ * @return The Description from the option or an empty string is no description was provided and the option is not deprecated.
+ */
+ public String getDescription() {
+ return option.isDeprecated() ? deprecatedFormatFunction.apply(option) : Util.defaultValue(option.getDescription(), "");
+ }
+
+ /**
+ * Gets the long Opt from the @{link Option} with the associate prefix.
+ *
+ * @return The long Opt from the @{link Option} with the associate prefix or an empty string.
+ */
+ public String getLongOpt() {
+ return Util.isEmpty(option.getLongOpt()) ? "" : longOptPrefix + option.getLongOpt();
+ }
+
+ /**
+ * Gets the Opt from the @{link Option} with the associate prefix.
+ *
+ * @return The Opt from the @{link Option} with the associate prefix or an empty string.
+ */
+ public String getOpt() {
+ return Util.isEmpty(option.getOpt()) ? "" : optPrefix + option.getOpt();
+ }
+
+ /**
+ * Gets the "since" value from the Option.
+ *
+ * @return The since valeu from the option or "--" if no since value was set.
+ */
+ public String getSince() {
+ return Util.defaultValue(option.getSince(), DEFAULT_LONG_OPT_PREFIX);
+ }
+
+ /**
+ * Gets the required flag from the enclosed {@link Option}.
+ *
+ * @return The required flag from the enclosed {@link Option}.
+ */
+ public boolean isRequired() {
+ return option.isRequired();
+ }
+
+ /**
+ * Wraps the provided text in the optional delimiters.
+ *
+ * @param text the text to wrap.
+ * @return The text wrapped in the optional delimiters or an eppty string of the text is null or an empty string.
+ */
+ public String toOptional(final String text) {
+ if (Util.isEmpty(text)) {
+ return "";
+ }
+ return optionalDelimiters[0] + text + optionalDelimiters[1];
+ }
+
+ /**
+ * Gets the syntax format for this option.
+ *
+ * @return the syntax format for this option as specified by the syntaxFormatFunction.
+ */
+ public String toSyntaxOption() {
+ return toSyntaxOption(isRequired());
+ }
+
+ /**
+ * Gets the syntax format for this option.
+ *
+ * @param isRequired if {@code true} the options is printed as a required option, otherwise it is optional.
+ * @return the syntax format for this option as specified by the syntaxFormatFunction.
+ */
+ public String toSyntaxOption(final boolean isRequired) {
+ return syntaxFormatFunction.apply(this, isRequired);
+ }
+}
diff --git a/src/main/java/org/apache/commons/cli/help/TableDefinition.java b/src/main/java/org/apache/commons/cli/help/TableDefinition.java
new file mode 100644
index 000000000..73d60e5b7
--- /dev/null
+++ b/src/main/java/org/apache/commons/cli/help/TableDefinition.java
@@ -0,0 +1,102 @@
+/*
+ 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
+
+ https://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.help;
+
+import java.util.List;
+
+/**
+ * The definition of a table to display.
+ *
+ * Aa table definition contains a caption and data that describes each column. Every column in the table may have
+ *
+ *
+ * A caption.
+ * A {@link TextStyle} that describes the width of the entry, its offset from the previous column (leftPad) and how much each line after the first should be
+ * indented (indent).
+ * A heading (String) is placed at the top of the column
+ * A collection of rows
+ *
+ *
+ * @since 1.10.0
+ */
+public interface TableDefinition {
+
+ /**
+ * A helper function to create a table instance from the various components.
+ *
+ * @param caption The caption, may be {@code null}.
+ * @param columnStyle a list of TextStyle elements defining the columns.
+ * @param headers the list of column headers.
+ * @param rows a collection of rows.
+ * @return A TableDefinition returning the parameters as appropriate.
+ */
+ static TableDefinition from(final String caption, final List columnStyle, final List headers, final Iterable> rows) {
+ return new TableDefinition() {
+
+ @Override
+ public String caption() {
+ return caption;
+ }
+
+ @Override
+ public List columnTextStyles() {
+ return columnStyle;
+ }
+
+ @Override
+ public List headers() {
+ return headers;
+ }
+
+ @Override
+ public Iterable> rows() {
+ return rows;
+ }
+ };
+ }
+
+ /**
+ * Gets the caption for the table. May be @{code null}.
+ *
+ * @return The caption for the table. May be @{code null}.
+ */
+ String caption();
+
+ /**
+ * Gets the list TextStyles. One for each column in order.
+ *
+ * @return the list of TextStyles.
+ */
+ List columnTextStyles();
+
+ /**
+ * Gets the list of header strings. One for each column in order.
+ *
+ * @return The list of header strings.
+ */
+ List headers();
+
+ /**
+ * Gets the collection of rows.
+ *
+ * Each row is a list of Strings, one for each column in the table.
+ *
+ *
+ * @return The collection of rows.
+ */
+ Iterable> rows();
+}
diff --git a/src/main/java/org/apache/commons/cli/help/TextHelpAppendable.java b/src/main/java/org/apache/commons/cli/help/TextHelpAppendable.java
new file mode 100644
index 000000000..232041436
--- /dev/null
+++ b/src/main/java/org/apache/commons/cli/help/TextHelpAppendable.java
@@ -0,0 +1,459 @@
+/*
+ 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
+
+ https://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.help;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+
+/**
+ * Writes text format output.
+ *
+ * @since 1.10.0
+ */
+public class TextHelpAppendable extends FilterHelpAppendable {
+
+ /** The default number of characters per line: {@value}. */
+ public static final int DEFAULT_WIDTH = 74;
+
+ /** The default padding to the left of each line: {@value}. */
+ public static final int DEFAULT_LEFT_PAD = 1;
+
+ /** The number of space characters to be prefixed to each description line: {@value}. */
+ public static final int DEFAULT_INDENT = 3;
+
+ /** The number of space characters before a list continuation line: {@value}. */
+ public static final int DEFAULT_LIST_INDENT = 7;
+
+ /** A blank line in the output: {@value}. */
+ private static final String BLANK_LINE = "";
+
+ /** The set of characters that are breaks in text. */
+ // @formatter:off
+ private static final Set BREAK_CHAR_SET = Collections.unmodifiableSet(new HashSet<>(Arrays.asList('\t', '\n', '\f', '\r',
+ (char) Character.LINE_SEPARATOR,
+ (char) Character.PARAGRAPH_SEPARATOR,
+ '\u000b', // VERTICAL TABULATION.
+ '\u001c', // FILE SEPARATOR.
+ '\u001d', // GROUP SEPARATOR.
+ '\u001e', // RECORD SEPARATOR.
+ '\u001f' // UNIT SEPARATOR.
+ )));
+ // @formatter:on
+
+ /**
+ * Finds the next text wrap position after {@code startPos} for the text in {@code text} with the column width {@code width}. The wrap point is the last
+ * position before startPos+width having a whitespace character (space, \n, \r). If there is no whitespace character before startPos+width, it will return
+ * startPos+width.
+ *
+ * @param text The text being searched for the wrap position
+ * @param width width of the wrapped text
+ * @param startPos position from which to start the lookup whitespace character
+ * @return position on which the text must be wrapped or @{code text.length()} if the wrap position is at the end of the text.
+ */
+ public static int indexOfWrap(final CharSequence text, final int width, final int startPos) {
+ if (width < 1) {
+ throw new IllegalArgumentException("Width must be greater than 0");
+ }
+ // handle case of width > text.
+ // the line ends before the max wrap pos or a new line char found
+ int limit = Math.min(startPos + width, text.length());
+ for (int idx = startPos; idx < limit; idx++) {
+ if (BREAK_CHAR_SET.contains(text.charAt(idx))) {
+ return idx;
+ }
+ }
+ if (startPos + width >= text.length()) {
+ return text.length();
+ }
+
+ limit = Math.min(startPos + width, text.length() - 1);
+ int pos;
+ // look for the last whitespace character before limit
+ for (pos = limit; pos >= startPos; --pos) {
+ if (Util.isWhitespace(text.charAt(pos))) {
+ break;
+ }
+ }
+ // if we found it return it, otherwise just chop at limit
+ return pos > startPos ? pos : limit - 1;
+ }
+
+ /**
+ * Creates a new TextHelpAppendable on {@link System#out}.
+ *
+ * @return a new TextHelpAppendable on {@link System#out}.
+ */
+ protected static TextHelpAppendable systemOut() {
+ return new TextHelpAppendable(System.out);
+ }
+
+ /** Defines the TextStyle for paragraph, and associated output formats. */
+ private final TextStyle.Builder textStyleBuilder;
+
+ /**
+ * Constructs an appendable filter built on top of the specified underlying appendable.
+ *
+ * @param output the underlying appendable to be assigned to the field {@code this.output} for later use, or {@code null} if this instance is to be created
+ * without an underlying stream.
+ */
+ public TextHelpAppendable(final Appendable output) {
+ super(output);
+ // @formatter:off
+ textStyleBuilder = TextStyle.builder()
+ .setMaxWidth(DEFAULT_WIDTH)
+ .setLeftPad(DEFAULT_LEFT_PAD)
+ .setIndent(DEFAULT_INDENT);
+ // @formatter:on
+ }
+
+ /**
+ * Adjusts the table format.
+ *
+ * Given the width of the page and the size of the table attempt to resize the columns to fit the page width if necessary. Adjustments are made as follows:
+ *
+ *
+ * The minimum size for a column may not be smaller than the length of the column header
+ * The maximum size is set to the maximum of the length of the header or the longest line length.
+ * If the total size of the columns is greater than the page wight, adjust the size of VARIABLE columns to attempt reduce the width to the the maximum
+ * size.
+ *
+ *
+ * Note: it is possible for the size of the columns to exceed the declared page width. In this case the table will extend beyond the desired page width.
+ *
+ *
+ * @param table the table to adjust.
+ * @return a new TableDefinition with adjusted values.
+ */
+ protected TableDefinition adjustTableFormat(final TableDefinition table) {
+ final List styleBuilders = new ArrayList<>();
+ for (int i = 0; i < table.columnTextStyles().size(); i++) {
+ final TextStyle style = table.columnTextStyles().get(i);
+ final TextStyle.Builder builder = TextStyle.builder().setTextStyle(style);
+ styleBuilders.add(builder);
+ final String header = table.headers().get(i);
+
+ if (style.getMaxWidth() < header.length() || style.getMaxWidth() == TextStyle.UNSET_MAX_WIDTH) {
+ builder.setMaxWidth(header.length());
+ }
+ if (style.getMinWidth() < header.length()) {
+ builder.setMinWidth(header.length());
+ }
+ for (final List row : table.rows()) {
+ final String cell = row.get(i);
+ if (cell.length() > builder.getMaxWidth()) {
+ builder.setMaxWidth(cell.length());
+ }
+ }
+ }
+ // calculate the total width.
+ int calcWidth = 0;
+ int adjustedMaxWidth = textStyleBuilder.getMaxWidth();
+ for (final TextStyle.Builder builder : styleBuilders) {
+ adjustedMaxWidth -= builder.getLeftPad();
+ if (builder.isScalable()) {
+ calcWidth += builder.getMaxWidth();
+ } else {
+ adjustedMaxWidth -= builder.getMaxWidth();
+ }
+ }
+ // rescale if necessary
+ if (calcWidth > adjustedMaxWidth) {
+ final double fraction = adjustedMaxWidth * 1.0 / calcWidth;
+ for (int i = 0; i < styleBuilders.size(); i++) {
+ final TextStyle.Builder builder = styleBuilders.get(i);
+ if (builder.isScalable()) {
+ // resize and remove the padding from the maxWidth calculation.
+ styleBuilders.set(i, resize(builder, fraction));
+ }
+ }
+ }
+ // regenerate the styles
+ final List styles = new ArrayList<>();
+ // adjust by removing the padding as it was not accounted for above.
+ styleBuilders.forEach(builder -> styles.add(builder.get()));
+ return TableDefinition.from(table.caption(), styles, table.headers(), table.rows());
+ }
+
+ @Override
+ public void appendHeader(final int level, final CharSequence text) throws IOException {
+ if (!Util.isEmpty(text)) {
+ if (level < 1) {
+ throw new IllegalArgumentException("level must be at least 1");
+ }
+ final char[] fillChars = { '=', '%', '+', '_' };
+ final int idx = Math.min(level, fillChars.length) - 1;
+ final TextStyle style = textStyleBuilder.get();
+ final Queue queue = makeColumnQueue(text, style);
+ queue.add(Util.repeatSpace(style.getLeftPad()) + Util.repeat(Math.min(text.length(), style.getMaxWidth()), fillChars[idx]));
+ queue.add(BLANK_LINE);
+ printQueue(queue);
+ }
+ }
+
+ @Override
+ public void appendList(final boolean ordered, final Collection list) throws IOException {
+ if (list != null && !list.isEmpty()) {
+ final TextStyle.Builder builder = TextStyle.builder().setLeftPad(textStyleBuilder.getLeftPad()).setIndent(DEFAULT_LIST_INDENT);
+ int i = 1;
+ for (final CharSequence line : list) {
+ final String entry = ordered ? String.format(" %s. %s", i++, Util.defaultValue(line, BLANK_LINE))
+ : String.format(" * %s", Util.defaultValue(line, BLANK_LINE));
+ builder.setMaxWidth(Math.min(textStyleBuilder.getMaxWidth(), entry.length()));
+ printQueue(makeColumnQueue(entry, builder.get()));
+ }
+ output.append(System.lineSeparator());
+ }
+ }
+
+ @Override
+ public void appendParagraph(final CharSequence paragraph) throws IOException {
+ if (!Util.isEmpty(paragraph)) {
+ final Queue queue = makeColumnQueue(paragraph, textStyleBuilder.get());
+ queue.add(BLANK_LINE);
+ printQueue(queue);
+ }
+ }
+
+ @Override
+ public void appendTable(final TableDefinition rawTable) throws IOException {
+ final TableDefinition table = adjustTableFormat(rawTable);
+ // write the table
+ appendParagraph(table.caption());
+ final List headerStyles = new ArrayList<>();
+ table.columnTextStyles().forEach(style -> headerStyles.add(TextStyle.builder().setTextStyle(style).setAlignment(TextStyle.Alignment.CENTER).get()));
+ writeColumnQueues(makeColumnQueues(table.headers(), headerStyles), headerStyles);
+ for (final List row : table.rows()) {
+ writeColumnQueues(makeColumnQueues(row, table.columnTextStyles()), table.columnTextStyles());
+ }
+ output.append(System.lineSeparator());
+ }
+
+ @Override
+ public void appendTitle(final CharSequence title) throws IOException {
+ if (!Util.isEmpty(title)) {
+ final TextStyle style = textStyleBuilder.get();
+ final Queue queue = makeColumnQueue(title, style);
+ queue.add(Util.repeatSpace(style.getLeftPad()) + Util.repeat(Math.min(title.length(), style.getMaxWidth()), '#'));
+ queue.add(BLANK_LINE);
+ printQueue(queue);
+ }
+ }
+
+ /**
+ * Gets the indent for the output.
+ *
+ * @return the indent of the page.
+ */
+ public int getIndent() {
+ return textStyleBuilder.getIndent();
+ }
+
+ /**
+ * Returns the left padding for the output.
+ *
+ * @return The left padding for the output.
+ */
+ public int getLeftPad() {
+ return textStyleBuilder.getLeftPad();
+ }
+
+ /**
+ * Gets the maximum width for the output
+ *
+ * @return the maximum width for the output.
+ */
+ public int getMaxWidth() {
+ return textStyleBuilder.getMaxWidth();
+ }
+
+ /**
+ * Gets the style builder used to format text that is not otherwise formatted.
+ *
+ * @return The style builder used to format text that is not otherwise formatted.
+ */
+ public TextStyle.Builder getTextStyleBuilder() {
+ return textStyleBuilder;
+ }
+
+ /**
+ * Creates a queue comprising strings extracted from columnData where the alignment and length are determined by the style.
+ *
+ * @param columnData The string to wrap
+ * @param style The TextStyle to guide the wrapping.
+ * @return A queue of the string wrapped.
+ */
+ protected Queue makeColumnQueue(final CharSequence columnData, final TextStyle style) {
+ final String lpad = Util.repeatSpace(style.getLeftPad());
+ final String indent = Util.repeatSpace(style.getIndent());
+ final Queue result = new LinkedList<>();
+ int wrapPos = 0;
+ int lastPos;
+ final int wrappedMaxWidth = style.getMaxWidth() - indent.length();
+ while (wrapPos < columnData.length()) {
+ final int workingWidth = wrapPos == 0 ? style.getMaxWidth() : wrappedMaxWidth;
+ lastPos = indexOfWrap(columnData, workingWidth, wrapPos);
+ final CharSequence working = columnData.subSequence(wrapPos, lastPos);
+ result.add(lpad + style.pad(wrapPos > 0, working));
+ wrapPos = Util.indexOfNonWhitespace(columnData, lastPos);
+ wrapPos = wrapPos == -1 ? lastPos + 1 : wrapPos;
+ }
+ return result;
+ }
+
+ /**
+ * For each column in the {@code columnData} apply the associated {@link TextStyle} and generated a queue of strings that are the maximum size of the column
+ * + the left pad.
+ *
+ * @param columnData The column data to output.
+ * @param styles the styles to apply.
+ * @return A list of queues of strings that represent each column in the table.
+ */
+ protected List> makeColumnQueues(final List columnData, final List styles) {
+ final List> result = new ArrayList<>();
+ for (int i = 0; i < columnData.size(); i++) {
+ result.add(makeColumnQueue(columnData.get(i), styles.get(i)));
+ }
+ return result;
+ }
+
+ /**
+ * Prints a queue of text.
+ *
+ * @param queue the queue of text to print.
+ * @throws IOException on output error.
+ */
+ private void printQueue(final Queue queue) throws IOException {
+ for (final String s : queue) {
+ appendFormat("%s%n", Util.rtrim(s));
+ }
+ }
+
+ /**
+ * Prints wrapped text using the TextHelpAppendable output style.
+ *
+ * @param text the text to wrap
+ * @throws IOException on output error.
+ */
+ public void printWrapped(final String text) throws IOException {
+ printQueue(makeColumnQueue(text, this.textStyleBuilder.get()));
+ }
+
+ /**
+ * Prints wrapped text.
+ *
+ * @param text the text to wrap
+ * @param style the style for the wrapped text.
+ * @throws IOException on output error.
+ */
+ public void printWrapped(final String text, final TextStyle style) throws IOException {
+ printQueue(makeColumnQueue(text, style));
+ }
+
+ /**
+ * Resizes an original width based on the fractional size it should be.
+ *
+ * @param orig the original size.
+ * @param fraction the fractional adjustment.
+ * @return the resized value.
+ */
+ private int resize(final int orig, final double fraction) {
+ return (int) (orig * fraction);
+ }
+
+ /**
+ * Resizes a TextStyle builder based on the fractional size.
+ *
+ * @param builder the builder to adjust.
+ * @param fraction the fractional size (for example percentage of the current size) that the builder should be.
+ * @return the builder with the maximum width and indent values resized.
+ */
+ protected TextStyle.Builder resize(final TextStyle.Builder builder, final double fraction) {
+ final double indentFrac = builder.getIndent() * 1.0 / builder.getMaxWidth();
+ builder.setMaxWidth(Math.max(resize(builder.getMaxWidth(), fraction), builder.getMinWidth()));
+ final int maxAdjust = builder.getMaxWidth() / 3;
+ int newIndent = builder.getMaxWidth() == 1 ? 0 : builder.getIndent();
+ if (newIndent > maxAdjust) {
+ newIndent = Math.min(resize(builder.getIndent(), indentFrac), maxAdjust);
+ }
+ builder.setIndent(newIndent);
+ return builder;
+ }
+
+ /**
+ * Sets the indent for the output.
+ *
+ * @param indent the indent used for paragraphs.
+ */
+ public void setIndent(final int indent) {
+ textStyleBuilder.setIndent(indent);
+ }
+
+ /**
+ * Sets the left padding: the number of characters from the left edge to start output.
+ *
+ * @param leftPad the left padding.
+ */
+ public void setLeftPad(final int leftPad) {
+ textStyleBuilder.setLeftPad(leftPad);
+ }
+
+ /**
+ * Sets the maximum width for the output.
+ *
+ * @param maxWidth the maximum width for the output.
+ */
+ public void setMaxWidth(final int maxWidth) {
+ textStyleBuilder.setMaxWidth(maxWidth);
+ }
+
+ /**
+ * Writes one line from each of the {@code columnQueues} until all the queues are exhausted. If an exhausted queue is encountered while other queues
+ * continue to have content the exhausted queue will produce empty text for the output width of the column (maximum width + left pad).
+ *
+ * @param columnQueues the List of queues that represent the columns of data.
+ * @param styles the TextStyle for each column.
+ * @throws IOException on output error.
+ */
+ protected void writeColumnQueues(final List> columnQueues, final List styles) throws IOException {
+ boolean moreData = true;
+ final String lPad = Util.repeatSpace(textStyleBuilder.get().getLeftPad());
+ while (moreData) {
+ output.append(lPad);
+ moreData = false;
+ for (int i = 0; i < columnQueues.size(); i++) {
+ final TextStyle style = styles.get(i);
+ final Queue columnQueue = columnQueues.get(i);
+ final String line = columnQueue.poll();
+ if (Util.isEmpty(line)) {
+ output.append(Util.repeatSpace(style.getMaxWidth() + style.getLeftPad()));
+ } else {
+ output.append(line);
+ }
+ moreData |= !columnQueue.isEmpty();
+ }
+ output.append(System.lineSeparator());
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/cli/help/TextStyle.java b/src/main/java/org/apache/commons/cli/help/TextStyle.java
new file mode 100644
index 000000000..fda8d2d9e
--- /dev/null
+++ b/src/main/java/org/apache/commons/cli/help/TextStyle.java
@@ -0,0 +1,396 @@
+/*
+ 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
+
+ https://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.help;
+
+import java.util.function.Supplier;
+
+/**
+ * The definition for styling recommendations blocks of text. Most common usage is to style columns in a table, but may also be used to specify default styling
+ * for a {@link HelpAppendable}. HelpWriters are free to ignore the TextStyle recommendations particularly where they are not supported or contradict common
+ * usage.
+ *
+ * @since 1.10.0
+ */
+public final class TextStyle {
+
+ /**
+ * The alignment possibilities.
+ */
+ public enum Alignment {
+
+ /**
+ * Left justifies the text.
+ */
+ LEFT,
+
+ /**
+ * Centers the text.
+ */
+ CENTER,
+
+ /**
+ * Right justifies the text.
+ */
+ RIGHT
+ }
+
+ /**
+ * The builder for the TextStyle. The default values are:
+ *
+ * alignment = LEFT
+ * leftPad = 0
+ * scaling = VARIABLE
+ * minWidth = 0
+ * maxWidth = UNSET_MAX_WIDTH
+ *
+ */
+ public static final class Builder implements Supplier {
+
+ /** The alignment. */
+ private Alignment alignment = Alignment.LEFT;
+
+ /** The left padding. */
+ private int leftPad;
+
+ /** The subsequent line indentation. */
+ private int indent;
+
+ /** The scalable flag. Identifies text blocks that can be made narrower or wider as needed by the HelpAppendable. */
+ private boolean scalable = true;
+
+ /** The minimum width. */
+ private int minWidth;
+
+ /** The maximum width. */
+ private int maxWidth = UNSET_MAX_WIDTH;
+
+ /**
+ * Constructs a new instance. The default values are:
+ *
+ * alignment = LEFT
+ * leftPad = 0
+ * scaling = VARIABLE
+ * minWidth = 0
+ * maxWidth = UNSET_MAX_WIDTH
+ *
+ */
+ private Builder() {
+ }
+
+ @Override
+ public TextStyle get() {
+ return new TextStyle(this);
+ }
+
+ /**
+ * Gets the currently specified indent value.
+ *
+ * @return The currently specified indent value.
+ */
+ public int getIndent() {
+ return indent;
+ }
+
+ /**
+ * Gets the currently specified leftPad.
+ *
+ * @return The currently specified leftPad.
+ */
+ public int getLeftPad() {
+ return leftPad;
+ }
+
+ /**
+ * Gets the currently specified maximum width value.
+ *
+ * @return The currently specified maximum width value.
+ */
+ public int getMaxWidth() {
+ return maxWidth;
+ }
+
+ /**
+ * Gets the currently specified minimum width value.
+ *
+ * @return The currently specified minimum width value.
+ */
+ public int getMinWidth() {
+ return minWidth;
+ }
+
+ /**
+ * Specifies if the column can be made wider or to narrower width to fit constraints of the HelpAppendable and formatting.
+ *
+ * @return The currently specified scaling value.
+ */
+ public boolean isScalable() {
+ return scalable;
+ }
+
+ /**
+ * Sets the alignment.
+ *
+ * @param alignment the desired alignment.
+ * @return this
+ */
+ public Builder setAlignment(final Alignment alignment) {
+ this.alignment = alignment;
+ return this;
+ }
+
+ /**
+ * Sets the indent value.
+ *
+ * @param indent the new indent value.
+ * @return this
+ */
+ public Builder setIndent(final int indent) {
+ this.indent = indent;
+ return this;
+ }
+
+ /**
+ * Sets the left padding.
+ *
+ * @param leftPad the new left padding.
+ * @return this
+ */
+ public Builder setLeftPad(final int leftPad) {
+ this.leftPad = leftPad;
+ return this;
+ }
+
+ /**
+ * Sets the currently specified minimum width.
+ *
+ * @param maxWidth The currently specified maximum width.
+ * @return this
+ */
+ public Builder setMaxWidth(final int maxWidth) {
+ this.maxWidth = maxWidth;
+ return this;
+ }
+
+ /**
+ * Sets the currently specified minimum width.
+ *
+ * @param minWidth The currently specified minimum width.
+ * @return this
+ */
+ public Builder setMinWidth(final int minWidth) {
+ this.minWidth = minWidth;
+ return this;
+ }
+
+ /**
+ * Sets whether the column can be made wider or to narrower width to fit constraints of the HelpAppendable and formatting.
+ *
+ * @param scalable Whether the text width can be adjusted.
+ * @return {@code this} instance.
+ */
+ public Builder setScalable(final boolean scalable) {
+ this.scalable = scalable;
+ return this;
+ }
+
+ /**
+ * Sets all properties from the given text style.
+ *
+ * @param style the source text style.
+ * @return {@code this} instance.
+ */
+ public Builder setTextStyle(final TextStyle style) {
+ this.alignment = style.alignment;
+ this.leftPad = style.leftPad;
+ this.indent = style.indent;
+ this.scalable = style.scalable;
+ this.minWidth = style.minWidth;
+ this.maxWidth = style.maxWidth;
+ return this;
+ }
+
+ }
+
+ /**
+ * The unset value for maxWidth: {@value}.
+ */
+ public static final int UNSET_MAX_WIDTH = Integer.MAX_VALUE;
+
+ /**
+ * The default style as generated by the default Builder.
+ */
+ public static final TextStyle DEFAULT = builder().get();
+
+ /**
+ * Creates a new builder.
+ *
+ * @return a new builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** The alignment. */
+ private final Alignment alignment;
+
+ /** The size of the left pad. This is placed before each line of text. */
+ private final int leftPad;
+
+ /** The size of the indent on the second and any subsequent lines of text. */
+ private final int indent;
+
+ /** The scaling allowed for the block. */
+ private final boolean scalable;
+
+ /** The minimum size of the text. */
+ private final int minWidth;
+
+ /** The maximum size of the text. */
+ private final int maxWidth;
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param builder the builder to build the text style from.
+ */
+ private TextStyle(final Builder builder) {
+ this.alignment = builder.alignment;
+ this.leftPad = builder.leftPad;
+ this.indent = builder.indent;
+ this.scalable = builder.scalable;
+ this.minWidth = builder.minWidth;
+ this.maxWidth = builder.maxWidth;
+ }
+
+ /**
+ * Gets the alignment.
+ *
+ * @return the alignment.
+ */
+ public Alignment getAlignment() {
+ return alignment;
+ }
+
+ /**
+ * Gets the indent value.
+ *
+ * @return the indent value.
+ */
+ public int getIndent() {
+ return indent;
+ }
+
+ /**
+ * Gets the left padding.
+ *
+ * @return the left padding.
+ */
+ public int getLeftPad() {
+ return leftPad;
+ }
+
+ /**
+ * gets the maximum width.
+ *
+ * @return The maximum width.
+ */
+ public int getMaxWidth() {
+ return maxWidth;
+ }
+
+ /**
+ * gets the minimum width.
+ *
+ * @return The minimum width.
+ */
+ public int getMinWidth() {
+ return minWidth;
+ }
+
+ /**
+ * Specifies if the column can be made wider or to narrower width to fit constraints of the HelpAppendable and formatting.
+ *
+ * @return the scaling value.
+ */
+ public boolean isScalable() {
+ return scalable;
+ }
+
+ /**
+ * Pads a string to the maximum width or optionally to the maximum width - indent.
+ *
+ * Returns the string unchanged if it is longer than the specified length.
+ * Will add the padding based on the alignment.
+ *
+ *
+ * @param addIndent if {@code true} account for the indent when padding the string.
+ * @param text the text to pad.
+ * @return the padded string.
+ */
+ public CharSequence pad(final boolean addIndent, final CharSequence text) {
+ if (text.length() >= maxWidth) {
+ return text;
+ }
+ String indentPad;
+ String rest;
+ final StringBuilder sb = new StringBuilder();
+ switch (alignment) {
+ case CENTER:
+ int padLen;
+ if (maxWidth == UNSET_MAX_WIDTH) {
+ padLen = addIndent ? indent : 0;
+ } else {
+ padLen = maxWidth - text.length();
+ }
+ final int left = padLen / 2;
+ indentPad = Util.repeatSpace(left);
+ rest = Util.repeatSpace(padLen - left);
+ sb.append(indentPad).append(text).append(rest);
+ break;
+ case LEFT:
+ case RIGHT:
+ default: // default should never happen. It is here to keep code coverage happy.
+ if (maxWidth == UNSET_MAX_WIDTH) {
+ indentPad = addIndent ? Util.repeatSpace(indent) : "";
+ rest = "";
+ } else {
+ int restLen = maxWidth - text.length();
+ if (addIndent && restLen > indent) {
+ indentPad = Util.repeatSpace(indent);
+ restLen -= indent;
+ } else {
+ indentPad = "";
+ }
+ rest = Util.repeatSpace(restLen);
+ }
+
+ if (alignment == Alignment.LEFT) {
+ sb.append(indentPad).append(text).append(rest);
+ } else {
+ sb.append(indentPad).append(rest).append(text);
+ }
+ break;
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("TextStyle{%s, l:%s, i:%s, %s, min:%s, max:%s}", alignment, leftPad, indent, scalable, minWidth,
+ maxWidth == UNSET_MAX_WIDTH ? "unset" : maxWidth);
+ }
+}
diff --git a/src/main/java/org/apache/commons/cli/help/Util.java b/src/main/java/org/apache/commons/cli/help/Util.java
new file mode 100644
index 000000000..2d8eb3b9c
--- /dev/null
+++ b/src/main/java/org/apache/commons/cli/help/Util.java
@@ -0,0 +1,136 @@
+/*
+ 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
+
+ https://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.help;
+
+import java.util.Arrays;
+
+/**
+ * Contains useful helper methods for classes within this package.
+ */
+final class Util {
+
+ /**
+ * A special value for index not found.
+ */
+ private static final int NOT_FOUND = -1;
+
+ /**
+ * Returns the {@code defaultValue} if {@code str} is empty.
+ *
+ * @param str The string to check.
+ * @param defaultValue the default value if the string is empty.
+ * @param The type of arguments.
+ * @return the {@code defaultValue} if {@code str} is empty,
+ */
+ static T defaultValue(final T str, final T defaultValue) {
+ return isEmpty(str) ? defaultValue : str;
+ }
+
+ /**
+ * Finds the index of the first non whitespace character.
+ *
+ * @param text the text to search in.
+ * @param startPos the starting position to search from.
+ * @return the index of the first non whitespace character or -1 if non found.
+ */
+ static int indexOfNonWhitespace(final CharSequence text, final int startPos) {
+ if (isEmpty(text)) {
+ return NOT_FOUND;
+ }
+ // the line ends before the max wrap pos or a new line char found
+ int idx = startPos;
+ while (idx < text.length() && isWhitespace(text.charAt(idx))) {
+ idx++;
+ }
+ return idx < text.length() ? idx : NOT_FOUND;
+ }
+
+ /**
+ * Tests whether the given string is null or empty.
+ *
+ * @param str The string to test.
+ * @return Whether the given string is null or empty.
+ */
+ static boolean isEmpty(final CharSequence str) {
+ return str == null || str.length() == 0;
+ }
+
+ /**
+ * Works around https://bugs.java.com/bugdatabase/view_bug?bug_id=8341522
+ *
+ * Affected Version: 8, 11, 17, 21, 24.
+ */
+ static boolean isWhitespace(final char c) {
+ return Character.isWhitespace(c) || Character.PARAGRAPH_SEPARATOR == c;
+ }
+
+ /**
+ * Removes the leading whitespace from the specified String.
+ *
+ * @param s The String to remove the leading padding from.
+ * @return The String of without the leading padding.
+ */
+ static String ltrim(final String s) {
+ final int pos = indexOfNonWhitespace(s, 0);
+ return pos == NOT_FOUND ? "" : s.substring(pos);
+ }
+
+ /**
+ * Constructs a string of specified length filled with the specified char.
+ *
+ * @param len the length of the final string.
+ * @param fillChar the character to file it will.
+ * @return A string of specified length filled with the specified char.
+ */
+ static String repeat(final int len, final char fillChar) {
+ final char[] padding = new char[len];
+ Arrays.fill(padding, fillChar);
+ return new String(padding);
+ }
+
+ /**
+ * Creates a String of padding of length {@code len}.
+ *
+ * @param len The length of the String of padding to create.
+ * @return The String of padding.
+ */
+ static String repeatSpace(final int len) {
+ return repeat(len, ' ');
+ }
+
+ /**
+ * Removes the trailing whitespace from the specified String.
+ *
+ * @param s The String to remove the trailing padding from.
+ * @return The String of without the trailing padding.
+ */
+ static String rtrim(final String s) {
+ if (isEmpty(s)) {
+ return s;
+ }
+ int pos = s.length();
+ while (pos > 0 && isWhitespace(s.charAt(pos - 1))) {
+ --pos;
+ }
+ return s.substring(0, pos);
+ }
+
+ private Util() {
+ // no instances
+ }
+}
diff --git a/src/main/java/org/apache/commons/cli/help/package-info.java b/src/main/java/org/apache/commons/cli/help/package-info.java
new file mode 100644
index 000000000..b216440f2
--- /dev/null
+++ b/src/main/java/org/apache/commons/cli/help/package-info.java
@@ -0,0 +1,34 @@
+/*
+ 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
+
+ https://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.
+ */
+
+/**
+ * The help production system.
+ *
+ * This package contains the classes to produce help output. In general,, there are 4 classes that may interest users.
+ *
+ *
+ * {@link org.apache.commons.cli.help.HelpFormatter HelpFormatter} - The class used to produce the help output for most users.
+ * {@link org.apache.commons.cli.help.HelpAppendable HelpAppendable} - Writes the output in a specific output format. For example
+ * {@link org.apache.commons.cli.help.TextHelpAppendable TextHelpAppendable} for text, other classes for XHTML, Markdown, and so on.
+ * {@link org.apache.commons.cli.help.OptionFormatter OptionFormatter} - Determines how to format the various data elements in an Option
+ * {@link org.apache.commons.cli.help.TableDefinition TableDefinition} - Useful for developers who want to build custom option displays or use the help
+ * system to produce additional information in the help system.
+ *
+ *
+ * @since 1.10.0
+ */
+package org.apache.commons.cli.help;
diff --git a/src/main/java/org/apache/commons/cli/overview.html b/src/main/java/org/apache/commons/cli/overview.html
deleted file mode 100644
index 2af1c30df..000000000
--- a/src/main/java/org/apache/commons/cli/overview.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
- Apache Commons CLI.
-
- The commons-cli package aides in parsing command-line arguments.
-
- Allow command-line arguments to be parsed against a descriptor of
- valid options (long and short), potentially with arguments.
-
- command-line arguments may be of the typical {@code String[]}
- form, but also may be a {@code java.util.List}. Indexes allow
- for parsing only a portion of the command-line. Also, functionality
- for parsing the command-line in phases is built in, allowing for
- 'cvs-style' command-lines, where some global options are specified
- before a 'command' argument, and command-specific options are
- specified after the command argument:
-
-
-
- myApp -p <port> command -p <printer>
-
-
-
-
-
The homepage for the project is
- Apache Commons/
-
diff --git a/src/main/java/org/apache/commons/cli/package-info.java b/src/main/java/org/apache/commons/cli/package-info.java
index 7358cee4d..ee05edeae 100644
--- a/src/main/java/org/apache/commons/cli/package-info.java
+++ b/src/main/java/org/apache/commons/cli/package-info.java
@@ -6,7 +6,7 @@
* (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
+ * https://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,
@@ -16,6 +16,6 @@
*/
/**
- * Apache Commons CLI provides a simple API for presenting, processing and validating a Command Line Interface.
+ * Apache Commons CLI provides a simple API for presenting, processing, and validating a Command Line Interface.
*/
package org.apache.commons.cli;
diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html
new file mode 100644
index 000000000..b1f2cc340
--- /dev/null
+++ b/src/main/javadoc/overview.html
@@ -0,0 +1,828 @@
+
+
+
+Apache Commons CLI Overview
+
+
+
+
+
+
+ 1. Introducing Apache Commons CLI
+
+
+ The Apacahe Commons CLI component parses command-line arguments for your application.
+
+ Commons CLI parses command-line arguments using a descriptor of
+ valid options (long and short), potentially with arguments.
+
+ Command-line arguments may be of the typical {@code String[]}
+ form, but also may be a {@code java.util.List}. Indexes allow
+ for parsing only a portion of the command-line. Also, functionality
+ for parsing the command-line in phases is built in, allowing for
+ 'cvs-style' command-lines, where some global options are specified
+ before a 'command' argument, and command-specific options are
+ specified after the command argument:
+
+
+ myApp -p <port> command -p <printer>
+
+
+ The homepage for the project is Apache Commons
+
+
+ There are three stages to command line processing. They are the
+ definition, parsing and interrogation stages. The following
+ sections discuss each of these stages in turn, and show how
+ to implement them with CLI.
+
+
+ Defining the CLI
+
+ Each command line must define the set of options that will be used
+ to define the interface to the application.
+
+
+ CLI uses the
+ Options class, as a container for
+
+ Option instances. There are two ways to create
+ Options in CLI. One of them is via the constructors,
+ the other way is via the factory methods defined in
+ Options.
+
+
+ The Usage Scenarios document provides
+ examples how to create an Options object and also
+ provides some real world examples.
+
+
+ The result of the definition stage is an Options
+ instance.
+
+
+
+ Parsing the CLI
+
+ The parsing stage is where the text passed into the
+ application via the command line is processed. The text is
+ processed according to the rules defined by the parser
+ implementation.
+
+
+ The parse method defined on
+
+ CommandLineParser takes an Options
+ instance and a String[] of arguments and
+ returns a
+
+ CommandLine .
+
+
+ The result of the parsing stage is a CommandLine
+ instance.
+
+
+
+ Interrogating the CLI
+
+ The interrogation stage is where the application queries the
+ CommandLine to decide what execution branch to
+ take depending on boolean options and uses the option values
+ to provide the application data.
+
+
+ This stage is implemented in the user code. The accessor methods
+ on CommandLine provide the interrogation capability
+ to the user code.
+
+
+ The result of the interrogation stage is that the user code
+ is fully informed of all the text that was supplied on the command
+ line and processed according to the parser and Options
+ rules.
+
+
+
+
+ 2. Using Apache Commons CLI
+
+ The following sections describe some example scenarios on how to
+ use CLI in applications.
+
+
+
+ Using a boolean option
+
+ A boolean option is represented on a command line by the presence
+ of the option, i.e. if the option is found then the option value
+ is true, otherwise the value is false.
+
+
+ The DateApp utility prints the current date to standard
+ output. If the -t option is present the current time is
+ also printed.
+
+
+
+ Creating the Options
+
+ An
+ Options object must be created and the Option must be
+ added to it.
+
+
+// create Options object
+Options options = new Options();
+
+// add t option
+options.addOption("t", false, "display current time");
+
+
+ The addOption method has three parameters. The first
+ parameter is a java.lang.String that represents the option.
+ The second parameter is a boolean that specifies whether the
+ option requires an argument or not. In the case of a boolean option
+ (sometimes referred to as a flag) an argument value is not present so
+ false is passed. The third parameter is the description
+ of the option. This description will be used in the usage text of the
+ application.
+
+
+
+ Parsing the command line arguments
+
+ The parse methods of CommandLineParser are used
+ to parse the command line arguments. There may be several implementations
+ of the CommandLineParser interface, the recommended one is the
+ DefaultParser.
+
+ CommandLineParser parser = new DefaultParser();
+CommandLine cmd = parser.parse(options, args);
+
+ Now we need to check if the t option is present. To do
+ this we will interrogate the
+ CommandLine
+ object. The hasOption method takes a
+ java.lang.String parameter and returns true if the option
+ represented by the java.lang.String is present, otherwise
+ it returns false.
+
+ if(cmd.hasOption("t")) {
+ // print the date and time
+} else {
+ // print the date
+}
+
+ Note
+
+
+ As of version 1.5, the
+ DefaultParser's constructor now has an override with
+ the signature DefaultParser(final boolean allowPartialMatching).
+ Given the following code:
+ final Options options = new Options();
+options.addOption(new Option("d", "debug", false, "Turn on debug."));
+options.addOption(new Option("e", "extract", false, "Turn on extract."));
+options.addOption(new Option("o", "option", true, "Turn on option with argument."));
+ we define "partial matching" as -de only matching the
+ "debug" option. We can consequently, now, turn this off and have
+ -de match both the debug option as well as the
+ extract option.
+
+
+
+ International Time
+
+ The InternationalDateApp utility extends the
+ DateApp utility by providing the ability to print the
+ date and time in any country in the world. To facilitate this a new
+ command line option, c, has been introduced.
+
+ // add c option
+options.addOption("c", true, "country code");
+
+ The second parameter is true this time. This specifies that the
+ c option requires an argument value. If the required option
+ argument value is specified on the command line it is returned,
+ otherwise null is returned.
+
+
+
+ Retrieving the argument value
+
+ The getOptionValue methods of CommandLine are
+ used to retrieve the argument values of options.
+
+ // get c option value
+String countryCode = cmd.getOptionValue("c");
+
+if(countryCode == null) {
+ // print default date
+} else {
+ // print date for country specified by countryCode
+}
+
+
+
+
+ 3. Using Ant as an Example
+
+ Ant will be used
+ here to illustrate how to create the Options required. The following
+ is the help output for Ant.
+
+ ant [options] [target [target2 [target3] ...]]
+ Options:
+ -help print this message
+ -projecthelp print project help information
+ -version print the version information and exit
+ -quiet be extra quiet
+ -verbose be extra verbose
+ -debug print debugging information
+ -emacs produce logging information without adornments
+ -logfile <file> use given file for log
+ -logger <classname> the class which is to perform logging
+ -listener <classname> add an instance of class as a project listener
+ -buildfile <file> use given buildfile
+ -D<property>=<value> use value for given property
+ -find <file> search for buildfile towards the root of the
+ filesystem and use it
+
+ Defining Boolean Options
+
+ Lets create the boolean options for the application as they
+ are the easiest to create. For clarity the constructors for
+ Option are used here.
+
+ Option help = new Option("help", "print this message");
+Option projecthelp = new Option("projecthelp", "print project help information");
+Option version = new Option("version", "print the version information and exit");
+Option quiet = new Option("quiet", "be extra quiet");
+Option verbose = new Option("verbose", "be extra verbose");
+Option debug = new Option("debug", "print debugging information");
+Option emacs = new Option("emacs",
+ "produce logging information without adornments");
+
+
+ Defining Argument Options
+
+ The argument options are created using the Option#Builder.
+
+ Option logFile = Option.builder("logfile")
+ .argName("file")
+ .hasArg()
+ .desc("use given file for log")
+ .build();
+
+Option logger = Option.builder("logger")
+ .argName("classname")
+ .hasArg()
+ .desc("the class which it to perform logging")
+ .build();
+
+Option listener = Option.builder("listener")
+ .argName("classname")
+ .hasArg()
+ .desc("add an instance of class as "
+ + "a project listener")
+ .build();
+
+Option buildFile = Option.builder("buildfile")
+ .argName("file")
+ .hasArg()
+ .desc("use given buildfile")
+ .build();
+
+Option find = Option.builder("find")
+ .argName("file")
+ .hasArg()
+ .desc("search for buildfile towards the "
+ + "root of the filesystem and use it")
+ .build();
+
+
+ Defining Java Property Option
+
+ The last option to create is the Java property, and it is also created
+ using the Option class' Builder.
+
+ Option property = Option property = Option.builder("D")
+ .hasArgs()
+ .valueSeparator('=')
+ .build();
+
+ The map of properties specified by this option can later be retrieved by
+ calling getOptionProperties("D") on the CommandLine.
+
+
+
+ Creating the Options
+
+ Now that we have created each
+ Option we need
+ to create the
+ Options
+ instance. This is achieved using the
+ addOption
+ method of Options.
+
+ Options options = new Options();
+
+options.addOption(help);
+options.addOption(projecthelp);
+options.addOption(version);
+options.addOption(quiet);
+options.addOption(verbose);
+options.addOption(debug);
+options.addOption(emacs);
+options.addOption(logfile);
+options.addOption(logger);
+options.addOption(listener);
+options.addOption(buildfile);
+options.addOption(find);
+options.addOption(property);
+
+ All the preparation is now complete, and we are now ready to
+ parse the command line arguments.
+
+
+
+ Creating the Parser
+
+ We now need to create a CommandLineParser. This will parse the command
+ line arguments, using the rules specified by the Options and
+ return an instance of CommandLine .
+
+ public static void main(String[] args) {
+ // create the parser
+ CommandLineParser parser = new DefaultParser();
+ try {
+ // parse the command line arguments
+ CommandLine line = parser.parse(options, args);
+ } catch (ParseException exp) {
+ // oops, something went wrong
+ System.err.println("Parsing failed. Reason: " + exp.getMessage());
+ }
+}
+
+
+ Querying the commandline
+
+ To see if an option has been passed the hasOption
+ method is used. The argument value can be retrieved using
+ the getOptionValue method.
+
+ // has the buildfile argument been passed?
+if (line.hasOption("buildfile")) {
+ // initialize the member variable
+ this.buildfile = line.getOptionValue("buildfile");
+}
+
+
+ Displaying Usage and Help
+
+ CLI also provides the means to automatically generate usage
+ and help information. This is achieved with the
+ HelpFormatter
+ class.
+
+ // automatically generate the help statement
+HelpFormatter formatter = new HelpFormatter();
+formatter.printHelp("ant", options);
+
+ When executed the following output is produced:
+
+ usage: ant
+-D <property=value> use value for given property
+-buildfile <file> use given buildfile
+-debug print debugging information
+-emacs produce logging information without adornments
+-file <file> search for buildfile towards the root of the
+ filesystem and use it
+-help print this message
+-listener <classname> add an instance of class as a project listener
+-logger <classname> the class which it to perform logging
+-projecthelp print project help information
+-quiet be extra quiet
+-verbose be extra verbose
+-version print the version information and exit
+
+ If you also require to have a usage statement printed
+ then calling formatter.printHelp("ant", options, true)
+ will generate a usage statement as well as the help information.
+
+
+
+
+
+ 4. Creating an ls Example
+
+ One of the most widely used command line applications in the *nix world
+ is ls. Due to the large number of options required for ls
+ this example will only cover a small proportion of the options. The following
+ is a section of the help output.
+
+ Usage: ls [OPTION]... [FILE]...
+List information about the FILEs (the current directory by default).
+Sort entries alphabetically if none of -cftuSUX nor --sort.
+
+-a, --all do not hide entries starting with .
+-A, --almost-all do not list implied . and ..
+-b, --escape print octal escapes for non-graphic characters
+ --block-size=SIZE use SIZE-byte blocks
+-B, --ignore-backups do not list implied entries ending with ~
+-c with -lt: sort by, and show, ctime (time of last
+ modification of file status information)
+ with -l: show ctime and sort by name
+ otherwise: sort by ctime
+-C list entries by columns
+
+ The following is the code that is used to create the
+ Options for this example.
+
+ // create the command line parser
+CommandLineParser parser = new DefaultParser();
+
+// create the Options
+Options options = new Options();
+options.addOption("a", "all", false, "do not hide entries starting with .");
+options.addOption("A", "almost-all", false, "do not list implied . and ..");
+options.addOption("b", "escape", false, "print octal escapes for non-graphic "
+ + "characters");
+options.addOption(Option.builder("SIZE").longOpt("block-size")
+ .desc("use SIZE-byte blocks")
+ .hasArg()
+ .build());
+options.addOption("B", "ignore-backups", false, "do not list implied entries "
+ + "ending with ~");
+options.addOption("c", false, "with -lt: sort by, and show, ctime (time of last "
+ + "modification of file status information) with "
+ + "-l:show ctime and sort by name otherwise: sort "
+ + "by ctime");
+options.addOption("C", false, "list entries by columns");
+
+String[] args = new String[]{ "--block-size=10" };
+
+try {
+ // parse the command line arguments
+ CommandLine line = parser.parse(options, args);
+
+ // validate that block-size has been set
+ if (line.hasOption("block-size")) {
+ // print the value of block-size
+ System.out.println(line.getOptionValue("block-size"));
+ }
+} catch (ParseException exp) {
+ System.out.println("Unexpected exception:" + exp.getMessage());
+}
+
+
+ 5. Converting (Parsing) Option Values
+
+ By in most cases the values on the command line are retrieved as Strings via the
+ commandLine.getOptionValue(key) command. However, it is possible for
+ the CLI library to convert the string into a different object. For example to specify
+ that the "count" option should return an Integer the following code could be used:
+
+
+public static void main(String[] args) {
+ Option count = Option.builder("count")
+ .hasArg()
+ .desc("the number of things")
+ .type(Integer.class)
+ .build();
+ Options options = new Options().addOption(count);
+ // create the parser
+ CommandLineParser parser = new DefaultParser();
+ try {
+ // parse the command line arguments
+ CommandLine line = parser.parse(options, args);
+ } catch (ParseException exp) {
+ // oops, something went wrong
+ System.err.println("Parsing failed. Reason: " + exp.getMessage());
+ }
+
+ try {
+ Integer value = line.getParsedOptionValue(count);
+ System.out.format("The value is %s%n", value );
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+}
+
+ The value types natively supported by commons-cli are:
+
+
+ Object.class - The string value must be the name of a class with a no argument constructor
+ Class.class - The string value must be the name of a class
+ Date.class - The string value must be a date parsable by new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy")
+ File.class - The string value is the name of the file.
+ Path.class - The string value is the name of a Path.
+ Number.class - The string value is a number representation can can be converted into an Integer or a Double.
+ URL.class - The string value is the textual representation of a URL
+ FileInputStream.class - The string value is passed to new FileInputStream(s).
+ Long.class - The string value is a valid argument to Long.parseLong().
+ Integer.class - The string value is a valid argument to Integer.parseInt().
+ Short.class - The string value is a valid argument to Short.parseShort().
+ Byte.class - The string value is a valid argument to Byte.parseByte().
+ Character.class - The string value is either a UTF-8 encoding for a character (e.g. "\\u0124") or the first character from the String."
+ Double.class - The string value is a valid argument to Double.parseDouble().
+ Float.class - The string value is a valid argument to Float.parseFloat().
+ BigInteger.class - The string value is a valid argument to new BigInteger(s).
+ BigDecimal.class - The string value is a valid argument to new BigDecimal(s).
+
+
+ Additional types may be added to the automatic parsing system by calling TypeHandler.register(Class<T> clazz, Converter<T> converter).
+ The Class<T> can be any defined class. The converter is a function that takes a String argument and returns an instance of
+ the class. Any exception thrown by the constructor will be caught and reported as a ParseException
+
+
+ Conversions can be specified without using the TypeHandler class by specifying the converter
+ directly during the option build. For example:
+
+
+ Option fooOpt = Option.builder("foo")
+ .hasArg()
+ .desc("the foo arg")
+ .converter(Foo::new)
+ .build();
+
+ The above will create an option that passes the string value to the Foo constructor when commandLine.getParsedOptionValue(fooOpt) is called.
+
+
+ Conversions that are added to the TypeHandler or that are specified directly will not deserialize if the option is serialized unless the type is registered with the TypeHandler
+ before deserialization begins.
+
+
+
+ 6. Deprecating Options
+
+ Options may be marked as deprecated using ghe Option.builder.deprecated() method.
+ Additional information may be specified by passing a DeprecatedAttributes instance to the
+ deprecated method.
+
+ Usage of the deprecated option is announced when the presence of the option is checked
+ or the value of the option is retrieved. By default, the announcement printed to Standard out.
+
+ The HelpFormatter output will by default show the description prefixed by "[Deprecated]"
+
+
+ The examples below will implement doSomething in the following code block.
+
+
+ public static void main(String[] args) {
+ Option n = Option.builder("n")
+ .deprecated(DeprecatedAttributes.builder()
+ .setDescription("Use '-count' instead")
+ .setForRemoval(true)
+ .setSince("now").get())
+ .hasArg()
+ .desc("the number of things")
+ .type(Integer.class)
+ .build();
+ Option count = Option.builder("count")
+ .hasArg()
+ .desc("the number of things")
+ .type(Integer.class)
+ .build();
+ Options options = new Options().addOption(n).addOption(count);
+
+ doSomething(options);
+ }
+
+
+ Changing Usage Announcement
+
+ The usage announcement may be changed by providing a Consumer<Option> to the
+ CommandLine.Builder.deprecatedHandler method. This is commonly used to log usage
+ of deprecated options rather than printing them on the standard output.
+
+
+ for example if doSomething is implemented as:
+
+
+ void doSomething(Options options) {
+ CommandLineParser parser = new DefaultParser();
+ CommandLine line;
+ try {
+ // parse the command line arguments
+ line = parser.parse(options, new String[] {"-n", "5"});
+ System.out.println("n=" + line.getParsedOptionValue("n"));
+ } catch (ParseException exp) {
+ // oops, something went wrong
+ System.err.println("Parsing failed. Reason: " + exp.getMessage());
+ }
+ }
+
+ The output of the run would be.
+
+
+
+ Option 'n': Deprecated for removal since now: Use '-count' instead
+ n=5
+
+
+ for example if doSomething is implemented as:
+
+
+
+ void doSomething(Options options) {
+ Consumer<Option> deprecatedUsageAnnouncement = o -> {
+ final StringBuilder buf = new StringBuilder()
+ .append("'")
+ .append(o.getOpt())
+ .append("'");
+ if (o.getLongOpt() != null) {
+ buf.append("'").append(o.getLongOpt()).append("'");
+ }
+ System.err.printf("ERROR: Option %s: %s%n", buf, o.getDeprecated());
+ };
+ DefaultParser parser = DefaultParser.builder().setDeprecatedHandler(deprecatedUsageAnnouncement).build();
+ CommandLine line;
+ try {
+ // parse the command line arguments
+ line = parser.parse(options, new String[] {"-n", "5"});
+ System.out.println("n=" + line.getParsedOptionValue("n"));
+ } catch (ParseException exp) {
+ // oops, something went wrong
+ System.err.println("Parsing failed. Reason: " + exp.getMessage());
+ }
+ }
+
+ The output of the run would be.
+
+
+ ERROR: Option 'n': Deprecated for removal since now: Use '-count' instead
+ n=5
+
+ However, the first line would be printed on system err instead of system out.
+
+
+
+
+ Changing help format
+ By default the help formater prints "[Deprecated]" in front of the description for the option. This can
+ be changed to display any values desired.
+
+
+ If doSomething is implemented as:
+
+
+ void doSomething(Options options) {
+ HelpFormatter formatter = HelpFormatter.builder().get();
+ formatter.printHelp("Command line syntax:", options);
+ }
+
+ To output is
+
+
+ usage: Command line syntax:
+ -count <arg> the number of things
+ -n <arg> [Deprecated] the number of things
+
+ The display of deprecated options may be changed through the use of the
+ HelpFormatter.Builder.setShowDeprecated() method.
+
+
+ Calling HelpFormatter.Builder.setShowDeprecated(false) will disable the "[Deprecated]" tag.
+ Calling HelpFormatter.Builder.setShowDeprecated with a Function<Option, String>
+ will use the output of the function as the description for the option.
+
+
+ As an example of the second case above, changing the implementation of doSomething to
+
+
+ void doSomething(Options options) {
+ Function<Option, String> disp = option -> String.format("%s. %s", HelpFormatter.getDescription(option),
+ option.getDeprecated().toString());
+ HelpFormatter formatter = HelpFormatter.builder().setShowDeprecated(disp).get();
+ formatter.printHelp("Command line syntax:", options);
+ }
+
+ changes the output to
+
+
+ usage: Command line syntax:
+ -count <arg> the number of things
+ -n <arg> the number of things. Deprecated for removal since now:
+ Use '-count' instead
+
+
+
+ 7. Defining Option Properties
+
+ The following are the properties that each
+ Option has. All of these
+ can be set using the accessors or using the methods
+ defined in the
+ Option.Builder .
+
+
+ Option Properties
+
+ Name
+ Type
+ Description
+
+
+ arg
+ boolean
+ A flag to say whether the option takes an argument.
+
+
+ args
+ boolean
+ A flag to say whether the option takes more than one argument.
+
+
+ argName
+ java.lang.String
+ The name of the argument value for the usage statement.
+
+
+ converter
+ org.apache.commons.cli.Converter<T, E extends Throwable>
+ A FunctionalInterface that converts a String to type T and may throw an exception E. When
+ CommandLine.getParsedValue() is called this FunctionalInterface will perform the conversion from the
+ command line argument to the parsed type. This is used when a desired type is not registered, or to
+ provide a custom type implementation. The 'type' property is not required to use the 'converter' property.
+
+
+ deprecated
+ org.apache.commons.cli.DeprecatedAttributes
+ Marks the option as deprecated and has the deprecation properties.
+
+
+ description
+ java.lang.String
+ A description of the function of the option.
+
+
+ longOpt
+ java.lang.String
+ An alias and more descriptive identification string. May be null or not specified if 'opt' is provided.
+
+
+ opt
+ java.lang.String
+ The identification string of the Option. May be null or not specified if 'longOpt' is provided.
+
+
+ optionalArg
+ boolean
+ A flag to say whether the option's argument is optional.
+
+
+ required
+ boolean
+ A flag to say whether the option must appear on the command line.
+
+
+ type
+ java.lang.Class<?>
+ The class of the object returned from getParsedValue(). The class must be registered in TypeHandler instance.
+ See also 'converter' property.
+
+
+ value
+ java.lang.String
+ The value of the option.
+
+
+ values
+ java.lang.String[]
+ The values of the option.
+
+
+ valueSeparator
+ char
+ The character value used to split the argument string, that is used in conjunction with multipleArgs e.g.
+ if the separator is ',' and the argument string is 'a,b,c' then there are three argument values, 'a', 'b'
+ and 'c'.
+
+
+
+
+ 8. Requirements
+
+
+ Java 8 or above.
+ If using OSGi, R7 or above.
+
+
diff --git a/src/media/commons-logo-component-100.xcf b/src/media/commons-logo-component-100.xcf
new file mode 100644
index 000000000..d203e3bad
Binary files /dev/null and b/src/media/commons-logo-component-100.xcf differ
diff --git a/src/media/commons-logo-component.xcf b/src/media/commons-logo-component.xcf
new file mode 100644
index 000000000..c1b2409fb
Binary files /dev/null and b/src/media/commons-logo-component.xcf differ
diff --git a/src/media/logo.png b/src/media/logo.png
new file mode 100644
index 000000000..d9d3358a7
Binary files /dev/null and b/src/media/logo.png differ
diff --git a/src/media/logo.xcf b/src/media/logo.xcf
deleted file mode 100644
index df9cbda2c..000000000
Binary files a/src/media/logo.xcf and /dev/null differ
diff --git a/src/site/resources/.htaccess b/src/site/resources/.htaccess
index 750861987..93ef36b67 100644
--- a/src/site/resources/.htaccess
+++ b/src/site/resources/.htaccess
@@ -5,7 +5,7 @@
# (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
+# https://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,
diff --git a/src/site/resources/images/logo.png b/src/site/resources/images/logo.png
index 442355059..d9d3358a7 100644
Binary files a/src/site/resources/images/logo.png and b/src/site/resources/images/logo.png differ
diff --git a/src/site/resources/images/svg/commandlines.svg b/src/site/resources/images/svg/commandlines.svg
index 3ab520cb6..d5cfafd3a 100644
--- a/src/site/resources/images/svg/commandlines.svg
+++ b/src/site/resources/images/svg/commandlines.svg
@@ -8,7 +8,7 @@
* (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
+ * https://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,
diff --git a/src/site/resources/images/svg/diagrams-cli2.js b/src/site/resources/images/svg/diagrams-cli2.js
index 9f1a9a277..38efec586 100644
--- a/src/site/resources/images/svg/diagrams-cli2.js
+++ b/src/site/resources/images/svg/diagrams-cli2.js
@@ -6,7 +6,7 @@
* (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
+ * https://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,
diff --git a/src/site/resources/images/svg/diagrams.css b/src/site/resources/images/svg/diagrams.css
index d8691da08..fc1426fbf 100644
--- a/src/site/resources/images/svg/diagrams.css
+++ b/src/site/resources/images/svg/diagrams.css
@@ -6,7 +6,7 @@
* (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
+ * https://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,
diff --git a/src/site/resources/images/svg/diagrams.js b/src/site/resources/images/svg/diagrams.js
index 4e8a5a72e..1125af059 100644
--- a/src/site/resources/images/svg/diagrams.js
+++ b/src/site/resources/images/svg/diagrams.js
@@ -6,7 +6,7 @@
* (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
+ * https://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,
diff --git a/src/site/resources/images/svg/options.svg b/src/site/resources/images/svg/options.svg
index cdb5160f9..ebebcb2fc 100644
--- a/src/site/resources/images/svg/options.svg
+++ b/src/site/resources/images/svg/options.svg
@@ -8,7 +8,7 @@
* (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
+ * https://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,
diff --git a/src/site/resources/images/svg/util.svg b/src/site/resources/images/svg/util.svg
index b86d760c4..346ed358c 100644
--- a/src/site/resources/images/svg/util.svg
+++ b/src/site/resources/images/svg/util.svg
@@ -7,7 +7,7 @@
* (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
+ * https://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,
diff --git a/src/site/resources/profile.jacoco b/src/site/resources/profile.jacoco
index e69de29bb..0314c63ff 100644
--- a/src/site/resources/profile.jacoco
+++ b/src/site/resources/profile.jacoco
@@ -0,0 +1,16 @@
+// 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
+//
+// https://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.
diff --git a/src/site/site.xml b/src/site/site.xml
index b45d24cab..ca1b03bae 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -1,45 +1,55 @@
-
-
- Apache Commons CLI
- /images/logo.png
- /index.html
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/site/xdoc/download_cli.xml b/src/site/xdoc/download_cli.xml
index 9f6cbc94a..0cf212d97 100644
--- a/src/site/xdoc/download_cli.xml
+++ b/src/site/xdoc/download_cli.xml
@@ -1,144 +1,146 @@
-
-
-
-
-
- Download Apache Commons CLI
- Apache Commons Documentation Team
-
-
-
-
-
- We recommend you use a mirror to download our release
- builds, but you must verify the integrity of
- the downloaded files using signatures downloaded from our main
- distribution directories. Recent releases (48 hours) may not yet
- be available from all the mirrors.
-
-
-
- You are currently using [preferred] . If you
- encounter a problem with this mirror, please select another
- mirror. If all mirrors are failing, there are backup
- mirrors (at the end of the mirrors list) that should be
- available.
-
- [if-any logo] [end]
-
-
-
-
-
- It is essential that you
- verify the integrity
- of downloaded files, preferably using the PGP signature (*.asc files);
- failing that using the SHA512 hash (*.sha512 checksum files).
-
-
- The KEYS
- file contains the public PGP keys used by Apache Commons developers
- to sign releases.
-
-
-
-
+
+
+
+
+
+ Download Apache Commons CLI
+ Apache Commons Team
+
+
+
+
+
+ We recommend you use a mirror to download our release
+ builds, but you must verify the integrity of
+ the downloaded files using signatures downloaded from our main
+ distribution directories. Recent releases (48 hours) may not yet
+ be available from all the mirrors.
+
+
+
+ You are currently using [preferred] . If you
+ encounter a problem with this mirror, please select another
+ mirror. If all mirrors are failing, there are backup
+ mirrors (at the end of the mirrors list) that should be
+ available.
+
+ [if-any logo] [end]
+
+
+
+
+
+ It is essential that you
+ verify the integrity
+ of downloaded files, preferably using the PGP signature (*.asc files);
+ failing that using the SHA512 hash (*.sha512 checksum files).
+
+
+ The KEYS
+ file contains the public PGP keys used by Apache Commons developers
+ to sign releases.
+
+
+
+
diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml
index fe72dd19d..e94720f1a 100644
--- a/src/site/xdoc/index.xml
+++ b/src/site/xdoc/index.xml
@@ -8,7 +8,7 @@
"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
+ https://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
@@ -17,18 +17,19 @@
specific language governing permissions and limitations
under the License.
-->
-
-
+
Apache Commons CLI
- commons-dev
+ Apache Commons Team
- The Apache Commons CLI library provides an API for parsing command line options passed to programs.
- It's also able to print help messages detailing the options available for a command line tool.
+ The Apache Commons CLI library provides an API for parsing command-line options passed to an application.
+ It can also print help detailing the options available for that application.
Commons CLI supports different types of options:
@@ -70,12 +71,12 @@ usage: ls
The Javadoc API documents are available online:
The source repository can be
- browsed .
+ browsed .
@@ -83,7 +84,7 @@ usage: ls
Download the latest version.
- The release notes are also available.
+ The release notes are also available.
For previous releases, see the Apache Archive .
diff --git a/src/site/xdoc/introduction.xml b/src/site/xdoc/introduction.xml
deleted file mode 100644
index c90b6738a..000000000
--- a/src/site/xdoc/introduction.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
-
-
- commons-dev
- Introducing Apache Commons CLI
-
-
-
-
-
- There are three stages to command line processing. They are the
- definition, parsing and interrogation stages. The following
- sections will discuss each of these stages in turn, and discuss how
- to implement them with CLI.
-
-
-
-
- Each command line must define the set of options that will be used
- to define the interface to the application.
-
-
- CLI uses the
- Options class, as a container for
-
- Option instances. There are two ways to create
- Options in CLI. One of them is via the constructors,
- the other way is via the factory methods defined in
- Options.
-
-
- The Usage Scenarios document provides
- examples how to create an Options object and also
- provides some real world examples.
-
-
- The result of the definition stage is an Options
- instance.
-
-
-
-
- The parsing stage is where the text passed into the
- application via the command line is processed. The text is
- processed according to the rules defined by the parser
- implementation.
-
-
- The parse method defined on
-
- CommandLineParser takes an Options
- instance and a String[] of arguments and
- returns a
-
- CommandLine .
-
-
- The result of the parsing stage is a CommandLine
- instance.
-
-
-
-
- The interrogation stage is where the application queries the
- CommandLine to decide what execution branch to
- take depending on boolean options and uses the option values
- to provide the application data.
-
-
- This stage is implemented in the user code. The accessor methods
- on CommandLine provide the interrogation capability
- to the user code.
-
-
- The result of the interrogation stage is that the user code
- is fully informed of all the text that was supplied on the command
- line and processed according to the parser and Options
- rules.
-
-
-
-
diff --git a/src/site/xdoc/issue-tracking.xml b/src/site/xdoc/issue-tracking.xml
index 9eaea4875..01ff5d784 100644
--- a/src/site/xdoc/issue-tracking.xml
+++ b/src/site/xdoc/issue-tracking.xml
@@ -7,7 +7,7 @@ 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
+ https://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,
@@ -41,10 +41,12 @@ limitations under the License.
| |
+======================================================================+
-->
-
+
Apache Commons CLI Issue tracking
- Apache Commons Documentation Team
+ Apache Commons Team
@@ -64,6 +66,7 @@ limitations under the License.
If you would like to report a bug, or raise an enhancement request with
Apache Commons CLI please do the following:
+
Search existing open bugs .
If you find your issue listed then please add a comment with your details.
@@ -73,30 +76,29 @@ limitations under the License.
Submit either a bug report
or enhancement request .
-
Please also remember these points:
+
the more information you provide, the better we can help you
test cases are vital, particularly for any proposed enhancements
the developers of Apache Commons CLI are all unpaid volunteers
-
- For more information on subversion and creating patches see the
+ For more information on creating patches see the
Apache Contributors Guide .
You may also find these links useful:
+
-
diff --git a/src/site/xdoc/mail-lists.xml b/src/site/xdoc/mail-lists.xml
index 112946170..f1dc8158c 100644
--- a/src/site/xdoc/mail-lists.xml
+++ b/src/site/xdoc/mail-lists.xml
@@ -7,7 +7,7 @@ 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
+ https://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,
@@ -39,10 +39,12 @@ limitations under the License.
| |
+======================================================================+
-->
-
+
Apache Commons CLI Mailing Lists
- Apache Commons Documentation Team
+ Apache Commons Team
@@ -53,15 +55,15 @@ limitations under the License.
To make it easier for people to only read messages related to components they are interested in,
the convention in Commons is to prefix the subject line of messages with the component's name,
for example:
-
- [cli] Problem with the ...
-
+
+ [cli] Problem with the ...
+
Questions related to the usage of Apache Commons CLI should be posted to the
- User List .
+ User List .
- The Developer List
+ The Developer List
is for questions and discussion related to the development of Apache Commons CLI.
Please do not cross-post; developers are also subscribed to the user list.
@@ -70,8 +72,10 @@ limitations under the License.
to subscribe.
- Note: please don't send patches or attachments to any of the mailing lists.
+ Note: please don't send patches or attachments to any of the mailing lists;
+ most of the lists are set up to drop attachments.
Patches are best handled via the Issue Tracking system.
+ If you have a GitHub account, most components also accept PRs (pull requests).
Otherwise, please upload the file to a public server and include the URL in the mail.
@@ -105,12 +109,11 @@ limitations under the License.
Subscribe
Unsubscribe
Post
- mail-archives.apache.org
+
lists.apache.org
- markmail.org
- www.mail-archive.com
- news.gmane.org
+
+ www.mail-archive.com
@@ -125,12 +128,11 @@ limitations under the License.
Subscribe
Unsubscribe
Post
- mail-archives.apache.org
+
lists.apache.org
- markmail.org
- www.mail-archive.com
- news.gmane.org
+
+ www.mail-archive.com
@@ -145,10 +147,10 @@ limitations under the License.
Subscribe
Unsubscribe
read only
- mail-archives.apache.org
+
lists.apache.org
- markmail.org
+
www.mail-archive.com
@@ -158,16 +160,16 @@ limitations under the License.
Commons Commits List
- Only for e-mails automatically generated by the source control sytem.
+ Only for e-mails automatically generated by the source control system.
Subscribe
Unsubscribe
read only
- mail-archives.apache.org
+
lists.apache.org
- markmail.org
+
www.mail-archive.com
@@ -199,13 +201,11 @@ limitations under the License.
Subscribe
Unsubscribe
read only
- mail-archives.apache.org
+
lists.apache.org
- markmail.org
- old.nabble.com
- www.mail-archive.com
- news.gmane.org
+
+ www.mail-archive.com
diff --git a/src/site/xdoc/properties.xml b/src/site/xdoc/properties.xml
deleted file mode 100644
index 9cd2e6b0e..000000000
--- a/src/site/xdoc/properties.xml
+++ /dev/null
@@ -1,109 +0,0 @@
-
-
-
-
-
- commons-dev
- Defining Option Properties
-
-
-
-
-
- The following are the properties that each
- Option has. All of these
- can be set using the accessors or using the methods
- defined in the
- OptionBuilder .
-
-
-
- Name
- Type
- Description
-
-
- opt
- java.lang.String
- the identification string of the Option.
-
-
- longOpt
- java.lang.String
- an alias and more descriptive identification string
-
-
- description
- java.lang.String
- a description of the function of the option
-
-
- required
- boolean
- a flag to say whether the option must appear on
- the command line.
-
-
- arg
- boolean
- a flag to say whether the option takes an argument
-
-
- args
- boolean
- a flag to say whether the option takes more than one argument
-
-
- optionalArg
- boolean
- a flag to say whether the option's argument is optional
-
-
- argName
- java.lang.String
- the name of the argument value for the usage statement
-
-
- valueSeparator
- char
- the character value used to split the argument string, that
- is used in conjunction with multipleArgs e.g.
- if the separator is ',' and the argument string is 'a,b,c' then
- there are three argument values, 'a', 'b' and 'c'.
-
-
- type
- java.lang.Object
- the type of the argument
-
-
- value
- java.lang.String
- the value of the option
-
-
- values
- java.lang.String[]
- the values of the option
-
-
-
-
-
diff --git a/src/site/xdoc/release_1_0.xml b/src/site/xdoc/release_1_0.xml
index 3bceb7e2e..c4bed3274 100644
--- a/src/site/xdoc/release_1_0.xml
+++ b/src/site/xdoc/release_1_0.xml
@@ -7,7 +7,7 @@ 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
+ https://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,
@@ -15,10 +15,12 @@ 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.
-->
-
+
Release notes for CLI 1.0
- Commons Documentation Team
+ Apache Commons Team
diff --git a/src/site/xdoc/release_1_2.xml b/src/site/xdoc/release_1_2.xml
index 8f83b7eaa..9a978ed05 100644
--- a/src/site/xdoc/release_1_2.xml
+++ b/src/site/xdoc/release_1_2.xml
@@ -7,7 +7,7 @@ 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
+ https://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,
@@ -15,10 +15,12 @@ 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.
-->
-
+
Release notes for CLI 1.2
- Commons Documentation Team
+ Apache Commons Team
diff --git a/src/site/xdoc/release_1_3.xml b/src/site/xdoc/release_1_3.xml
index 5c2de24a7..421485c04 100644
--- a/src/site/xdoc/release_1_3.xml
+++ b/src/site/xdoc/release_1_3.xml
@@ -7,7 +7,7 @@ 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
+ https://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,
@@ -15,10 +15,12 @@ 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.
-->
-
+
Release notes for CLI 1.3
- Commons Documentation Team
+ Apache Commons Team
diff --git a/src/site/xdoc/release_1_3_1.xml b/src/site/xdoc/release_1_3_1.xml
index 3a8d6985a..2be55e05e 100644
--- a/src/site/xdoc/release_1_3_1.xml
+++ b/src/site/xdoc/release_1_3_1.xml
@@ -7,7 +7,7 @@ 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
+ https://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,
@@ -15,10 +15,12 @@ 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.
-->
-
+
Release notes for CLI 1.3.1
- Commons Documentation Team
+ Apache Commons Team
diff --git a/src/site/xdoc/release_1_4.xml b/src/site/xdoc/release_1_4.xml
index 70545f1df..7600ce15c 100644
--- a/src/site/xdoc/release_1_4.xml
+++ b/src/site/xdoc/release_1_4.xml
@@ -7,7 +7,7 @@ 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
+ https://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,
@@ -15,10 +15,12 @@ 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.
-->
-
+
Release notes for CLI 1.4
- Commons Documentation Team
+ Apache Commons Team
diff --git a/src/site/xdoc/security.xml b/src/site/xdoc/security.xml
new file mode 100644
index 000000000..34b02c75d
--- /dev/null
+++ b/src/site/xdoc/security.xml
@@ -0,0 +1,51 @@
+
+
+
+
+ Apache Commons Security Reports
+ Apache Commons Team
+
+
+
+
+ For information about reporting or asking questions about security, please see
+ Apache Commons Security .
+
+ This page lists all security vulnerabilities fixed in released versions of this component.
+
+ Please note that binary patches are never provided. If you need to apply a source code patch, use the building instructions for the component version
+ that you are using.
+
+
+ If you need help on building this component or other help on following the instructions to mitigate the known vulnerabilities listed here, please send
+ your questions to the public
+ user mailing list .
+
+ If you have encountered an unlisted security vulnerability or other unexpected behavior that has security impact, or if the descriptions here are
+ incomplete, please report them privately to the Apache Security Team. Thank you.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/site/xdoc/upgrading-1.0-to-1.1.xml b/src/site/xdoc/upgrading-1.0-to-1.1.xml
index 866477fb0..4c804d17f 100644
--- a/src/site/xdoc/upgrading-1.0-to-1.1.xml
+++ b/src/site/xdoc/upgrading-1.0-to-1.1.xml
@@ -7,7 +7,7 @@ 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
+ https://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,
@@ -15,10 +15,12 @@ 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.
-->
-
+
Upgrading from 1.0 to 1.1
- Commons Documentation Team
+ Apache Commons Team
@@ -41,7 +43,7 @@ limitations under the License.
diff --git a/src/site/xdoc/usage.xml b/src/site/xdoc/usage.xml
deleted file mode 100644
index ccda3c856..000000000
--- a/src/site/xdoc/usage.xml
+++ /dev/null
@@ -1,388 +0,0 @@
-
-
-
-
-
- commons-dev
- Using Apache Commons CLI
-
-
-
-
-
- The following sections describe some example scenarios on how to
- use CLI in applications.
-
-
-
-
- A boolean option is represented on a command line by the presence
- of the option, i.e. if the option is found then the option value
- is true, otherwise the value is false.
-
-
- The DateApp utility prints the current date to standard
- output. If the -t option is present the current time is
- also printed.
-
-
-
-
- An
- Options object must be created and the Option must be
- added to it.
-
-
-// create Options object
-Options options = new Options();
-
-// add t option
-options.addOption("t", false, "display current time");
-
- The addOption method has three parameters. The first
- parameter is a java.lang.String that represents the option.
- The second parameter is a boolean that specifies whether the
- option requires an argument or not. In the case of a boolean option
- (sometimes referred to as a flag) an argument value is not present so
- false is passed. The third parameter is the description
- of the option. This description will be used in the usage text of the
- application.
-
-
-
-
- The parse methods of CommandLineParser are used
- to parse the command line arguments. There may be several implementations
- of the CommandLineParser interface, the recommended one is the
- DefaultParser.
-
- CommandLineParser parser = new DefaultParser();
-CommandLine cmd = parser.parse(options, args);
-
- Now we need to check if the t option is present. To do
- this we will interrogate the
- CommandLine
- object. The hasOption method takes a
- java.lang.String parameter and returns true if the option
- represented by the java.lang.String is present, otherwise
- it returns false.
-
- if(cmd.hasOption("t")) {
- // print the date and time
-}
-else {
- // print the date
-}
-
-
Note.
- As of version 1.5, the
- DefaultParser's constructor now has an override with
- the signature DefaultParser(final boolean allowPartialMatching).
- Given the following code:
- final Options options = new Options();
-options.addOption(new Option("d", "debug", false, "Turn on debug."));
-options.addOption(new Option("e", "extract", false, "Turn on extract."));
-options.addOption(new Option("o", "option", true, "Turn on option with argument."));
- we define "partial matching" as -de only matching the
- "debug" option. We can consequently, now, turn this off and have
- -de match both the debug option as well as the
- extract option.
-
-
-
-
- The InternationalDateApp utility extends the
- DateApp utility by providing the ability to print the
- date and time in any country in the world. To facilitate this a new
- command line option, c, has been introduced.
-
- // add c option
-options.addOption("c", true, "country code");
-
- The second parameter is true this time. This specifies that the
- c option requires an argument value. If the required option
- argument value is specified on the command line it is returned,
- otherwise null is returned.
-
-
-
-
- The getOptionValue methods of CommandLine are
- used to retrieve the argument values of options.
-
- // get c option value
-String countryCode = cmd.getOptionValue("c");
-
-if(countryCode == null) {
- // print default date
-}
-else {
- // print date for country specified by countryCode
-}
-
-
-
-
-
- Ant will be used
- here to illustrate how to create the Options required. The following
- is the help output for Ant.
-
- ant [options] [target [target2 [target3] ...]]
- Options:
- -help print this message
- -projecthelp print project help information
- -version print the version information and exit
- -quiet be extra quiet
- -verbose be extra verbose
- -debug print debugging information
- -emacs produce logging information without adornments
- -logfile <file> use given file for log
- -logger <classname> the class which is to perform logging
- -listener <classname> add an instance of class as a project listener
- -buildfile <file> use given buildfile
- -D<property>=<value> use value for given property
- -find <file> search for buildfile towards the root of the
- filesystem and use it
-
-
- Lets create the boolean options for the application as they
- are the easiest to create. For clarity the constructors for
- Option are used here.
-
- Option help = new Option("help", "print this message");
-Option projecthelp = new Option("projecthelp", "print project help information");
-Option version = new Option("version", "print the version information and exit");
-Option quiet = new Option("quiet", "be extra quiet");
-Option verbose = new Option("verbose", "be extra verbose");
-Option debug = new Option("debug", "print debugging information");
-Option emacs = new Option("emacs",
- "produce logging information without adornments");
-
-
-
- The argument options are created using the Option#Builder.
-
- Option logfile = Option.builder("logfile")
- .argName("file")
- .hasArg()
- .desc("use given file for log")
- .build();
-
-Option logger = Option.builder("logger")
- .argName("classname")
- .hasArg()
- .desc("the class which it to perform logging")
- .build();
-
-Option listener = Option.builder("listener")
- .argName("classname")
- .hasArg()
- .desc("add an instance of class as "
- + "a project listener")
- .build();
-
-Option buildfile = Option.builder("buildfile")
- .argName("file")
- .hasArg()
- .desc("use given buildfile")
- .build();
-
-Option find = Option.builde("find")
- .argName("file")
- .hasArg()
- .desc("search for buildfile towards the "
- + "root of the filesystem and use it")
- .build();
-
-
-
- The last option to create is the Java property and it is also created
- using the OptionBuilder.
-
- Option property = Option property = Option.builder("D")
- .hasArgs()
- .valueSeparator('=')
- .build();
-
- The map of properties specified by this option can later be retrieved by
- calling getOptionProperties("D") on the CommandLine.
-
-
-
-
- Now that we have created each
- Option we need
- to create the
- Options
- instance. This is achieved using the
- addOption
- method of Options.
-
- Options options = new Options();
-
-options.addOption(help);
-options.addOption(projecthelp);
-options.addOption(version);
-options.addOption(quiet);
-options.addOption(verbose);
-options.addOption(debug);
-options.addOption(emacs);
-options.addOption(logfile);
-options.addOption(logger);
-options.addOption(listener);
-options.addOption(buildfile);
-options.addOption(find);
-options.addOption(property);
-
- All the preperation is now complete and we are now ready to
- parse the command line arguments.
-
-
-
-
- We now need to create a CommandLineParser. This will parse the command
- line arguments, using the rules specified by the Options and
- return an instance of CommandLine .
-
- public static void main(String[] args) {
- // create the parser
- CommandLineParser parser = new DefaultParser();
- try {
- // parse the command line arguments
- CommandLine line = parser.parse(options, args);
- }
- catch (ParseException exp) {
- // oops, something went wrong
- System.err.println("Parsing failed. Reason: " + exp.getMessage());
- }
-}
-
-
-
- To see if an option has been passed the hasOption
- method is used. The argument value can be retrieved using
- the getOptionValue method.
-
- // has the buildfile argument been passed?
-if(line.hasOption("buildfile")) {
- // initialise the member variable
- this.buildfile = line.getOptionValue("buildfile");
-}
-
-
-
- CLI also provides the means to automatically generate usage
- and help information. This is achieved with the
- HelpFormatter
- class.
-
- // automatically generate the help statement
-HelpFormatter formatter = new HelpFormatter();
-formatter.printHelp("ant", options);
-
- When executed the following output is produced:
-
- usage: ant
--D <property=value> use value for given property
--buildfile <file> use given buildfile
--debug print debugging information
--emacs produce logging information without adornments
--file <file> search for buildfile towards the root of the
- filesystem and use it
--help print this message
--listener <classname> add an instance of class as a project listener
--logger <classname> the class which it to perform logging
--projecthelp print project help information
--quiet be extra quiet
--verbose be extra verbose
--version print the version information and exit
-
- If you also require to have a usage statement printed
- then calling formatter.printHelp("ant", options, true)
- will generate a usage statment as well as the help information.
-
-
-
-
-
-
- One of the most widely used command line applications in the *nix world
- is ls. Due to the large number of options required for ls
- this example will only cover a small proportion of the options. The following
- is a section of the help output.
-
- Usage: ls [OPTION]... [FILE]...
-List information about the FILEs (the current directory by default).
-Sort entries alphabetically if none of -cftuSUX nor --sort.
-
--a, --all do not hide entries starting with .
--A, --almost-all do not list implied . and ..
--b, --escape print octal escapes for non-graphic characters
- --block-size=SIZE use SIZE-byte blocks
--B, --ignore-backups do not list implied entries ending with ~
--c with -lt: sort by, and show, ctime (time of last
- modification of file status information)
- with -l: show ctime and sort by name
- otherwise: sort by ctime
--C list entries by columns
-
- The following is the code that is used to create the
- Options for this example.
-
- // create the command line parser
-CommandLineParser parser = new DefaultParser();
-
-// create the Options
-Options options = new Options();
-options.addOption("a", "all", false, "do not hide entries starting with .");
-options.addOption("A", "almost-all", false, "do not list implied . and ..");
-options.addOption("b", "escape", false, "print octal escapes for non-graphic "
- + "characters");
-options.addOption(Option.builder("SIZE").longOpt("block-size")
- .desc("use SIZE-byte blocks")
- .hasArg()
- .build());
-options.addOption("B", "ignore-backups", false, "do not list implied entries "
- + "ending with ~");
-options.addOption("c", false, "with -lt: sort by, and show, ctime (time of last "
- + "modification of file status information) with "
- + "-l:show ctime and sort by name otherwise: sort "
- + "by ctime");
-options.addOption("C", false, "list entries by columns");
-
-String[] args = new String[]{ "--block-size=10" };
-
-try {
- // parse the command line arguments
- CommandLine line = parser.parse(options, args);
-
- // validate that block-size has been set
- if (line.hasOption("block-size")) {
- // print the value of block-size
- System.out.println(line.getOptionValue("block-size"));
- }
-}
-catch (ParseException exp) {
- System.out.println("Unexpected exception:" + exp.getMessage());
-}
-
-
-
diff --git a/src/test/java/org/apache/commons/cli/AbstractParserTestCase.java b/src/test/java/org/apache/commons/cli/AbstractParserTestCase.java
new file mode 100644
index 000000000..58657821a
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/AbstractParserTestCase.java
@@ -0,0 +1,865 @@
+/*
+ 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
+
+ https://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 static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Abstract test case testing common parser features.
+ *
+ * TODO Needs a rework using JUnit parameterized tests.
+ */
+public abstract class AbstractParserTestCase {
+
+ protected CommandLineParser parser;
+
+ protected Options options;
+
+ @SuppressWarnings("deprecation")
+ private CommandLine parse(final CommandLineParser parser, final Options options, final String[] args, final Properties properties) throws ParseException {
+ if (parser instanceof Parser) {
+ return ((Parser) parser).parse(options, args, properties);
+ }
+ if (parser instanceof DefaultParser) {
+ return ((DefaultParser) parser).parse(options, args, properties);
+ }
+ throw new UnsupportedOperationException("Default options not supported by this parser");
+ }
+
+ @BeforeEach
+ public void setUp() {
+ //@formatter:off
+ options = new Options()
+ .addOption("a", "enable-a", false, "turn [a] on or off")
+ .addOption("b", "bfile", true, "set the value of [b]")
+ .addOption("c", "copt", false, "turn [c] on or off");
+ //@formatter:on
+ }
+
+ @Test
+ void testAmbiguousArgParsing() throws Exception {
+ final String[] args = { "-=-" };
+ final Options options = new Options();
+ assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, args));
+ }
+
+ @Test
+ void testAmbiguousLongWithoutEqualSingleDash() throws Exception {
+ final String[] args = { "-b", "-foobar" };
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("foo").hasOptionalArg().create('f'));
+ options.addOption(OptionBuilder.withLongOpt("bar").hasOptionalArg().create('b'));
+ final CommandLine cl = parser.parse(options, args);
+ assertTrue(cl.hasOption("b"));
+ assertTrue(cl.hasOption("f"));
+ assertEquals("bar", cl.getOptionValue("foo"));
+ }
+
+ @Test
+ void testAmbiguousLongWithoutEqualSingleDash2() throws Exception {
+ final String[] args = { "-b", "-foobar" };
+ final Options options = new Options();
+ options.addOption(Option.builder().longOpt("foo").option("f").optionalArg(true).get());
+ options.addOption(Option.builder().longOpt("bar").option("b").optionalArg(false).get());
+ final CommandLine cl = parser.parse(options, args);
+ assertTrue(cl.hasOption("b"));
+ assertTrue(cl.hasOption("f"));
+ assertEquals("bar", cl.getOptionValue("foo"));
+ }
+
+ @Test
+ void testAmbiguousPartialLongOption1() throws Exception {
+ final String[] args = { "--ver" };
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("version").create());
+ options.addOption(OptionBuilder.withLongOpt("verbose").create());
+ final AmbiguousOptionException e = assertThrows(AmbiguousOptionException.class, () -> parser.parse(options, args));
+ assertEquals("--ver", e.getOption(), "Partial option");
+ assertNotNull(e.getMatchingOptions(), "Matching options null");
+ assertEquals(2, e.getMatchingOptions().size(), "Matching options size");
+ }
+
+ @Test
+ void testAmbiguousPartialLongOption2() throws Exception {
+ final String[] args = { "-ver" };
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("version").create());
+ options.addOption(OptionBuilder.withLongOpt("verbose").create());
+ final AmbiguousOptionException e = assertThrows(AmbiguousOptionException.class, () -> parser.parse(options, args));
+ assertEquals("-ver", e.getOption(), "Partial option");
+ assertNotNull(e.getMatchingOptions(), "Matching options null");
+ assertEquals(2, e.getMatchingOptions().size(), "Matching options size");
+ }
+
+ @Test
+ void testAmbiguousPartialLongOption3() throws Exception {
+ final String[] args = { "--ver=1" };
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("version").create());
+ options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
+ final AmbiguousOptionException e = assertThrows(AmbiguousOptionException.class, () -> parser.parse(options, args));
+ assertEquals("--ver", e.getOption(), "Partial option");
+ assertNotNull(e.getMatchingOptions(), "Matching options null");
+ assertEquals(2, e.getMatchingOptions().size(), "Matching options size");
+ }
+
+ @Test
+ void testAmbiguousPartialLongOption4() throws Exception {
+ final String[] args = { "-ver=1" };
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("version").create());
+ options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
+ final AmbiguousOptionException e = assertThrows(AmbiguousOptionException.class, () -> parser.parse(options, args));
+ assertEquals("-ver", e.getOption(), "Partial option");
+ assertNotNull(e.getMatchingOptions(), "Matching options null");
+ assertEquals(2, e.getMatchingOptions().size(), "Matching options size");
+ }
+
+ @Test
+ void testArgumentStartingWithHyphen() throws Exception {
+ final String[] args = { "-b", "-foo" };
+ final CommandLine cl = parser.parse(options, args);
+ assertEquals("-foo", cl.getOptionValue("b"));
+ }
+
+ @Test
+ void testBursting() throws Exception {
+ final String[] args = { "-acbtoast", "foo", "bar" };
+ final CommandLine cl = parser.parse(options, args);
+ assertTrue(cl.hasOption("a"), "Confirm -a is set");
+ assertTrue(cl.hasOption("b"), "Confirm -b is set");
+ assertTrue(cl.hasOption("c"), "Confirm -c is set");
+ assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
+ assertEquals(2, cl.getArgList().size(), "Confirm size of extra args");
+ }
+
+ @Test
+ void testDoubleDash1() throws Exception {
+ final String[] args = { "--copt", "--", "-b", "toast" };
+ final CommandLine cl = parser.parse(options, args);
+ assertTrue(cl.hasOption("c"), "Confirm -c is set");
+ assertFalse(cl.hasOption("b"), "Confirm -b is not set");
+ assertEquals(2, cl.getArgList().size(), "Confirm 2 extra args: " + cl.getArgList().size());
+ }
+
+ @Test
+ void testDoubleDash2() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.hasArg().create('n'));
+ options.addOption(OptionBuilder.create('m'));
+ final MissingArgumentException e = assertThrows(MissingArgumentException.class, () -> parser.parse(options, new String[] { "-n", "--", "-m" }));
+ assertNotNull(e.getOption(), "option null");
+ assertEquals("n", e.getOption().getOpt());
+ }
+
+ @Test
+ void testLongOptionQuoteHandling() throws Exception {
+ final String[] args = { "--bfile", "\"quoted string\"" };
+ final CommandLine cl = parser.parse(options, args);
+ assertEquals("quoted string", cl.getOptionValue("b"), "Confirm --bfile \"arg\" strips quotes");
+ }
+
+ @Test
+ void testLongOptionWithEqualsQuoteHandling() throws Exception {
+ final String[] args = { "--bfile=\"quoted string\"" };
+ final CommandLine cl = parser.parse(options, args);
+ assertEquals("quoted string", cl.getOptionValue("b"), "Confirm --bfile=\"arg\" strips quotes");
+ }
+
+ @Test
+ void testLongWithEqualDoubleDash() throws Exception {
+ final String[] args = { "--foo=bar" };
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
+ final CommandLine cl = parser.parse(options, args);
+ assertEquals("bar", cl.getOptionValue("foo"));
+ }
+
+ @Test
+ void testLongWithEqualSingleDash() throws Exception {
+ final String[] args = { "-foo=bar" };
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
+ final CommandLine cl = parser.parse(options, args);
+ assertEquals("bar", cl.getOptionValue("foo"));
+ }
+
+ @Test
+ void testLongWithoutEqualDoubleDash() throws Exception {
+ final String[] args = { "--foobar" };
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
+ final CommandLine cl = parser.parse(options, args, true);
+ assertFalse(cl.hasOption("foo")); // foo isn't expected to be recognized with a double dash
+ }
+
+ @Test
+ void testLongWithoutEqualSingleDash() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
+ final CommandLine cl = parser.parse(options, new String[] { "-foobar" });
+ assertEquals("bar", cl.getOptionValue("foo"));
+ }
+
+ @Test
+ void testLongWithUnexpectedArgument1() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
+ final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, new String[] { "--foo=bar" }));
+ assertEquals("--foo=bar", e.getOption());
+ }
+
+ @Test
+ void testLongWithUnexpectedArgument2() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
+ final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, new String[] { "-foobar" }));
+ assertEquals("-foobar", e.getOption());
+ }
+
+ @Test
+ void testMissingArg() throws Exception {
+ final MissingArgumentException e = assertThrows(MissingArgumentException.class, () -> parser.parse(options, new String[] { "-b" }));
+ assertEquals("b", e.getOption().getOpt(), "option missing an argument");
+ }
+
+ @Test
+ void testMissingArgWithBursting() throws Exception {
+ final String[] args = { "-acb" };
+ final MissingArgumentException e = assertThrows(MissingArgumentException.class, () -> parser.parse(options, args));
+ assertEquals("b", e.getOption().getOpt(), "option missing an argument");
+ }
+
+ @Test
+ void testMissingRequiredGroup() throws Exception {
+ final OptionGroup optionGroup = new OptionGroup();
+ optionGroup.addOption(OptionBuilder.create("a"));
+ optionGroup.addOption(OptionBuilder.create("b"));
+ optionGroup.setRequired(true);
+ final Options options = new Options();
+ options.addOptionGroup(optionGroup);
+ options.addOption(OptionBuilder.isRequired().create("c"));
+ final MissingOptionException e = assertThrows(MissingOptionException.class, () -> parser.parse(options, new String[] { "-c" }));
+ assertEquals(1, e.getMissingOptions().size());
+ assertTrue(e.getMissingOptions().get(0) instanceof OptionGroup);
+ }
+
+ @Test
+ void testMissingRequiredOption() {
+ final String[] args = { "-a" };
+ final Options options = new Options();
+ options.addOption("a", "enable-a", false, null);
+ options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
+ final MissingOptionException e = assertThrows(MissingOptionException.class, () -> parser.parse(options, args));
+ assertEquals("Missing required option: b", e.getMessage(), "Incorrect exception message");
+ assertTrue(e.getMissingOptions().contains("b"));
+ }
+
+ @Test
+ void testMissingRequiredOptions() {
+ final String[] args = { "-a" };
+ final Options options = new Options();
+ options.addOption("a", "enable-a", false, null);
+ options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
+ options.addOption(OptionBuilder.withLongOpt("cfile").hasArg().isRequired().create('c'));
+ final MissingOptionException e = assertThrows(MissingOptionException.class, () -> parser.parse(options, args));
+ assertEquals("Missing required options: b, c", e.getMessage(), "Incorrect exception message");
+ assertTrue(e.getMissingOptions().contains("b"));
+ assertTrue(e.getMissingOptions().contains("c"));
+ }
+
+ @Test
+ void testMultiple() throws Exception {
+ final String[] args = { "-c", "foobar", "-b", "toast" };
+ CommandLine cl = parser.parse(options, args, true);
+ assertTrue(cl.hasOption("c"), "Confirm -c is set");
+ assertEquals(3, cl.getArgList().size(), "Confirm 3 extra args: " + cl.getArgList().size());
+ cl = parser.parse(options, cl.getArgs());
+ assertFalse(cl.hasOption("c"), "Confirm -c is not set");
+ assertTrue(cl.hasOption("b"), "Confirm -b is set");
+ assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
+ assertEquals(1, cl.getArgList().size(), "Confirm 1 extra arg: " + cl.getArgList().size());
+ assertEquals("foobar", cl.getArgList().get(0), "Confirm value of extra arg: " + cl.getArgList().get(0));
+ }
+
+ @Test
+ void testMultipleWithLong() throws Exception {
+ final String[] args = { "--copt", "foobar", "--bfile", "toast" };
+ CommandLine cl = parser.parse(options, args, true);
+ assertTrue(cl.hasOption("c"), "Confirm -c is set");
+ assertEquals(3, cl.getArgList().size(), "Confirm 3 extra args: " + cl.getArgList().size());
+ cl = parser.parse(options, cl.getArgs());
+ assertFalse(cl.hasOption("c"), "Confirm -c is not set");
+ assertTrue(cl.hasOption("b"), "Confirm -b is set");
+ assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
+ assertEquals(1, cl.getArgList().size(), "Confirm 1 extra arg: " + cl.getArgList().size());
+ assertEquals("foobar", cl.getArgList().get(0), "Confirm value of extra arg: " + cl.getArgList().get(0));
+ }
+
+ @Test
+ void testMultipleWithNull() throws Exception {
+ final String[] args = { null, "-c", null, "foobar", null, "-b", null, "toast", null };
+ CommandLine cl = parser.parse(options, args, true);
+ assertTrue(cl.hasOption("c"), "Confirm -c is set");
+ assertEquals(3, cl.getArgList().size(), "Confirm 3 extra args: " + cl.getArgList().size());
+ cl = parser.parse(options, cl.getArgs());
+ assertFalse(cl.hasOption("c"), "Confirm -c is not set");
+ assertTrue(cl.hasOption("b"), "Confirm -b is set");
+ assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
+ assertEquals(1, cl.getArgList().size(), "Confirm 1 extra arg: " + cl.getArgList().size());
+ assertEquals("foobar", cl.getArgList().get(0), "Confirm value of extra arg: " + cl.getArgList().get(0));
+ }
+
+ @Test
+ void testNegativeArgument() throws Exception {
+ final String[] args = { "-b", "-1" };
+ final CommandLine cl = parser.parse(options, args);
+ assertEquals("-1", cl.getOptionValue("b"));
+ }
+
+ @Test
+ void testNegativeOption() throws Exception {
+ final String[] args = { "-b", "-1" };
+ options.addOption("1", false, null);
+ final CommandLine cl = parser.parse(options, args);
+ assertEquals("-1", cl.getOptionValue("b"));
+ }
+
+ @Test
+ void testOptionalArgsOptionBuilder() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.hasOptionalArgs(2).create('i'));
+ final Properties properties = new Properties();
+
+ CommandLine cmd = parse(parser, options, new String[] { "-i" }, properties);
+ assertTrue(cmd.hasOption("i"));
+ assertNull(cmd.getOptionValues("i"));
+
+ cmd = parse(parser, options, new String[] { "-i", "paper" }, properties);
+ assertTrue(cmd.hasOption("i"));
+ assertArrayEquals(new String[] { "paper" }, cmd.getOptionValues("i"));
+
+ cmd = parse(parser, options, new String[] { "-i", "paper", "scissors" }, properties);
+ assertTrue(cmd.hasOption("i"));
+ assertArrayEquals(new String[] { "paper", "scissors" }, cmd.getOptionValues("i"));
+
+ cmd = parse(parser, options, new String[] { "-i", "paper", "scissors", "rock" }, properties);
+ assertTrue(cmd.hasOption("i"));
+ assertArrayEquals(new String[] { "paper", "scissors" }, cmd.getOptionValues("i"));
+ assertArrayEquals(new String[] { "rock" }, cmd.getArgs());
+ }
+
+ @Test
+ void testOptionalArgsOptionDotBuilder() throws Exception {
+ final Options options = new Options();
+ options.addOption(Option.builder("i").numberOfArgs(2).optionalArg(true).get());
+ final Properties properties = new Properties();
+
+ CommandLine cmd = parse(parser, options, new String[] { "-i" }, properties);
+ assertTrue(cmd.hasOption("i"));
+ assertNull(cmd.getOptionValues("i"));
+
+ cmd = parse(parser, options, new String[] { "-i", "paper" }, properties);
+ assertTrue(cmd.hasOption("i"));
+ assertArrayEquals(new String[] { "paper" }, cmd.getOptionValues("i"));
+
+ cmd = parse(parser, options, new String[] { "-i", "paper", "scissors" }, properties);
+ assertTrue(cmd.hasOption("i"));
+ assertArrayEquals(new String[] { "paper", "scissors" }, cmd.getOptionValues("i"));
+
+ cmd = parse(parser, options, new String[] { "-i", "paper", "scissors", "rock" }, properties);
+ assertTrue(cmd.hasOption("i"));
+ assertArrayEquals(new String[] { "paper", "scissors" }, cmd.getOptionValues("i"));
+ assertArrayEquals(new String[] { "rock" }, cmd.getArgs());
+
+ options.addOption(Option.builder("j").numberOfArgs(3).optionalArg(true).get());
+ cmd = parse(parser, options, new String[] { "-j" }, properties);
+ }
+
+ @Test
+ void testOptionAndRequiredOption() throws Exception {
+ final String[] args = { "-a", "-b", "file" };
+
+ final Options options = new Options();
+ options.addOption("a", "enable-a", false, null);
+ options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
+
+ final CommandLine cl = parser.parse(options, args);
+
+ assertTrue(cl.hasOption("a"), "Confirm -a is set");
+ assertTrue(cl.hasOption("b"), "Confirm -b is set");
+ assertEquals("file", cl.getOptionValue("b"), "Confirm arg of -b");
+ assertTrue(cl.getArgList().isEmpty(), "Confirm NO of extra args");
+ }
+
+ @Test
+ void testOptionGroup() throws Exception {
+ final OptionGroup optionGroup = new OptionGroup();
+ assertFalse(optionGroup.isSelected());
+ optionGroup.addOption(OptionBuilder.create("a"));
+ optionGroup.addOption(OptionBuilder.create("b"));
+
+ final Options options = new Options();
+ options.addOptionGroup(optionGroup);
+
+ parser.parse(options, new String[] { "-b" });
+
+ assertTrue(optionGroup.isSelected());
+ assertEquals("b", optionGroup.getSelected(), "selected option");
+ }
+
+ @Test
+ void testOptionGroupLong() throws Exception {
+ final OptionGroup optionGroup = new OptionGroup();
+ optionGroup.addOption(OptionBuilder.withLongOpt("foo").create());
+ optionGroup.addOption(OptionBuilder.withLongOpt("bar").create());
+
+ final Options options = new Options();
+ options.addOptionGroup(optionGroup);
+
+ final CommandLine cl = parser.parse(options, new String[] { "--bar" });
+
+ assertTrue(cl.hasOption("bar"));
+ assertTrue(optionGroup.isSelected());
+ assertEquals("bar", optionGroup.getSelected(), "selected option");
+ }
+
+ @Test
+ void testPartialLongOptionSingleDash() throws Exception {
+ final String[] args = { "-ver" };
+
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("version").create());
+ options.addOption(OptionBuilder.hasArg().create('v'));
+
+ final CommandLine cl = parser.parse(options, args);
+
+ assertTrue(cl.hasOption("version"), "Confirm --version is set");
+ assertFalse(cl.hasOption("v"), "Confirm -v is not set");
+ }
+
+ @Test
+ void testPropertiesOption1() throws Exception {
+ final String[] args = { "-Jsource=1.5", "-J", "target", "1.5", "foo" };
+
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withValueSeparator().hasArgs(2).create('J'));
+
+ final CommandLine cl = parser.parse(options, args);
+
+ final List values = Arrays.asList(cl.getOptionValues("J"));
+ assertNotNull(values, "null values");
+ assertEquals(4, values.size(), "number of values");
+ assertEquals("source", values.get(0), "value 1");
+ assertEquals("1.5", values.get(1), "value 2");
+ assertEquals("target", values.get(2), "value 3");
+ assertEquals("1.5", values.get(3), "value 4");
+
+ final List> argsleft = cl.getArgList();
+ assertEquals(1, argsleft.size(), "Should be 1 arg left");
+ assertEquals("foo", argsleft.get(0), "Expecting foo");
+ }
+
+ @Test
+ void testPropertiesOption2() throws Exception {
+ final String[] args = { "-Dparam1", "-Dparam2=value2", "-D" };
+
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withValueSeparator().hasOptionalArgs(2).create('D'));
+
+ final CommandLine cl = parser.parse(options, args);
+
+ final Properties props = cl.getOptionProperties("D");
+ assertNotNull(props, "null properties");
+ assertEquals(2, props.size(), "number of properties in " + props);
+ assertEquals("true", props.getProperty("param1"), "property 1");
+ assertEquals("value2", props.getProperty("param2"), "property 2");
+
+ final List> argsleft = cl.getArgList();
+ assertEquals(0, argsleft.size(), "Should be no arg left");
+ }
+
+ @Test
+ void testPropertyOptionFlags() throws Exception {
+ final Options options = new Options();
+ options.addOption("a", false, "toggle -a");
+ options.addOption("c", "c", false, "toggle -c");
+ options.addOption(OptionBuilder.hasOptionalArg().create('e'));
+
+ Properties properties = new Properties();
+ properties.setProperty("a", "true");
+ properties.setProperty("c", "yes");
+ properties.setProperty("e", "1");
+
+ CommandLine cmd = parse(parser, options, null, properties);
+ assertTrue(cmd.hasOption("a"));
+ assertTrue(cmd.hasOption("c"));
+ assertTrue(cmd.hasOption("e"));
+
+ properties = new Properties();
+ properties.setProperty("a", "false");
+ properties.setProperty("c", "no");
+ properties.setProperty("e", "0");
+
+ cmd = parse(parser, options, null, properties);
+ assertFalse(cmd.hasOption("a"));
+ assertFalse(cmd.hasOption("c"));
+ assertTrue(cmd.hasOption("e")); // this option accepts an argument
+
+ properties = new Properties();
+ properties.setProperty("a", "TRUE");
+ properties.setProperty("c", "nO");
+ properties.setProperty("e", "TrUe");
+
+ cmd = parse(parser, options, null, properties);
+ assertTrue(cmd.hasOption("a"));
+ assertFalse(cmd.hasOption("c"));
+ assertTrue(cmd.hasOption("e"));
+
+ properties = new Properties();
+ properties.setProperty("a", "just a string");
+ properties.setProperty("e", "");
+
+ cmd = parse(parser, options, null, properties);
+ assertFalse(cmd.hasOption("a"));
+ assertFalse(cmd.hasOption("c"));
+ assertTrue(cmd.hasOption("e"));
+
+ properties = new Properties();
+ properties.setProperty("a", "0");
+ properties.setProperty("c", "1");
+
+ cmd = parse(parser, options, null, properties);
+ assertFalse(cmd.hasOption("a"));
+ assertTrue(cmd.hasOption("c"));
+ }
+
+ @Test
+ void testPropertyOptionGroup() throws Exception {
+ final Options options = new Options();
+
+ final OptionGroup optionGroup1 = new OptionGroup();
+ optionGroup1.addOption(new Option("a", null));
+ optionGroup1.addOption(new Option("b", null));
+ options.addOptionGroup(optionGroup1);
+
+ final OptionGroup optionGroup2 = new OptionGroup();
+ optionGroup2.addOption(new Option("x", null));
+ optionGroup2.addOption(new Option("y", null));
+ options.addOptionGroup(optionGroup2);
+
+ final String[] args = { "-a" };
+
+ final Properties properties = new Properties();
+ properties.put("b", "true");
+ properties.put("x", "true");
+
+ final CommandLine cmd = parse(parser, options, args, properties);
+
+ assertTrue(cmd.hasOption("a"));
+ assertFalse(cmd.hasOption("b"));
+ assertTrue(cmd.hasOption("x"));
+ assertFalse(cmd.hasOption("y"));
+ }
+
+ @Test
+ void testPropertyOptionMultipleValues() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.hasArgs().withValueSeparator(',').create('k'));
+
+ final Properties properties = new Properties();
+ properties.setProperty("k", "one,two");
+
+ final CommandLine cmd = parse(parser, options, null, properties);
+ assertTrue(cmd.hasOption("k"));
+ final String[] values = { "one", "two" };
+ assertArrayEquals(values, cmd.getOptionValues('k'));
+ }
+
+ @Test
+ void testPropertyOptionRequired() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.isRequired().create("f"));
+
+ final Properties properties = new Properties();
+ properties.setProperty("f", "true");
+
+ final CommandLine cmd = parse(parser, options, null, properties);
+ assertTrue(cmd.hasOption("f"));
+ }
+
+ @Test
+ void testPropertyOptionSingularValue() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.hasOptionalArgs(2).withLongOpt("hide").create());
+
+ final Properties properties = new Properties();
+ properties.setProperty("hide", "seek");
+
+ final CommandLine cmd = parse(parser, options, null, properties);
+ assertTrue(cmd.hasOption("hide"));
+ assertEquals("seek", cmd.getOptionValue("hide"));
+ assertFalse(cmd.hasOption("fake"));
+ }
+
+ @Test
+ void testPropertyOptionUnexpected() throws Exception {
+ final Options options = new Options();
+ final Properties properties = new Properties();
+ properties.setProperty("f", "true");
+ assertThrows(UnrecognizedOptionException.class, () -> parse(parser, options, null, properties));
+ }
+
+ @Test
+ void testPropertyOverrideValues() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.hasOptionalArgs(2).create('i'));
+ options.addOption(OptionBuilder.hasOptionalArgs().create('j'));
+
+ final String[] args = { "-j", "found", "-i", "ink" };
+
+ final Properties properties = new Properties();
+ properties.setProperty("j", "seek");
+
+ final CommandLine cmd = parse(parser, options, args, properties);
+ assertTrue(cmd.hasOption("j"));
+ assertEquals("found", cmd.getOptionValue("j"));
+ assertTrue(cmd.hasOption("i"));
+ assertEquals("ink", cmd.getOptionValue("i"));
+ assertFalse(cmd.hasOption("fake"));
+ }
+
+ @Test
+ void testReuseOptionsTwice() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.isRequired().create('v'));
+ // first parsing
+ parser.parse(options, new String[] { "-v" });
+ // second parsing, with the same Options instance and an invalid command line
+ assertThrows(MissingOptionException.class, () -> parser.parse(options, new String[0]));
+ }
+
+ @Test
+ void testShortOptionConcatenatedQuoteHandling() throws Exception {
+ final CommandLine cl = parser.parse(options, new String[] { "-b\"quoted string\"" });
+ assertEquals("quoted string", cl.getOptionValue("b"), "Confirm -b\"arg\" strips quotes");
+ }
+
+ @Test
+ void testShortOptionQuoteHandling() throws Exception {
+ final CommandLine cl = parser.parse(options, new String[] { "-b", "\"quoted string\"" });
+ assertEquals("quoted string", cl.getOptionValue("b"), "Confirm -b \"arg\" strips quotes");
+ }
+
+ @Test
+ void testShortWithEqual() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
+ final CommandLine cl = parser.parse(options, new String[] { "-f=bar" });
+ assertEquals("bar", cl.getOptionValue("foo"));
+ }
+
+ @Test
+ void testShortWithoutEqual() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
+ final CommandLine cl = parser.parse(options, new String[] { "-fbar" });
+ assertEquals("bar", cl.getOptionValue("foo"));
+ }
+
+ @Test
+ void testShortWithUnexpectedArgument() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
+ final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, new String[] { "-f=bar" }));
+ assertEquals("-f=bar", e.getOption());
+ }
+
+ @Test
+ void testSimpleLong() throws Exception {
+ final CommandLine cl = parser.parse(options, new String[] { "--enable-a", "--bfile", "toast", "foo", "bar" });
+ assertTrue(cl.hasOption("a"), "Confirm -a is set");
+ assertTrue(cl.hasOption("b"), "Confirm -b is set");
+ assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
+ assertEquals("toast", cl.getOptionValue("bfile"), "Confirm arg of --bfile");
+ assertEquals(2, cl.getArgList().size(), "Confirm size of extra args");
+ }
+
+ @Test
+ void testSimpleShort() throws Exception {
+ final CommandLine cl = parser.parse(options, new String[] { "-a", "-b", "toast", "foo", "bar" });
+ assertTrue(cl.hasOption("a"), "Confirm -a is set");
+ assertTrue(cl.hasOption("b"), "Confirm -b is set");
+ assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
+ assertEquals(2, cl.getArgList().size(), "Confirm size of extra args");
+ }
+
+ @Test
+ void testSingleDash() throws Exception {
+ final CommandLine cl = parser.parse(options, new String[] { "--copt", "-b", "-", "-a", "-" });
+ assertTrue(cl.hasOption("a"), "Confirm -a is set");
+ assertTrue(cl.hasOption("b"), "Confirm -b is set");
+ assertEquals("-", cl.getOptionValue("b"), "Confirm arg of -b");
+ assertEquals(1, cl.getArgList().size(), "Confirm 1 extra arg: " + cl.getArgList().size());
+ assertEquals("-", cl.getArgList().get(0), "Confirm value of extra arg: " + cl.getArgList().get(0));
+ }
+
+ @Test
+ void testStopAtExpectedArg() throws Exception {
+ final String[] args = { "-b", "foo" };
+ final CommandLine cl = parser.parse(options, args, true);
+ assertTrue(cl.hasOption('b'), "Confirm -b is set");
+ assertEquals("foo", cl.getOptionValue('b'), "Confirm -b is set");
+ assertTrue(cl.getArgList().isEmpty(), "Confirm no extra args: " + cl.getArgList().size());
+ }
+
+ @Test
+ void testStopAtNonOptionLong() throws Exception {
+ final String[] args = { "--zop==1", "-abtoast", "--b=bar" };
+ final CommandLine cl = parser.parse(options, args, true);
+ assertFalse(cl.hasOption("a"), "Confirm -a is not set");
+ assertFalse(cl.hasOption("b"), "Confirm -b is not set");
+ assertEquals(3, cl.getArgList().size(), "Confirm 3 extra args: " + cl.getArgList().size());
+ }
+
+ @Test
+ void testStopAtNonOptionShort() throws Exception {
+ final CommandLine cl = parser.parse(options, new String[] { "-z", "-a", "-btoast" }, true);
+ assertFalse(cl.hasOption("a"), "Confirm -a is not set");
+ assertEquals(3, cl.getArgList().size(), "Confirm 3 extra args: " + cl.getArgList().size());
+ }
+
+ @Test
+ void testStopAtUnexpectedArg() throws Exception {
+ final CommandLine cl = parser.parse(options, new String[] { "-c", "foober", "-b", "toast" }, true);
+ assertTrue(cl.hasOption("c"), "Confirm -c is set");
+ assertEquals(3, cl.getArgList().size(), "Confirm 3 extra args: " + cl.getArgList().size());
+ }
+
+ @Test
+ void testStopBursting() throws Exception {
+ final CommandLine cl = parser.parse(options, new String[] { "-azc" }, true);
+ assertTrue(cl.hasOption("a"), "Confirm -a is set");
+ assertFalse(cl.hasOption("c"), "Confirm -c is not set");
+ assertEquals(1, cl.getArgList().size(), "Confirm 1 extra arg: " + cl.getArgList().size());
+ assertTrue(cl.getArgList().contains("zc"));
+ }
+
+ @Test
+ void testStopBursting2() throws Exception {
+ CommandLine cl = parser.parse(options, new String[] { "-c", "foobar", "-btoast" }, true);
+ assertTrue(cl.hasOption("c"), "Confirm -c is set");
+ assertEquals(2, cl.getArgList().size(), "Confirm 2 extra args: " + cl.getArgList().size());
+ cl = parser.parse(options, cl.getArgs());
+ assertFalse(cl.hasOption("c"), "Confirm -c is not set");
+ assertTrue(cl.hasOption("b"), "Confirm -b is set");
+ assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
+ assertEquals(1, cl.getArgList().size(), "Confirm 1 extra arg: " + cl.getArgList().size());
+ assertEquals("foobar", cl.getArgList().get(0), "Confirm value of extra arg: " + cl.getArgList().get(0));
+ }
+
+ @Test
+ void testUnambiguousPartialLongOption1() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("version").create());
+ options.addOption(OptionBuilder.withLongOpt("help").create());
+ final CommandLine cl = parser.parse(options, new String[] { "--ver" });
+ assertTrue(cl.hasOption("version"), "Confirm --version is set");
+ }
+
+ @Test
+ void testUnambiguousPartialLongOption2() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("version").create());
+ options.addOption(OptionBuilder.withLongOpt("help").create());
+ final CommandLine cl = parser.parse(options, new String[] { "-ver" });
+ assertTrue(cl.hasOption("version"), "Confirm --version is set");
+ }
+
+ @Test
+ void testUnambiguousPartialLongOption3() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
+ options.addOption(OptionBuilder.withLongOpt("help").create());
+ final CommandLine cl = parser.parse(options, new String[] { "--ver=1" });
+ assertTrue(cl.hasOption("verbose"), "Confirm --verbose is set");
+ assertEquals("1", cl.getOptionValue("verbose"));
+ }
+
+ @Test
+ void testUnambiguousPartialLongOption4() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
+ options.addOption(OptionBuilder.withLongOpt("help").create());
+ final CommandLine cl = parser.parse(options, new String[] { "-ver=1" });
+ assertTrue(cl.hasOption("verbose"), "Confirm --verbose is set");
+ assertEquals("1", cl.getOptionValue("verbose"));
+ }
+
+ @Test
+ void testUnlimitedArgs() throws Exception {
+ final Options options = new Options();
+ options.addOption(OptionBuilder.hasArgs().create("e"));
+ options.addOption(OptionBuilder.hasArgs().create("f"));
+ final CommandLine cl = parser.parse(options, new String[] { "-e", "one", "two", "-f", "alpha" });
+ assertTrue(cl.hasOption("e"), "Confirm -e is set");
+ assertEquals(2, cl.getOptionValues("e").length, "number of arg for -e");
+ assertTrue(cl.hasOption("f"), "Confirm -f is set");
+ assertEquals(1, cl.getOptionValues("f").length, "number of arg for -f");
+ }
+
+ @Test
+ void testUnrecognizedOption() throws Exception {
+ final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class,
+ () -> parser.parse(options, new String[] { "-a", "-d", "-b", "toast", "foo", "bar" }));
+ assertEquals("-d", e.getOption());
+ }
+
+ @Test
+ void testUnrecognizedOptionWithBursting() throws Exception {
+ final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class,
+ () -> parser.parse(options, new String[] { "-adbtoast", "foo", "bar" }));
+ assertEquals("-adbtoast", e.getOption());
+ }
+
+ @Test
+ void testWithRequiredOption() throws Exception {
+ final String[] args = { "-b", "file" };
+ final Options options = new Options();
+ options.addOption("a", "enable-a", false, null);
+ options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
+ final CommandLine cl = parser.parse(options, args);
+ assertFalse(cl.hasOption("a"), "Confirm -a is NOT set");
+ assertTrue(cl.hasOption("b"), "Confirm -b is set");
+ assertEquals("file", cl.getOptionValue("b"), "Confirm arg of -b");
+ assertTrue(cl.getArgList().isEmpty(), "Confirm NO of extra args");
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/AlreadySelectedExceptionTest.java b/src/test/java/org/apache/commons/cli/AlreadySelectedExceptionTest.java
new file mode 100644
index 000000000..07ef961b8
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/AlreadySelectedExceptionTest.java
@@ -0,0 +1,41 @@
+/*
+ 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
+
+ https://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 static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link AlreadySelectedException}.
+ */
+class AlreadySelectedExceptionTest {
+
+ @Test
+ void testConstructor() {
+ assertEquals("a", new AlreadySelectedException("a").getMessage());
+ assertNull(new AlreadySelectedException("a").getOption());
+ final Option option = new Option("a", "d");
+ final OptionGroup optionGroup = new OptionGroup();
+ assertNotNull(new AlreadySelectedException(optionGroup, option).getMessage());
+ assertEquals(option, new AlreadySelectedException(optionGroup, option).getOption());
+ assertEquals(optionGroup, new AlreadySelectedException(optionGroup, option).getOptionGroup());
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/ApplicationTest.java b/src/test/java/org/apache/commons/cli/ApplicationTest.java
index 9ea0ee8c5..59b32917a 100644
--- a/src/test/java/org/apache/commons/cli/ApplicationTest.java
+++ b/src/test/java/org/apache/commons/cli/ApplicationTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,13 +17,13 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.PrintWriter;
import java.io.StringWriter;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
/**
* This is a collection of tests that test real world applications command lines.
@@ -39,12 +39,12 @@ Licensed to the Apache Software Foundation (ASF) under one or more
*
*/
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class ApplicationTest {
+class ApplicationTest {
/**
* Ant test
*/
@Test
- public void testAnt() throws Exception {
+ void testAnt() throws Exception {
// use the GNU parser
final CommandLineParser parser = new GnuParser();
final Options options = new Options();
@@ -86,7 +86,7 @@ public void testAnt() throws Exception {
}
@Test
- public void testGroovy() throws Exception {
+ void testGroovy() throws Exception {
final Options options = new Options();
//@formatter:off
@@ -125,7 +125,7 @@ public void testGroovy() throws Exception {
options.addOption(
OptionBuilder.withArgName("extension")
.hasOptionalArg()
- .withDescription("modify files in place; create backup if extension is given (e.g. \'.bak\')")
+ .withDescription("modify files in place; create backup if extension is given (for example \'.bak\')")
.create('i'));
options.addOption(
OptionBuilder.hasArg(false)
@@ -156,7 +156,7 @@ public void testGroovy() throws Exception {
}
@Test
- public void testLs() throws Exception {
+ void testLs() throws Exception {
// create the command line parser
final CommandLineParser parser = new PosixParser();
final Options options = new Options();
@@ -186,7 +186,7 @@ public void testLs() throws Exception {
* author Slawek Zachcial
*/
@Test
- public void testMan() {
+ void testMan() {
final String cmdLine = "man [-c|-f|-k|-w|-tZT device] [-adlhu7V] [-Mpath] [-Ppager] [-Slist] [-msystem] [-pstring] [-Llocale] [-eextension] [section]"
+ " page ...";
//@formatter:off
@@ -197,7 +197,7 @@ public void testMan() {
addOption("f", "whatis", false, "equivalent to whatis.").
addOption("k", "apropos", false, "equivalent to apropos.").
addOption("w", "location", false, "print physical location of man page(s).").
- addOption("l", "local-file", false, "interpret 'page' argument(s) as local filename(s)").
+ addOption("l", "local-file", false, "interpret 'page' argument(s) as local file name(s)").
addOption("u", "update", false, "force a cache consistency check.").
//FIXME - should generate -r,--prompt string
addOption("r", "prompt", true, "provide 'less' pager with prompt.").
@@ -215,7 +215,7 @@ public void testMan() {
//FIXME - should generate -S,--sections list
addOption("S", "sections", true, "use colon separated section list.").
//FIXME - should generate -m,--systems system
- addOption("m", "systems", true, "search for man pages from other unix system(s).").
+ addOption("m", "systems", true, "search for man pages from other Unix system(s).").
//FIXME - should generate -L,--locale locale
addOption("L", "locale", true, "define the locale for this particular man search.").
//FIXME - should generate -p,--preprocessor string
@@ -227,7 +227,7 @@ public void testMan() {
//@formatter:on
final HelpFormatter hf = new HelpFormatter();
- final String eol = System.getProperty("line.separator");
+ final String eol = System.lineSeparator();
final StringWriter out = new StringWriter();
hf.printHelp(new PrintWriter(out), 60, cmdLine, null, options, HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, null, false);
//@formatter:off
@@ -248,13 +248,13 @@ public void testMan() {
" -h,--help show this usage message." + eol +
" -k,--apropos equivalent to apropos." + eol +
" -l,--local-file interpret 'page' argument(s) as" + eol +
- " local filename(s)" + eol +
+ " local file name(s)" + eol +
" -L,--locale define the locale for this" + eol +
" particular man search." + eol +
" -M,--manpath set search path for manual pages" + eol +
" to 'path'." + eol +
" -m,--systems search for man pages from other" + eol +
- " unix system(s)." + eol +
+ " Unix system(s)." + eol +
" -P,--pager use program 'pager' to display" + eol +
" output." + eol +
" -p,--preprocessor string indicates which" + eol +
@@ -279,7 +279,7 @@ public void testMan() {
* Real world test with long and short options.
*/
@Test
- public void testNLT() throws Exception {
+ void testNLT() throws Exception {
final Option help = new Option("h", "help", false, "print this message");
final Option version = new Option("v", "version", false, "print version information");
final Option newRun = new Option("n", "new", false, "Create NLT cache entries only for new items");
diff --git a/src/test/java/org/apache/commons/cli/ArgumentIsOptionTest.java b/src/test/java/org/apache/commons/cli/ArgumentIsOptionTest.java
index 1e2c4bb1f..91ab4d187 100644
--- a/src/test/java/org/apache/commons/cli/ArgumentIsOptionTest.java
+++ b/src/test/java/org/apache/commons/cli/ArgumentIsOptionTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,54 +17,54 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class ArgumentIsOptionTest {
+class ArgumentIsOptionTest {
private Options options;
private CommandLineParser parser;
- @Before
+ @BeforeEach
public void setUp() {
options = new Options().addOption("p", false, "Option p").addOption("attr", true, "Option accepts argument");
parser = new PosixParser();
}
@Test
- public void testOption() throws Exception {
+ void testOption() throws Exception {
final String[] args = {"-p"};
final CommandLine cl = parser.parse(options, args);
- assertTrue("Confirm -p is set", cl.hasOption("p"));
- assertFalse("Confirm -attr is not set", cl.hasOption("attr"));
- assertEquals("Confirm all arguments recognized", 0, cl.getArgs().length);
+ assertTrue(cl.hasOption("p"), "Confirm -p is set");
+ assertFalse(cl.hasOption("attr"), "Confirm -attr is not set");
+ assertEquals(0, cl.getArgs().length, "Confirm all arguments recognized");
}
@Test
- public void testOptionAndOptionWithArgument() throws Exception {
+ void testOptionAndOptionWithArgument() throws Exception {
final String[] args = {"-p", "-attr", "p"};
final CommandLine cl = parser.parse(options, args);
- assertTrue("Confirm -p is set", cl.hasOption("p"));
- assertTrue("Confirm -attr is set", cl.hasOption("attr"));
- assertEquals("Confirm arg of -attr", "p", cl.getOptionValue("attr"));
- assertEquals("Confirm all arguments recognized", 0, cl.getArgs().length);
+ assertTrue(cl.hasOption("p"), "Confirm -p is set");
+ assertTrue(cl.hasOption("attr"), "Confirm -attr is set");
+ assertEquals("p", cl.getOptionValue("attr"), "Confirm arg of -attr");
+ assertEquals(0, cl.getArgs().length, "Confirm all arguments recognized");
}
@Test
- public void testOptionWithArgument() throws Exception {
+ void testOptionWithArgument() throws Exception {
final String[] args = {"-attr", "p"};
final CommandLine cl = parser.parse(options, args);
- assertFalse("Confirm -p is set", cl.hasOption("p"));
- assertTrue("Confirm -attr is set", cl.hasOption("attr"));
- assertEquals("Confirm arg of -attr", "p", cl.getOptionValue("attr"));
- assertEquals("Confirm all arguments recognized", 0, cl.getArgs().length);
+ assertFalse(cl.hasOption("p"), "Confirm -p is set");
+ assertTrue(cl.hasOption("attr"), "Confirm -attr is set");
+ assertEquals("p", cl.getOptionValue("attr"), "Confirm arg of -attr");
+ assertEquals(0, cl.getArgs().length, "Confirm all arguments recognized");
}
}
diff --git a/src/test/java/org/apache/commons/cli/BasicParserTest.java b/src/test/java/org/apache/commons/cli/BasicParserTest.java
index fe7a823e5..420b609cc 100644
--- a/src/test/java/org/apache/commons/cli/BasicParserTest.java
+++ b/src/test/java/org/apache/commons/cli/BasicParserTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,14 +17,17 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+/**
+ * TODO Needs a rework using JUnit parameterized tests.
+ */
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class BasicParserTest extends ParserTestCase {
+class BasicParserTest extends AbstractParserTestCase {
@Override
- @Before
+ @BeforeEach
public void setUp() {
super.setUp();
parser = new BasicParser();
@@ -32,157 +35,163 @@ public void setUp() {
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testAmbiguousLongWithoutEqualSingleDash() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testAmbiguousLongWithoutEqualSingleDash() throws Exception {
+ }
+
+ @Override
+ @Test
+ @Disabled("not supported by the BasicParser")
+ void testAmbiguousLongWithoutEqualSingleDash2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testAmbiguousPartialLongOption1() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testAmbiguousPartialLongOption1() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testAmbiguousPartialLongOption2() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testAmbiguousPartialLongOption2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testAmbiguousPartialLongOption3() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testAmbiguousPartialLongOption3() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testAmbiguousPartialLongOption4() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testAmbiguousPartialLongOption4() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testBursting() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testBursting() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testDoubleDash2() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testDoubleDash2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testLongOptionWithEqualsQuoteHandling() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testLongOptionWithEqualsQuoteHandling() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testLongWithEqualDoubleDash() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testLongWithEqualDoubleDash() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testLongWithEqualSingleDash() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testLongWithEqualSingleDash() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testLongWithoutEqualSingleDash() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testLongWithoutEqualSingleDash() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testMissingArgWithBursting() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testMissingArgWithBursting() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser (CLI-184)")
- public void testNegativeOption() throws Exception {
+ @Disabled("not supported by the BasicParser (CLI-184)")
+ void testNegativeOption() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testPartialLongOptionSingleDash() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testPartialLongOptionSingleDash() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testPropertiesOption1() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testPropertiesOption1() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testPropertiesOption2() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testPropertiesOption2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testShortOptionConcatenatedQuoteHandling() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testShortOptionConcatenatedQuoteHandling() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testShortWithEqual() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testShortWithEqual() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testShortWithoutEqual() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testShortWithoutEqual() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testStopBursting() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testStopBursting() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testStopBursting2() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testStopBursting2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testUnambiguousPartialLongOption1() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testUnambiguousPartialLongOption1() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testUnambiguousPartialLongOption2() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testUnambiguousPartialLongOption2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testUnambiguousPartialLongOption3() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testUnambiguousPartialLongOption3() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testUnambiguousPartialLongOption4() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testUnambiguousPartialLongOption4() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the BasicParser")
- public void testUnrecognizedOptionWithBursting() throws Exception {
+ @Disabled("not supported by the BasicParser")
+ void testUnrecognizedOptionWithBursting() throws Exception {
}
}
diff --git a/src/test/java/org/apache/commons/cli/CommandLineTest.java b/src/test/java/org/apache/commons/cli/CommandLineTest.java
index b0f7f470d..6de605a54 100644
--- a/src/test/java/org/apache/commons/cli/CommandLineTest.java
+++ b/src/test/java/org/apache/commons/cli/CommandLineTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,58 +17,315 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Properties;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class CommandLineTest {
+
+ private enum Count { ONE, TWO, THREE }
+
+ private static Stream createHasOptionParameters() throws ParseException {
+ final List lst = new ArrayList<>();
+ final Option optT = Option.builder().option("T").longOpt("tee").deprecated().optionalArg(true).get();
+ final Option optU = Option.builder("U").longOpt("you").optionalArg(true).get();
+ final OptionGroup optionGroup = new OptionGroup().addOption(optT).addOption(optU);
+
+ final String[] foobar = { "foo", "bar" };
+ // T set
+ lst.add(Arguments.of(new String[] {"-T"}, optT, optionGroup, true, true, true, true, optT));
+ lst.add(Arguments.of(new String[] {"-T", "foo"}, optT, optionGroup, true, true, true, true, optT));
+ lst.add(Arguments.of(new String[] {"--tee"}, optT, optionGroup, true, true, true, true, optT));
+ lst.add(Arguments.of(new String[] {"--tee", "foo"}, optT, optionGroup, true, true, true, true, optT));
+
+ lst.add(Arguments.of(new String[] {"-U"}, optT, optionGroup, false, false, false, true, optU));
+ lst.add(Arguments.of(new String[] {"-U", "foo", "bar"}, optT, optionGroup, false, false, false, true, optU));
+ lst.add(Arguments.of(new String[] {"--you"}, optT, optionGroup, false, false, false, true, optU));
+ lst.add(Arguments.of(new String[] {"--you", "foo", "bar"}, optT, optionGroup, false, false, false, true, optU));
+
+ // U set
+ lst.add(Arguments.of(new String[] {"-T"}, optU, optionGroup, false, false, true, true, optT));
+ lst.add(Arguments.of(new String[] {"-T", "foo", "bar"}, optU, optionGroup, false, false, true, true, optT));
+ lst.add(Arguments.of(new String[] {"--tee"}, optU, optionGroup, false, false, true, true, optT));
+ lst.add(Arguments.of(new String[] {"--tee", "foo", "bar"}, optU, optionGroup, false, false, true, true, optT));
+
+ lst.add(Arguments.of(new String[] {"-U"}, optU, optionGroup, false, true, false, true, optU));
+ lst.add(Arguments.of(new String[] {"-U", "foo", "bar"}, optU, optionGroup, false, true, false, true, optU));
+ lst.add(Arguments.of(new String[] {"--you"}, optU, optionGroup, false, true, false, true, optU));
+ lst.add(Arguments.of(new String[] {"--you", "foo", "bar"}, optU, optionGroup, false, true, false, true, optU));
+
+ return lst.stream();
+ }
+
+ private static Stream createOptionValueParameters() throws ParseException {
+ final List lst = new ArrayList<>();
+ final Option optT = Option.builder().option("T").longOpt("tee").deprecated().optionalArg(true).get();
+ final Option optU = Option.builder("U").longOpt("you").optionalArg(true).get();
+ final OptionGroup optionGroup = new OptionGroup().addOption(optT).addOption(optU);
+
+ // T set
+ lst.add(Arguments.of(new String[] {"-T"}, optT, optionGroup, true, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"-T", "foo"}, optT, optionGroup, true, "foo", true, "foo", optT));
+ lst.add(Arguments.of(new String[] {"--tee"}, optT, optionGroup, true, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"--tee", "foo"}, optT, optionGroup, true, "foo", true, "foo", optT));
+
+ lst.add(Arguments.of(new String[] {"-U"}, optT, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"-U", "foo"}, optT, optionGroup, false, null, false, "foo", optU));
+ lst.add(Arguments.of(new String[] {"--you"}, optT, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"--you", "foo"}, optT, optionGroup, false, null, false, "foo", optU));
+
+ // U set
+ lst.add(Arguments.of(new String[] {"-T"}, optU, optionGroup, false, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"-T", "foo"}, optU, optionGroup, false, null, true, "foo", optT));
+ lst.add(Arguments.of(new String[] {"--tee"}, optU, optionGroup, false, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"--tee", "foo"}, optU, optionGroup, false, null, true, "foo", optT));
+
+ lst.add(Arguments.of(new String[] {"-U"}, optU, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"-U", "foo"}, optU, optionGroup, false, "foo", false, "foo", optU));
+ lst.add(Arguments.of(new String[] {"--you"}, optU, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"--you", "foo"}, optU, optionGroup, false, "foo", false, "foo", optU));
+
+ return lst.stream();
+ }
+
+ private static Stream createOptionValuesParameters() throws ParseException {
+ final List lst = new ArrayList<>();
+ final Option optT = Option.builder().option("T").longOpt("tee").numberOfArgs(2).deprecated().optionalArg(true).get();
+ final Option optU = Option.builder("U").longOpt("you").numberOfArgs(2).optionalArg(true).get();
+ final OptionGroup optionGroup = new OptionGroup().addOption(optT).addOption(optU);
+
+ final String[] foobar = { "foo", "bar" };
+ // T set
+ lst.add(Arguments.of(new String[] {"-T"}, optT, optionGroup, true, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"-T", "foo", "bar"}, optT, optionGroup, true, foobar, true, foobar, optT));
+ lst.add(Arguments.of(new String[] {"--tee"}, optT, optionGroup, true, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"--tee", "foo", "bar"}, optT, optionGroup, true, foobar, true, foobar, optT));
+
+ lst.add(Arguments.of(new String[] {"-U"}, optT, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"-U", "foo", "bar"}, optT, optionGroup, false, null, false, foobar, optU));
+ lst.add(Arguments.of(new String[] {"--you"}, optT, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"--you", "foo", "bar"}, optT, optionGroup, false, null, false, foobar, optU));
+
+ // U set
+ lst.add(Arguments.of(new String[] {"-T"}, optU, optionGroup, false, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"-T", "foo", "bar"}, optU, optionGroup, false, null, true, foobar, optT));
+ lst.add(Arguments.of(new String[] {"--tee"}, optU, optionGroup, false, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"--tee", "foo", "bar"}, optU, optionGroup, false, null, true, foobar, optT));
+
+ lst.add(Arguments.of(new String[] {"-U"}, optU, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"-U", "foo", "bar"}, optU, optionGroup, false, foobar, false, foobar, optU));
+ lst.add(Arguments.of(new String[] {"--you"}, optU, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"--you", "foo", "bar"}, optU, optionGroup, false, foobar, false, foobar, optU));
+
+ return lst.stream();
+ }
+
+ private static Stream createParsedOptionValueParameters() throws ParseException {
+ final List lst = new ArrayList<>();
+ final Option optT = Option.builder().option("T").longOpt("tee").deprecated().type(Integer.class).optionalArg(true).get();
+ final Option optU = Option.builder("U").longOpt("you").type(Integer.class).optionalArg(true).get();
+ final OptionGroup optionGroup = new OptionGroup().addOption(optT).addOption(optU);
+ final Integer expected = Integer.valueOf(1);
+
+ // T set
+ lst.add(Arguments.of(new String[] {"-T"}, optT, optionGroup, true, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"-T", "1"}, optT, optionGroup, true, expected, true, expected, optT));
+ lst.add(Arguments.of(new String[] {"--tee"}, optT, optionGroup, true, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"--tee", "1"}, optT, optionGroup, true, expected, true, expected, optT));
+
+ lst.add(Arguments.of(new String[] {"-U"}, optT, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"-U", "1"}, optT, optionGroup, false, null, false, expected, optU));
+ lst.add(Arguments.of(new String[] {"--you"}, optT, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"--you", "1"}, optT, optionGroup, false, null, false, expected, optU));
+
+ // U set
+ lst.add(Arguments.of(new String[] {"-T"}, optU, optionGroup, false, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"-T", "1"}, optU, optionGroup, false, null, true, expected, optT));
+ lst.add(Arguments.of(new String[] {"--tee"}, optU, optionGroup, false, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"--tee", "1"}, optU, optionGroup, false, null, true, expected, optT));
+
+ lst.add(Arguments.of(new String[] {"-U"}, optU, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"-U", "1"}, optU, optionGroup, false, expected, false, expected, optU));
+ lst.add(Arguments.of(new String[] {"--you"}, optU, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"--you", "1"}, optU, optionGroup, false, expected, false, expected, optU));
+
+ return lst.stream();
+ }
+
+ private static Stream createParsedOptionValuesParameters() throws ParseException {
+ final List lst = new ArrayList<>();
+ final Option optT = Option.builder().option("T").longOpt("tee").deprecated().type(Integer.class).optionalArg(true).hasArgs().get();
+ final Option optU = Option.builder("U").longOpt("you").type(Integer.class).optionalArg(true).hasArgs().get();
+ final OptionGroup optionGroup = new OptionGroup().addOption(optT).addOption(optU);
+ final Integer[] expected = {1, 2};
+
+ // T set
+ lst.add(Arguments.of(new String[] {"-T"}, optT, optionGroup, true, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"-T", "1", "2"}, optT, optionGroup, true, expected, true, expected, optT));
+ lst.add(Arguments.of(new String[] {"--tee"}, optT, optionGroup, true, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"--tee", "1", "2"}, optT, optionGroup, true, expected, true, expected, optT));
+
+ lst.add(Arguments.of(new String[] {"-U"}, optT, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"-U", "1", "2"}, optT, optionGroup, false, null, false, expected, optU));
+ lst.add(Arguments.of(new String[] {"--you"}, optT, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"--you", "1", "2"}, optT, optionGroup, false, null, false, expected, optU));
+
+ // U set
+ lst.add(Arguments.of(new String[] {"-T"}, optU, optionGroup, false, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"-T", "1", "2"}, optU, optionGroup, false, null, true, expected, optT));
+ lst.add(Arguments.of(new String[] {"--tee"}, optU, optionGroup, false, null, true, null, optT));
+ lst.add(Arguments.of(new String[] {"--tee", "1", "2"}, optU, optionGroup, false, null, true, expected, optT));
+
+ lst.add(Arguments.of(new String[] {"-U"}, optU, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"-U", "1", "2"}, optU, optionGroup, false, expected, false, expected, optU));
+ lst.add(Arguments.of(new String[] {"--you"}, optU, optionGroup, false, null, false, null, optU));
+ lst.add(Arguments.of(new String[] {"--you", "1", "2"}, optU, optionGroup, false, expected, false, expected, optU));
+
+ return lst.stream();
+ }
+
+ char asChar(final Option opt) {
+ return opt.getOpt().charAt(0);
+ }
+
+ private void assertWritten(final boolean optDep, final ByteArrayOutputStream baos) {
+ System.out.flush();
+ if (optDep) {
+ assertEquals("Option 'T''tee': Deprecated", baos.toString().trim());
+ } else {
+ assertEquals("", baos.toString());
+ }
+ baos.reset();
+ }
+
+ /**
+ * verifies that the deprecation handler has been called only once or not at all.
+ * @param optDep {@code true} if the dependency should have been logged.
+ * @param handler The list that the deprecation is logged to.
+ * @param opt The option that triggered the logging. May be (@code null} if {@code optDep} is {@code false}.
+ */
+ void checkHandler(final boolean optDep, final List handler, final Option opt) {
+ if (optDep) {
+ assertEquals(1, handler.size());
+ assertEquals(opt, handler.get(0));
+ } else {
+ assertEquals(0, handler.size());
+ }
+ handler.clear();
+ }
+
+ @Test
+ void testBadGetParsedOptionValue() throws Exception {
+
+ final Options options = new Options();
+ options.addOption(Option.builder("i").hasArg().type(Number.class).get());
+ options.addOption(Option.builder("c").hasArg().converter(s -> Count.valueOf(s.toUpperCase())).get());
+
+ final CommandLineParser parser = new DefaultParser();
+ final CommandLine cmd = parser.parse(options, new String[] {"-i", "foo", "-c", "bar"});
+
+ assertEquals(NumberFormatException.class, assertThrows(ParseException.class, () -> cmd.getParsedOptionValue("i")).getCause().getClass());
+ assertEquals(IllegalArgumentException.class, assertThrows(ParseException.class, () -> cmd.getParsedOptionValue("c")).getCause().getClass());
+ }
-import org.junit.Test;
+ @Test
+ void testBuilderBuild() {
+ // @formatter:off
+ final CommandLine cmd = CommandLine.builder()
+ .addArg("foo")
+ .addArg("bar")
+ .addOption(Option.builder("T").get())
+ .build();
+ // @formatter:on
+ assertEquals("foo", cmd.getArgs()[0]);
+ assertEquals("bar", cmd.getArgList().get(1));
+ assertEquals("T", cmd.getOptions()[0].getOpt());
+ }
+
+ @Test
+ void testBuilderGet() {
+ // @formatter:off
+ final CommandLine cmd = CommandLine.builder()
+ .addArg("foo")
+ .addArg("bar")
+ .addOption(Option.builder("T").get())
+ .get();
+ // @formatter:on
+ assertEquals("foo", cmd.getArgs()[0]);
+ assertEquals("bar", cmd.getArgList().get(1));
+ assertEquals("T", cmd.getOptions()[0].getOpt());
+ }
+
+ @Test
+ void testBuilderNullArgs() {
+ final CommandLine.Builder builder = CommandLine.builder();
+ builder.addArg(null).addArg(null);
+ builder.addOption(Option.builder("T").get());
+ final CommandLine cmd = builder.build();
-@SuppressWarnings("deprecation") // tests some deprecated classes
-public class CommandLineTest {
+ assertEquals(0, cmd.getArgs().length);
+ assertEquals("T", cmd.getOptions()[0].getOpt());
+ }
@Test
- public void testBuilder() {
- final CommandLine.Builder builder = new CommandLine.Builder();
+ void testBuilderNullOption() {
+ final CommandLine.Builder builder = CommandLine.builder();
builder.addArg("foo").addArg("bar");
- builder.addOption(Option.builder("T").build());
+ builder.addOption(null);
+ builder.addOption(null);
+ builder.addOption(null);
final CommandLine cmd = builder.build();
assertEquals("foo", cmd.getArgs()[0]);
assertEquals("bar", cmd.getArgList().get(1));
- assertEquals("T", cmd.getOptions()[0].getOpt());
+ assertEquals(0, cmd.getOptions().length);
}
@Test
- public void testGetOptionProperties() throws Exception {
+ void testGetOptionProperties() throws Exception {
final String[] args = {"-Dparam1=value1", "-Dparam2=value2", "-Dparam3", "-Dparam4=value4", "-D", "--property", "foo=bar"};
final Options options = new Options();
- options.addOption(OptionBuilder.withValueSeparator().hasOptionalArgs(2).create('D'));
- options.addOption(OptionBuilder.withValueSeparator().hasArgs(2).withLongOpt("property").create());
+ options.addOption(Option.builder("D").valueSeparator().optionalArg(true).numberOfArgs(2).get());
+ options.addOption(Option.builder().valueSeparator().numberOfArgs(2).longOpt("property").get());
final Parser parser = new GnuParser();
final CommandLine cl = parser.parse(options, args);
final Properties props = cl.getOptionProperties("D");
- assertNotNull("null properties", props);
- assertEquals("number of properties in " + props, 4, props.size());
- assertEquals("property 1", "value1", props.getProperty("param1"));
- assertEquals("property 2", "value2", props.getProperty("param2"));
- assertEquals("property 3", "true", props.getProperty("param3"));
- assertEquals("property 4", "value4", props.getProperty("param4"));
-
- assertEquals("property with long format", "bar", cl.getOptionProperties("property").getProperty("foo"));
+ assertNotNull(props, "null properties");
+ assertEquals(4, props.size(), "number of properties in " + props);
+ assertEquals("value1", props.getProperty("param1"), "property 1");
+ assertEquals("value2", props.getProperty("param2"), "property 2");
+ assertEquals("true", props.getProperty("param3"), "property 3");
+ assertEquals("value4", props.getProperty("param4"), "property 4");
+
+ assertEquals("bar", cl.getOptionProperties("property").getProperty("foo"), "property with long format");
}
@Test
- public void testGetOptionPropertiesWithOption() throws Exception {
+ void testGetOptionPropertiesWithOption() throws Exception {
final String[] args = {"-Dparam1=value1", "-Dparam2=value2", "-Dparam3", "-Dparam4=value4", "-D", "--property", "foo=bar"};
final Options options = new Options();
- final Option optionD = OptionBuilder.withValueSeparator().hasOptionalArgs(2).create('D');
- final Option optionProperty = OptionBuilder.withValueSeparator().hasArgs(2).withLongOpt("property").create();
+ final Option optionD = Option.builder("D").valueSeparator().numberOfArgs(2).optionalArg(true).get();
+ final Option optionProperty = Option.builder().valueSeparator().numberOfArgs(2).longOpt("property").get();
options.addOption(optionD);
options.addOption(optionProperty);
@@ -76,18 +333,32 @@ public void testGetOptionPropertiesWithOption() throws Exception {
final CommandLine cl = parser.parse(options, args);
final Properties props = cl.getOptionProperties(optionD);
- assertNotNull("null properties", props);
- assertEquals("number of properties in " + props, 4, props.size());
- assertEquals("property 1", "value1", props.getProperty("param1"));
- assertEquals("property 2", "value2", props.getProperty("param2"));
- assertEquals("property 3", "true", props.getProperty("param3"));
- assertEquals("property 4", "value4", props.getProperty("param4"));
+ assertNotNull(props, "null properties");
+ assertEquals(4, props.size(), "number of properties in " + props);
+ assertEquals("value1", props.getProperty("param1"), "property 1");
+ assertEquals("value2", props.getProperty("param2"), "property 2");
+ assertEquals("true", props.getProperty("param3"), "property 3");
+ assertEquals("value4", props.getProperty("param4"), "property 4");
+
+ assertEquals("bar", cl.getOptionProperties(optionProperty).getProperty("foo"), "property with long format");
+ }
+
+ @Test
+ void testGetOptionsBuilder() {
+ final CommandLine cmd = CommandLine.builder().build();
+ assertNotNull(cmd.getOptions());
+ assertEquals(0, cmd.getOptions().length);
- assertEquals("property with long format", "bar", cl.getOptionProperties(optionProperty).getProperty("foo"));
+ cmd.addOption(null);
+ cmd.addOption(new Option("a", null));
+ cmd.addOption(new Option("b", null));
+ cmd.addOption(new Option("c", null));
+
+ assertEquals(3, cmd.getOptions().length);
}
@Test
- public void testGetOptions() {
+ void testGetOptionsCtor() {
final CommandLine cmd = new CommandLine();
assertNotNull(cmd.getOptions());
assertEquals(0, cmd.getOptions().length);
@@ -95,61 +366,657 @@ public void testGetOptions() {
cmd.addOption(new Option("a", null));
cmd.addOption(new Option("b", null));
cmd.addOption(new Option("c", null));
+ cmd.addOption(null);
assertEquals(3, cmd.getOptions().length);
}
- @Test
- public void testGetParsedOptionValue() throws Exception {
- final Options options = new Options();
- options.addOption(OptionBuilder.hasArg().withType(Number.class).create("i"));
- options.addOption(OptionBuilder.hasArg().create("f"));
+ /**
+ * Test for get option value with and without default values. Verifies that deprecated options only report as
+ * deprecated once.
+ * @param args the argument strings to parse.
+ * @param opt the option to check for values with.
+ * @param optionGroup the option group to check for values with.
+ * @param optDep {@code true} if the opt is deprecated.
+ * @param optValue The value expected from opt.
+ * @param grpDep {@code true} if the group is deprecated.
+ * @param grpValue the value expected from the group.
+ * @param grpOpt the option that is expected to be processed by the group.
+ * @throws ParseException on parse error.
+ */
+ @ParameterizedTest(name = "{0}, {1}")
+ @MethodSource("createOptionValueParameters")
+ void testGetOptionValue(final String[] args, final Option opt, final OptionGroup optionGroup, final boolean optDep,
+ final String optValue, final boolean grpDep, final String grpValue, final Option grpOpt) throws ParseException {
+ final Options options = new Options().addOptionGroup(optionGroup);
+ final List handler = new ArrayList<>();
+ final CommandLine commandLine = DefaultParser.builder().setDeprecatedHandler(handler::add).get().parse(options, args);
+ final Supplier thinger = () -> "thing";
+ final OptionGroup otherGroup = new OptionGroup().addOption(Option.builder("o").longOpt("other").hasArg().get())
+ .addOption(Option.builder().option("p").longOpt("part").hasArg().get());
+ final OptionGroup nullGroup = null;
+
+ // test char option
+ assertEquals(optValue, commandLine.getOptionValue(asChar(opt)));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(asChar(opt), "thing"));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(asChar(opt), thinger));
+ checkHandler(optDep, handler, opt);
+
+ // test short option arg
+ assertEquals(optValue, commandLine.getOptionValue(opt.getOpt()));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt.getOpt(), "thing"));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt.getOpt(), thinger));
+ checkHandler(optDep, handler, opt);
+
+ // test long option arg
+ assertEquals(optValue, commandLine.getOptionValue(opt.getLongOpt()));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt.getLongOpt(), "thing"));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt.getLongOpt(), thinger));
+ checkHandler(optDep, handler, opt);
+
+ // test Option arg
+ assertEquals(optValue, commandLine.getOptionValue(opt));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt, "thing"));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt, thinger));
+ checkHandler(optDep, handler, opt);
+
+ // test option group arg
+ assertEquals(grpValue, commandLine.getOptionValue(optionGroup));
+ checkHandler(grpDep, handler, grpOpt);
+
+ assertEquals(grpValue == null ? "thing" : grpValue, commandLine.getOptionValue(optionGroup, "thing"));
+ checkHandler(grpDep, handler, grpOpt);
+
+ assertEquals(grpValue == null ? "thing" : grpValue, commandLine.getOptionValue(optionGroup, thinger));
+ checkHandler(grpDep, handler, grpOpt);
+
+ // test other group arg
+ assertNull(commandLine.getOptionValue(otherGroup));
+ checkHandler(false, handler, grpOpt);
+
+ assertEquals("thing", commandLine.getOptionValue(otherGroup, "thing"));
+ checkHandler(false, handler, grpOpt);
+
+ assertEquals("thing", commandLine.getOptionValue(otherGroup, thinger));
+ checkHandler(false, handler, grpOpt);
+
+ // test null Group arg
+ assertNull(commandLine.getOptionValue(nullGroup));
+ checkHandler(false, handler, grpOpt);
+
+ assertEquals("thing", commandLine.getOptionValue(nullGroup, "thing"));
+ checkHandler(false, handler, grpOpt);
+
+ assertEquals("thing", commandLine.getOptionValue(nullGroup, thinger));
+ checkHandler(false, handler, grpOpt);
+
+ // test not an option
+ assertNull(commandLine.getOptionValue("Nope"));
+ checkHandler(false, handler, opt);
+
+ assertEquals("thing", commandLine.getOptionValue("Nope", "thing"));
+ checkHandler(false, handler, opt);
+
+ assertEquals("thing", commandLine.getOptionValue("Nope", thinger));
+ checkHandler(false, handler, opt);
+ }
- final CommandLineParser parser = new DefaultParser();
- final CommandLine cmd = parser.parse(options, new String[] {"-i", "123", "-f", "foo"});
+ /**
+ * Test for get option values with and without default values. Verifies that deprecated options only report as
+ * deprecated once.
+ * @param args the argument strings to parse.
+ * @param opt the option to check for values with.
+ * @param optionGroup the option group to check for values with.
+ * @param optDep {@code true} if the opt is deprecated.
+ * @param optValue The value expected from opt.
+ * @param grpDep {@code true} if the group is deprecated.
+ * @param grpValue the value expected from the group.
+ * @param grpOpt the option that is expected to be processed by the group.
+ * @throws ParseException on parse error.
+ */
+ @ParameterizedTest(name = "{0}, {1}")
+ @MethodSource("createOptionValuesParameters")
+ void testGetOptionValues(final String[] args, final Option opt, final OptionGroup optionGroup, final boolean optDep,
+ final String[] optValue, final boolean grpDep, final String[] grpValue, final Option grpOpt) throws ParseException {
+ final Options options = new Options().addOptionGroup(optionGroup);
+ final List handler = new ArrayList<>();
+ final CommandLine commandLine = DefaultParser.builder().setDeprecatedHandler(handler::add).get().parse(options, args);
+ final OptionGroup otherGroup = new OptionGroup().addOption(Option.builder("o").longOpt("other").hasArg().get())
+ .addOption(Option.builder().option("p").longOpt("part").hasArg().get());
+ final OptionGroup nullGroup = null;
+
+ // test char option arg
+ assertArrayEquals(optValue, commandLine.getOptionValues(asChar(opt)));
+ checkHandler(optDep, handler, opt);
+
+ // test short option arg
+ assertArrayEquals(optValue, commandLine.getOptionValues(opt.getOpt()));
+ checkHandler(optDep, handler, opt);
+
+ // test long option arg
+ assertArrayEquals(optValue, commandLine.getOptionValues(opt.getLongOpt()));
+ checkHandler(optDep, handler, opt);
+
+ // test Option arg
+ assertArrayEquals(optValue, commandLine.getOptionValues(opt));
+ checkHandler(optDep, handler, opt);
+
+ // test OptionGroup arg
+ assertArrayEquals(grpValue, commandLine.getOptionValues(optionGroup));
+ checkHandler(grpDep, handler, grpOpt);
+
+ // test not an option
+ assertNull(commandLine.getOptionValues("Nope"));
+ checkHandler(false, handler, opt);
+
+ // test other group arg
+ assertNull(commandLine.getOptionValues(otherGroup));
+ checkHandler(false, handler, grpOpt);
+
+ // test null group arg
+ assertNull(commandLine.getOptionValues(nullGroup));
+ checkHandler(false, handler, grpOpt);
+ }
+
+ @ParameterizedTest(name = "{0}, {1}")
+ @MethodSource("createParsedOptionValueParameters")
+ void testGetParsedOptionValue(final String[] args, final Option opt, final OptionGroup optionGroup, final boolean optDep,
+ final Integer optValue, final boolean grpDep, final Integer grpValue, final Option grpOpt) throws ParseException {
+ final Options options = new Options().addOptionGroup(optionGroup);
+ final List handler = new ArrayList<>();
+ final CommandLine commandLine = DefaultParser.builder().setDeprecatedHandler(handler::add).get().parse(options, args);
+ final Supplier thinger = () -> 2;
+ final OptionGroup otherGroup = new OptionGroup().addOption(Option.builder("o").longOpt("other").hasArg().get())
+ .addOption(Option.builder().option("p").longOpt("part").hasArg().get());
+ final OptionGroup nullGroup = null;
+ final Integer thing = 2;
+
+ // test char option arg
+ assertEquals(optValue, commandLine.getParsedOptionValue(asChar(opt)));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValue(asChar(opt), thing));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValue(asChar(opt), thinger));
+ checkHandler(optDep, handler, opt);
+
+ // test short option arg
+ assertEquals(optValue, commandLine.getParsedOptionValue(opt.getOpt()));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValue(opt.getOpt(), thing));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValue(opt.getOpt(), thinger));
+ checkHandler(optDep, handler, opt);
+
+ // test long option arg
+ assertEquals(optValue, commandLine.getParsedOptionValue(opt.getLongOpt()));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValue(opt.getLongOpt(), thing));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValue(opt.getLongOpt(), thinger));
+ checkHandler(optDep, handler, opt);
+
+ // test Option arg
+ assertEquals(optValue, commandLine.getParsedOptionValue(opt));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValue(opt, thing));
+ checkHandler(optDep, handler, opt);
+
+ assertEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValue(opt, thinger));
+ checkHandler(optDep, handler, opt);
+
+ // test OptionGroup arg
+ assertEquals(grpValue, commandLine.getParsedOptionValue(optionGroup));
+ checkHandler(grpDep, handler, grpOpt);
+
+ assertEquals(grpValue == null ? thing : grpValue, commandLine.getParsedOptionValue(optionGroup, thing));
+ checkHandler(grpDep, handler, grpOpt);
+
+ assertEquals(grpValue == null ? thing : grpValue, commandLine.getParsedOptionValue(optionGroup, thinger));
+ checkHandler(grpDep, handler, grpOpt);
+
+ // test other Group arg
+ assertNull(commandLine.getParsedOptionValue(otherGroup));
+ checkHandler(false, handler, grpOpt);
- assertEquals(123, ((Number) cmd.getParsedOptionValue("i")).intValue());
- assertEquals("foo", cmd.getParsedOptionValue("f"));
+ assertEquals(thing, commandLine.getParsedOptionValue(otherGroup, thing));
+ checkHandler(false, handler, grpOpt);
+
+ assertEquals(thing, commandLine.getParsedOptionValue(otherGroup, thinger));
+ checkHandler(false, handler, grpOpt);
+
+ // test null Group arg
+ assertNull(commandLine.getParsedOptionValue(nullGroup));
+ checkHandler(false, handler, grpOpt);
+
+ assertEquals(thing, commandLine.getParsedOptionValue(nullGroup, thing));
+ checkHandler(false, handler, grpOpt);
+
+ assertEquals(thing, commandLine.getParsedOptionValue(nullGroup, thinger));
+ checkHandler(false, handler, grpOpt);
+
+ // test not an option
+ assertNull(commandLine.getParsedOptionValue("Nope"));
+ checkHandler(false, handler, opt);
+
+ assertEquals(thing, commandLine.getParsedOptionValue("Nope", thing));
+ checkHandler(false, handler, opt);
+
+ assertEquals(thing, commandLine.getParsedOptionValue("Nope", thinger));
+ checkHandler(false, handler, opt);
}
- @Test
- public void testGetParsedOptionValueWithChar() throws Exception {
- final Options options = new Options();
- options.addOption(Option.builder("i").hasArg().type(Number.class).build());
- options.addOption(Option.builder("f").hasArg().build());
+ @ParameterizedTest(name = "{0}, {1}")
+ @MethodSource("createParsedOptionValuesParameters")
+ void testGetParsedOptionValues(final String[] args, final Option opt, final OptionGroup optionGroup, final boolean optDep,
+ final Integer[] optValue, final boolean grpDep, final Integer[] grpValue, final Option grpOpt) throws ParseException {
+ final Options options = new Options().addOptionGroup(optionGroup);
+ final List handler = new ArrayList<>();
+ final CommandLine commandLine = DefaultParser.builder().setDeprecatedHandler(handler::add).get().parse(options, args);
+ final Supplier thinger = () -> new Integer[]{2, 3};
+ final OptionGroup otherGroup = new OptionGroup().addOption(Option.builder("o").longOpt("other").hasArg().get())
+ .addOption(Option.builder().option("p").longOpt("part").hasArg().get());
+ final OptionGroup nullGroup = null;
+ final Integer[] thing = {2, 3};
- final CommandLineParser parser = new DefaultParser();
- final CommandLine cmd = parser.parse(options, new String[] {"-i", "123", "-f", "foo"});
+ // test char option arg
+ assertArrayEquals(optValue, commandLine.getParsedOptionValues(asChar(opt)));
+ checkHandler(optDep, handler, opt);
+
+ assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(asChar(opt), thing));
+ checkHandler(optDep, handler, opt);
+
+ assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(asChar(opt), thinger));
+ checkHandler(optDep, handler, opt);
+
+ // test short option arg
+ assertArrayEquals(optValue, commandLine.getParsedOptionValues(opt.getOpt()));
+ checkHandler(optDep, handler, opt);
+
+ assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt.getOpt(), thing));
+ checkHandler(optDep, handler, opt);
+
+ assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt.getOpt(), thinger));
+ checkHandler(optDep, handler, opt);
+
+ // test long option arg
+ assertArrayEquals(optValue, commandLine.getParsedOptionValues(opt.getLongOpt()));
+ checkHandler(optDep, handler, opt);
+
+ assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt.getLongOpt(), thing));
+ checkHandler(optDep, handler, opt);
+
+ assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt.getLongOpt(), thinger));
+ checkHandler(optDep, handler, opt);
- assertEquals(123, ((Number) cmd.getParsedOptionValue('i')).intValue());
- assertEquals("foo", cmd.getParsedOptionValue('f'));
+ // test Option arg
+ assertArrayEquals(optValue, commandLine.getParsedOptionValues(opt));
+ checkHandler(optDep, handler, opt);
+
+ assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt, thing));
+ checkHandler(optDep, handler, opt);
+
+ assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt, thinger));
+ checkHandler(optDep, handler, opt);
+
+ // test OptionGroup arg
+ assertArrayEquals(grpValue, commandLine.getParsedOptionValues(optionGroup));
+ checkHandler(grpDep, handler, grpOpt);
+
+ assertArrayEquals(grpValue == null ? thing : grpValue, commandLine.getParsedOptionValues(optionGroup, thing));
+ checkHandler(grpDep, handler, grpOpt);
+
+ assertArrayEquals(grpValue == null ? thing : grpValue, commandLine.getParsedOptionValues(optionGroup, thinger));
+ checkHandler(grpDep, handler, grpOpt);
+
+ // test other Group arg
+ assertNull(commandLine.getParsedOptionValues(otherGroup));
+ checkHandler(false, handler, grpOpt);
+
+ assertArrayEquals(thing, commandLine.getParsedOptionValues(otherGroup, thing));
+ checkHandler(false, handler, grpOpt);
+
+ assertArrayEquals(thing, commandLine.getParsedOptionValues(otherGroup, thinger));
+ checkHandler(false, handler, grpOpt);
+
+ // test null Group arg
+ assertNull(commandLine.getParsedOptionValues(nullGroup));
+ checkHandler(false, handler, grpOpt);
+
+ assertArrayEquals(thing, commandLine.getParsedOptionValues(nullGroup, thing));
+ checkHandler(false, handler, grpOpt);
+
+ assertArrayEquals(thing, commandLine.getParsedOptionValues(nullGroup, thinger));
+ checkHandler(false, handler, grpOpt);
+
+ // test not an option
+ assertNull(commandLine.getParsedOptionValues("Nope"));
+ checkHandler(false, handler, opt);
+
+ assertArrayEquals(thing, commandLine.getParsedOptionValues("Nope", thing));
+ checkHandler(false, handler, opt);
+
+ assertArrayEquals(thing, commandLine.getParsedOptionValues("Nope", thinger));
+ checkHandler(false, handler, opt);
}
- @Test
- public void testGetParsedOptionValueWithOption() throws Exception {
- final Options options = new Options();
- final Option optI = Option.builder("i").hasArg().type(Number.class).build();
- final Option optF = Option.builder("f").hasArg().build();
- options.addOption(optI);
- options.addOption(optF);
+ /**
+ * Tests the hasOption calls.
+ * @param args the argument strings to parse.
+ * @param opt the option to check for values with.
+ * @param optionGroup the option group to check for values with.
+ * @param optDep {@code true} if the opt is deprecated.
+ * @param has {@code true} if the opt is present.
+ * @param grpDep {@code true} if the group is deprecated.
+ * @param hasGrp {@code true} if the group is present.
+ * @param grpOpt the option that is expected to be processed by the group.
+ * @throws ParseException on parsing error.
+ */
+ @ParameterizedTest(name = "{0}, {1}")
+ @MethodSource("createHasOptionParameters")
+ void testHasOption(final String[] args, final Option opt, final OptionGroup optionGroup, final boolean optDep,
+ final boolean has, final boolean grpDep, final boolean hasGrp, final Option grpOpt) throws ParseException {
+ final Options options = new Options().addOptionGroup(optionGroup);
+ final List handler = new ArrayList<>();
+ final CommandLine commandLine = DefaultParser.builder().setDeprecatedHandler(handler::add).get().parse(options, args);
+ final OptionGroup otherGroup = new OptionGroup().addOption(Option.builder("o").longOpt("other").hasArg().get())
+ .addOption(Option.builder().option("p").longOpt("part").hasArg().get());
+ final OptionGroup nullGroup = null;
+
+ // test char option arg
+ assertEquals(has, commandLine.hasOption(asChar(opt)));
+ checkHandler(optDep, handler, opt);
+
+ // test short option arg
+ assertEquals(has, commandLine.hasOption(opt.getOpt()));
+ checkHandler(optDep, handler, opt);
+
+ // test long option arg
+ assertEquals(has, commandLine.hasOption(opt.getLongOpt()));
+ checkHandler(optDep, handler, opt);
+
+ // test Option arg
+ assertEquals(has, commandLine.hasOption(opt));
+ checkHandler(optDep, handler, opt);
+
+ // test OptionGroup arg
+ assertEquals(hasGrp, commandLine.hasOption(optionGroup));
+ checkHandler(grpDep, handler, grpOpt);
+
+ // test other group arg
+ assertFalse(commandLine.hasOption(otherGroup));
+ checkHandler(false, handler, grpOpt);
+
+ // test null group arg
+ assertFalse(commandLine.hasOption(nullGroup));
+ checkHandler(false, handler, grpOpt);
+
+ // test not an option
+ assertFalse(commandLine.hasOption("Nope"));
+ checkHandler(false, handler, opt);
+ }
- final CommandLineParser parser = new DefaultParser();
- final CommandLine cmd = parser.parse(options, new String[] {"-i", "123", "-f", "foo"});
+ /**
+ * Tests the hasOption calls.
+ * @param args the argument strings to parse.
+ * @param opt the option to check for values with.
+ * @param optionGroup the option group to check for values with.
+ * @param optDep {@code true} if the opt is deprecated.
+ * @param has {@code true} if the opt is present.
+ * @param grpDep {@code true} if the group is deprecated.
+ * @param hasGrp {@code true} if the group is present.
+ * @param grpOpt the option that is expected to be processed by the group.
+ * @throws ParseException on parsing error.
+ */
+ @ParameterizedTest(name = "{0}, {1}")
+ @MethodSource("createHasOptionParameters")
+ void testHasOptionNoDeprecationHandler(final String[] args, final Option opt, final OptionGroup optionGroup, final boolean optDep,
+ final boolean has, final boolean grpDep, final boolean hasGrp, final Option grpOpt) throws ParseException {
+ final Options options = new Options().addOptionGroup(optionGroup);
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final CommandLine commandLine = DefaultParser.builder().get().parse(options, args);
+ final PrintStream ps = System.out;
+ try {
+ System.setOut(new PrintStream(baos));
+
+ // test char option arg
+ assertEquals(has, commandLine.hasOption(asChar(opt)));
+ assertWritten(optDep, baos);
+
+ // test short option arg
+ assertEquals(has, commandLine.hasOption(opt.getOpt()));
+ assertWritten(optDep, baos);
+
+ // test long option arg
+ assertEquals(has, commandLine.hasOption(opt.getLongOpt()));
+ assertWritten(optDep, baos);
+
+ // test Option arg
+ assertEquals(has, commandLine.hasOption(opt));
+ assertWritten(optDep, baos);
+
+ // test OptionGroup arg
+ assertEquals(hasGrp, commandLine.hasOption(optionGroup));
+ assertWritten(grpDep, baos);
+
+ // test not an option
+ assertFalse(commandLine.hasOption("Nope"));
+ assertWritten(false, baos);
+ } finally {
+ System.setOut(ps);
+ }
+ }
+
+ /**
+ * Tests the hasOption calls.
+ * @param args the argument strings to parse.
+ * @param opt the option to check for values with.
+ * @param optionGroup the option group to check for values with.
+ * @param optDep {@code true} if the opt is deprecated.
+ * @param has {@code true} if the opt is present.
+ * @param grpDep {@code true} if the group is deprecated.
+ * @param hasGrp {@code true} if the group is present.
+ * @param grpOpt the option that is expected to be processed by the group.
+ * @throws ParseException on parsing error.
+ */
+ @ParameterizedTest(name = "{0}, {1}")
+ @MethodSource("createHasOptionParameters")
+ void testHasOptionNullDeprecationHandler(final String[] args, final Option opt, final OptionGroup optionGroup, final boolean optDep,
+ final boolean has, final boolean grpDep, final boolean hasGrp, final Option grpOpt) throws ParseException {
+ final Options options = new Options().addOptionGroup(optionGroup);
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final CommandLine commandLine = DefaultParser.builder().setDeprecatedHandler(null).get().parse(options, args);
+ final PrintStream ps = System.out;
+ try {
+ System.setOut(new PrintStream(baos));
+
+ // test char option arg
+ assertEquals(has, commandLine.hasOption(asChar(opt)));
+ assertWritten(false, baos);
+
+ // test short option arg
+ assertEquals(has, commandLine.hasOption(opt.getOpt()));
+ assertWritten(false, baos);
+
+ // test long option arg
+ assertEquals(has, commandLine.hasOption(opt.getLongOpt()));
+ assertWritten(false, baos);
+
+ // test Option arg
+ assertEquals(has, commandLine.hasOption(opt));
+ assertWritten(false, baos);
+
+ // test OptionGroup arg
+ assertEquals(hasGrp, commandLine.hasOption(optionGroup));
+ assertWritten(false, baos);
+
+ // test not an option
+ assertFalse(commandLine.hasOption("Nope"));
+ assertWritten(false, baos);
+ } finally {
+ System.setOut(ps);
+ }
+ }
+
+ @ParameterizedTest(name = "{0}, {1}")
+ @MethodSource("createOptionValueParameters")
+ void testNoDeprecationHandler(final String[] args, final Option opt, final OptionGroup optionGroup, final boolean optDep,
+ final String optValue, final boolean grpDep, final String grpValue, final Option grpOpt) throws ParseException {
+ final Options options = new Options().addOptionGroup(optionGroup);
+ final CommandLine commandLine = DefaultParser.builder().get().parse(options, args);
+ final Supplier thinger = () -> "thing";
+ final Supplier nullSupplier = null;
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final PrintStream ps = System.out;
+ try {
+ System.setOut(new PrintStream(baos));
+
+ final OptionGroup otherGroup = new OptionGroup().addOption(Option.builder("o").longOpt("other").hasArg().get())
+ .addOption(Option.builder().option("p").longOpt("part").hasArg().get());
+ final OptionGroup nullGroup = null;
+
+ // test char option
+ assertEquals(optValue, commandLine.getOptionValue(asChar(opt)));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(asChar(opt), "thing"));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(asChar(opt), thinger));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue, commandLine.getOptionValue(asChar(opt), nullSupplier));
+ assertWritten(optDep, baos);
+
+ // test short option arg
+ assertEquals(optValue, commandLine.getOptionValue(opt.getOpt()));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt.getOpt(), "thing"));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt.getOpt(), thinger));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue, commandLine.getOptionValue(opt.getOpt(), nullSupplier));
+ assertWritten(optDep, baos);
+
+ // test long option arg
+ assertEquals(optValue, commandLine.getOptionValue(opt.getLongOpt()));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt.getLongOpt(), "thing"));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt.getLongOpt(), thinger));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue, commandLine.getOptionValue(opt.getLongOpt(), nullSupplier));
+ assertWritten(optDep, baos);
+
+ // test Option arg
+ assertEquals(optValue, commandLine.getOptionValue(opt));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt, "thing"));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue == null ? "thing" : optValue, commandLine.getOptionValue(opt, thinger));
+ assertWritten(optDep, baos);
+
+ assertEquals(optValue, commandLine.getOptionValue(opt, nullSupplier));
+ assertWritten(optDep, baos);
+
+ // test optionGroup arg
+ assertEquals(grpValue, commandLine.getOptionValue(optionGroup));
+ assertWritten(grpDep, baos);
+
+ assertEquals(grpValue == null ? "thing" : grpValue, commandLine.getOptionValue(optionGroup, "thing"));
+ assertWritten(grpDep, baos);
+
+ assertEquals(grpValue == null ? "thing" : grpValue, commandLine.getOptionValue(optionGroup, thinger));
+ assertWritten(grpDep, baos);
+
+ assertEquals(grpValue, commandLine.getOptionValue(optionGroup, nullSupplier));
+ assertWritten(grpDep, baos);
+
+ // test other group arg
+ assertNull(commandLine.getOptionValue(otherGroup));
+ assertWritten(false, baos);
+
+ assertEquals("thing", commandLine.getOptionValue(otherGroup, "thing"));
+ assertWritten(false, baos);
+
+ assertEquals("thing", commandLine.getOptionValue(otherGroup, thinger));
+ assertWritten(false, baos);
+
+ assertNull(commandLine.getOptionValue(otherGroup, nullSupplier));
+ assertWritten(false, baos);
+
+ // test null Group arg
+ assertNull(commandLine.getOptionValue(nullGroup));
+ assertWritten(false, baos);
+
+ assertEquals("thing", commandLine.getOptionValue(nullGroup, "thing"));
+ assertWritten(false, baos);
+
+ assertEquals("thing", commandLine.getOptionValue(nullGroup, thinger));
+ assertWritten(false, baos);
+
+ assertNull(commandLine.getOptionValue(nullGroup, nullSupplier));
+ assertWritten(false, baos);
+
+ // test not an option
+ assertNull(commandLine.getOptionValue("Nope"));
+ assertWritten(false, baos);
+
+ assertEquals("thing", commandLine.getOptionValue("Nope", "thing"));
+ assertWritten(false, baos);
+
+ assertEquals("thing", commandLine.getOptionValue("Nope", thinger));
+ assertWritten(false, baos);
- assertEquals(123, ((Number) cmd.getParsedOptionValue(optI)).intValue());
- assertEquals("foo", cmd.getParsedOptionValue(optF));
+ assertNull(commandLine.getOptionValue("Nope", nullSupplier));
+ assertWritten(false, baos);
+ } finally {
+ System.setOut(ps);
+ }
}
@Test
- public void testNullhOption() throws Exception {
+ void testNullOption() throws Exception {
final Options options = new Options();
- final Option optI = Option.builder("i").hasArg().type(Number.class).build();
- final Option optF = Option.builder("f").hasArg().build();
+ final Option optI = Option.builder("i").hasArg().type(Number.class).get();
+ final Option optF = Option.builder("f").hasArg().get();
options.addOption(optI);
options.addOption(optF);
final CommandLineParser parser = new DefaultParser();
final CommandLine cmd = parser.parse(options, new String[] {"-i", "123", "-f", "foo"});
assertNull(cmd.getOptionValue((Option) null));
assertNull(cmd.getParsedOptionValue((Option) null));
+ assertNull(cmd.getOptionValue((OptionGroup) null));
+ assertNull(cmd.getParsedOptionValue((OptionGroup) null));
}
}
diff --git a/src/test/java/org/apache/commons/cli/ConverterTests.java b/src/test/java/org/apache/commons/cli/ConverterTests.java
new file mode 100644
index 000000000..8691d0757
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/ConverterTests.java
@@ -0,0 +1,128 @@
+/*
+ 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
+
+ https://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 static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.net.URL;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests for standard Converters.
+ */
+public class ConverterTests {
+
+ // A class without a default constructor.
+ public class AClassWithoutADefaultConstructor {
+ public AClassWithoutADefaultConstructor(final int i) {
+ }
+ }
+
+ private static Stream numberTestParameters() {
+ final List lst = new ArrayList<>();
+
+ lst.add(Arguments.of("123", Long.valueOf("123")));
+ lst.add(Arguments.of("12.3", Double.valueOf("12.3")));
+ lst.add(Arguments.of("-123", Long.valueOf("-123")));
+ lst.add(Arguments.of("-12.3", Double.valueOf("-12.3")));
+ lst.add(Arguments.of(".3", Double.valueOf("0.3")));
+ lst.add(Arguments.of("-.3", Double.valueOf("-0.3")));
+ lst.add(Arguments.of("0x5F", null));
+ lst.add(Arguments.of("2,3", null));
+ lst.add(Arguments.of("1.2.3", null));
+
+ return lst.stream();
+ }
+
+ @Test
+ void testClass() throws Exception {
+
+ assertNotNull(Converter.CLASS.apply(this.getClass().getName()), this.getClass().getName());
+ assertNotNull(Converter.CLASS.apply(this.getClass().getCanonicalName()), this.getClass().getCanonicalName());
+ assertThrows(ClassNotFoundException.class, () -> Converter.CLASS.apply(this.getClass().getSimpleName()),
+ this.getClass().getSimpleName());
+ assertNotNull(Converter.CLASS.apply(this.getClass().getTypeName()), this.getClass().getTypeName());
+
+ assertThrows(ClassNotFoundException.class, () -> Converter.CLASS.apply("foo.bar"));
+ assertNotNull(Converter.CLASS.apply(AClassWithoutADefaultConstructor.class.getName()));
+ }
+
+ @Test
+ void testDate() throws Exception {
+ assertThrows(java.text.ParseException.class, () -> Converter.DATE.apply("whatever"));
+
+ /*
+ * Dates calculated from strings are dependent upon configuration and environment settings for the
+ * machine on which the test is running. To avoid this problem, convert the time into a string
+ * and then unparse that using the converter. This produces strings that always match the correct
+ * time zone.
+ */
+ final Date expected = new Date(1023400137000L);
+ final DateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
+ final String formatted = dateFormat.format(expected);
+ assertEquals(expected, Converter.DATE.apply(formatted));
+
+ assertThrows(java.text.ParseException.class, () -> Converter.DATE.apply("Jun 06 17:48:57 EDT 2002"));
+ }
+
+ @Test
+ void testFile() throws Exception {
+ final URL url = this.getClass().getClassLoader().getResource("./org/apache/commons/cli/existing-readable.file");
+ final String fileName = url.toString().substring("file:".length());
+ assertNotNull(Converter.FILE.apply(fileName));
+ }
+
+ @ParameterizedTest
+ @MethodSource("numberTestParameters")
+ void testNumber(final String str, final Number expected) throws Exception {
+ if (expected != null) {
+ assertEquals(expected, Converter.NUMBER.apply(str));
+ } else {
+ assertThrows(NumberFormatException.class, () -> Converter.NUMBER.apply(str));
+ }
+ }
+
+ @Test
+ void testObject() throws Exception {
+ assertNotNull(Converter.OBJECT.apply(this.getClass().getName()), this.getClass().getName());
+ assertNotNull(Converter.OBJECT.apply(this.getClass().getCanonicalName()), this.getClass().getCanonicalName());
+ assertThrows(ClassNotFoundException.class, () -> Converter.OBJECT.apply(this.getClass().getSimpleName()),
+ this.getClass().getSimpleName());
+ assertNotNull(Converter.OBJECT.apply(this.getClass().getTypeName()), this.getClass().getTypeName());
+
+ assertThrows(ClassNotFoundException.class, () -> Converter.OBJECT.apply("foo.bar"));
+ assertThrows(NoSuchMethodException.class, () -> Converter.OBJECT.apply(AClassWithoutADefaultConstructor.class.getName()));
+ }
+
+ @Test
+ void testUrl() throws Exception {
+ assertEquals(new URL("http://apache.org"), Converter.URL.apply("http://apache.org"));
+ assertThrows(java.net.MalformedURLException.class, () -> Converter.URL.apply("foo.bar"));
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/DefaultParserTest.java b/src/test/java/org/apache/commons/cli/DefaultParserTest.java
index 0fd835a16..c162323f0 100644
--- a/src/test/java/org/apache/commons/cli/DefaultParserTest.java
+++ b/src/test/java/org/apache/commons/cli/DefaultParserTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,107 +17,392 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class DefaultParserTest extends ParserTestCase {
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.apache.commons.cli.DefaultParser.Builder;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+
+class DefaultParserTest extends AbstractParserTestCase {
+
+ static class ExternalArgumentsProvider implements ArgumentsProvider {
+
+ @Override
+ public Stream extends Arguments> provideArguments(final ExtensionContext context) {
+ return Stream.of(
+ /* Arguments:
+ * 1. test case name
+ * 2. parser
+ * 3. input string
+ * 4. expected option value
+ * 5. checked option
+ * 6. assertion message
+ */
+ Arguments.of(
+ "Long option quote handling DEFAULT behavior",
+ DefaultParser.builder().get(),
+ new String[]{"--bfile", "\"quoted string\""},
+ "quoted string",
+ "b",
+ "Confirm --bfile=\"arg\" strips quotes"
+ ),
+ Arguments.of(
+ "Long option with equals quote handling DEFAULT behavior",
+ DefaultParser.builder().get(),
+ new String[]{"--bfile=\"quoted string\""},
+ "\"quoted string\"",
+ "b",
+ "Confirm --bfile=\"arg\" keeps quotes"
+ ),
+ Arguments.of(
+ "Short option quote handling DEFAULT behavior",
+ DefaultParser.builder().get(),
+ new String[]{"-b", "\"quoted string\""},
+ "quoted string",
+ "b",
+ "Confirm -b\"arg\" strips quotes"
+ ),
+ Arguments.of(
+ "Short option concatenated quote handling DEFAULT behavior",
+ DefaultParser.builder().get(),
+ new String[]{"-b\"quoted string\""},
+ "\"quoted string\"",
+ "b",
+ "Confirm -b\"arg\" keeps quotes"
+ ),
+ Arguments.of(
+ "Long option quote handling WITHOUT strip",
+ DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
+ new String[]{"--bfile", "\"quoted string\""},
+ "\"quoted string\"",
+ "b",
+ "Confirm --bfile \"arg\" keeps quotes"
+ ),
+ Arguments.of(
+ "Long option with equals quote handling WITHOUT strip",
+ DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
+ new String[]{"--bfile=\"quoted string\""},
+ "\"quoted string\"",
+ "b",
+ "Confirm --bfile=\"arg\" keeps quotes"
+ ),
+ Arguments.of(
+ "Short option quote handling WITHOUT strip",
+ DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
+ new String[]{"-b", "\"quoted string\""},
+ "\"quoted string\"",
+ "b",
+ "Confirm -b\"arg\" keeps quotes"
+ ),
+ Arguments.of(
+ "Short option concatenated quote handling WITHOUT strip",
+ DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
+ new String[]{"-b\"quoted string\""},
+ "\"quoted string\"",
+ "b",
+ "Confirm -b\"arg\" keeps quotes"
+ ),
+ Arguments.of(
+ "Long option quote handling WITH strip",
+ DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
+ new String[]{"--bfile", "\"quoted string\""},
+ "quoted string",
+ "b",
+ "Confirm --bfile \"arg\" strips quotes"
+ ),
+ Arguments.of(
+ "Long option With Equals Quote Handling WITH Strip",
+ DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
+ new String[]{"--bfile=\"quoted string\""},
+ "quoted string",
+ "b",
+ "Confirm --bfile=\"arg\" strips quotes"
+ ),
+ Arguments.of(
+ "Short option quote handling WITH strip",
+ DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
+ new String[]{"-b", "\"quoted string\""},
+ "quoted string",
+ "b",
+ "Confirm -b \"arg\" strips quotes"
+ ),
+ Arguments.of(
+ "Short option concatenated quote handling WITH strip",
+ DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
+ new String[]{"-b\"quoted string\""},
+ "quoted string",
+ "b",
+ "Confirm -b\"arg\" strips quotes"
+ )
+ );
+ }
+ }
@Override
- @Before
+ @BeforeEach
public void setUp() {
super.setUp();
parser = new DefaultParser();
}
@Test
- public void testBuilder() {
- parser = DefaultParser.builder()
+ void testBuilder() {
+ // @formatter:off
+ final Builder builder = DefaultParser.builder()
.setStripLeadingAndTrailingQuotes(false)
.setAllowPartialMatching(false)
- .build();
+ .setDeprecatedHandler(null);
+ // @formatter:on
+ parser = builder.build();
+ assertEquals(DefaultParser.class, parser.getClass());
+ parser = builder.get();
assertEquals(DefaultParser.class, parser.getClass());
}
@Test
- public void testLongOptionQuoteHandlingWithoutStrip() throws Exception {
- parser = DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).build();
- final String[] args = {"--bfile", "\"quoted string\""};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("Confirm --bfile \"arg\" keeps quotes", "\"quoted string\"", cl.getOptionValue("b"));
+ void testDeprecated() throws ParseException {
+ final Set handler = new HashSet<>();
+ parser = DefaultParser.builder().setDeprecatedHandler(handler::add).build();
+ final Option opt1 = Option.builder().option("d1").deprecated().get();
+ // @formatter:off
+ final Option opt2 = Option.builder().option("d2").deprecated(DeprecatedAttributes.builder()
+ .setForRemoval(true)
+ .setSince("1.0")
+ .setDescription("Do this instead.").get()).get();
+ // @formatter:on
+ final Option opt3 = Option.builder().option("a").get();
+ // @formatter:off
+ final CommandLine cl = parser.parse(new Options()
+ .addOption(opt1)
+ .addOption(opt2)
+ .addOption(opt3),
+ new String[] {"-d1", "-d2", "-a"});
+ // @formatter:on
+ // Trigger handler:
+ assertTrue(cl.hasOption(opt1.getOpt()));
+ assertTrue(cl.hasOption(opt2.getOpt()));
+ assertTrue(cl.hasOption(opt3.getOpt()));
+ // Assert handler was triggered
+ assertTrue(handler.contains(opt1));
+ assertTrue(handler.contains(opt2));
+ assertFalse(handler.contains(opt3));
}
@Test
- public void testLongOptionQuoteHandlingWithStrip() throws Exception {
- parser = DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).build();
- final String[] args = {"--bfile", "\"quoted string\""};
+ void testLegacyStopAtNonOption() throws ParseException {
+ final Option a = Option.builder().option("a").longOpt("first-letter").get();
+ final Option b = Option.builder().option("b").longOpt("second-letter").get();
+ final Option c = Option.builder().option("c").longOpt("third-letter").get();
- final CommandLine cl = parser.parse(options, args);
+ final Options options = new Options();
+ options.addOption(a);
+ options.addOption(b);
+ options.addOption(c);
- assertEquals("Confirm --bfile \"arg\" strips quotes", "quoted string", cl.getOptionValue("b"));
- }
+ final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"}; // -d is rogue option
- @Override
- @Test
- public void testLongOptionWithEqualsQuoteHandling() throws Exception {
- final String[] args = {"--bfile=\"quoted string\""};
+ final DefaultParser parser = new DefaultParser();
- final CommandLine cl = parser.parse(options, args);
+ final CommandLine commandLine = parser.parse(options, args, null, true);
+ assertEquals(3, commandLine.getOptions().length);
+ assertEquals(3, commandLine.getArgs().length);
+ assertTrue(commandLine.getArgList().contains("-d"));
+ assertTrue(commandLine.getArgList().contains("arg1"));
+ assertTrue(commandLine.getArgList().contains("arg2"));
- assertEquals("Confirm --bfile=\"arg\" strips quotes", "\"quoted string\"", cl.getOptionValue("b"));
+ final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, args, null, false));
+ assertTrue(e.getMessage().contains("-d"));
}
+ @Override
@Test
- public void testLongOptionWithEqualsQuoteHandlingWithoutStrip() throws Exception {
- parser = DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).build();
- final String[] args = {"--bfile=\"quoted string\""};
+ @Disabled("Test case handled in the parameterized tests as \"DEFAULT behavior\"")
+ void testLongOptionWithEqualsQuoteHandling() throws Exception {
+ }
+ @ParameterizedTest(name = "{index}. {0}")
+ @ArgumentsSource(ExternalArgumentsProvider.class)
+ void testParameterized(final String testName, final CommandLineParser parser, final String[] args, final String expected,
+ final String option, final String message) throws Exception {
final CommandLine cl = parser.parse(options, args);
- assertEquals("Confirm --bfile=\"arg\" keeps quotes", "\"quoted string\"", cl.getOptionValue("b"));
+ assertEquals(expected, cl.getOptionValue(option), message);
}
@Test
- public void testLongOptionWithEqualsQuoteHandlingWithStrip() throws Exception {
- parser = DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).build();
- final String[] args = {"--bfile=\"quoted string\""};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("Confirm --bfile=\"arg\" strips quotes", "quoted string", cl.getOptionValue("b"));
+ void testParseIgnoreHappyPath() throws ParseException {
+ final Option a = Option.builder().option("a").longOpt("first-letter").get();
+ final Option b = Option.builder().option("b").longOpt("second-letter").get();
+ final Option c = Option.builder().option("c").longOpt("third-letter").get();
+ final Option d = Option.builder().option("d").longOpt("fourth-letter").get();
+
+ final Options baseOptions = new Options();
+ baseOptions.addOption(a);
+ baseOptions.addOption(b);
+ final Options specificOptions = new Options();
+ specificOptions.addOption(a);
+ specificOptions.addOption(b);
+ specificOptions.addOption(c);
+ specificOptions.addOption(d);
+
+ final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"};
+
+ final DefaultParser parser = new DefaultParser();
+
+ final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.IGNORE, args);
+ assertEquals(2, baseCommandLine.getOptions().length);
+ assertEquals(2, baseCommandLine.getArgs().length);
+ assertTrue(baseCommandLine.hasOption("a"));
+ assertTrue(baseCommandLine.hasOption("b"));
+ assertFalse(baseCommandLine.hasOption("c"));
+ assertFalse(baseCommandLine.hasOption("d"));
+ assertFalse(baseCommandLine.getArgList().contains("-a"));
+ assertFalse(baseCommandLine.getArgList().contains("-b"));
+ assertFalse(baseCommandLine.getArgList().contains("-c"));
+ assertFalse(baseCommandLine.getArgList().contains("-d"));
+ assertTrue(baseCommandLine.getArgList().contains("arg1"));
+ assertTrue(baseCommandLine.getArgList().contains("arg2"));
+
+ final CommandLine specificCommandLine = parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args);
+ assertEquals(4, specificCommandLine.getOptions().length);
+ assertEquals(2, specificCommandLine.getArgs().length);
+ assertTrue(specificCommandLine.hasOption("a"));
+ assertTrue(specificCommandLine.hasOption("b"));
+ assertTrue(specificCommandLine.hasOption("c"));
+ assertTrue(specificCommandLine.hasOption("d"));
+ assertFalse(specificCommandLine.getArgList().contains("-a"));
+ assertFalse(specificCommandLine.getArgList().contains("-b"));
+ assertFalse(specificCommandLine.getArgList().contains("-c"));
+ assertFalse(specificCommandLine.getArgList().contains("-d"));
+ assertTrue(specificCommandLine.getArgList().contains("arg1"));
+ assertTrue(specificCommandLine.getArgList().contains("arg2"));
}
- @Override
@Test
- public void testShortOptionConcatenatedQuoteHandling() throws Exception {
- final String[] args = {"-b\"quoted string\""};
-
- final CommandLine cl = parser.parse(options, args);
-
- //This is behaviour is not consistent with the other parsers, but is required for backwards compatibility
- assertEquals("Confirm -b\"arg\" keeps quotes", "\"quoted string\"", cl.getOptionValue("b"));
+ void testParseIgnoreNonHappyPath() throws ParseException {
+ final Option a = Option.builder().option("a").longOpt("first-letter").get();
+ final Option b = Option.builder().option("b").longOpt("second-letter").get();
+ final Option c = Option.builder().option("c").longOpt("third-letter").get();
+
+ final Options baseOptions = new Options();
+ baseOptions.addOption(a);
+ baseOptions.addOption(b);
+ final Options specificOptions = new Options();
+ specificOptions.addOption(a);
+ specificOptions.addOption(b);
+ specificOptions.addOption(c);
+
+ final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"}; // -d is rogue option
+
+ final DefaultParser parser = new DefaultParser();
+
+ final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.IGNORE, args);
+ assertEquals(2, baseCommandLine.getOptions().length);
+ assertEquals(2, baseCommandLine.getArgs().length);
+
+ final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class,
+ () -> parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args));
+ assertTrue(e.getMessage().contains("-d"));
}
@Test
- public void testShortOptionQuoteHandlingWithoutStrip() throws Exception {
- parser = DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).build();
- final String[] args = {"-b", "\"quoted string\""};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("Confirm -b \"arg\" keeps quotes", "\"quoted string\"", cl.getOptionValue("b"));
+ void testParseNullOption() throws ParseException {
+ // Edge case
+ assertThrows(NullPointerException.class, () -> new DefaultParser().parse(null, null, DefaultParser.NonOptionAction.IGNORE, "-a"));
}
@Test
- public void testShortOptionQuoteHandlingWithStrip() throws Exception {
- parser = DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).build();
- final String[] args = {"-b", "\"quoted string\""};
+ void testParseSkipHappyPath() throws ParseException {
+ final Option a = Option.builder().option("a").longOpt("first-letter").get();
+ final Option b = Option.builder().option("b").longOpt("second-letter").get();
+ final Option c = Option.builder().option("c").longOpt("third-letter").get();
+ final Option d = Option.builder().option("d").longOpt("fourth-letter").get();
+
+ final Options baseOptions = new Options();
+ baseOptions.addOption(a);
+ baseOptions.addOption(b);
+ final Options specificOptions = new Options();
+ specificOptions.addOption(a);
+ specificOptions.addOption(b);
+ specificOptions.addOption(c);
+ specificOptions.addOption(d);
+
+ final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"};
+
+ final DefaultParser parser = new DefaultParser();
+
+ final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.SKIP, args);
+ assertEquals(2, baseCommandLine.getOptions().length);
+ assertEquals(4, baseCommandLine.getArgs().length);
+ assertTrue(baseCommandLine.hasOption("a"));
+ assertTrue(baseCommandLine.hasOption("b"));
+ assertFalse(baseCommandLine.hasOption("c"));
+ assertFalse(baseCommandLine.hasOption("d"));
+ assertFalse(baseCommandLine.getArgList().contains("-a"));
+ assertFalse(baseCommandLine.getArgList().contains("-b"));
+ assertTrue(baseCommandLine.getArgList().contains("-c"));
+ assertTrue(baseCommandLine.getArgList().contains("-d"));
+ assertTrue(baseCommandLine.getArgList().contains("arg1"));
+ assertTrue(baseCommandLine.getArgList().contains("arg2"));
+
+ final CommandLine specificCommandLine = parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args);
+ assertEquals(4, specificCommandLine.getOptions().length);
+ assertEquals(2, specificCommandLine.getArgs().length);
+ assertTrue(specificCommandLine.hasOption("a"));
+ assertTrue(specificCommandLine.hasOption("b"));
+ assertTrue(specificCommandLine.hasOption("c"));
+ assertTrue(specificCommandLine.hasOption("d"));
+ assertFalse(specificCommandLine.getArgList().contains("-a"));
+ assertFalse(specificCommandLine.getArgList().contains("-b"));
+ assertFalse(specificCommandLine.getArgList().contains("-c"));
+ assertFalse(specificCommandLine.getArgList().contains("-d"));
+ assertTrue(specificCommandLine.getArgList().contains("arg1"));
+ assertTrue(specificCommandLine.getArgList().contains("arg2"));
+ }
- final CommandLine cl = parser.parse(options, args);
+ @Test
+ void testParseSkipNonHappyPath() throws ParseException {
+ final Option a = Option.builder().option("a").longOpt("first-letter").get();
+ final Option b = Option.builder().option("b").longOpt("second-letter").get();
+ final Option c = Option.builder().option("c").longOpt("third-letter").get();
+
+ final Options baseOptions = new Options();
+ baseOptions.addOption(a);
+ baseOptions.addOption(b);
+ final Options specificOptions = new Options();
+ specificOptions.addOption(a);
+ specificOptions.addOption(b);
+ specificOptions.addOption(c);
+
+ final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"}; // -d is rogue option
+
+ final DefaultParser parser = new DefaultParser();
+
+ final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.SKIP, args);
+ assertEquals(2, baseCommandLine.getOptions().length);
+ assertEquals(4, baseCommandLine.getArgs().length);
+
+ final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class,
+ () -> parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args));
+ assertTrue(e.getMessage().contains("-d"));
+ }
- assertEquals("Confirm -b \"arg\" strips quotes", "quoted string", cl.getOptionValue("b"));
+ @Override
+ @Test
+ @Disabled("Test case handled in the parameterized tests as \"DEFAULT behavior\"")
+ void testShortOptionConcatenatedQuoteHandling() throws Exception {
}
}
diff --git a/src/test/java/org/apache/commons/cli/DeprecatedAttributesTest.java b/src/test/java/org/apache/commons/cli/DeprecatedAttributesTest.java
new file mode 100644
index 000000000..6fb128695
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/DeprecatedAttributesTest.java
@@ -0,0 +1,75 @@
+/*
+ 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
+
+ https://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 static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class DeprecatedAttributesTest {
+
+ @Test
+ void testBuilderNonDefaults() {
+ // @formatter:off
+ final DeprecatedAttributes value = DeprecatedAttributes.builder()
+ .setDescription("Use Bar instead!")
+ .setForRemoval(true)
+ .setSince("2.0")
+ .get();
+ // @formatter:on
+ assertEquals("Use Bar instead!", value.getDescription());
+ assertEquals("2.0", value.getSince());
+ assertEquals(true, value.isForRemoval());
+ }
+
+ @Test
+ void testBuilderNonDefaultsToString() {
+ // @formatter:off
+ assertEquals("Deprecated for removal since 2.0: Use Bar instead!", DeprecatedAttributes.builder()
+ .setDescription("Use Bar instead!")
+ .setForRemoval(true)
+ .setSince("2.0")
+ .get().toString());
+ assertEquals("Deprecated for removal: Use Bar instead!", DeprecatedAttributes.builder()
+ .setDescription("Use Bar instead!")
+ .setForRemoval(true)
+ .get().toString());
+ assertEquals("Deprecated since 2.0: Use Bar instead!",
+ DeprecatedAttributes.builder()
+ .setDescription("Use Bar instead!")
+ .setSince("2.0")
+ .get().toString());
+ assertEquals("Deprecated: Use Bar instead!", DeprecatedAttributes.builder()
+ .setDescription("Use Bar instead!")
+ .get().toString());
+ // @formatter:on
+ }
+
+ @Test
+ void testDefaultBuilder() {
+ final DeprecatedAttributes defaultValue = DeprecatedAttributes.builder().get();
+ assertEquals(DeprecatedAttributes.DEFAULT.getDescription(), defaultValue.getDescription());
+ assertEquals(DeprecatedAttributes.DEFAULT.getSince(), defaultValue.getSince());
+ assertEquals(DeprecatedAttributes.DEFAULT.isForRemoval(), defaultValue.isForRemoval());
+ }
+
+ @Test
+ void testDefaultToString() {
+ assertEquals("Deprecated", DeprecatedAttributes.DEFAULT.toString());
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/DisablePartialMatchingTest.java b/src/test/java/org/apache/commons/cli/DisablePartialMatchingTest.java
index a71b5bfa7..64f865174 100644
--- a/src/test/java/org/apache/commons/cli/DisablePartialMatchingTest.java
+++ b/src/test/java/org/apache/commons/cli/DisablePartialMatchingTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,14 +17,14 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-public class DisablePartialMatchingTest {
+class DisablePartialMatchingTest {
@Test
- public void testDisablePartialMatching() throws Exception {
+ void testDisablePartialMatching() throws Exception {
final CommandLineParser parser = new DefaultParser(false);
final Options options = new Options();
@@ -35,13 +35,13 @@ public void testDisablePartialMatching() throws Exception {
final CommandLine line = parser.parse(options, new String[] {"-de", "--option=foobar"});
- assertTrue("There should be an option debug in any case...", line.hasOption("debug"));
- assertTrue("There should be an extract option because partial matching is off", line.hasOption("extract"));
- assertTrue("There should be an option option with a argument value", line.hasOption("option"));
+ assertTrue(line.hasOption("debug"), "There should be an option debug in any case...");
+ assertTrue(line.hasOption("extract"), "There should be an extract option because partial matching is off");
+ assertTrue(line.hasOption("option"), "There should be an option option with a argument value");
}
@Test
- public void testRegularPartialMatching() throws Exception {
+ void testRegularPartialMatching() throws Exception {
final CommandLineParser parser = new DefaultParser();
final Options options = new Options();
@@ -52,8 +52,8 @@ public void testRegularPartialMatching() throws Exception {
final CommandLine line = parser.parse(options, new String[] {"-de", "--option=foobar"});
- assertTrue("There should be an option debug in any case...", line.hasOption("debug"));
- assertFalse("There should not be an extract option because partial matching only selects debug", line.hasOption("extract"));
- assertTrue("There should be an option option with a argument value", line.hasOption("option"));
+ assertTrue(line.hasOption("debug"), "There should be an option debug in any case...");
+ assertFalse(line.hasOption("extract"), "There should not be an extract option because partial matching only selects debug");
+ assertTrue(line.hasOption("option"), "There should be an option option with a argument value");
}
}
diff --git a/src/test/java/org/apache/commons/cli/GnuParserTest.java b/src/test/java/org/apache/commons/cli/GnuParserTest.java
index a08ca8bc5..f928c50bd 100644
--- a/src/test/java/org/apache/commons/cli/GnuParserTest.java
+++ b/src/test/java/org/apache/commons/cli/GnuParserTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,14 +17,17 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+/**
+ * TODO Needs a rework using JUnit parameterized tests.
+ */
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class GnuParserTest extends ParserTestCase {
+class GnuParserTest extends AbstractParserTestCase {
@Override
- @Before
+ @BeforeEach
public void setUp() {
super.setUp();
parser = new GnuParser();
@@ -32,127 +35,133 @@ public void setUp() {
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testAmbiguousLongWithoutEqualSingleDash() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testAmbiguousLongWithoutEqualSingleDash() throws Exception {
+ }
+
+ @Override
+ @Test
+ @Disabled("not supported by the GnuParser")
+ void testAmbiguousLongWithoutEqualSingleDash2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testAmbiguousPartialLongOption1() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testAmbiguousPartialLongOption1() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testAmbiguousPartialLongOption2() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testAmbiguousPartialLongOption2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testAmbiguousPartialLongOption3() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testAmbiguousPartialLongOption3() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testAmbiguousPartialLongOption4() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testAmbiguousPartialLongOption4() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testBursting() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testBursting() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testDoubleDash2() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testDoubleDash2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testLongWithoutEqualSingleDash() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testLongWithoutEqualSingleDash() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testLongWithUnexpectedArgument1() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testLongWithUnexpectedArgument1() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testLongWithUnexpectedArgument2() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testLongWithUnexpectedArgument2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testMissingArgWithBursting() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testMissingArgWithBursting() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser (CLI-184)")
- public void testNegativeOption() throws Exception {
+ @Disabled("not supported by the GnuParser (CLI-184)")
+ void testNegativeOption() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testPartialLongOptionSingleDash() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testPartialLongOptionSingleDash() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testShortWithUnexpectedArgument() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testShortWithUnexpectedArgument() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testStopBursting() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testStopBursting() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testStopBursting2() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testStopBursting2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testUnambiguousPartialLongOption1() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testUnambiguousPartialLongOption1() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testUnambiguousPartialLongOption2() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testUnambiguousPartialLongOption2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testUnambiguousPartialLongOption3() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testUnambiguousPartialLongOption3() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testUnambiguousPartialLongOption4() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testUnambiguousPartialLongOption4() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the GnuParser")
- public void testUnrecognizedOptionWithBursting() throws Exception {
+ @Disabled("not supported by the GnuParser")
+ void testUnrecognizedOptionWithBursting() throws Exception {
}
}
diff --git a/src/test/java/org/apache/commons/cli/HelpFormatterTest.java b/src/test/java/org/apache/commons/cli/HelpFormatterTest.java
index abeb4321f..c935229ce 100644
--- a/src/test/java/org/apache/commons/cli/HelpFormatterTest.java
+++ b/src/test/java/org/apache/commons/cli/HelpFormatterTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,54 +17,106 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.Comparator;
-
-import org.junit.Test;
+import java.io.UncheckedIOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.apache.commons.cli.HelpFormatter.Builder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
/**
* Test case for the HelpFormatter class.
*/
-public class HelpFormatterTest {
- private static final String EOL = System.getProperty("line.separator");
+class HelpFormatterTest {
+
+ private static final String EOL = System.lineSeparator();
+
+ static Stream deprecatedOptionsProvider() {
+ final List lst = new ArrayList<>();
+ Option option = Option.builder("a").longOpt("aaa").desc("dddd dddd dddd")
+ .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("now")
+ .setDescription("Why why why").get()).get();
+
+ HelpFormatter hf = HelpFormatter.builder().get();
+ lst.add(Arguments.of(hf, option, "[Deprecated] dddd dddd dddd"));
+
+ hf = HelpFormatter.builder().setShowDeprecated(false).get();
+ lst.add(Arguments.of(hf, option, "dddd dddd dddd"));
+
+ hf = HelpFormatter.builder().setShowDeprecated(true).get();
+ lst.add(Arguments.of(hf, option, "[Deprecated] dddd dddd dddd"));
+
+ hf = HelpFormatter.builder().setShowDeprecated(o -> String.format("%s [%s]", HelpFormatter.getDescription(o), o.getDeprecated())).get();
+ lst.add(Arguments.of(hf, option, "dddd dddd dddd [Deprecated for removal since now: Why why why]"));
+
+ option = Option.builder("a").longOpt("aaa")
+ .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("now")
+ .setDescription("Why why why").get()).get();
+
+ hf = HelpFormatter.builder().get();
+ lst.add(Arguments.of(hf, option, "[Deprecated]"));
+
+ hf = HelpFormatter.builder().setShowDeprecated(false).get();
+ lst.add(Arguments.of(hf, option, ""));
+
+ hf = HelpFormatter.builder().setShowDeprecated(true).get();
+ lst.add(Arguments.of(hf, option, "[Deprecated]"));
+
+ hf = HelpFormatter.builder().setShowDeprecated(o -> String.format("%s [%s]", HelpFormatter.getDescription(o), o.getDeprecated())).get();
+ lst.add(Arguments.of(hf, option, "[Deprecated for removal since now: Why why why]"));
+
+ return lst.stream();
+ }
@Test
- public void testAccessors() {
+ void testAccessors() {
final HelpFormatter formatter = new HelpFormatter();
formatter.setArgName("argname");
- assertEquals("arg name", "argname", formatter.getArgName());
+ assertEquals("argname", formatter.getArgName(), "arg name");
formatter.setDescPadding(3);
- assertEquals("desc padding", 3, formatter.getDescPadding());
+ assertEquals(3, formatter.getDescPadding(), "desc padding");
formatter.setLeftPadding(7);
- assertEquals("left padding", 7, formatter.getLeftPadding());
+ assertEquals(7, formatter.getLeftPadding(), "left padding");
formatter.setLongOptPrefix("~~");
- assertEquals("long opt prefix", "~~", formatter.getLongOptPrefix());
+ assertEquals("~~", formatter.getLongOptPrefix(), "long opt prefix");
formatter.setNewLine("\n");
- assertEquals("new line", "\n", formatter.getNewLine());
+ assertEquals("\n", formatter.getNewLine(), "new line");
formatter.setOptPrefix("~");
- assertEquals("opt prefix", "~", formatter.getOptPrefix());
+ assertEquals("~", formatter.getOptPrefix(), "opt prefix");
formatter.setSyntaxPrefix("-> ");
- assertEquals("syntax prefix", "-> ", formatter.getSyntaxPrefix());
+ assertEquals("-> ", formatter.getSyntaxPrefix(), "syntax prefix");
formatter.setWidth(80);
- assertEquals("width", 80, formatter.getWidth());
+ assertEquals(80, formatter.getWidth(), "width");
}
@Test
- public void testAutomaticUsage() {
+ void testAutomaticUsage() {
final HelpFormatter hf = new HelpFormatter();
Options options;
String expected = "usage: app [-a]";
@@ -74,20 +126,20 @@ public void testAutomaticUsage() {
options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa");
hf.printUsage(pw, 60, "app", options);
pw.flush();
- assertEquals("simple auto usage", expected, out.toString().trim());
+ assertEquals(expected, out.toString().trim(), "simple auto usage");
out.reset();
expected = "usage: app [-a] [-b]";
options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa").addOption("b", false, "bbb");
hf.printUsage(pw, 60, "app", options);
pw.flush();
- assertEquals("simple auto usage", expected, out.toString().trim());
+ assertEquals(expected, out.toString().trim(), "simple auto usage");
out.reset();
}
@Test
- public void testDefaultArgName() {
- final Option option = Option.builder("f").hasArg().required(true).build();
+ void testDefaultArgName() {
+ final Option option = Option.builder("f").hasArg().required(true).get();
final Options options = new Options();
options.addOption(option);
@@ -101,35 +153,54 @@ public void testDefaultArgName() {
assertEquals("usage: app -f " + EOL, out.toString());
}
+ @ParameterizedTest
+ @ValueSource(ints = { -100, -1, 0 })
+ void testDeprecatedFindWrapPosZeroWidth(final int width) {
+ final int pos = new HelpFormatter().findWrapPos("Hello World", width, 0);
+ assertEquals(width, pos);
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { -100, -1, 0 })
+ void testDeprecatedPrintOptionsZeroWidth(final int width) {
+ final Options options = new Options();
+ options.addOption("h", "help", false, "Show help");
+ final StringWriter out = new StringWriter();
+ final PrintWriter pw = new PrintWriter(out);
+ new HelpFormatter().printOptions(pw, width, options, 1, 3);
+ final String result = out.toString();
+ assertNotNull(result);
+ }
+
@Test
- public void testFindWrapPos() {
+ void testFindWrapPos() {
final HelpFormatter hf = new HelpFormatter();
String text = "This is a test.";
// text width should be max 8; the wrap position is 7
- assertEquals("wrap position", 7, hf.findWrapPos(text, 8, 0));
+ assertEquals(7, hf.findWrapPos(text, 8, 0), "wrap position");
// starting from 8 must give -1 - the wrap pos is after end
- assertEquals("wrap position 2", -1, hf.findWrapPos(text, 8, 8));
+ assertEquals(-1, hf.findWrapPos(text, 8, 8), "wrap position 2");
// words longer than the width are cut
text = "aaaa aa";
- assertEquals("wrap position 3", 3, hf.findWrapPos(text, 3, 0));
+ assertEquals(3, hf.findWrapPos(text, 3, 0), "wrap position 3");
// last word length is equal to the width
text = "aaaaaa aaaaaa";
- assertEquals("wrap position 4", 6, hf.findWrapPos(text, 6, 0));
- assertEquals("wrap position 4", -1, hf.findWrapPos(text, 6, 7));
+ assertEquals(6, hf.findWrapPos(text, 6, 0), "wrap position 4");
+ assertEquals(-1, hf.findWrapPos(text, 6, 7), "wrap position 4");
text = "aaaaaa\n aaaaaa";
- assertEquals("wrap position 5", 7, hf.findWrapPos(text, 6, 0));
+ assertEquals(7, hf.findWrapPos(text, 6, 0), "wrap position 5");
text = "aaaaaa\t aaaaaa";
- assertEquals("wrap position 6", 7, hf.findWrapPos(text, 6, 0));
+ assertEquals(7, hf.findWrapPos(text, 6, 0), "wrap position 6");
}
@Test
- public void testHeaderStartingWithLineSeparator() {
+ void testHeaderStartingWithLineSeparator0() {
// related to Bugzilla #21215
final Options options = new Options();
final HelpFormatter formatter = new HelpFormatter();
@@ -138,6 +209,26 @@ public void testHeaderStartingWithLineSeparator() {
final StringWriter out = new StringWriter();
formatter.printHelp(new PrintWriter(out), 80, "foobar", header, options, 2, 2, footer, true);
//@formatter:off
+ assertEquals(
+ "usage: foobar" + EOL +
+ EOL +
+ "Header" + EOL +
+ EOL +
+ "Footer" + EOL,
+ out.toString());
+ //@formatter:on
+ }
+
+ @Test
+ void testHeaderStartingWithLineSeparator1() {
+ // related to Bugzilla #21215
+ final Options options = new Options();
+ final String header = EOL + "Header";
+ final String footer = "Footer";
+ final Builder builder = HelpFormatter.builder();
+ StringWriter out = new StringWriter();
+ builder.setPrintWriter(new PrintWriter(out)).get().printHelp(new PrintWriter(out), 80, "foobar", header, options, 2, 2, footer, true);
+ //@formatter:off
assertEquals(
"usage: foobar" + EOL +
"" + EOL +
@@ -146,14 +237,66 @@ public void testHeaderStartingWithLineSeparator() {
"Footer" + EOL,
out.toString());
//@formatter:on
+ out = new StringWriter();
+ builder.setPrintWriter(new PrintWriter(out)).get().printHelp("foobar", header, options, footer);
+ //@formatter:off
+ assertEquals(
+ "usage: foobar" + EOL +
+ EOL +
+ "Header" + EOL +
+ EOL +
+ "Footer" + EOL,
+ out.toString());
+ //@formatter:on
+ out = new StringWriter();
+ builder.setPrintWriter(new PrintWriter(out)).get().printHelp(80, "foobar", header, options, footer);
+ //@formatter:off
+ assertEquals(
+ "usage: foobar" + EOL +
+ EOL +
+ "Header" + EOL +
+ EOL +
+ "Footer" + EOL,
+ out.toString());
+ //@formatter:on
+ out = new StringWriter();
+ builder.setPrintWriter(new PrintWriter(out)).get().printHelp("foobar", header, options, footer, false);
+ //@formatter:off
+ assertEquals(
+ "usage: foobar" + EOL +
+ EOL +
+ "Header" + EOL +
+ EOL +
+ "Footer" + EOL,
+ out.toString());
+ //@formatter:on
+ out = new StringWriter();
+ builder.setPrintWriter(new PrintWriter(out)).get().printHelp("foobar", header, options, footer, true);
+ //@formatter:off
+ assertEquals(
+ "usage: foobar" + EOL +
+ EOL +
+ "Header" + EOL +
+ EOL +
+ "Footer" + EOL,
+ out.toString());
+ //@formatter:on
+ out = new StringWriter();
+ builder.setPrintWriter(new PrintWriter(out)).get().printHelp("foobar", options, false);
+ //@formatter:off
+ assertEquals(
+ "usage: foobar" + EOL +
+ "" + EOL,
+ out.toString());
+ //@formatter:on
}
@Test
- public void testHelpWithLongOptSeparator() {
+ void testHelpWithLongOptSeparator() {
final Options options = new Options();
options.addOption("f", true, "the file");
- options.addOption(Option.builder("s").longOpt("size").desc("the size").hasArg().argName("SIZE").build());
- options.addOption(Option.builder().longOpt("age").desc("the age").hasArg().build());
+ options.addOption(Option.builder("s").longOpt("size").desc("the size").hasArg().argName("SIZE").get());
+ options.addOption(Option.builder().longOpt("age").desc("the age").hasArg().get());
final HelpFormatter formatter = new HelpFormatter();
assertEquals(HelpFormatter.DEFAULT_LONG_OPT_SEPARATOR, formatter.getLongOptSeparator());
@@ -177,7 +320,7 @@ public void testHelpWithLongOptSeparator() {
}
@Test
- public void testIndentedHeaderAndFooter() {
+ void testIndentedHeaderAndFooter() {
// related to CLI-207
final Options options = new Options();
final HelpFormatter formatter = new HelpFormatter();
@@ -198,7 +341,7 @@ public void testIndentedHeaderAndFooter() {
}
@Test
- public void testOptionWithoutShortFormat() {
+ void testOptionWithoutShortFormat() {
// related to Bugzilla #19383 (CLI-67)
final Options options = new Options();
options.addOption(new Option("a", "aaa", false, "aaaaaaa"));
@@ -219,44 +362,34 @@ public void testOptionWithoutShortFormat() {
}
@Test
- public void testOptionWithoutShortFormat2() {
+ void testOptionWithoutShortFormat2() {
// related to Bugzilla #27635 (CLI-26)
final Option help = new Option("h", "help", false, "print this message");
final Option version = new Option("v", "version", false, "print version information");
final Option newRun = new Option("n", "new", false, "Create NLT cache entries only for new items");
final Option trackerRun = new Option("t", "tracker", false, "Create NLT cache entries only for tracker items");
-
//@formatter:off
final Option timeLimit = Option.builder("l")
- .longOpt("limit")
- .hasArg()
- .valueSeparator()
- .desc("Set time limit for execution, in mintues")
- .build();
-
+ .longOpt("limit")
+ .hasArg()
+ .valueSeparator()
+ .desc("Set time limit for execution, in mintues").get();
final Option age = Option.builder("a").longOpt("age")
- .hasArg()
- .valueSeparator()
- .desc("Age (in days) of cache item before being recomputed")
- .build();
-
+ .hasArg()
+ .valueSeparator()
+ .desc("Age (in days) of cache item before being recomputed").get();
final Option server = Option.builder("s").longOpt("server")
- .hasArg()
- .valueSeparator()
- .desc("The NLT server address")
- .build();
-
+ .hasArg()
+ .valueSeparator()
+ .desc("The NLT server address").get();
final Option numResults = Option.builder("r").longOpt("results")
- .hasArg()
- .valueSeparator()
- .desc("Number of results per item")
- .build();
-
+ .hasArg()
+ .valueSeparator()
+ .desc("Number of results per item").get();
final Option configFile = Option.builder().longOpt("config")
- .hasArg()
- .valueSeparator()
- .desc("Use the specified configuration file")
- .build();
+ .hasArg()
+ .valueSeparator()
+ .desc("Use the specified configuration file").get();
//@formatter:on
final Options mOptions = new Options();
@@ -271,7 +404,7 @@ public void testOptionWithoutShortFormat2() {
mOptions.addOption(configFile);
final HelpFormatter formatter = new HelpFormatter();
- final String eol = System.getProperty("line.separator");
+ final String eol = System.lineSeparator();
final StringWriter out = new StringWriter();
formatter.printHelp(new PrintWriter(out), 80, "commandline", "header", mOptions, 2, 2, "footer", true);
//@formatter:off
@@ -293,8 +426,28 @@ public void testOptionWithoutShortFormat2() {
//@formatter:on
}
+ @ParameterizedTest
+ @MethodSource("deprecatedOptionsProvider")
+ void testPrintDeprecatedOptions(final HelpFormatter hf, final Option option, final String expectedTxt) {
+ final StringBuffer sb = new StringBuffer();
+
+ final int leftPad = 1;
+ final int descPad = 3;
+ final String lpad = hf.createPadding(leftPad);
+ final String dpad = hf.createPadding(descPad);
+ Options options;
+ final StringBuilder expected = new StringBuilder().append(lpad).append("-a,--aaa");
+
+ options = new Options().addOption(option);
+ if (expectedTxt.length() > 0) {
+ expected.append(dpad).append(expectedTxt);
+ }
+ hf.renderOptions(sb, 160, options, leftPad, descPad);
+ assertEquals(expected.toString(), sb.toString());
+ }
+
@Test
- public void testPrintHelpNewlineFooter() {
+ void testPrintHelpNewlineFooter() {
final HelpFormatter formatter = new HelpFormatter();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintWriter pw = new PrintWriter(out);
@@ -317,11 +470,11 @@ public void testPrintHelpNewlineFooter() {
"-ab" + EOL +
EOL;
pw.flush();
- assertEquals("footer newline", expected, out.toString());
+ assertEquals(expected, out.toString(), "footer newline");
}
@Test
- public void testPrintHelpNewlineHeader() {
+ void testPrintHelpNewlineHeader() {
final HelpFormatter formatter = new HelpFormatter();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintWriter pw = new PrintWriter(out);
@@ -344,36 +497,44 @@ public void testPrintHelpNewlineHeader() {
"-ab" + EOL +
"footer" + EOL;
pw.flush();
- assertEquals("header newline", expected, out.toString());
+ assertEquals(expected, out.toString(), "header newline");
}
@Test
- public void testPrintHelpWithEmptySyntax() {
+ void testPrintHelpWithEmptySyntax() {
final HelpFormatter formatter = new HelpFormatter();
- try {
- formatter.printHelp(null, new Options());
- fail("null command line syntax should be rejected");
- } catch (final IllegalArgumentException e) {
- // expected
- }
+ assertThrows(IllegalArgumentException.class, () -> formatter.printHelp(null, new Options()), "null command line syntax should be rejected");
+ assertThrows(IllegalArgumentException.class, () -> formatter.printHelp(null, new Options(), true), "null command line syntax should be rejected");
+ assertThrows(IllegalArgumentException.class, () -> formatter.printHelp(null, new Options(), false), "null command line syntax should be rejected");
+ assertThrows(IllegalArgumentException.class, () -> formatter.printHelp("", new Options(), true), "null command line syntax should be rejected");
+ assertThrows(IllegalArgumentException.class, () -> formatter.printHelp("", new Options(), false), "null command line syntax should be rejected");
+ }
- try {
- formatter.printHelp("", new Options());
- fail("empty command line syntax should be rejected");
- } catch (final IllegalArgumentException e) {
- // expected
+ @Test
+ void testPrintHelpWithSince() {
+ final String [] expected = {"usage: Command syntax", "Header", "Options Since Description",
+ " -n,--no-since - Description for n", " -W,--with-since 1.19.0 Descripton for W", "footer"};
+ final Options options = new Options()
+ .addOption(Option.builder("W").longOpt("with-since").since("1.19.0").desc("Descripton for W").get())
+ .addOption(Option.builder("n").longOpt("no-since").desc("Description for n").get());
+
+ final HelpFormatter formatter = HelpFormatter.builder().setShowSince(true).get();
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos))) {
+ formatter.printHelp(pw, 80, "Command syntax", "Header", options, 2, 5, "footer", false);
}
+ assertArrayEquals(expected, baos.toString().split(System.lineSeparator()));
}
@Test
- public void testPrintOptionGroupUsage() {
- final OptionGroup group = new OptionGroup();
- group.addOption(Option.builder("a").build());
- group.addOption(Option.builder("b").build());
- group.addOption(Option.builder("c").build());
+ void testPrintOptionGroupUsage() {
+ final OptionGroup optionGroup = new OptionGroup();
+ optionGroup.addOption(Option.builder("a").get());
+ optionGroup.addOption(Option.builder("b").get());
+ optionGroup.addOption(Option.builder("c").get());
final Options options = new Options();
- options.addOptionGroup(group);
+ options.addOptionGroup(optionGroup);
final StringWriter out = new StringWriter();
@@ -384,7 +545,7 @@ public void testPrintOptionGroupUsage() {
}
@Test
- public void testPrintOptions() {
+ void testPrintOptions() {
final StringBuffer sb = new StringBuffer();
final HelpFormatter hf = new HelpFormatter();
final int leftPad = 1;
@@ -397,36 +558,36 @@ public void testPrintOptions() {
options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa");
expected = lpad + "-a" + dpad + "aaaa aaaa aaaa aaaa aaaa";
hf.renderOptions(sb, 60, options, leftPad, descPad);
- assertEquals("simple non-wrapped option", expected, sb.toString());
+ assertEquals(expected, sb.toString(), "simple non-wrapped option");
int nextLineTabStop = leftPad + descPad + "-a".length();
expected = lpad + "-a" + dpad + "aaaa aaaa aaaa" + EOL + hf.createPadding(nextLineTabStop) + "aaaa aaaa";
sb.setLength(0);
hf.renderOptions(sb, nextLineTabStop + 17, options, leftPad, descPad);
- assertEquals("simple wrapped option", expected, sb.toString());
+ assertEquals(expected, sb.toString(), "simple wrapped option");
options = new Options().addOption("a", "aaa", false, "dddd dddd dddd dddd");
expected = lpad + "-a,--aaa" + dpad + "dddd dddd dddd dddd";
sb.setLength(0);
hf.renderOptions(sb, 60, options, leftPad, descPad);
- assertEquals("long non-wrapped option", expected, sb.toString());
+ assertEquals(expected, sb.toString(), "long non-wrapped option");
nextLineTabStop = leftPad + descPad + "-a,--aaa".length();
expected = lpad + "-a,--aaa" + dpad + "dddd dddd" + EOL + hf.createPadding(nextLineTabStop) + "dddd dddd";
sb.setLength(0);
hf.renderOptions(sb, 25, options, leftPad, descPad);
- assertEquals("long wrapped option", expected, sb.toString());
+ assertEquals(expected, sb.toString(), "long wrapped option");
options = new Options().addOption("a", "aaa", false, "dddd dddd dddd dddd").addOption("b", false, "feeee eeee eeee eeee");
expected = lpad + "-a,--aaa" + dpad + "dddd dddd" + EOL + hf.createPadding(nextLineTabStop) + "dddd dddd" + EOL + lpad + "-b " + dpad
+ "feeee eeee" + EOL + hf.createPadding(nextLineTabStop) + "eeee eeee";
sb.setLength(0);
hf.renderOptions(sb, 25, options, leftPad, descPad);
- assertEquals("multiple wrapped options", expected, sb.toString());
+ assertEquals(expected, sb.toString(), "multiple wrapped options");
}
@Test
- public void testPrintOptionWithEmptyArgNameUsage() {
+ void testPrintOptionWithEmptyArgNameUsage() {
final Option option = new Option("f", true, null);
option.setArgName("");
option.setRequired(true);
@@ -443,15 +604,15 @@ public void testPrintOptionWithEmptyArgNameUsage() {
}
@Test
- public void testPrintRequiredOptionGroupUsage() {
- final OptionGroup group = new OptionGroup();
- group.addOption(Option.builder("a").build());
- group.addOption(Option.builder("b").build());
- group.addOption(Option.builder("c").build());
- group.setRequired(true);
+ void testPrintRequiredOptionGroupUsage() {
+ final OptionGroup optionGroup = new OptionGroup();
+ optionGroup.addOption(Option.builder("a").get());
+ optionGroup.addOption(Option.builder("b").get());
+ optionGroup.addOption(Option.builder("c").get());
+ optionGroup.setRequired(true);
final Options options = new Options();
- options.addOptionGroup(group);
+ options.addOptionGroup(optionGroup);
final StringWriter out = new StringWriter();
@@ -463,20 +624,14 @@ public void testPrintRequiredOptionGroupUsage() {
// uses the test for CLI-131 to implement CLI-155
@Test
- public void testPrintSortedUsage() {
+ void testPrintSortedUsage() {
final Options opts = new Options();
opts.addOption(new Option("a", "first"));
opts.addOption(new Option("b", "second"));
opts.addOption(new Option("c", "third"));
final HelpFormatter helpFormatter = new HelpFormatter();
- helpFormatter.setOptionComparator(new Comparator() {
- @Override
- public int compare(final Option opt1, final Option opt2) {
- // reverses the functionality of the default comparator
- return opt2.getKey().compareToIgnoreCase(opt1.getKey());
- }
- });
+ helpFormatter.setOptionComparator((opt1, opt2) -> opt2.getKey().compareToIgnoreCase(opt1.getKey()));
final StringWriter out = new StringWriter();
helpFormatter.printUsage(new PrintWriter(out), 80, "app", opts);
@@ -485,7 +640,7 @@ public int compare(final Option opt1, final Option opt2) {
}
@Test
- public void testPrintSortedUsageWithNullComparator() {
+ void testPrintSortedUsageWithNullComparator() {
final Options opts = new Options();
opts.addOption(new Option("c", "first"));
opts.addOption(new Option("b", "second"));
@@ -503,7 +658,7 @@ public void testPrintSortedUsageWithNullComparator() {
// This test ensures the options are properly sorted
// See https://issues.apache.org/jira/browse/CLI-131
@Test
- public void testPrintUsage() {
+ void testPrintUsage() {
final Option optionA = new Option("a", "first");
final Option optionB = new Option("b", "second");
final Option optionC = new Option("c", "third");
@@ -520,7 +675,25 @@ public void testPrintUsage() {
}
@Test
- public void testRenderWrappedTextMultiLine() {
+ void testRenderSince() throws IOException {
+ final String[] expected = {"Options Since Description", " -n,--no-since - Description for n",
+ " -W,--with-since 1.19.0 Descripton for W"};
+ final Options options = new Options()
+ .addOption(Option.builder("W").longOpt("with-since").since("1.19.0").desc("Descripton for W").get())
+ .addOption(Option.builder("n").longOpt("no-since").desc("Description for n").get());
+ final HelpFormatter formatter = HelpFormatter.builder().setShowSince(true).get();
+
+ final StringBuffer sb = new StringBuffer();
+ formatter.renderOptions(sb, 50, options, 2, 5);
+ assertArrayEquals(expected, sb.toString().split(System.lineSeparator()));
+ // check internal exception handling for coverage
+ final HelpFormatter spy = spy(formatter);
+ when(spy.appendOptions(sb, 50, options, 2, 5)).thenThrow(IOException.class);
+ assertThrows(UncheckedIOException.class, () -> spy.renderOptions(sb, 50, options, 2, 5));
+ }
+
+ @Test
+ void testRenderWrappedTextMultiLine() {
// multi line text
final int width = 16;
final int padding = 0;
@@ -532,11 +705,11 @@ public void testRenderWrappedTextMultiLine() {
final StringBuffer sb = new StringBuffer();
new HelpFormatter().renderWrappedText(sb, width, padding, expected);
- assertEquals("multi line text", expected, sb.toString());
+ assertEquals(expected, sb.toString(), "multi line text");
}
@Test
- public void testRenderWrappedTextMultiLinePadded() {
+ void testRenderWrappedTextMultiLinePadded() {
// multi-line padded text
final int width = 16;
final int padding = 4;
@@ -551,11 +724,11 @@ public void testRenderWrappedTextMultiLinePadded() {
final StringBuffer sb = new StringBuffer();
new HelpFormatter().renderWrappedText(sb, width, padding, text);
- assertEquals("multi-line padded text", expected, sb.toString());
+ assertEquals(expected, sb.toString(), "multi-line padded text");
}
@Test
- public void testRenderWrappedTextSingleLine() {
+ void testRenderWrappedTextSingleLine() throws IOException {
// single line text
final int width = 12;
final int padding = 0;
@@ -564,11 +737,16 @@ public void testRenderWrappedTextSingleLine() {
final StringBuffer sb = new StringBuffer();
new HelpFormatter().renderWrappedText(sb, width, padding, text);
- assertEquals("single line text", expected, sb.toString());
+ assertEquals(expected, sb.toString(), "single line text");
+ // check internal exception handling for coverage
+ final HelpFormatter spy = spy(new HelpFormatter());
+ when(spy.appendWrappedText(sb, width, padding, text)).thenThrow(IOException.class);
+ assertThrows(UncheckedIOException.class, () -> spy.renderWrappedText(sb, width, padding, text));
+
}
@Test
- public void testRenderWrappedTextSingleLinePadded() {
+ void testRenderWrappedTextSingleLinePadded() {
// single line padded text
final int width = 12;
final int padding = 4;
@@ -577,11 +755,11 @@ public void testRenderWrappedTextSingleLinePadded() {
final StringBuffer sb = new StringBuffer();
new HelpFormatter().renderWrappedText(sb, width, padding, text);
- assertEquals("single line padded text", expected, sb.toString());
+ assertEquals(expected, sb.toString(), "single line padded text");
}
@Test
- public void testRenderWrappedTextSingleLinePadded2() {
+ void testRenderWrappedTextSingleLinePadded2() {
// single line padded text 2
final int width = 53;
final int padding = 24;
@@ -595,11 +773,11 @@ public void testRenderWrappedTextSingleLinePadded2() {
final StringBuffer sb = new StringBuffer();
new HelpFormatter().renderWrappedText(sb, width, padding, text);
- assertEquals("single line padded text 2", expected, sb.toString());
+ assertEquals(expected, sb.toString(), "single line padded text 2");
}
@Test
- public void testRenderWrappedTextWordCut() {
+ void testRenderWrappedTextWordCut() {
final int width = 7;
final int padding = 0;
final String text = "Thisisatest.";
@@ -607,11 +785,11 @@ public void testRenderWrappedTextWordCut() {
final StringBuffer sb = new StringBuffer();
new HelpFormatter().renderWrappedText(sb, width, padding, text);
- assertEquals("cut and wrap", expected, sb.toString());
+ assertEquals(expected, sb.toString(), "cut and wrap");
}
@Test
- public void testRtrim() {
+ void testRtrim() {
final HelpFormatter formatter = new HelpFormatter();
assertNull(formatter.rtrim(null));
@@ -620,11 +798,11 @@ public void testRtrim() {
}
@Test
- public void testUsageWithLongOptSeparator() {
+ void testUsageWithLongOptSeparator() {
final Options options = new Options();
options.addOption("f", true, "the file");
- options.addOption(Option.builder("s").longOpt("size").desc("the size").hasArg().argName("SIZE").build());
- options.addOption(Option.builder().longOpt("age").desc("the age").hasArg().build());
+ options.addOption(Option.builder("s").longOpt("size").desc("the size").hasArg().argName("SIZE").get());
+ options.addOption(Option.builder().longOpt("age").desc("the age").hasArg().get());
final HelpFormatter formatter = new HelpFormatter();
formatter.setLongOptSeparator("=");
diff --git a/src/test/java/org/apache/commons/cli/MissingOptionExceptionTest.java b/src/test/java/org/apache/commons/cli/MissingOptionExceptionTest.java
new file mode 100644
index 000000000..e0644c125
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/MissingOptionExceptionTest.java
@@ -0,0 +1,51 @@
+/*
+ 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
+
+ https://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 static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link MissingOptionException}.
+ */
+public class MissingOptionExceptionTest {
+
+ @Test
+ void testGetMessage() {
+ final List originalList = new ArrayList<>();
+ originalList.add("optA");
+ originalList.add("optB");
+ final MissingOptionException exception = new MissingOptionException(originalList);
+ assertEquals("Missing required options: optA, optB", exception.getMessage());
+ assertEquals("Missing required options: ", new MissingOptionException(new ArrayList<>()).getMessage());
+ }
+
+ @Test
+ void testGetMissingOptions() {
+ final List originalList = new ArrayList<>();
+ originalList.add("optA");
+ originalList.add("optB");
+ final MissingOptionException exception = new MissingOptionException(originalList);
+ assertEquals(Arrays.asList("optA", "optB"), exception.getMissingOptions());
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/OptionBuilderTest.java b/src/test/java/org/apache/commons/cli/OptionBuilderTest.java
index 1df79dd54..e87e32d7e 100644
--- a/src/test/java/org/apache/commons/cli/OptionBuilderTest.java
+++ b/src/test/java/org/apache/commons/cli/OptionBuilderTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,65 +17,54 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
@SuppressWarnings("deprecation") // OptionBuilder is marked deprecated
-public class OptionBuilderTest {
- @Test
- public void testBaseOptionCharOpt() {
- final Option base = OptionBuilder.withDescription("option description").create('o');
+class OptionBuilderTest {
+ @Test
+ void testBaseOptionCharOpt() {
+ OptionBuilder.withDescription("option description");
+ final Option base = OptionBuilder.create('o');
assertEquals("o", base.getOpt());
assertEquals("option description", base.getDescription());
assertFalse(base.hasArg());
}
@Test
- public void testBaseOptionStringOpt() {
- final Option base = OptionBuilder.withDescription("option description").create("o");
-
+ void testBaseOptionStringOpt() {
+ OptionBuilder.withDescription("option description");
+ final Option base = OptionBuilder.create("o");
assertEquals("o", base.getOpt());
assertEquals("option description", base.getDescription());
assertFalse(base.hasArg());
}
@Test
- public void testBuilderIsResettedAlways() {
- try {
- OptionBuilder.withDescription("JUnit").create('"');
- fail("IllegalArgumentException expected");
- } catch (final IllegalArgumentException e) {
- // expected
- }
- assertNull("we inherited a description", OptionBuilder.create('x').getDescription());
-
- try {
- OptionBuilder.withDescription("JUnit").create();
- fail("IllegalArgumentException expected");
- } catch (final IllegalArgumentException e) {
- // expected
- }
- assertNull("we inherited a description", OptionBuilder.create('x').getDescription());
+ void testBuilderIsResettedAlways() {
+ OptionBuilder.withDescription("JUnit");
+ assertThrows(IllegalArgumentException.class, () -> OptionBuilder.create('"'));
+ assertNull(OptionBuilder.create('x').getDescription(), "we inherited a description");
+ assertThrows(IllegalStateException.class, (Executable) OptionBuilder::create);
+ assertNull(OptionBuilder.create('x').getDescription(), "we inherited a description");
}
@Test
- public void testCompleteOption() {
- //@formatter:off
- final Option simple = OptionBuilder.withLongOpt("simple option")
- .hasArg()
- .isRequired()
- .hasArgs()
- .withType(Float.class)
- .withDescription("this is a simple option")
- .create('s');
- //@formatter:on
-
+ void testCompleteOption() {
+ OptionBuilder.withLongOpt("simple option");
+ OptionBuilder.hasArg();
+ OptionBuilder.isRequired();
+ OptionBuilder.hasArgs();
+ OptionBuilder.withType(Float.class);
+ OptionBuilder.withDescription("this is a simple option");
+ final Option simple = OptionBuilder.create('s');
assertEquals("s", simple.getOpt());
assertEquals("simple option", simple.getLongOpt());
assertEquals("this is a simple option", simple.getDescription());
@@ -86,86 +75,54 @@ public void testCompleteOption() {
}
@Test
- public void testCreateIncompleteOption() {
- try {
- OptionBuilder.hasArg().create();
- fail("Incomplete option should be rejected");
- } catch (final IllegalArgumentException e) {
- // expected
-
- // implicitly reset the builder
- OptionBuilder.create("opt");
- }
+ void testCreateIncompleteOption() {
+ assertThrows(IllegalStateException.class, (Executable) OptionBuilder::create);
+ // implicitly reset the builder
+ OptionBuilder.create("opt");
}
@Test
- public void testIllegalOptions() {
+ void testIllegalOptions() {
+ OptionBuilder.withDescription("option description");
// bad single character option
- try {
- OptionBuilder.withDescription("option description").create('"');
- fail("IllegalArgumentException not caught");
- } catch (final IllegalArgumentException exp) {
- // success
- }
-
+ assertThrows(IllegalArgumentException.class, () -> OptionBuilder.create('"'));
// bad character in option string
- try {
- OptionBuilder.create("opt`");
- fail("IllegalArgumentException not caught");
- } catch (final IllegalArgumentException exp) {
- // success
- }
-
+ assertThrows(IllegalArgumentException.class, () -> OptionBuilder.create("opt`"));
// valid option
- try {
- OptionBuilder.create("opt");
- // success
- } catch (final IllegalArgumentException exp) {
- fail("IllegalArgumentException caught");
- }
+ OptionBuilder.create("opt");
}
@Test
- public void testOptionArgNumbers() {
- //@formatter:off
- final Option opt = OptionBuilder.withDescription("option description")
- .hasArgs(2)
- .create('o');
- //@formatter:on
+ void testOptionArgNumbers() {
+ OptionBuilder.withDescription("option description");
+ OptionBuilder.hasArgs(2);
+ final Option opt = OptionBuilder.create('o');
assertEquals(2, opt.getArgs());
}
@Test
- public void testSpecialOptChars() throws Exception {
+ void testSpecialOptChars() throws Exception {
+ OptionBuilder.withDescription("help options");
// '?'
- final Option opt1 = OptionBuilder.withDescription("help options").create('?');
+ final Option opt1 = OptionBuilder.create('?');
assertEquals("?", opt1.getOpt());
-
+ OptionBuilder.withDescription("read from stdin");
// '@'
- final Option opt2 = OptionBuilder.withDescription("read from stdin").create('@');
+ final Option opt2 = OptionBuilder.create('@');
assertEquals("@", opt2.getOpt());
-
// ' '
- try {
- OptionBuilder.create(' ');
- fail("IllegalArgumentException not caught");
- } catch (final IllegalArgumentException e) {
- // success
- }
+ assertThrows(IllegalArgumentException.class, () -> OptionBuilder.create(' '));
}
@Test
- public void testTwoCompleteOptions() {
- //@formatter:off
- Option simple = OptionBuilder.withLongOpt("simple option")
- .hasArg()
- .isRequired()
- .hasArgs()
- .withType(Float.class)
- .withDescription("this is a simple option")
- .create('s');
- //@formatter:on
-
+ void testTwoCompleteOptions() {
+ OptionBuilder.withLongOpt("simple option");
+ OptionBuilder.hasArg();
+ OptionBuilder.isRequired();
+ OptionBuilder.hasArgs();
+ OptionBuilder.withType(Float.class);
+ OptionBuilder.withDescription("this is a simple option");
+ Option simple = OptionBuilder.create('s');
assertEquals("s", simple.getOpt());
assertEquals("simple option", simple.getLongOpt());
assertEquals("this is a simple option", simple.getDescription());
@@ -173,14 +130,10 @@ public void testTwoCompleteOptions() {
assertTrue(simple.hasArg());
assertTrue(simple.isRequired());
assertTrue(simple.hasArgs());
-
- //@formatter:off
- simple = OptionBuilder.withLongOpt("dimple option")
- .hasArg()
- .withDescription("this is a dimple option")
- .create('d');
- //@formatter:on
-
+ OptionBuilder.withLongOpt("dimple option");
+ OptionBuilder.hasArg();
+ OptionBuilder.withDescription("this is a dimple option");
+ simple = OptionBuilder.create('d');
assertEquals("d", simple.getOpt());
assertEquals("dimple option", simple.getLongOpt());
assertEquals("this is a dimple option", simple.getDescription());
diff --git a/src/test/java/org/apache/commons/cli/OptionCountTest.java b/src/test/java/org/apache/commons/cli/OptionCountTest.java
new file mode 100644
index 000000000..ca42d1d65
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/OptionCountTest.java
@@ -0,0 +1,59 @@
+/*
+ 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
+
+ https://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 static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class OptionCountTest {
+ private static final Option VERBOSITY = new Option("v", "verbosity: use multiple times for more");
+ private static final Options OPTIONS = new Options().addOption(VERBOSITY);
+
+ @Test
+ void testFiveSwitchesMixed() throws ParseException {
+ final CommandLine cmdLine = new DefaultParser().parse(OPTIONS, new String[]{"-v", "-vvv", "-v"});
+ assertEquals(5, cmdLine.getOptionCount(VERBOSITY));
+ }
+
+ @Test
+ void testNoSwitch() throws ParseException {
+ final CommandLine cmdLine = new DefaultParser().parse(OPTIONS, new String[]{});
+ assertEquals(0, cmdLine.getOptionCount(VERBOSITY));
+ }
+
+ @Test
+ void testOneSwitch() throws ParseException {
+ final CommandLine cmdLine = new DefaultParser().parse(OPTIONS, new String[]{"-v"});
+ assertEquals(1, cmdLine.getOptionCount(VERBOSITY));
+ assertEquals(1, cmdLine.getOptionCount("v"));
+ assertEquals(1, cmdLine.getOptionCount('v'));
+ }
+
+ @Test
+ void testThreeSwitches() throws ParseException {
+ final CommandLine cmdLine = new DefaultParser().parse(OPTIONS, new String[]{"-v", "-v", "-v"});
+ assertEquals(3, cmdLine.getOptionCount(VERBOSITY));
+ }
+
+ @Test
+ void testThreeSwitchesCompact() throws ParseException {
+ final CommandLine cmdLine = new DefaultParser().parse(OPTIONS, new String[]{"-vvv"});
+ assertEquals(3, cmdLine.getOptionCount(VERBOSITY));
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/OptionGroupTest.java b/src/test/java/org/apache/commons/cli/OptionGroupTest.java
index f182beb83..56b288b5a 100644
--- a/src/test/java/org/apache/commons/cli/OptionGroupTest.java
+++ b/src/test/java/org/apache/commons/cli/OptionGroupTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,223 +17,197 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Properties;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class OptionGroupTest {
+class OptionGroupTest {
+
private Options options;
private final Parser parser = new PosixParser();
- @Before
+ @BeforeEach
public void setUp() {
final Option file = new Option("f", "file", false, "file to process");
final Option dir = new Option("d", "directory", false, "directory to process");
- final OptionGroup group = new OptionGroup();
- group.addOption(file);
- group.addOption(dir);
- options = new Options().addOptionGroup(group);
+ final OptionGroup optionGroup1 = new OptionGroup();
+ optionGroup1.addOption(file);
+ optionGroup1.addOption(dir);
+ options = new Options().addOptionGroup(optionGroup1);
final Option section = new Option("s", "section", false, "section to process");
final Option chapter = new Option("c", "chapter", false, "chapter to process");
- final OptionGroup group2 = new OptionGroup();
- group2.addOption(section);
- group2.addOption(chapter);
+ final OptionGroup optionGroup2 = new OptionGroup();
+ optionGroup2.addOption(section);
+ optionGroup2.addOption(chapter);
- options.addOptionGroup(group2);
+ options.addOptionGroup(optionGroup2);
final Option importOpt = new Option(null, "import", false, "section to process");
final Option exportOpt = new Option(null, "export", false, "chapter to process");
- final OptionGroup group3 = new OptionGroup();
- group3.addOption(importOpt);
- group3.addOption(exportOpt);
- options.addOptionGroup(group3);
+ final OptionGroup optionGroup3 = new OptionGroup();
+ optionGroup3.addOption(importOpt);
+ optionGroup3.addOption(exportOpt);
+ options.addOptionGroup(optionGroup3);
options.addOption("r", "revision", false, "revision number");
}
@Test
- public void testGetNames() {
- final OptionGroup group = new OptionGroup();
- group.addOption(OptionBuilder.create('a'));
- group.addOption(OptionBuilder.create('b'));
-
- assertNotNull("null names", group.getNames());
- assertEquals(2, group.getNames().size());
- assertTrue(group.getNames().contains("a"));
- assertTrue(group.getNames().contains("b"));
+ void testGetNames() {
+ final OptionGroup optionGroup = new OptionGroup();
+ assertFalse(optionGroup.isSelected());
+ optionGroup.addOption(OptionBuilder.create('a'));
+ optionGroup.addOption(OptionBuilder.create('b'));
+ assertNotNull(optionGroup.getNames(), "null names");
+ assertEquals(2, optionGroup.getNames().size());
+ assertTrue(optionGroup.getNames().contains("a"));
+ assertTrue(optionGroup.getNames().contains("b"));
}
@Test
- public void testNoOptionsExtraArgs() throws Exception {
+ void testNoOptionsExtraArgs() throws Exception {
final String[] args = {"arg1", "arg2"};
-
final CommandLine cl = parser.parse(options, args);
-
- assertFalse("Confirm -r is NOT set", cl.hasOption("r"));
- assertFalse("Confirm -f is NOT set", cl.hasOption("f"));
- assertFalse("Confirm -d is NOT set", cl.hasOption("d"));
- assertFalse("Confirm -s is NOT set", cl.hasOption("s"));
- assertFalse("Confirm -c is NOT set", cl.hasOption("c"));
- assertEquals("Confirm TWO extra args", 2, cl.getArgList().size());
+ assertFalse(cl.hasOption("r"), "Confirm -r is NOT set");
+ assertFalse(cl.hasOption("f"), "Confirm -f is NOT set");
+ assertFalse(cl.hasOption("d"), "Confirm -d is NOT set");
+ assertFalse(cl.hasOption("s"), "Confirm -s is NOT set");
+ assertFalse(cl.hasOption("c"), "Confirm -c is NOT set");
+ assertEquals(2, cl.getArgList().size(), "Confirm TWO extra args");
}
@Test
- public void testSingleLongOption() throws Exception {
+ void testSingleLongOption() throws Exception {
final String[] args = {"--file"};
-
final CommandLine cl = parser.parse(options, args);
-
- assertFalse("Confirm -r is NOT set", cl.hasOption("r"));
- assertTrue("Confirm -f is set", cl.hasOption("f"));
- assertFalse("Confirm -d is NOT set", cl.hasOption("d"));
- assertFalse("Confirm -s is NOT set", cl.hasOption("s"));
- assertFalse("Confirm -c is NOT set", cl.hasOption("c"));
- assertTrue("Confirm no extra args", cl.getArgList().isEmpty());
+ assertFalse(cl.hasOption("r"), "Confirm -r is NOT set");
+ assertTrue(cl.hasOption("f"), "Confirm -f is set");
+ assertFalse(cl.hasOption("d"), "Confirm -d is NOT set");
+ assertFalse(cl.hasOption("s"), "Confirm -s is NOT set");
+ assertFalse(cl.hasOption("c"), "Confirm -c is NOT set");
+ assertTrue(cl.getArgList().isEmpty(), "Confirm no extra args");
}
@Test
- public void testSingleOption() throws Exception {
+ void testSingleOption() throws Exception {
final String[] args = {"-r"};
-
final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm -r is set", cl.hasOption("r"));
- assertFalse("Confirm -f is NOT set", cl.hasOption("f"));
- assertFalse("Confirm -d is NOT set", cl.hasOption("d"));
- assertFalse("Confirm -s is NOT set", cl.hasOption("s"));
- assertFalse("Confirm -c is NOT set", cl.hasOption("c"));
- assertTrue("Confirm no extra args", cl.getArgList().isEmpty());
+ assertTrue(cl.hasOption("r"), "Confirm -r is set");
+ assertFalse(cl.hasOption("f"), "Confirm -f is NOT set");
+ assertFalse(cl.hasOption("d"), "Confirm -d is NOT set");
+ assertFalse(cl.hasOption("s"), "Confirm -s is NOT set");
+ assertFalse(cl.hasOption("c"), "Confirm -c is NOT set");
+ assertTrue(cl.getArgList().isEmpty(), "Confirm no extra args");
}
@Test
- public void testSingleOptionFromGroup() throws Exception {
+ void testSingleOptionFromGroup() throws Exception {
final String[] args = {"-f"};
-
final CommandLine cl = parser.parse(options, args);
-
- assertFalse("Confirm -r is NOT set", cl.hasOption("r"));
- assertTrue("Confirm -f is set", cl.hasOption("f"));
- assertFalse("Confirm -d is NOT set", cl.hasOption("d"));
- assertFalse("Confirm -s is NOT set", cl.hasOption("s"));
- assertFalse("Confirm -c is NOT set", cl.hasOption("c"));
- assertTrue("Confirm no extra args", cl.getArgList().isEmpty());
+ assertFalse(cl.hasOption("r"), "Confirm -r is NOT set");
+ assertTrue(cl.hasOption("f"), "Confirm -f is set");
+ assertFalse(cl.hasOption("d"), "Confirm -d is NOT set");
+ assertFalse(cl.hasOption("s"), "Confirm -s is NOT set");
+ assertFalse(cl.hasOption("c"), "Confirm -c is NOT set");
+ assertTrue(cl.getArgList().isEmpty(), "Confirm no extra args");
}
@Test
- public void testToString() {
- final OptionGroup group1 = new OptionGroup();
- group1.addOption(new Option(null, "foo", false, "Foo"));
- group1.addOption(new Option(null, "bar", false, "Bar"));
-
- if (!"[--bar Bar, --foo Foo]".equals(group1.toString())) {
- assertEquals("[--foo Foo, --bar Bar]", group1.toString());
+ void testToString() {
+ final OptionGroup optionGroup1 = new OptionGroup();
+ optionGroup1.addOption(new Option(null, "foo", false, "Foo"));
+ optionGroup1.addOption(new Option(null, "bar", false, "Bar"));
+ if (!"[--bar Bar, --foo Foo]".equals(optionGroup1.toString())) {
+ assertEquals("[--foo Foo, --bar Bar]", optionGroup1.toString());
}
-
- final OptionGroup group2 = new OptionGroup();
- group2.addOption(new Option("f", "foo", false, "Foo"));
- group2.addOption(new Option("b", "bar", false, "Bar"));
-
- if (!"[-b Bar, -f Foo]".equals(group2.toString())) {
- assertEquals("[-f Foo, -b Bar]", group2.toString());
+ final OptionGroup optionGroup2 = new OptionGroup();
+ optionGroup2.addOption(new Option("f", "foo", false, "Foo"));
+ optionGroup2.addOption(new Option("b", "bar", false, "Bar"));
+ if (!"[-b Bar, -f Foo]".equals(optionGroup2.toString())) {
+ assertEquals("[-f Foo, -b Bar]", optionGroup2.toString());
}
}
@Test
- public void testTwoLongOptionsFromGroup() throws Exception {
- final String[] args = {"--file", "--directory"};
-
- try {
- parser.parse(options, args);
- fail("two arguments from group not allowed");
- } catch (final AlreadySelectedException e) {
- assertNotNull("null option group", e.getOptionGroup());
- assertEquals("selected option", "f", e.getOptionGroup().getSelected());
- assertEquals("option", "d", e.getOption().getOpt());
- }
+ void testTwoLongOptionsFromGroup() throws Exception {
+ final String[] args = { "--file", "--directory" };
+ final AlreadySelectedException e = assertThrows(AlreadySelectedException.class, () -> parser.parse(options, args));
+ assertNotNull(e.getOptionGroup(), "null option group");
+ assertTrue(e.getOptionGroup().isSelected());
+ assertEquals("f", e.getOptionGroup().getSelected(), "selected option");
+ assertEquals("d", e.getOption().getOpt(), "option");
}
@Test
- public void testTwoOptionsFromDifferentGroup() throws Exception {
+ void testTwoOptionsFromDifferentGroup() throws Exception {
final String[] args = {"-f", "-s"};
-
final CommandLine cl = parser.parse(options, args);
- assertFalse("Confirm -r is NOT set", cl.hasOption("r"));
- assertTrue("Confirm -f is set", cl.hasOption("f"));
- assertFalse("Confirm -d is NOT set", cl.hasOption("d"));
- assertTrue("Confirm -s is set", cl.hasOption("s"));
- assertFalse("Confirm -c is NOT set", cl.hasOption("c"));
- assertTrue("Confirm NO extra args", cl.getArgList().isEmpty());
+ assertFalse(cl.hasOption("r"), "Confirm -r is NOT set");
+ assertTrue(cl.hasOption("f"), "Confirm -f is set");
+ assertFalse(cl.hasOption("d"), "Confirm -d is NOT set");
+ assertTrue(cl.hasOption("s"), "Confirm -s is set");
+ assertFalse(cl.hasOption("c"), "Confirm -c is NOT set");
+ assertTrue(cl.getArgList().isEmpty(), "Confirm NO extra args");
}
@Test
- public void testTwoOptionsFromGroup() throws Exception {
- final String[] args = {"-f", "-d"};
-
- try {
- parser.parse(options, args);
- fail("two arguments from group not allowed");
- } catch (final AlreadySelectedException e) {
- assertNotNull("null option group", e.getOptionGroup());
- assertEquals("selected option", "f", e.getOptionGroup().getSelected());
- assertEquals("option", "d", e.getOption().getOpt());
- }
+ void testTwoOptionsFromGroup() throws Exception {
+ final String[] args = { "-f", "-d" };
+ final AlreadySelectedException e = assertThrows(AlreadySelectedException.class, () -> parser.parse(options, args));
+ assertNotNull(e.getOptionGroup(), "null option group");
+ assertTrue(e.getOptionGroup().isSelected());
+ assertEquals("f", e.getOptionGroup().getSelected(), "selected option");
+ assertEquals("d", e.getOption().getOpt(), "option");
}
@Test
- public void testTwoOptionsFromGroupWithProperties() throws Exception {
+ void testTwoOptionsFromGroupWithProperties() throws Exception {
final String[] args = {"-f"};
-
final Properties properties = new Properties();
properties.put("d", "true");
-
final CommandLine cl = parser.parse(options, args, properties);
assertTrue(cl.hasOption("f"));
assertFalse(cl.hasOption("d"));
}
@Test
- public void testTwoValidLongOptions() throws Exception {
+ void testTwoValidLongOptions() throws Exception {
final String[] args = {"--revision", "--file"};
-
final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm -r is set", cl.hasOption("r"));
- assertTrue("Confirm -f is set", cl.hasOption("f"));
- assertFalse("Confirm -d is NOT set", cl.hasOption("d"));
- assertFalse("Confirm -s is NOT set", cl.hasOption("s"));
- assertFalse("Confirm -c is NOT set", cl.hasOption("c"));
- assertTrue("Confirm no extra args", cl.getArgList().isEmpty());
+ assertTrue(cl.hasOption("r"), "Confirm -r is set");
+ assertTrue(cl.hasOption("f"), "Confirm -f is set");
+ assertFalse(cl.hasOption("d"), "Confirm -d is NOT set");
+ assertFalse(cl.hasOption("s"), "Confirm -s is NOT set");
+ assertFalse(cl.hasOption("c"), "Confirm -c is NOT set");
+ assertTrue(cl.getArgList().isEmpty(), "Confirm no extra args");
}
@Test
- public void testTwoValidOptions() throws Exception {
+ void testTwoValidOptions() throws Exception {
final String[] args = {"-r", "-f"};
-
final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm -r is set", cl.hasOption("r"));
- assertTrue("Confirm -f is set", cl.hasOption("f"));
- assertFalse("Confirm -d is NOT set", cl.hasOption("d"));
- assertFalse("Confirm -s is NOT set", cl.hasOption("s"));
- assertFalse("Confirm -c is NOT set", cl.hasOption("c"));
- assertTrue("Confirm no extra args", cl.getArgList().isEmpty());
+ assertTrue(cl.hasOption("r"), "Confirm -r is set");
+ assertTrue(cl.hasOption("f"), "Confirm -f is set");
+ assertFalse(cl.hasOption("d"), "Confirm -d is NOT set");
+ assertFalse(cl.hasOption("s"), "Confirm -s is NOT set");
+ assertFalse(cl.hasOption("c"), "Confirm -c is NOT set");
+ assertTrue(cl.getArgList().isEmpty(), "Confirm no extra args");
}
@Test
- public void testValidLongOnlyOptions() throws Exception {
+ void testValidLongOnlyOptions() throws Exception {
final CommandLine cl1 = parser.parse(options, new String[] {"--export"});
- assertTrue("Confirm --export is set", cl1.hasOption("export"));
-
+ assertTrue(cl1.hasOption("export"), "Confirm --export is set");
final CommandLine cl2 = parser.parse(options, new String[] {"--import"});
- assertTrue("Confirm --import is set", cl2.hasOption("import"));
+ assertTrue(cl2.hasOption("import"), "Confirm --import is set");
}
}
diff --git a/src/test/java/org/apache/commons/cli/OptionTest.java b/src/test/java/org/apache/commons/cli/OptionTest.java
index 14542d981..2c7b234c2 100644
--- a/src/test/java/org/apache/commons/cli/OptionTest.java
+++ b/src/test/java/org/apache/commons/cli/OptionTest.java
@@ -6,7 +6,7 @@
* (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
+ * https://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,
@@ -17,17 +17,25 @@
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.junit.Test;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
-public class OptionTest {
- private static class DefaultOption extends Option {
+import org.junit.jupiter.api.Test;
+
+class OptionTest {
+
+ private static final class DefaultOption extends Option {
private static final long serialVersionUID = 1L;
private final String defaultValue;
@@ -43,7 +51,7 @@ public String getValue() {
}
}
- private static class TestOption extends Option {
+ private static final class TestOption extends Option {
private static final long serialVersionUID = 1L;
TestOption(final String opt, final boolean hasArg, final String description) throws IllegalArgumentException {
@@ -52,13 +60,14 @@ private static class TestOption extends Option {
@Override
public boolean addValue(final String value) {
- addValueForProcessing(value);
+ processValue(value);
return true;
}
}
private static void checkOption(final Option option, final String opt, final String description, final String longOpt, final int numArgs,
- final String argName, final boolean required, final boolean optionalArg, final char valueSeparator, final Class> cls) {
+ final String argName, final boolean required, final boolean optionalArg, final char valueSeparator, final Class> cls, final String deprecatedDesc,
+ final Boolean deprecatedForRemoval, final String deprecatedSince) {
assertEquals(opt, option.getOpt());
assertEquals(description, option.getDescription());
assertEquals(longOpt, option.getLongOpt());
@@ -67,85 +76,151 @@ private static void checkOption(final Option option, final String opt, final Str
assertEquals(required, option.isRequired());
assertEquals(optionalArg, option.hasOptionalArg());
+ assertEquals(numArgs > 0, option.hasArg());
+ assertEquals(numArgs > 0, option.acceptsArg());
assertEquals(valueSeparator, option.getValueSeparator());
assertEquals(cls, option.getType());
+ if (deprecatedDesc != null) {
+ assertEquals(deprecatedDesc, option.getDeprecated().getDescription());
+ }
+ if (deprecatedForRemoval != null) {
+ assertEquals(deprecatedForRemoval, option.getDeprecated().isForRemoval());
+ }
+ if (deprecatedSince != null) {
+ assertEquals(deprecatedSince, option.getDeprecated().getSince());
+ }
}
- @Test(expected = IllegalArgumentException.class)
- public void testBuilderInsufficientParams1() {
- Option.builder().desc("desc").build();
+ private Option roundTrip(final Option o) throws IOException, ClassNotFoundException {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(o);
+ final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ final ObjectInputStream ois = new ObjectInputStream(bais);
+ return (Option) ois.readObject();
}
- @Test(expected = IllegalArgumentException.class)
- public void testBuilderInsufficientParams2() {
- Option.builder(null).desc("desc").build();
+ @Test
+ void testAddValue() {
+ final Option option = new Option("f", null);
+ assertThrows(UnsupportedOperationException.class, () -> option.addValue(""));
+ assertThrows(IllegalStateException.class, () -> option.processValue(""));
}
- @Test(expected = IllegalArgumentException.class)
- public void testBuilderInvalidOptionName1() {
- Option.builder().option("invalid?");
+ @Test
+ void testBuilderDeprecatedBuildEmpty() {
+ assertThrows(IllegalStateException.class, () -> Option.builder().build());
}
- @Test(expected = IllegalArgumentException.class)
- public void testBuilderInvalidOptionName2() {
- Option.builder().option("invalid@");
+ @Test
+ void testBuilderEmpty() {
+ assertThrows(IllegalStateException.class, () -> Option.builder().get());
}
- @Test(expected = IllegalArgumentException.class)
- public void testBuilderInvalidOptionName3() {
- Option.builder("invalid?");
+ @Test
+ void testBuilderInsufficientParams1() {
+ assertThrows(IllegalStateException.class, () -> Option.builder().desc("desc").get());
}
- @Test(expected = IllegalArgumentException.class)
- public void testBuilderInvalidOptionName4() {
- Option.builder("invalid@");
+ @Test
+ void testBuilderInsufficientParams2() {
+ assertThrows(IllegalStateException.class, () -> Option.builder(null).desc("desc").get());
+ }
+
+ @Test
+ void testBuilderInvalidOptionName0() {
+ assertThrows(IllegalStateException.class, () -> Option.builder().option(null).get());
+ assertThrows(IllegalArgumentException.class, () -> Option.builder().option(""));
+ assertThrows(IllegalArgumentException.class, () -> Option.builder().option(" "));
+ }
+
+ @Test
+ void testBuilderInvalidOptionName1() {
+ assertThrows(IllegalArgumentException.class, () -> Option.builder().option("invalid?"));
+ }
+
+ @Test
+ void testBuilderInvalidOptionName2() {
+ assertThrows(IllegalArgumentException.class, () -> Option.builder().option("invalid@"));
+ }
+
+ @Test
+ void testBuilderInvalidOptionName3() {
+ assertThrows(IllegalArgumentException.class, () -> Option.builder("invalid?"));
}
@Test
- public void testBuilderMethods() {
+ void testBuilderInvalidOptionName4() {
+ assertThrows(IllegalArgumentException.class, () -> Option.builder("invalid@"));
+ }
+
+ @Test
+ void testBuilderMethods() {
final char defaultSeparator = (char) 0;
- checkOption(Option.builder("a").desc("desc").build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class);
- checkOption(Option.builder("a").desc("desc").build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class);
- checkOption(Option.builder("a").desc("desc").longOpt("aaa").build(), "a", "desc", "aaa", Option.UNINITIALIZED, null, false, false, defaultSeparator,
- String.class);
- checkOption(Option.builder("a").desc("desc").hasArg(true).build(), "a", "desc", null, 1, null, false, false, defaultSeparator, String.class);
- checkOption(Option.builder("a").desc("desc").hasArg(false).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
- String.class);
- checkOption(Option.builder("a").desc("desc").hasArg(true).build(), "a", "desc", null, 1, null, false, false, defaultSeparator, String.class);
- checkOption(Option.builder("a").desc("desc").numberOfArgs(3).build(), "a", "desc", null, 3, null, false, false, defaultSeparator, String.class);
- checkOption(Option.builder("a").desc("desc").required(true).build(), "a", "desc", null, Option.UNINITIALIZED, null, true, false, defaultSeparator,
- String.class);
- checkOption(Option.builder("a").desc("desc").required(false).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
- String.class);
-
- checkOption(Option.builder("a").desc("desc").argName("arg1").build(), "a", "desc", null, Option.UNINITIALIZED, "arg1", false, false, defaultSeparator,
- String.class);
- checkOption(Option.builder("a").desc("desc").optionalArg(false).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
- String.class);
- checkOption(Option.builder("a").desc("desc").optionalArg(true).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, true, defaultSeparator,
- String.class);
- checkOption(Option.builder("a").desc("desc").valueSeparator(':').build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, ':',
- String.class);
- checkOption(Option.builder("a").desc("desc").type(Integer.class).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
- Integer.class);
- checkOption(Option.builder().option("a").desc("desc").type(Integer.class).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false,
- defaultSeparator, Integer.class);
- }
-
- @Test
- public void testClear() {
+ checkOption(Option.builder("a").desc("desc").get(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class, null,
+ null, null);
+ checkOption(Option.builder("a").desc("desc").get(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class, null,
+ null, null);
+ checkOption(Option.builder("a").desc("desc").longOpt("aaa").get(), "a", "desc", "aaa", Option.UNINITIALIZED, null, false, false, defaultSeparator,
+ String.class, null, null, null);
+ checkOption(Option.builder("a").desc("desc").hasArg(true).get(), "a", "desc", null, 1, null, false, false, defaultSeparator, String.class, null, null,
+ null);
+ checkOption(Option.builder("a").desc("desc").hasArg(false).get(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
+ String.class, null, null, null);
+ checkOption(Option.builder("a").desc("desc").hasArg(true).get(), "a", "desc", null, 1, null, false, false, defaultSeparator, String.class, null, null,
+ null);
+ checkOption(Option.builder("a").desc("desc").numberOfArgs(3).get(), "a", "desc", null, 3, null, false, false, defaultSeparator, String.class, null,
+ null, null);
+ checkOption(Option.builder("a").desc("desc").required(true).get(), "a", "desc", null, Option.UNINITIALIZED, null, true, false, defaultSeparator,
+ String.class, null, null, null);
+ checkOption(Option.builder("a").desc("desc").required(false).get(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
+ String.class, null, null, null);
+
+ checkOption(Option.builder("a").desc("desc").argName("arg1").get(), "a", "desc", null, Option.UNINITIALIZED, "arg1", false, false, defaultSeparator,
+ String.class, null, null, null);
+ checkOption(Option.builder("a").desc("desc").optionalArg(false).get(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
+ String.class, null, null, null);
+ checkOption(Option.builder("a").desc("desc").optionalArg(true).get(), "a", "desc", null, 1, null, false, true, defaultSeparator, String.class, null,
+ null, null);
+ checkOption(Option.builder("a").desc("desc").valueSeparator(':').get(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, ':',
+ String.class, null, null, null);
+ checkOption(Option.builder("a").desc("desc").type(Integer.class).get(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
+ Integer.class, null, null, null);
+ checkOption(Option.builder("a").desc("desc").type(null).get(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
+ String.class, null, null, null);
+ checkOption(Option.builder().option("a").desc("desc").type(Integer.class).get(), "a", "desc", null, Option.UNINITIALIZED, null, false, false,
+ defaultSeparator, Integer.class, null, null, null);
+ // Deprecated
+ checkOption(Option.builder().option("a").desc("desc").type(Integer.class).deprecated().get(), "a", "desc", null, Option.UNINITIALIZED, null, false,
+ false, defaultSeparator, Integer.class, "", false, "");
+ checkOption(Option.builder().option("a").desc("desc").type(Integer.class).deprecated(DeprecatedAttributes.builder().get()).get(), "a", "desc", null,
+ Option.UNINITIALIZED, null, false, false, defaultSeparator, Integer.class, "", false, "");
+ checkOption(Option.builder().option("a").desc("desc").type(Integer.class).deprecated(DeprecatedAttributes.builder().setDescription("X").get()).get(),
+ "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, Integer.class, "X", false, "");
+ checkOption(
+ Option.builder().option("a").desc("desc").type(Integer.class)
+ .deprecated(DeprecatedAttributes.builder().setDescription("X").setForRemoval(true).get()).get(),
+ "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, Integer.class, "X", true, "");
+ checkOption(
+ Option.builder().option("a").desc("desc").type(Integer.class)
+ .deprecated(DeprecatedAttributes.builder().setDescription("X").setForRemoval(true).setSince("2.0").get()).get(),
+ "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, Integer.class, "X", true, "2.0");
+ }
+
+ @Test
+ void testClear() {
final TestOption option = new TestOption("x", true, "");
- assertEquals(0, option.getValuesList().size());
+ assertTrue(option.getValuesList().isEmpty());
option.addValue("a");
assertEquals(1, option.getValuesList().size());
option.clearValues();
- assertEquals(0, option.getValuesList().size());
+ assertTrue(option.getValuesList().isEmpty());
}
// See https://issues.apache.org/jira/browse/CLI-21
@Test
- public void testClone() {
+ void testClone() {
final TestOption a = new TestOption("a", true, "");
final TestOption b = (TestOption) a.clone();
assertEquals(a, b);
@@ -156,19 +231,33 @@ public void testClone() {
b.addValue("b1");
b.addValue("b2");
assertEquals(1, a.getArgs());
- assertEquals(0, a.getValuesList().size());
+ assertTrue(a.getValuesList().isEmpty());
assertEquals(2, b.getValues().length);
}
@Test
- public void testGetValue() {
+ void testEquals() {
+ final Option option1a = new Option("1", null);
+ final Option option1b = new Option("1", null);
+ final Option option2 = new Option("2", null);
+ assertEquals(option1a, option1a);
+ assertEquals(option1a, option1b);
+ assertEquals(option1b, option1a);
+ assertNotEquals(option1a, option2);
+ assertNotEquals(option1b, option2);
+ assertNotEquals(option2, option1a);
+ assertNotEquals(option2, "");
+ }
+
+ @Test
+ void testGetValue() {
final Option option = new Option("f", null);
option.setArgs(Option.UNLIMITED_VALUES);
assertEquals("default", option.getValue("default"));
assertNull(option.getValue(0));
- option.addValueForProcessing("foo");
+ option.processValue("foo");
assertEquals("foo", option.getValue());
assertEquals("foo", option.getValue(0));
@@ -176,7 +265,7 @@ public void testGetValue() {
}
@Test
- public void testHasArgName() {
+ void testHasArgName() {
final Option option = new Option("f", null);
option.setArgName(null);
@@ -190,7 +279,7 @@ public void testHasArgName() {
}
@Test
- public void testHasArgs() {
+ void testHasArgs() {
final Option option = new Option("f", null);
option.setArgs(0);
@@ -210,18 +299,62 @@ public void testHasArgs() {
}
@Test
- public void testHashCode() {
- assertNotEquals(Option.builder("test").build().hashCode(), Option.builder("test2").build().hashCode());
- assertNotEquals(Option.builder("test").build().hashCode(), Option.builder().longOpt("test").build().hashCode());
- assertNotEquals(Option.builder("test").build().hashCode(), Option.builder("test").longOpt("long test").build().hashCode());
+ void testHashCode() {
+ assertNotEquals(Option.builder("test").get().hashCode(), Option.builder("test2").get().hashCode());
+ assertNotEquals(Option.builder("test").get().hashCode(), Option.builder().longOpt("test").get().hashCode());
+ assertNotEquals(Option.builder("test").get().hashCode(), Option.builder("test").longOpt("long test").get().hashCode());
+ }
+
+ @Test
+ public void testProcessValue() {
+ final Option option = new Option("D", true, "Define property");
+ option.setValueSeparator('=');
+ final NullPointerException exception = assertThrows(NullPointerException.class, () -> option.processValue(null));
+ assertTrue(exception.getMessage().contains("value"));
+ }
+
+ @Test
+ void testSerialization() throws IOException, ClassNotFoundException {
+ final Option option = Option.builder("o").type(TypeHandlerTest.Instantiable.class).get();
+ assertEquals(Converter.DEFAULT, option.getConverter());
+ Option roundtrip = roundTrip(option);
+ assertEquals(Converter.DEFAULT, roundtrip.getConverter());
+ // verify unregistered class converters and verifiers get reset to default.
+ // converters are NOT Serializable, use a serialization proxy if you want that.
+ option.setConverter(Converter.DATE);
+ roundtrip = roundTrip(option);
+ assertEquals(Converter.DEFAULT, roundtrip.getConverter());
+ // verify registered class converters and verifiers do not get reset to default.
+ // converters are NOT Serializable, use a serialization proxy if you want that.
+ // verify earlier values still set.
+ assertEquals(Converter.DATE, option.getConverter());
+ roundtrip = roundTrip(option);
+ assertEquals(Converter.DEFAULT, roundtrip.getConverter());
}
@Test
- public void testSubclass() {
+ void testSubclass() {
final Option option = new DefaultOption("f", "file", "myfile.txt");
final Option clone = (Option) option.clone();
assertEquals("myfile.txt", clone.getValue());
assertEquals(DefaultOption.class, clone.getClass());
}
+ @Test
+ void testTypeClass() {
+ final Option option = new Option("f", null);
+ assertEquals(String.class, option.getType());
+ option.setType(CharSequence.class);
+ assertEquals(CharSequence.class, option.getType());
+ }
+
+ @Test
+ void testTypeObject() {
+ final Option option = new Option("f", null);
+ assertEquals(String.class, option.getType());
+ @SuppressWarnings("cast")
+ final Object type = CharSequence.class; // Do NOT remove cast
+ option.setType(type);
+ assertEquals(CharSequence.class, option.getType());
+ }
}
diff --git a/src/test/java/org/apache/commons/cli/OptionValidatorTest.java b/src/test/java/org/apache/commons/cli/OptionValidatorTest.java
new file mode 100644
index 000000000..6ecbdb856
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/OptionValidatorTest.java
@@ -0,0 +1,183 @@
+/*
+ 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
+
+ https://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 static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class OptionValidatorTest {
+
+ /*
+ * Exemplars of various types of characters
+ */
+
+ private static final String LETTERS = "a\u00D1"; // a and Ñ
+
+ // '\u0660' through '\u0669', Arabic-Indic digits, '\u06F0' through '\u06F9',
+ // Extended Arabic-Indic digits
+ // '\u0966' through '\u096F', Devanagari digits, '\uFF10' through '\uFF19',
+ // Fullwidth digits
+ private static final String DIGITS = "1\u0661\u06f2\u0968\uFF14";
+
+ private static final String CURRENCY = "€$";
+
+ // this is the complete puncutation set do not modify it as Character.isJavaIdentifierPart filters
+ // the good and bad ones out in the setup.
+ private static final String PUNCTUATION = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
+
+ private static final String COMBINING_MARK = "\u0303";
+
+ private static final String NON_SPACING_MARK = "\u0CBF";
+
+ private static final String IDENTIFIER_IGNORABLE = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008";
+
+ private static String acceptablePunctuation;
+
+ private static String notAcceptablePunctuation;
+
+ private static String additionalOptonChars;
+ private static String additionalLongChars;
+
+ private static String firstChars;
+ private static String notFirstChars;
+
+ private static String restChars;
+ private static String notRestChars;
+
+ private static Stream optionParameters() {
+
+ final List args = new ArrayList<>();
+
+ args.add(Arguments.of("CamelCase", true, "Camel case error"));
+ args.add(Arguments.of("Snake_case", true, "Snake case error"));
+ args.add(Arguments.of("_leadingUnderscore", true, "Leading underscore error"));
+ args.add(Arguments.of("kabob-case", true, "Kabob case error"));
+ args.add(Arguments.of("-leadingDash", false, "Leading dash error"));
+ args.add(Arguments.of("lowercase", true, "Lower case error"));
+ args.add(Arguments.of("UPPERCASE", true, "Upper case error"));
+
+ // build passing test cases
+ for (final char c : firstChars.toCharArray()) {
+ final String s = String.format("%sMoreText", c);
+ args.add(Arguments.of(s, true, String.format("testing: First character '%s'", c)));
+ }
+
+ for (final char c : restChars.toCharArray()) {
+ final String s = String.format("Some%sText", c);
+ args.add(Arguments.of(s, true, String.format("testing: Middle character '%s'", c)));
+ }
+
+ // build failing test cases
+ for (final char c : notFirstChars.toCharArray()) {
+ final String s = String.format("%sMoreText", c);
+ args.add(Arguments.of(s, false, String.format("testing: Bad first character '%s'", c)));
+ }
+
+ for (final char c : notRestChars.toCharArray()) {
+ final String s = String.format("Some%sText", c);
+ args.add(Arguments.of(s, false, String.format("testing: Bad middle character '%s'", c)));
+ }
+
+ return args.stream();
+ }
+
+ @BeforeAll
+ public static void setup() {
+ StringBuilder sb = new StringBuilder();
+ final StringBuilder sb2 = new StringBuilder();
+ int idx;
+
+ for (final char c : PUNCTUATION.toCharArray()) {
+ if (Character.isJavaIdentifierPart(c)) {
+ sb.append(c);
+ } else {
+ sb2.append(c);
+ }
+ }
+ acceptablePunctuation = sb.toString();
+ notAcceptablePunctuation = sb2.toString();
+
+ sb = new StringBuilder();
+ for (final char c : OptionValidator.ADDITIONAL_LONG_CHARS) {
+ sb.append(c);
+ }
+ additionalLongChars = sb.toString();
+
+ sb = new StringBuilder();
+ for (final char c : OptionValidator.ADDITIONAL_OPTION_CHARS) {
+ sb.append(c);
+ }
+ additionalOptonChars = sb.toString();
+
+ final String javaIdentifierPart = LETTERS + DIGITS + CURRENCY + acceptablePunctuation + COMBINING_MARK
+ + NON_SPACING_MARK + IDENTIFIER_IGNORABLE;
+
+ firstChars = additionalOptonChars + javaIdentifierPart;
+
+ sb = new StringBuilder(notAcceptablePunctuation).append(additionalLongChars);
+ for (final char c : OptionValidator.ADDITIONAL_OPTION_CHARS) {
+ while ((idx = sb.indexOf(Character.toString(c))) > -1) {
+ sb.deleteCharAt(idx);
+ }
+ }
+ notFirstChars = sb.toString();
+
+ restChars = additionalLongChars + javaIdentifierPart;
+ sb = new StringBuilder(notAcceptablePunctuation).append(additionalOptonChars);
+ for (final char c : OptionValidator.ADDITIONAL_LONG_CHARS) {
+ while ((idx = sb.indexOf(Character.toString(c))) > -1) {
+ sb.deleteCharAt(idx);
+ }
+ }
+ notRestChars = sb.toString();
+
+ }
+
+ @Test
+ void testExclusivity() {
+ /* since we modify acceptable chars by add and removing ADDITIONAL* chars we must verify that they do not exist in the
+ * base javaIdentiferPart that is used in OptionValidator to validate basic characters */
+ for (final char c : OptionValidator.ADDITIONAL_LONG_CHARS) {
+ assertFalse(Character.isJavaIdentifierPart(c), () -> String.format("'%s' should not be in 'ADDITIONAL_LONG_CHARS", c));
+ }
+ for (final char c : OptionValidator.ADDITIONAL_OPTION_CHARS) {
+ assertFalse(Character.isJavaIdentifierPart(c), () -> String.format("'%s' should not be in 'ADDITIONAL_OPTION_CHARS", c));
+ }
+ }
+
+ @ParameterizedTest(name = "{2}")
+ @MethodSource("optionParameters")
+ void testValidate(final String str, final boolean expected, final String name) {
+ if (expected) {
+ assertEquals(str, OptionValidator.validate(str));
+ } else {
+ assertThrows(IllegalArgumentException.class, () -> OptionValidator.validate(str));
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/OptionsTest.java b/src/test/java/org/apache/commons/cli/OptionsTest.java
index 8452cba08..f0ac95258 100644
--- a/src/test/java/org/apache/commons/cli/OptionsTest.java
+++ b/src/test/java/org/apache/commons/cli/OptionsTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,73 +17,202 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class OptionsTest {
+class OptionsTest {
+
+ private void assertToStrings(final Option option) {
+ // Should never throw.
+ // Should return a String, not null.
+ assertNotNull(option.toString());
+ assertNotNull(option.toDeprecatedString());
+ }
+
+ @Test
+ void testAddConflictingOptions() {
+ final Options options1 = new Options();
+ final OptionGroup optionGroup1 = new OptionGroup();
+ optionGroup1.addOption(Option.builder("a").get());
+ optionGroup1.addOption(Option.builder("b").get());
+ options1.addOptionGroup(optionGroup1);
+ options1.addOption(Option.builder("x").get());
+ options1.addOption(Option.builder("y").get());
+ final Options options2 = new Options();
+ final OptionGroup optionGroup2 = new OptionGroup();
+ optionGroup2.addOption(Option.builder("x").type(Integer.class).get());
+ optionGroup2.addOption(Option.builder("b").type(Integer.class).get());
+ options2.addOptionGroup(optionGroup2);
+ options2.addOption(Option.builder("c").get());
+ assertThrows(IllegalArgumentException.class, () -> options1.addOptions(options2));
+ }
+
+ @Test
+ void testAddNonConflictingOptions() {
+ final Options options1 = new Options();
+ final OptionGroup optionGroup1 = new OptionGroup();
+ optionGroup1.addOption(Option.builder("a").get());
+ optionGroup1.addOption(Option.builder("b").get());
+ options1.addOptionGroup(optionGroup1);
+ options1.addOption(Option.builder("x").get());
+ options1.addOption(Option.builder("y").get());
+
+ final Options options2 = new Options();
+ final OptionGroup group2 = new OptionGroup();
+ group2.addOption(Option.builder("c").type(Integer.class).get());
+ group2.addOption(Option.builder("d").type(Integer.class).get());
+ options2.addOptionGroup(group2);
+ options1.addOption(Option.builder("e").get());
+ options1.addOption(Option.builder("f").get());
+
+ final Options underTest = new Options();
+ underTest.addOptions(options1);
+ underTest.addOptions(options2);
+
+ final List expected = Arrays.asList(optionGroup1, group2);
+ assertTrue(expected.size() == underTest.getOptionGroups().size() && expected.containsAll(underTest.getOptionGroups()));
+ final Set expectOpt = new HashSet<>(options1.getOptions());
+ expectOpt.addAll(options2.getOptions());
+ assertEquals(8, expectOpt.size());
+ assertTrue(expectOpt.size() == underTest.getOptions().size() && expectOpt.containsAll(underTest.getOptions()));
+ }
+
+ @Test
+ void testAddOptions() {
+ final Options options = new Options();
+
+ final OptionGroup optionGroup1 = new OptionGroup();
+ optionGroup1.addOption(Option.builder("a").get());
+ optionGroup1.addOption(Option.builder("b").get());
+
+ options.addOptionGroup(optionGroup1);
+
+ options.addOption(Option.builder("X").get());
+ options.addOption(Option.builder("y").get());
+
+ final Options underTest = new Options();
+ underTest.addOptions(options);
+
+ assertEquals(options.getOptionGroups(), underTest.getOptionGroups());
+ assertArrayEquals(options.getOptions().toArray(), underTest.getOptions().toArray());
+ }
+
@Test
- public void testDuplicateLong() {
- final Options opts = new Options();
- opts.addOption("a", "--a", false, "toggle -a");
- opts.addOption("a", "--a", false, "toggle -a*");
- assertEquals("last one in wins", "toggle -a*", opts.getOption("a").getDescription());
+ void testAddOptions2X() {
+ final Options options = new Options();
+
+ final OptionGroup optionGroup1 = new OptionGroup();
+ optionGroup1.addOption(Option.builder("a").get());
+ optionGroup1.addOption(Option.builder("b").get());
+
+ options.addOptionGroup(optionGroup1);
+
+ options.addOption(Option.builder("X").get());
+ options.addOption(Option.builder("y").get());
+
+ assertThrows(IllegalArgumentException.class, () -> options.addOptions(options));
}
@Test
- public void testDuplicateSimple() {
- final Options opts = new Options();
- opts.addOption("a", false, "toggle -a");
- opts.addOption("a", true, "toggle -a*");
+ void testDeprecated() {
+ final Options options = new Options();
+ options.addOption(Option.builder().option("a").get());
+ options.addOption(Option.builder().option("b").deprecated().get());
+ options.addOption(Option.builder().option("c")
+ .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("2.0").setDescription("Use X.").get()).get());
+ options.addOption(Option.builder().option("d").deprecated().longOpt("longD").hasArgs().get());
+ // toString()
+ assertTrue(options.getOption("a").toString().startsWith("[ Option a"));
+ assertTrue(options.getOption("b").toString().startsWith("[ Option b"));
+ assertTrue(options.getOption("c").toString().startsWith("[ Option c"));
+ // toDeprecatedString()
+ assertFalse(options.getOption("a").toDeprecatedString().startsWith("Option a"));
+ assertEquals("Option 'b': Deprecated", options.getOption("b").toDeprecatedString());
+ assertEquals("Option 'c': Deprecated for removal since 2.0: Use X.", options.getOption("c").toDeprecatedString());
+ assertToStrings(options.getOption("a"));
+ assertToStrings(options.getOption("b"));
+ assertToStrings(options.getOption("c"));
+ assertToStrings(options.getOption("d"));
+ }
- assertEquals("last one in wins", "toggle -a*", opts.getOption("a").getDescription());
+ @Test
+ void testDuplicateLong() {
+ final Options options = new Options();
+ options.addOption("a", "--a", false, "toggle -a");
+ options.addOption("a", "--a", false, "toggle -a*");
+ assertEquals("toggle -a*", options.getOption("a").getDescription(), "last one in wins");
+ assertToStrings(options.getOption("a"));
}
@Test
- public void testGetMatchingOpts() {
+ void testDuplicateSimple() {
final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("version").create());
- options.addOption(OptionBuilder.withLongOpt("verbose").create());
+ options.addOption("a", false, "toggle -a");
+ assertToStrings(options.getOption("a"));
+ options.addOption("a", true, "toggle -a*");
+ assertEquals("toggle -a*", options.getOption("a").getDescription(), "last one in wins");
+ assertToStrings(options.getOption("a"));
+ }
+ @Test
+ void testGetMatchingOpts() {
+ final Options options = new Options();
+ OptionBuilder.withLongOpt("version");
+ options.addOption(OptionBuilder.create());
+ OptionBuilder.withLongOpt("verbose");
+ options.addOption(OptionBuilder.create());
assertTrue(options.getMatchingOptions("foo").isEmpty());
assertEquals(1, options.getMatchingOptions("version").size());
assertEquals(2, options.getMatchingOptions("ver").size());
+ assertToStrings(options.getOption("version"));
+ assertToStrings(options.getOption("verbose"));
}
@Test
- public void testGetOptionsGroups() {
+ void testGetOptionsGroups() {
final Options options = new Options();
- final OptionGroup group1 = new OptionGroup();
- group1.addOption(OptionBuilder.create('a'));
- group1.addOption(OptionBuilder.create('b'));
+ final OptionGroup optionGroup1 = new OptionGroup();
+ optionGroup1.addOption(OptionBuilder.create('a'));
+ optionGroup1.addOption(OptionBuilder.create('b'));
- final OptionGroup group2 = new OptionGroup();
- group2.addOption(OptionBuilder.create('x'));
- group2.addOption(OptionBuilder.create('y'));
+ final OptionGroup optionGroup2 = new OptionGroup();
+ optionGroup2.addOption(OptionBuilder.create('x'));
+ optionGroup2.addOption(OptionBuilder.create('y'));
- options.addOptionGroup(group1);
- options.addOptionGroup(group2);
+ options.addOptionGroup(optionGroup1);
+ options.addOptionGroup(optionGroup2);
assertNotNull(options.getOptionGroups());
assertEquals(2, options.getOptionGroups().size());
}
@Test
- public void testHelpOptions() {
- final Option longOnly1 = OptionBuilder.withLongOpt("long-only1").create();
- final Option longOnly2 = OptionBuilder.withLongOpt("long-only2").create();
+ void testHelpOptions() {
+ OptionBuilder.withLongOpt("long-only1");
+ final Option longOnly1 = OptionBuilder.create();
+ OptionBuilder.withLongOpt("long-only2");
+ final Option longOnly2 = OptionBuilder.create();
final Option shortOnly1 = OptionBuilder.create("1");
final Option shortOnly2 = OptionBuilder.create("2");
- final Option bothA = OptionBuilder.withLongOpt("bothA").create("a");
- final Option bothB = OptionBuilder.withLongOpt("bothB").create("b");
+ OptionBuilder.withLongOpt("bothA");
+ final Option bothA = OptionBuilder.create("a");
+ OptionBuilder.withLongOpt("bothB");
+ final Option bothB = OptionBuilder.create("b");
final Options options = new Options();
options.addOption(longOnly1);
@@ -103,66 +232,71 @@ public void testHelpOptions() {
final Collection helpOptions = options.helpOptions();
- assertTrue("Everything in all should be in help", helpOptions.containsAll(allOptions));
- assertTrue("Everything in help should be in all", allOptions.containsAll(helpOptions));
+ assertTrue(helpOptions.containsAll(allOptions), "Everything in all should be in help");
+ assertTrue(allOptions.containsAll(helpOptions), "Everything in help should be in all");
}
@Test
- public void testLong() {
- final Options opts = new Options();
-
- opts.addOption("a", "--a", false, "toggle -a");
- opts.addOption("b", "--b", true, "set -b");
-
- assertTrue(opts.hasOption("a"));
- assertTrue(opts.hasOption("b"));
+ void testLong() {
+ final Options options = new Options();
+ options.addOption("a", "--a", false, "toggle -a");
+ options.addOption("b", "--b", true, "set -b");
+ assertTrue(options.hasOption("a"));
+ assertTrue(options.hasOption("b"));
}
@Test
- public void testMissingOptionException() throws ParseException {
+ void testMissingOptionException() throws ParseException {
final Options options = new Options();
- options.addOption(OptionBuilder.isRequired().create("f"));
- try {
- new PosixParser().parse(options, new String[0]);
- fail("Expected MissingOptionException to be thrown");
- } catch (final MissingOptionException e) {
- assertEquals("Missing required option: f", e.getMessage());
- }
+ OptionBuilder.isRequired();
+ options.addOption(OptionBuilder.create("f"));
+ final MissingOptionException e = assertThrows(MissingOptionException.class, () -> new PosixParser().parse(options, new String[0]));
+ assertEquals("Missing required option: f", e.getMessage());
}
@Test
- public void testMissingOptionsException() throws ParseException {
+ void testMissingOptionsException() throws ParseException {
final Options options = new Options();
- options.addOption(OptionBuilder.isRequired().create("f"));
- options.addOption(OptionBuilder.isRequired().create("x"));
- try {
- new PosixParser().parse(options, new String[0]);
- fail("Expected MissingOptionException to be thrown");
- } catch (final MissingOptionException e) {
- assertEquals("Missing required options: f, x", e.getMessage());
- }
+ OptionBuilder.isRequired();
+ options.addOption(OptionBuilder.create("f"));
+ OptionBuilder.isRequired();
+ options.addOption(OptionBuilder.create("x"));
+ final MissingOptionException e = assertThrows(MissingOptionException.class, () -> new PosixParser().parse(options, new String[0]));
+ assertEquals("Missing required options: f, x", e.getMessage());
}
@Test
- public void testSimple() {
- final Options opts = new Options();
-
- opts.addOption("a", false, "toggle -a");
- opts.addOption("b", true, "toggle -b");
+ void testRequiredOptionInGroupShouldNotBeInRequiredList() {
+ final String key = "a";
+ final Option option = new Option(key, "along", false, "Option A");
+ option.setRequired(true);
+ final Options options = new Options();
+ options.addOption(option);
+ assertTrue(options.getRequiredOptions().contains(key));
+ final OptionGroup optionGroup = new OptionGroup();
+ optionGroup.addOption(option);
+ options.addOptionGroup(optionGroup);
+ assertFalse(options.getOption(key).isRequired());
+ assertFalse(options.getRequiredOptions().contains(key), "Option in group shouldn't be in required options list.");
+ }
- assertTrue(opts.hasOption("a"));
- assertTrue(opts.hasOption("b"));
+ @Test
+ void testSimple() {
+ final Options options = new Options();
+ options.addOption("a", false, "toggle -a");
+ options.addOption("b", true, "toggle -b");
+ assertTrue(options.hasOption("a"));
+ assertTrue(options.hasOption("b"));
}
@Test
- public void testToString() {
+ void testToString() {
final Options options = new Options();
options.addOption("f", "foo", true, "Foo");
options.addOption("b", "bar", false, "Bar");
-
final String s = options.toString();
- assertNotNull("null string returned", s);
- assertTrue("foo option missing", s.toLowerCase().contains("foo"));
- assertTrue("bar option missing", s.toLowerCase().contains("bar"));
+ assertNotNull(s, "null string returned");
+ assertTrue(s.toLowerCase().contains("foo"), "foo option missing");
+ assertTrue(s.toLowerCase().contains("bar"), "bar option missing");
}
}
diff --git a/src/test/java/org/apache/commons/cli/ParseExceptionTest.java b/src/test/java/org/apache/commons/cli/ParseExceptionTest.java
new file mode 100644
index 000000000..1f7dfed1c
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/ParseExceptionTest.java
@@ -0,0 +1,40 @@
+/*
+ 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
+
+ https://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 static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link ParseException}.
+ */
+class ParseExceptionTest {
+
+ @Test
+ void testConstructor() {
+ assertEquals("a", new ParseException("a").getMessage());
+ final Throwable t = new IOException();
+ assertEquals(t, new ParseException(t).getCause());
+ assertEquals(t, ParseException.wrap(t).getCause());
+ final ParseException pe = new ParseException("A");
+ assertEquals(pe, ParseException.wrap(pe));
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/ParserTestCase.java b/src/test/java/org/apache/commons/cli/ParserTestCase.java
deleted file mode 100644
index 5a6e5a0ee..000000000
--- a/src/test/java/org/apache/commons/cli/ParserTestCase.java
+++ /dev/null
@@ -1,1010 +0,0 @@
-/*
- 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 static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Properties;
-
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Abstract test case testing common parser features.
- */
-public abstract class ParserTestCase {
- protected CommandLineParser parser;
-
- protected Options options;
-
- @SuppressWarnings("deprecation")
- private CommandLine parse(final CommandLineParser parser, final Options opts, final String[] args, final Properties properties) throws ParseException {
- if (parser instanceof Parser) {
- return ((Parser) parser).parse(opts, args, properties);
- }
- if (parser instanceof DefaultParser) {
- return ((DefaultParser) parser).parse(opts, args, properties);
- }
- throw new UnsupportedOperationException("Default options not supported by this parser");
- }
-
- @Before
- public void setUp() {
- //@formatter:off
- options = new Options()
- .addOption("a", "enable-a", false, "turn [a] on or off")
- .addOption("b", "bfile", true, "set the value of [b]")
- .addOption("c", "copt", false, "turn [c] on or off");
- //@formatter:on
- }
-
- @Test
- public void testAmbiguousLongWithoutEqualSingleDash() throws Exception {
- final String[] args = {"-b", "-foobar"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("foo").hasOptionalArg().create('f'));
- options.addOption(OptionBuilder.withLongOpt("bar").hasOptionalArg().create('b'));
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue(cl.hasOption("b"));
- assertTrue(cl.hasOption("f"));
- assertEquals("bar", cl.getOptionValue("foo"));
- }
-
- @Test
- public void testAmbiguousPartialLongOption1() throws Exception {
- final String[] args = {"--ver"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("version").create());
- options.addOption(OptionBuilder.withLongOpt("verbose").create());
-
- boolean caught = false;
-
- try {
- parser.parse(options, args);
- } catch (final AmbiguousOptionException e) {
- caught = true;
- assertEquals("Partial option", "--ver", e.getOption());
- assertNotNull("Matching options null", e.getMatchingOptions());
- assertEquals("Matching options size", 2, e.getMatchingOptions().size());
- }
-
- assertTrue("Confirm MissingArgumentException caught", caught);
- }
-
- @Test
- public void testAmbiguousPartialLongOption2() throws Exception {
- final String[] args = {"-ver"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("version").create());
- options.addOption(OptionBuilder.withLongOpt("verbose").create());
-
- boolean caught = false;
-
- try {
- parser.parse(options, args);
- } catch (final AmbiguousOptionException e) {
- caught = true;
- assertEquals("Partial option", "-ver", e.getOption());
- assertNotNull("Matching options null", e.getMatchingOptions());
- assertEquals("Matching options size", 2, e.getMatchingOptions().size());
- }
-
- assertTrue("Confirm MissingArgumentException caught", caught);
- }
-
- @Test
- public void testAmbiguousPartialLongOption3() throws Exception {
- final String[] args = {"--ver=1"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("version").create());
- options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
-
- boolean caught = false;
-
- try {
- parser.parse(options, args);
- } catch (final AmbiguousOptionException e) {
- caught = true;
- assertEquals("Partial option", "--ver", e.getOption());
- assertNotNull("Matching options null", e.getMatchingOptions());
- assertEquals("Matching options size", 2, e.getMatchingOptions().size());
- }
-
- assertTrue("Confirm MissingArgumentException caught", caught);
- }
-
- @Test
- public void testAmbiguousPartialLongOption4() throws Exception {
- final String[] args = {"-ver=1"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("version").create());
- options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
-
- boolean caught = false;
-
- try {
- parser.parse(options, args);
- } catch (final AmbiguousOptionException e) {
- caught = true;
- assertEquals("Partial option", "-ver", e.getOption());
- assertNotNull("Matching options null", e.getMatchingOptions());
- assertEquals("Matching options size", 2, e.getMatchingOptions().size());
- }
-
- assertTrue("Confirm MissingArgumentException caught", caught);
- }
-
- @Test
- public void testArgumentStartingWithHyphen() throws Exception {
- final String[] args = {"-b", "-foo"};
-
- final CommandLine cl = parser.parse(options, args);
- assertEquals("-foo", cl.getOptionValue("b"));
- }
-
- @Test
- public void testBursting() throws Exception {
- final String[] args = {"-acbtoast", "foo", "bar"};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm -a is set", cl.hasOption("a"));
- assertTrue("Confirm -b is set", cl.hasOption("b"));
- assertTrue("Confirm -c is set", cl.hasOption("c"));
- assertEquals("Confirm arg of -b", "toast", cl.getOptionValue("b"));
- assertEquals("Confirm size of extra args", 2, cl.getArgList().size());
- }
-
- @Test
- public void testDoubleDash1() throws Exception {
- final String[] args = {"--copt", "--", "-b", "toast"};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm -c is set", cl.hasOption("c"));
- assertFalse("Confirm -b is not set", cl.hasOption("b"));
- assertEquals("Confirm 2 extra args: " + cl.getArgList().size(), 2, cl.getArgList().size());
- }
-
- @Test
- public void testDoubleDash2() throws Exception {
- final Options options = new Options();
- options.addOption(OptionBuilder.hasArg().create('n'));
- options.addOption(OptionBuilder.create('m'));
-
- try {
- parser.parse(options, new String[] {"-n", "--", "-m"});
- fail("MissingArgumentException not thrown for option -n");
- } catch (final MissingArgumentException e) {
- assertNotNull("option null", e.getOption());
- assertEquals("n", e.getOption().getOpt());
- }
- }
-
- @Test
- public void testLongOptionQuoteHandling() throws Exception {
- final String[] args = {"--bfile", "\"quoted string\""};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("Confirm --bfile \"arg\" strips quotes", "quoted string", cl.getOptionValue("b"));
- }
-
- @Test
- public void testLongOptionWithEqualsQuoteHandling() throws Exception {
- final String[] args = {"--bfile=\"quoted string\""};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("Confirm --bfile=\"arg\" strips quotes", "quoted string", cl.getOptionValue("b"));
- }
-
- @Test
- public void testLongWithEqualDoubleDash() throws Exception {
- final String[] args = {"--foo=bar"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("bar", cl.getOptionValue("foo"));
- }
-
- @Test
- public void testLongWithEqualSingleDash() throws Exception {
- final String[] args = {"-foo=bar"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("bar", cl.getOptionValue("foo"));
- }
-
- @Test
- public void testLongWithoutEqualDoubleDash() throws Exception {
- final String[] args = {"--foobar"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
-
- final CommandLine cl = parser.parse(options, args, true);
-
- assertFalse(cl.hasOption("foo")); // foo isn't expected to be recognized with a double dash
- }
-
- @Test
- public void testLongWithoutEqualSingleDash() throws Exception {
- final String[] args = {"-foobar"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("bar", cl.getOptionValue("foo"));
- }
-
- @Test
- public void testLongWithUnexpectedArgument1() throws Exception {
- final String[] args = {"--foo=bar"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
-
- try {
- parser.parse(options, args);
- } catch (final UnrecognizedOptionException e) {
- assertEquals("--foo=bar", e.getOption());
- return;
- }
-
- fail("UnrecognizedOptionException not thrown");
- }
-
- @Test
- public void testLongWithUnexpectedArgument2() throws Exception {
- final String[] args = {"-foobar"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
-
- try {
- parser.parse(options, args);
- } catch (final UnrecognizedOptionException e) {
- assertEquals("-foobar", e.getOption());
- return;
- }
-
- fail("UnrecognizedOptionException not thrown");
- }
-
- @Test
- public void testMissingArg() throws Exception {
- final String[] args = {"-b"};
-
- boolean caught = false;
-
- try {
- parser.parse(options, args);
- } catch (final MissingArgumentException e) {
- caught = true;
- assertEquals("option missing an argument", "b", e.getOption().getOpt());
- }
-
- assertTrue("Confirm MissingArgumentException caught", caught);
- }
-
- @Test
- public void testMissingArgWithBursting() throws Exception {
- final String[] args = {"-acb"};
-
- boolean caught = false;
-
- try {
- parser.parse(options, args);
- } catch (final MissingArgumentException e) {
- caught = true;
- assertEquals("option missing an argument", "b", e.getOption().getOpt());
- }
-
- assertTrue("Confirm MissingArgumentException caught", caught);
- }
-
- @Test
- public void testMissingRequiredGroup() throws Exception {
- final OptionGroup group = new OptionGroup();
- group.addOption(OptionBuilder.create("a"));
- group.addOption(OptionBuilder.create("b"));
- group.setRequired(true);
-
- final Options options = new Options();
- options.addOptionGroup(group);
- options.addOption(OptionBuilder.isRequired().create("c"));
-
- try {
- parser.parse(options, new String[] {"-c"});
- fail("MissingOptionException not thrown");
- } catch (final MissingOptionException e) {
- assertEquals(1, e.getMissingOptions().size());
- assertTrue(e.getMissingOptions().get(0) instanceof OptionGroup);
- } catch (final ParseException e) {
- fail("Expected to catch MissingOptionException");
- }
- }
-
- @Test
- public void testMissingRequiredOption() {
- final String[] args = {"-a"};
-
- final Options options = new Options();
- options.addOption("a", "enable-a", false, null);
- options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
-
- try {
- parser.parse(options, args);
- fail("exception should have been thrown");
- } catch (final MissingOptionException e) {
- assertEquals("Incorrect exception message", "Missing required option: b", e.getMessage());
- assertTrue(e.getMissingOptions().contains("b"));
- } catch (final ParseException e) {
- fail("expected to catch MissingOptionException");
- }
- }
-
- @Test
- public void testMissingRequiredOptions() {
- final String[] args = {"-a"};
-
- final Options options = new Options();
- options.addOption("a", "enable-a", false, null);
- options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
- options.addOption(OptionBuilder.withLongOpt("cfile").hasArg().isRequired().create('c'));
-
- try {
- parser.parse(options, args);
- fail("exception should have been thrown");
- } catch (final MissingOptionException e) {
- assertEquals("Incorrect exception message", "Missing required options: b, c", e.getMessage());
- assertTrue(e.getMissingOptions().contains("b"));
- assertTrue(e.getMissingOptions().contains("c"));
- } catch (final ParseException e) {
- fail("expected to catch MissingOptionException");
- }
- }
-
- @Test
- public void testMultiple() throws Exception {
- final String[] args = {"-c", "foobar", "-b", "toast"};
-
- CommandLine cl = parser.parse(options, args, true);
- assertTrue("Confirm -c is set", cl.hasOption("c"));
- assertEquals("Confirm 3 extra args: " + cl.getArgList().size(), 3, cl.getArgList().size());
-
- cl = parser.parse(options, cl.getArgs());
-
- assertFalse("Confirm -c is not set", cl.hasOption("c"));
- assertTrue("Confirm -b is set", cl.hasOption("b"));
- assertEquals("Confirm arg of -b", "toast", cl.getOptionValue("b"));
- assertEquals("Confirm 1 extra arg: " + cl.getArgList().size(), 1, cl.getArgList().size());
- assertEquals("Confirm value of extra arg: " + cl.getArgList().get(0), "foobar", cl.getArgList().get(0));
- }
-
- @Test
- public void testMultipleWithLong() throws Exception {
- final String[] args = {"--copt", "foobar", "--bfile", "toast"};
-
- CommandLine cl = parser.parse(options, args, true);
- assertTrue("Confirm -c is set", cl.hasOption("c"));
- assertEquals("Confirm 3 extra args: " + cl.getArgList().size(), 3, cl.getArgList().size());
-
- cl = parser.parse(options, cl.getArgs());
-
- assertFalse("Confirm -c is not set", cl.hasOption("c"));
- assertTrue("Confirm -b is set", cl.hasOption("b"));
- assertEquals("Confirm arg of -b", "toast", cl.getOptionValue("b"));
- assertEquals("Confirm 1 extra arg: " + cl.getArgList().size(), 1, cl.getArgList().size());
- assertEquals("Confirm value of extra arg: " + cl.getArgList().get(0), "foobar", cl.getArgList().get(0));
- }
-
- @Test
- public void testNegativeArgument() throws Exception {
- final String[] args = {"-b", "-1"};
-
- final CommandLine cl = parser.parse(options, args);
- assertEquals("-1", cl.getOptionValue("b"));
- }
-
- @Test
- public void testNegativeOption() throws Exception {
- final String[] args = {"-b", "-1"};
-
- options.addOption("1", false, null);
-
- final CommandLine cl = parser.parse(options, args);
- assertEquals("-1", cl.getOptionValue("b"));
- }
-
- @Test
- public void testOptionAndRequiredOption() throws Exception {
- final String[] args = {"-a", "-b", "file"};
-
- final Options options = new Options();
- options.addOption("a", "enable-a", false, null);
- options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm -a is set", cl.hasOption("a"));
- assertTrue("Confirm -b is set", cl.hasOption("b"));
- assertEquals("Confirm arg of -b", "file", cl.getOptionValue("b"));
- assertTrue("Confirm NO of extra args", cl.getArgList().isEmpty());
- }
-
- @Test
- public void testOptionGroup() throws Exception {
- final OptionGroup group = new OptionGroup();
- group.addOption(OptionBuilder.create("a"));
- group.addOption(OptionBuilder.create("b"));
-
- final Options options = new Options();
- options.addOptionGroup(group);
-
- parser.parse(options, new String[] {"-b"});
-
- assertEquals("selected option", "b", group.getSelected());
- }
-
- @Test
- public void testOptionGroupLong() throws Exception {
- final OptionGroup group = new OptionGroup();
- group.addOption(OptionBuilder.withLongOpt("foo").create());
- group.addOption(OptionBuilder.withLongOpt("bar").create());
-
- final Options options = new Options();
- options.addOptionGroup(group);
-
- final CommandLine cl = parser.parse(options, new String[] {"--bar"});
-
- assertTrue(cl.hasOption("bar"));
- assertEquals("selected option", "bar", group.getSelected());
- }
-
- @Test
- public void testPartialLongOptionSingleDash() throws Exception {
- final String[] args = {"-ver"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("version").create());
- options.addOption(OptionBuilder.hasArg().create('v'));
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm --version is set", cl.hasOption("version"));
- assertFalse("Confirm -v is not set", cl.hasOption("v"));
- }
-
- @Test
- public void testPropertiesOption1() throws Exception {
- final String[] args = {"-Jsource=1.5", "-J", "target", "1.5", "foo"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withValueSeparator().hasArgs(2).create('J'));
-
- final CommandLine cl = parser.parse(options, args);
-
- final List values = Arrays.asList(cl.getOptionValues("J"));
- assertNotNull("null values", values);
- assertEquals("number of values", 4, values.size());
- assertEquals("value 1", "source", values.get(0));
- assertEquals("value 2", "1.5", values.get(1));
- assertEquals("value 3", "target", values.get(2));
- assertEquals("value 4", "1.5", values.get(3));
-
- final List> argsleft = cl.getArgList();
- assertEquals("Should be 1 arg left", 1, argsleft.size());
- assertEquals("Expecting foo", "foo", argsleft.get(0));
- }
-
- @Test
- public void testPropertiesOption2() throws Exception {
- final String[] args = {"-Dparam1", "-Dparam2=value2", "-D"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withValueSeparator().hasOptionalArgs(2).create('D'));
-
- final CommandLine cl = parser.parse(options, args);
-
- final Properties props = cl.getOptionProperties("D");
- assertNotNull("null properties", props);
- assertEquals("number of properties in " + props, 2, props.size());
- assertEquals("property 1", "true", props.getProperty("param1"));
- assertEquals("property 2", "value2", props.getProperty("param2"));
-
- final List> argsleft = cl.getArgList();
- assertEquals("Should be no arg left", 0, argsleft.size());
- }
-
- @Test
- public void testPropertyOptionFlags() throws Exception {
- final Options opts = new Options();
- opts.addOption("a", false, "toggle -a");
- opts.addOption("c", "c", false, "toggle -c");
- opts.addOption(OptionBuilder.hasOptionalArg().create('e'));
-
- Properties properties = new Properties();
- properties.setProperty("a", "true");
- properties.setProperty("c", "yes");
- properties.setProperty("e", "1");
-
- CommandLine cmd = parse(parser, opts, null, properties);
- assertTrue(cmd.hasOption("a"));
- assertTrue(cmd.hasOption("c"));
- assertTrue(cmd.hasOption("e"));
-
- properties = new Properties();
- properties.setProperty("a", "false");
- properties.setProperty("c", "no");
- properties.setProperty("e", "0");
-
- cmd = parse(parser, opts, null, properties);
- assertFalse(cmd.hasOption("a"));
- assertFalse(cmd.hasOption("c"));
- assertTrue(cmd.hasOption("e")); // this option accepts an argument
-
- properties = new Properties();
- properties.setProperty("a", "TRUE");
- properties.setProperty("c", "nO");
- properties.setProperty("e", "TrUe");
-
- cmd = parse(parser, opts, null, properties);
- assertTrue(cmd.hasOption("a"));
- assertFalse(cmd.hasOption("c"));
- assertTrue(cmd.hasOption("e"));
-
- properties = new Properties();
- properties.setProperty("a", "just a string");
- properties.setProperty("e", "");
-
- cmd = parse(parser, opts, null, properties);
- assertFalse(cmd.hasOption("a"));
- assertFalse(cmd.hasOption("c"));
- assertTrue(cmd.hasOption("e"));
-
- properties = new Properties();
- properties.setProperty("a", "0");
- properties.setProperty("c", "1");
-
- cmd = parse(parser, opts, null, properties);
- assertFalse(cmd.hasOption("a"));
- assertTrue(cmd.hasOption("c"));
- }
-
- @Test
- public void testPropertyOptionGroup() throws Exception {
- final Options opts = new Options();
-
- final OptionGroup group1 = new OptionGroup();
- group1.addOption(new Option("a", null));
- group1.addOption(new Option("b", null));
- opts.addOptionGroup(group1);
-
- final OptionGroup group2 = new OptionGroup();
- group2.addOption(new Option("x", null));
- group2.addOption(new Option("y", null));
- opts.addOptionGroup(group2);
-
- final String[] args = {"-a"};
-
- final Properties properties = new Properties();
- properties.put("b", "true");
- properties.put("x", "true");
-
- final CommandLine cmd = parse(parser, opts, args, properties);
-
- assertTrue(cmd.hasOption("a"));
- assertFalse(cmd.hasOption("b"));
- assertTrue(cmd.hasOption("x"));
- assertFalse(cmd.hasOption("y"));
- }
-
- @Test
- public void testPropertyOptionMultipleValues() throws Exception {
- final Options opts = new Options();
- opts.addOption(OptionBuilder.hasArgs().withValueSeparator(',').create('k'));
-
- final Properties properties = new Properties();
- properties.setProperty("k", "one,two");
-
- final String[] values = {"one", "two"};
-
- final CommandLine cmd = parse(parser, opts, null, properties);
- assertTrue(cmd.hasOption("k"));
- assertArrayEquals(values, cmd.getOptionValues('k'));
- }
-
- @Test
- public void testPropertyOptionRequired() throws Exception {
- final Options opts = new Options();
- opts.addOption(OptionBuilder.isRequired().create("f"));
-
- final Properties properties = new Properties();
- properties.setProperty("f", "true");
-
- final CommandLine cmd = parse(parser, opts, null, properties);
- assertTrue(cmd.hasOption("f"));
- }
-
- @Test
- public void testPropertyOptionSingularValue() throws Exception {
- final Options opts = new Options();
- opts.addOption(OptionBuilder.hasOptionalArgs(2).withLongOpt("hide").create());
-
- final Properties properties = new Properties();
- properties.setProperty("hide", "seek");
-
- final CommandLine cmd = parse(parser, opts, null, properties);
- assertTrue(cmd.hasOption("hide"));
- assertEquals("seek", cmd.getOptionValue("hide"));
- assertFalse(cmd.hasOption("fake"));
- }
-
- @Test
- public void testPropertyOptionUnexpected() throws Exception {
- final Options opts = new Options();
-
- final Properties properties = new Properties();
- properties.setProperty("f", "true");
-
- try {
- parse(parser, opts, null, properties);
- fail("UnrecognizedOptionException expected");
- } catch (final UnrecognizedOptionException e) {
- // expected
- }
- }
-
- @Test
- public void testPropertyOverrideValues() throws Exception {
- final Options opts = new Options();
- opts.addOption(OptionBuilder.hasOptionalArgs(2).create('i'));
- opts.addOption(OptionBuilder.hasOptionalArgs().create('j'));
-
- final String[] args = {"-j", "found", "-i", "ink"};
-
- final Properties properties = new Properties();
- properties.setProperty("j", "seek");
-
- final CommandLine cmd = parse(parser, opts, args, properties);
- assertTrue(cmd.hasOption("j"));
- assertEquals("found", cmd.getOptionValue("j"));
- assertTrue(cmd.hasOption("i"));
- assertEquals("ink", cmd.getOptionValue("i"));
- assertFalse(cmd.hasOption("fake"));
- }
-
- @Test
- public void testReuseOptionsTwice() throws Exception {
- final Options opts = new Options();
- opts.addOption(OptionBuilder.isRequired().create('v'));
-
- // first parsing
- parser.parse(opts, new String[] {"-v"});
-
- try {
- // second parsing, with the same Options instance and an invalid command line
- parser.parse(opts, new String[0]);
- fail("MissingOptionException not thrown");
- } catch (final MissingOptionException e) {
- // expected
- }
- }
-
- @Test
- public void testShortOptionConcatenatedQuoteHandling() throws Exception {
- final String[] args = {"-b\"quoted string\""};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("Confirm -b\"arg\" strips quotes", "quoted string", cl.getOptionValue("b"));
- }
-
- @Test
- public void testShortOptionQuoteHandling() throws Exception {
- final String[] args = {"-b", "\"quoted string\""};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("Confirm -b \"arg\" strips quotes", "quoted string", cl.getOptionValue("b"));
- }
-
- @Test
- public void testShortWithEqual() throws Exception {
- final String[] args = {"-f=bar"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("bar", cl.getOptionValue("foo"));
- }
-
- @Test
- public void testShortWithoutEqual() throws Exception {
- final String[] args = {"-fbar"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
-
- final CommandLine cl = parser.parse(options, args);
-
- assertEquals("bar", cl.getOptionValue("foo"));
- }
-
- @Test
- public void testShortWithUnexpectedArgument() throws Exception {
- final String[] args = {"-f=bar"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
-
- try {
- parser.parse(options, args);
- } catch (final UnrecognizedOptionException e) {
- assertEquals("-f=bar", e.getOption());
- return;
- }
-
- fail("UnrecognizedOptionException not thrown");
- }
-
- @Test
- public void testSimpleLong() throws Exception {
- final String[] args = {"--enable-a", "--bfile", "toast", "foo", "bar"};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm -a is set", cl.hasOption("a"));
- assertTrue("Confirm -b is set", cl.hasOption("b"));
- assertEquals("Confirm arg of -b", "toast", cl.getOptionValue("b"));
- assertEquals("Confirm arg of --bfile", "toast", cl.getOptionValue("bfile"));
- assertEquals("Confirm size of extra args", 2, cl.getArgList().size());
- }
-
- @Test
- public void testSimpleShort() throws Exception {
- final String[] args = {"-a", "-b", "toast", "foo", "bar"};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm -a is set", cl.hasOption("a"));
- assertTrue("Confirm -b is set", cl.hasOption("b"));
- assertEquals("Confirm arg of -b", "toast", cl.getOptionValue("b"));
- assertEquals("Confirm size of extra args", 2, cl.getArgList().size());
- }
-
- @Test
- public void testSingleDash() throws Exception {
- final String[] args = {"--copt", "-b", "-", "-a", "-"};
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm -a is set", cl.hasOption("a"));
- assertTrue("Confirm -b is set", cl.hasOption("b"));
- assertEquals("Confirm arg of -b", "-", cl.getOptionValue("b"));
- assertEquals("Confirm 1 extra arg: " + cl.getArgList().size(), 1, cl.getArgList().size());
- assertEquals("Confirm value of extra arg: " + cl.getArgList().get(0), "-", cl.getArgList().get(0));
- }
-
- @Test
- public void testStopAtExpectedArg() throws Exception {
- final String[] args = {"-b", "foo"};
-
- final CommandLine cl = parser.parse(options, args, true);
-
- assertTrue("Confirm -b is set", cl.hasOption('b'));
- assertEquals("Confirm -b is set", "foo", cl.getOptionValue('b'));
- assertTrue("Confirm no extra args: " + cl.getArgList().size(), cl.getArgList().isEmpty());
- }
-
- @Test
- public void testStopAtNonOptionLong() throws Exception {
- final String[] args = {"--zop==1", "-abtoast", "--b=bar"};
-
- final CommandLine cl = parser.parse(options, args, true);
-
- assertFalse("Confirm -a is not set", cl.hasOption("a"));
- assertFalse("Confirm -b is not set", cl.hasOption("b"));
- assertEquals("Confirm 3 extra args: " + cl.getArgList().size(), 3, cl.getArgList().size());
- }
-
- @Test
- public void testStopAtNonOptionShort() throws Exception {
- final String[] args = {"-z", "-a", "-btoast"};
-
- final CommandLine cl = parser.parse(options, args, true);
- assertFalse("Confirm -a is not set", cl.hasOption("a"));
- assertEquals("Confirm 3 extra args: " + cl.getArgList().size(), 3, cl.getArgList().size());
- }
-
- @Test
- public void testStopAtUnexpectedArg() throws Exception {
- final String[] args = {"-c", "foober", "-b", "toast"};
-
- final CommandLine cl = parser.parse(options, args, true);
- assertTrue("Confirm -c is set", cl.hasOption("c"));
- assertEquals("Confirm 3 extra args: " + cl.getArgList().size(), 3, cl.getArgList().size());
- }
-
- @Test
- public void testStopBursting() throws Exception {
- final String[] args = {"-azc"};
-
- final CommandLine cl = parser.parse(options, args, true);
- assertTrue("Confirm -a is set", cl.hasOption("a"));
- assertFalse("Confirm -c is not set", cl.hasOption("c"));
-
- assertEquals("Confirm 1 extra arg: " + cl.getArgList().size(), 1, cl.getArgList().size());
- assertTrue(cl.getArgList().contains("zc"));
- }
-
- @Test
- public void testStopBursting2() throws Exception {
- final String[] args = {"-c", "foobar", "-btoast"};
-
- CommandLine cl = parser.parse(options, args, true);
- assertTrue("Confirm -c is set", cl.hasOption("c"));
- assertEquals("Confirm 2 extra args: " + cl.getArgList().size(), 2, cl.getArgList().size());
-
- cl = parser.parse(options, cl.getArgs());
-
- assertFalse("Confirm -c is not set", cl.hasOption("c"));
- assertTrue("Confirm -b is set", cl.hasOption("b"));
- assertEquals("Confirm arg of -b", "toast", cl.getOptionValue("b"));
- assertEquals("Confirm 1 extra arg: " + cl.getArgList().size(), 1, cl.getArgList().size());
- assertEquals("Confirm value of extra arg: " + cl.getArgList().get(0), "foobar", cl.getArgList().get(0));
- }
-
- @Test
- public void testUnambiguousPartialLongOption1() throws Exception {
- final String[] args = {"--ver"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("version").create());
- options.addOption(OptionBuilder.withLongOpt("help").create());
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm --version is set", cl.hasOption("version"));
- }
-
- @Test
- public void testUnambiguousPartialLongOption2() throws Exception {
- final String[] args = {"-ver"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("version").create());
- options.addOption(OptionBuilder.withLongOpt("help").create());
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm --version is set", cl.hasOption("version"));
- }
-
- @Test
- public void testUnambiguousPartialLongOption3() throws Exception {
- final String[] args = {"--ver=1"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
- options.addOption(OptionBuilder.withLongOpt("help").create());
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm --verbose is set", cl.hasOption("verbose"));
- assertEquals("1", cl.getOptionValue("verbose"));
- }
-
- @Test
- public void testUnambiguousPartialLongOption4() throws Exception {
- final String[] args = {"-ver=1"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
- options.addOption(OptionBuilder.withLongOpt("help").create());
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm --verbose is set", cl.hasOption("verbose"));
- assertEquals("1", cl.getOptionValue("verbose"));
- }
-
- @Test
- public void testUnlimitedArgs() throws Exception {
- final String[] args = {"-e", "one", "two", "-f", "alpha"};
-
- final Options options = new Options();
- options.addOption(OptionBuilder.hasArgs().create("e"));
- options.addOption(OptionBuilder.hasArgs().create("f"));
-
- final CommandLine cl = parser.parse(options, args);
-
- assertTrue("Confirm -e is set", cl.hasOption("e"));
- assertEquals("number of arg for -e", 2, cl.getOptionValues("e").length);
- assertTrue("Confirm -f is set", cl.hasOption("f"));
- assertEquals("number of arg for -f", 1, cl.getOptionValues("f").length);
- }
-
- @Test
- public void testUnrecognizedOption() throws Exception {
- final String[] args = {"-a", "-d", "-b", "toast", "foo", "bar"};
-
- try {
- parser.parse(options, args);
- fail("UnrecognizedOptionException wasn't thrown");
- } catch (final UnrecognizedOptionException e) {
- assertEquals("-d", e.getOption());
- }
- }
-
- @Test
- public void testUnrecognizedOptionWithBursting() throws Exception {
- final String[] args = {"-adbtoast", "foo", "bar"};
-
- try {
- parser.parse(options, args);
- fail("UnrecognizedOptionException wasn't thrown");
- } catch (final UnrecognizedOptionException e) {
- assertEquals("-adbtoast", e.getOption());
- }
- }
-
- @Test
- public void testWithRequiredOption() throws Exception {
- final String[] args = {"-b", "file"};
-
- final Options options = new Options();
- options.addOption("a", "enable-a", false, null);
- options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
-
- final CommandLine cl = parser.parse(options, args);
-
- assertFalse("Confirm -a is NOT set", cl.hasOption("a"));
- assertTrue("Confirm -b is set", cl.hasOption("b"));
- assertEquals("Confirm arg of -b", "file", cl.getOptionValue("b"));
- assertTrue("Confirm NO of extra args", cl.getArgList().isEmpty());
- }
-
-}
diff --git a/src/test/java/org/apache/commons/cli/PatternOptionBuilderTest.java b/src/test/java/org/apache/commons/cli/PatternOptionBuilderTest.java
index 27c442a20..dd1013eae 100644
--- a/src/test/java/org/apache/commons/cli/PatternOptionBuilderTest.java
+++ b/src/test/java/org/apache/commons/cli/PatternOptionBuilderTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,172 +17,156 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Vector;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
/**
* Test case for the PatternOptionBuilder class.
*/
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class PatternOptionBuilderTest {
+class PatternOptionBuilderTest {
+
@Test
- public void testClassPattern() throws Exception {
+ void testClassPattern() throws Exception {
final Options options = PatternOptionBuilder.parsePattern("c+d+");
final CommandLineParser parser = new PosixParser();
final CommandLine line = parser.parse(options, new String[] {"-c", "java.util.Calendar", "-d", "System.DateTime"});
-
- assertEquals("c value", Calendar.class, line.getOptionObject("c"));
- assertNull("d value", line.getOptionObject("d"));
+ assertEquals(Calendar.class, line.getOptionObject("c"), "c value");
+ assertNull(line.getOptionObject("d"), "d value");
}
@Test
- public void testEmptyPattern() {
+ void testEmptyPattern() {
final Options options = PatternOptionBuilder.parsePattern("");
assertTrue(options.getOptions().isEmpty());
}
@Test
- public void testExistingFilePattern() throws Exception {
+ void testExistingFilePattern() throws Exception {
final Options options = PatternOptionBuilder.parsePattern("g<");
final CommandLineParser parser = new PosixParser();
final CommandLine line = parser.parse(options, new String[] {"-g", "src/test/resources/org/apache/commons/cli/existing-readable.file"});
-
final Object parsedReadableFileStream = line.getOptionObject("g");
-
- assertNotNull("option g not parsed", parsedReadableFileStream);
- assertTrue("option g not FileInputStream", parsedReadableFileStream instanceof FileInputStream);
+ assertNotNull(parsedReadableFileStream, "option g not parsed");
+ assertInstanceOf(FileInputStream.class, parsedReadableFileStream, "option g not FileInputStream");
}
@Test
- public void testExistingFilePatternFileNotExist() throws Exception {
+ void testExistingFilePatternFileNotExist() throws Exception {
final Options options = PatternOptionBuilder.parsePattern("f<");
final CommandLineParser parser = new PosixParser();
final CommandLine line = parser.parse(options, new String[] {"-f", "non-existing.file"});
-
- assertNull("option f parsed", line.getOptionObject("f"));
+ assertNull(line.getOptionObject("f"), "option f parsed");
}
@Test
- public void testNumberPattern() throws Exception {
+ void testNumberPattern() throws Exception {
final Options options = PatternOptionBuilder.parsePattern("n%d%x%");
final CommandLineParser parser = new PosixParser();
+ // 3,5 fails validation.
+ //assertThrows(ParseException.class, () -> parser.parse(options, new String[] {"-n", "1", "-d", "2.1", "-x", "3,5"}));
final CommandLine line = parser.parse(options, new String[] {"-n", "1", "-d", "2.1", "-x", "3,5"});
-
- assertEquals("n object class", Long.class, line.getOptionObject("n").getClass());
- assertEquals("n value", Long.valueOf(1), line.getOptionObject("n"));
-
- assertEquals("d object class", Double.class, line.getOptionObject("d").getClass());
- assertEquals("d value", Double.valueOf(2.1), line.getOptionObject("d"));
-
- assertNull("x object", line.getOptionObject("x"));
+ assertEquals(Long.class, line.getOptionObject("n").getClass(), "n object class");
+ assertEquals(Long.valueOf(1), line.getOptionObject("n"), "n value");
+ assertEquals(Double.class, line.getOptionObject("d").getClass(), "d object class");
+ assertEquals(Double.valueOf(2.1), line.getOptionObject("d"), "d value");
+ assertNull(line.getOptionObject("x"), "x object");
}
@Test
- public void testObjectPattern() throws Exception {
+ void testObjectPattern() throws Exception {
final Options options = PatternOptionBuilder.parsePattern("o@i@n@");
final CommandLineParser parser = new PosixParser();
final CommandLine line = parser.parse(options, new String[] {"-o", "java.lang.String", "-i", "java.util.Calendar", "-n", "System.DateTime"});
-
- assertEquals("o value", "", line.getOptionObject("o"));
- assertNull("i value", line.getOptionObject("i"));
- assertNull("n value", line.getOptionObject("n"));
+ assertEquals("", line.getOptionObject("o"), "o value");
+ assertNull(line.getOptionObject("i"), "i value");
+ assertNull(line.getOptionObject("n"), "n value");
}
@Test
- public void testRequiredOption() throws Exception {
+ void testRequiredOption() throws Exception {
final Options options = PatternOptionBuilder.parsePattern("!n%m%");
final CommandLineParser parser = new PosixParser();
-
- try {
- parser.parse(options, new String[] {""});
- fail("MissingOptionException wasn't thrown");
- } catch (final MissingOptionException e) {
- assertEquals(1, e.getMissingOptions().size());
- assertTrue(e.getMissingOptions().contains("n"));
- }
+ final MissingOptionException e = assertThrows(MissingOptionException.class, () -> parser.parse(options, new String[] { "" }));
+ assertEquals(1, e.getMissingOptions().size());
+ assertTrue(e.getMissingOptions().contains("n"));
}
@Test
- public void testSimplePattern() throws Exception {
+ void testSimplePattern() throws Exception {
+ /*
+ * Dates calculated from strings are dependent upon configuration and environment settings for the
+ * machine on which the test is running. To avoid this problem, convert the time into a string
+ * and then unparse that using the converter. This produces strings that always match the correct
+ * time zone.
+ */
final Options options = PatternOptionBuilder.parsePattern("a:b@cde>f+n%t/m*z#");
+ final Date expectedDate = new Date(1023400137000L);
+ final DateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
final String[] args = {"-c", "-a", "foo", "-b", "java.util.Vector", "-e", "build.xml", "-f", "java.util.Calendar", "-n", "4.5", "-t",
- "https://commons.apache.org", "-z", "Thu Jun 06 17:48:57 EDT 2002", "-m", "test*"};
-
+ "https://commons.apache.org", "-z", dateFormat.format(expectedDate), "-m", "test*"};
final CommandLineParser parser = new PosixParser();
final CommandLine line = parser.parse(options, args);
-
- assertEquals("flag a", "foo", line.getOptionValue("a"));
- assertEquals("string flag a", "foo", line.getOptionObject("a"));
- assertEquals("object flag b", new Vector<>(), line.getOptionObject("b"));
- assertTrue("boolean true flag c", line.hasOption("c"));
- assertFalse("boolean false flag d", line.hasOption("d"));
- assertEquals("file flag e", new File("build.xml"), line.getOptionObject("e"));
- assertEquals("class flag f", Calendar.class, line.getOptionObject("f"));
- assertEquals("number flag n", Double.valueOf(4.5), line.getOptionObject("n"));
- assertEquals("url flag t", new URL("https://commons.apache.org"), line.getOptionObject("t"));
-
+ assertEquals("foo", line.getOptionValue("a"), "flag a");
+ assertEquals("foo", line.getOptionObject("a"), "string flag a");
+ assertEquals(new Vector<>(), line.getOptionObject("b"), "object flag b");
+ assertTrue(line.hasOption("c"), "boolean true flag c");
+ assertFalse(line.hasOption("d"), "boolean false flag d");
+ assertEquals(new File("build.xml"), line.getOptionObject("e"), "file flag e");
+ assertEquals(Calendar.class, line.getOptionObject("f"), "class flag f");
+ assertEquals(Double.valueOf(4.5), line.getOptionObject("n"), "number flag n");
+ assertEquals(new URL("https://commons.apache.org"), line.getOptionObject("t"), "url flag t");
// tests the char methods of CommandLine that delegate to the String methods
- assertEquals("flag a", "foo", line.getOptionValue('a'));
- assertEquals("string flag a", "foo", line.getOptionObject('a'));
- assertEquals("object flag b", new Vector<>(), line.getOptionObject('b'));
- assertTrue("boolean true flag c", line.hasOption('c'));
- assertFalse("boolean false flag d", line.hasOption('d'));
- assertEquals("file flag e", new File("build.xml"), line.getOptionObject('e'));
- assertEquals("class flag f", Calendar.class, line.getOptionObject('f'));
- assertEquals("number flag n", Double.valueOf(4.5), line.getOptionObject('n'));
- assertEquals("url flag t", new URL("https://commons.apache.org"), line.getOptionObject('t'));
-
+ assertEquals("foo", line.getOptionValue('a'), "flag a");
+ assertEquals("foo", line.getOptionObject('a'), "string flag a");
+ assertEquals(new Vector<>(), line.getOptionObject('b'), "object flag b");
+ assertTrue(line.hasOption('c'), "boolean true flag c");
+ assertFalse(line.hasOption('d'), "boolean false flag d");
+ assertEquals(new File("build.xml"), line.getOptionObject('e'), "file flag e");
+ assertEquals(Calendar.class, line.getOptionObject('f'), "class flag f");
+ assertEquals(Double.valueOf(4.5), line.getOptionObject('n'), "number flag n");
+ assertEquals(new URL("https://commons.apache.org"), line.getOptionObject('t'), "url flag t");
// FILES NOT SUPPORTED YET
- try {
- assertEquals("files flag m", new File[0], line.getOptionObject('m'));
- fail("Multiple files are not supported yet, should have failed");
- } catch (final UnsupportedOperationException uoe) {
- // expected
- }
-
- // DATES NOT SUPPORTED YET
- try {
- assertEquals("date flag z", new Date(1023400137276L), line.getOptionObject('z'));
- fail("Date is not supported yet, should have failed");
- } catch (final UnsupportedOperationException uoe) {
- // expected
- }
+ assertThrows(UnsupportedOperationException.class, () -> line.getOptionObject('m'));
+ assertEquals(expectedDate, line.getOptionObject('z'), "date flag z");
+
}
@Test
- public void testUntypedPattern() throws Exception {
+ void testUntypedPattern() throws Exception {
final Options options = PatternOptionBuilder.parsePattern("abc");
final CommandLineParser parser = new PosixParser();
final CommandLine line = parser.parse(options, new String[] {"-abc"});
-
assertTrue(line.hasOption('a'));
- assertNull("value a", line.getOptionObject('a'));
+ assertNull(line.getOptionObject('a'), "value a");
assertTrue(line.hasOption('b'));
- assertNull("value b", line.getOptionObject('b'));
+ assertNull(line.getOptionObject('b'), "value b");
assertTrue(line.hasOption('c'));
- assertNull("value c", line.getOptionObject('c'));
+ assertNull(line.getOptionObject('c'), "value c");
}
@Test
- public void testURLPattern() throws Exception {
+ void testURLPattern() throws Exception {
final Options options = PatternOptionBuilder.parsePattern("u/v/");
final CommandLineParser parser = new PosixParser();
final CommandLine line = parser.parse(options, new String[] {"-u", "https://commons.apache.org", "-v", "foo://commons.apache.org"});
-
- assertEquals("u value", new URL("https://commons.apache.org"), line.getOptionObject("u"));
- assertNull("v value", line.getOptionObject("v"));
+ assertEquals(new URL("https://commons.apache.org"), line.getOptionObject("u"), "u value");
+ assertNull(line.getOptionObject("v"), "v value");
}
}
diff --git a/src/test/java/org/apache/commons/cli/PosixParserTest.java b/src/test/java/org/apache/commons/cli/PosixParserTest.java
index 5efb938e1..4ce063e43 100644
--- a/src/test/java/org/apache/commons/cli/PosixParserTest.java
+++ b/src/test/java/org/apache/commons/cli/PosixParserTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,17 +17,19 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
/**
* Test case for the PosixParser.
+ *
+ * TODO Needs a rework using JUnit parameterized tests.
*/
-public class PosixParserTest extends ParserTestCase {
+class PosixParserTest extends AbstractParserTestCase {
@Override
@SuppressWarnings("deprecation")
- @Before
+ @BeforeEach
public void setUp() {
super.setUp();
parser = new PosixParser();
@@ -35,55 +37,61 @@ public void setUp() {
@Override
@Test
- @Ignore("not supported by the PosixParser")
- public void testAmbiguousLongWithoutEqualSingleDash() throws Exception {
+ @Disabled("not supported by the PosixParser")
+ void testAmbiguousLongWithoutEqualSingleDash() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the PosixParser")
- public void testAmbiguousPartialLongOption4() throws Exception {
+ @Disabled("not supported by the PosixParser")
+ void testAmbiguousLongWithoutEqualSingleDash2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the PosixParser")
- public void testDoubleDash2() throws Exception {
+ @Disabled("not supported by the PosixParser")
+ void testAmbiguousPartialLongOption4() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the PosixParser")
- public void testLongWithEqualSingleDash() throws Exception {
+ @Disabled("not supported by the PosixParser")
+ void testDoubleDash2() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the PosixParser")
- public void testLongWithoutEqualSingleDash() throws Exception {
+ @Disabled("not supported by the PosixParser")
+ void testLongWithEqualSingleDash() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the PosixParser")
- public void testLongWithUnexpectedArgument1() throws Exception {
+ @Disabled("not supported by the PosixParser")
+ void testLongWithoutEqualSingleDash() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the PosixParser (CLI-184)")
- public void testNegativeOption() throws Exception {
+ @Disabled("not supported by the PosixParser")
+ void testLongWithUnexpectedArgument1() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the PosixParser")
- public void testShortWithEqual() throws Exception {
+ @Disabled("not supported by the PosixParser (CLI-184)")
+ void testNegativeOption() throws Exception {
}
@Override
@Test
- @Ignore("not supported by the PosixParser")
- public void testUnambiguousPartialLongOption4() throws Exception {
+ @Disabled("not supported by the PosixParser")
+ void testShortWithEqual() throws Exception {
+ }
+
+ @Override
+ @Test
+ @Disabled("not supported by the PosixParser")
+ void testUnambiguousPartialLongOption4() throws Exception {
}
}
diff --git a/src/test/java/org/apache/commons/cli/SolrCliTest.java b/src/test/java/org/apache/commons/cli/SolrCliTest.java
new file mode 100644
index 000000000..ab5f88718
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/SolrCliTest.java
@@ -0,0 +1,155 @@
+/*
+ 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
+
+ https://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 static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.Locale;
+
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test fixtures used in SOLR tests.
+ */
+class SolrCliTest {
+
+ public static final String ZK_HOST = "localhost:9983";
+
+ public static final String DEFAULT_CONFIG_SET = "_default";
+
+ public static final Option OPTION_ZKHOST_DEPRECATED =
+ // @formatter:off
+ Option.builder("zkHost")
+ .longOpt("zkHost")
+ .deprecated(
+ DeprecatedAttributes.builder()
+ .setForRemoval(true)
+ .setSince("9.6")
+ .setDescription("Use --zk-host instead")
+ .get())
+ .argName("HOST")
+ .hasArg()
+ .required(false)
+ .desc("Zookeeper connection string; unnecessary if ZK_HOST is defined in solr.in.sh; otherwise, defaults to "
+ + ZK_HOST
+ + '.').get();
+ // @formatter:on
+
+ public static final Option OPTION_ZKHOST =
+ // @formatter:off
+ Option.builder("z")
+ .longOpt("zk-host")
+ .argName("HOST")
+ .hasArg()
+ .required(false)
+ .desc("Zookeeper connection string; unnecessary if ZK_HOST is defined in solr.in.sh; otherwise, defaults to "
+ + ZK_HOST
+ + '.').get();
+ // @formatter:on
+
+ public static final Option OPTION_SOLRURL_DEPRECATED =
+ // @formatter:off
+ Option.builder("solrUrl")
+ .longOpt("solrUrl")
+ .deprecated(
+ DeprecatedAttributes.builder()
+ .setForRemoval(true)
+ .setSince("9.6")
+ .setDescription("Use --solr-url instead")
+ .get())
+ .argName("HOST")
+ .hasArg()
+ .required(false)
+ .desc("Base Solr URL, which can be used to determine the zk-host if that's not known; defaults to: "
+ + getDefaultSolrUrl()
+ + '.').get();
+ // @formatter:on
+
+ public static final Option OPTION_SOLRURL =
+ // @formatter:off
+ Option.builder("url")
+ .longOpt("solr-url")
+ .argName("HOST")
+ .hasArg()
+ .required(false)
+ .desc("Base Solr URL, which can be used to determine the zk-host if that's not known; defaults to: "
+ + getDefaultSolrUrl()
+ + '.').get();
+ // @formatter:on
+
+ public static final Option OPTION_VERBOSE =
+ // @formatter:off
+ Option.builder("v")
+ .longOpt("verbose")
+ .argName("verbose")
+ .required(false)
+ .desc("Enable more verbose command output.").get();
+ // @formatter:on
+
+ public static final Option OPTION_HELP =
+ // @formatter:off
+ Option.builder("h")
+ .longOpt("help")
+ .required(false)
+ .desc("Print this message.").get();
+ // @formatter:on
+
+ public static final Option OPTION_RECURSE =
+ // @formatter:off
+ Option.builder("r")
+ .longOpt("recurse")
+ .argName("recurse")
+ .hasArg()
+ .required(false)
+ .desc("Recurse (true|false), default is false.").get();
+ // @formatter:on
+
+ public static final Option OPTION_CREDENTIALS =
+ // @formatter:off
+ Option.builder("u")
+ .longOpt("credentials")
+ .argName("credentials")
+ .hasArg()
+ .required(false)
+ .desc("Credentials in the format username:password. Example: --credentials solr:SolrRocks").get();
+ // @formatter:on
+
+ private static String getDefaultSolrUrl() {
+ final String scheme = "http";
+ final String host = "localhost";
+ final String port = "8983";
+ return String.format(Locale.ROOT, "%s://%s:%s", StringUtils.toRootLowerCase(scheme), host, port);
+ }
+
+ @Test
+ void testOptions() {
+ // sanity checks
+ assertNotNull(DEFAULT_CONFIG_SET);
+ assertNotNull(OPTION_CREDENTIALS);
+ assertNotNull(OPTION_HELP);
+ assertNotNull(OPTION_RECURSE);
+ assertNotNull(OPTION_SOLRURL);
+ assertNotNull(OPTION_SOLRURL_DEPRECATED);
+ assertNotNull(OPTION_VERBOSE);
+ assertNotNull(OPTION_ZKHOST);
+ assertNotNull(OPTION_ZKHOST_DEPRECATED);
+ assertNotNull(ZK_HOST);
+ assertNotNull(getDefaultSolrUrl());
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/SolrCreateToolTest.java b/src/test/java/org/apache/commons/cli/SolrCreateToolTest.java
new file mode 100644
index 000000000..a80c39c44
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/SolrCreateToolTest.java
@@ -0,0 +1,106 @@
+/*
+ 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
+
+ https://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 static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+class SolrCreateToolTest {
+
+ private List getOptions() {
+ // @formatter:off
+ return Arrays.asList(
+ SolrCliTest.OPTION_ZKHOST,
+ SolrCliTest.OPTION_SOLRURL,
+ SolrCliTest.OPTION_ZKHOST_DEPRECATED,
+ SolrCliTest.OPTION_SOLRURL,
+ Option.builder("c")
+ .longOpt("name")
+ .argName("NAME")
+ .hasArg()
+ .required(true)
+ .desc("Name of collection or core to create.")
+ .get(),
+ Option.builder("s")
+ .longOpt("shards")
+ .argName("#")
+ .hasArg()
+ .required(false)
+ .desc("Number of shards; default is 1.")
+ .get(),
+ Option.builder("rf")
+ .longOpt("replication-factor")
+ .argName("#")
+ .hasArg()
+ .required(false)
+ .desc("Number of copies of each document across the collection (replicas per shard); default is 1.")
+ .get(),
+ Option.builder("d")
+ .longOpt("confdir")
+ .argName("NAME")
+ .hasArg()
+ .required(false)
+ .desc("Configuration directory to copy when creating the new collection; default is "
+ + SolrCliTest.DEFAULT_CONFIG_SET
+ + '.')
+ .get(),
+ Option.builder("n")
+ .longOpt("confname")
+ .argName("NAME")
+ .hasArg()
+ .required(false)
+ .desc("Configuration name; default is the collection name.")
+ .get(),
+ SolrCliTest.OPTION_CREDENTIALS);
+ // @formatter:on
+ }
+
+ private String printHelp(final HelpFormatter formatter) {
+ final Options options = new Options();
+ getOptions().forEach(options::addOption);
+ final String cmdLineSyntax = getClass().getName();
+ final StringWriter out = new StringWriter();
+ final PrintWriter pw = new PrintWriter(out);
+ formatter.printHelp(pw, formatter.getWidth(), cmdLineSyntax, null, options, formatter.getLeftPadding(), formatter.getDescPadding(), null, false);
+ pw.flush();
+ final String actual = out.toString();
+ assertTrue(actual.contains("-z,--zk-host Zookeeper connection string; unnecessary"));
+ return actual;
+ }
+
+ @Test
+ void testHelpFormatter() {
+ final HelpFormatter formatter = new HelpFormatter();
+ final String actual = printHelp(formatter);
+ assertFalse(actual.contains("Deprecated"));
+ }
+
+ @Test
+ void testHelpFormatterDeprecated() {
+ final HelpFormatter formatter = HelpFormatter.builder().setShowDeprecated(true).get();
+ final String actual = printHelp(formatter);
+ assertTrue(actual.contains("-zkHost,--zkHost [Deprecated] Zookeeper connection"));
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/TypeHandlerTest.java b/src/test/java/org/apache/commons/cli/TypeHandlerTest.java
index 03fd8711a..987450734 100644
--- a/src/test/java/org/apache/commons/cli/TypeHandlerTest.java
+++ b/src/test/java/org/apache/commons/cli/TypeHandlerTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,116 +17,248 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
import java.io.FileInputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.MalformedURLException;
import java.net.URL;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
-import org.junit.Test;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junitpioneer.jupiter.DefaultLocale;
-public class TypeHandlerTest {
+class TypeHandlerTest {
+ /** Used for Class and Object creation tests. */
public static class Instantiable {
+
+ @Override
+ public boolean equals(final Object arg0) {
+ return arg0 instanceof Instantiable;
+ }
+
+ @Override
+ public int hashCode() {
+ return 1;
+ }
}
+ /** Used for Class and Object negative creation tests */
public static final class NotInstantiable {
private NotInstantiable() {
}
- }
- @Test
- public void testCreateValueClass() throws Exception {
- final Object clazz = TypeHandler.createValue(Instantiable.class.getName(), PatternOptionBuilder.CLASS_VALUE);
- assertEquals(Instantiable.class, clazz);
}
- @Test(expected = ParseException.class)
- public void testCreateValueClass_notFound() throws Exception {
- TypeHandler.createValue("what ever", PatternOptionBuilder.CLASS_VALUE);
- }
+ /** Always returns the same Path. */
+ private static final Converter PATH_CONVERTER = s -> Paths.get("foo");
- @Test(expected = UnsupportedOperationException.class)
- public void testCreateValueDate() throws Exception {
- TypeHandler.createValue("what ever", PatternOptionBuilder.DATE_VALUE);
- }
+ private static Stream createDateFixtures() {
+ return Stream.of(Date.from(Instant.EPOCH), Date.from(Instant.ofEpochSecond(0)), Date.from(Instant.ofEpochSecond(40_000)));
- @Test
- public void testCreateValueExistingFile() throws Exception {
- try (FileInputStream result = TypeHandler.createValue("src/test/resources/org/apache/commons/cli/existing-readable.file",
- PatternOptionBuilder.EXISTING_FILE_VALUE)) {
- assertNotNull(result);
- }
}
- @Test(expected = ParseException.class)
- public void testCreateValueExistingFile_nonExistingFile() throws Exception {
- TypeHandler.createValue("non-existing.file", PatternOptionBuilder.EXISTING_FILE_VALUE);
+ private static Stream createValueTestParameters() throws MalformedURLException {
+ // force the PatternOptionBuilder to load / modify the TypeHandler table.
+ @SuppressWarnings("unused")
+ final Class> loadStatic = PatternOptionBuilder.FILES_VALUE;
+ // reset the type handler table.
+ // TypeHandler.resetConverters();
+ final List list = new ArrayList<>();
+
+ /*
+ * Dates calculated from strings are dependent upon configuration and environment settings for the machine on which the test is running. To avoid this
+ * problem, convert the time into a string and then unparse that using the converter. This produces strings that always match the correct time zone.
+ */
+ final Date date = new Date(1023400137000L);
+ final DateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
+
+ list.add(Arguments.of(Instantiable.class.getName(), PatternOptionBuilder.CLASS_VALUE, Instantiable.class));
+ list.add(Arguments.of("what ever", PatternOptionBuilder.CLASS_VALUE, ParseException.class));
+
+ list.add(Arguments.of("what ever", PatternOptionBuilder.DATE_VALUE, ParseException.class));
+ list.add(Arguments.of(dateFormat.format(date), PatternOptionBuilder.DATE_VALUE, date));
+ list.add(Arguments.of("Jun 06 17:48:57 EDT 2002", PatternOptionBuilder.DATE_VALUE, ParseException.class));
+
+ list.add(Arguments.of("non-existing.file", PatternOptionBuilder.EXISTING_FILE_VALUE, ParseException.class));
+
+ list.add(Arguments.of("some-file.txt", PatternOptionBuilder.FILE_VALUE, new File("some-file.txt")));
+
+ list.add(Arguments.of("some-path.txt", Path.class, new File("some-path.txt").toPath()));
+
+ // the PatternOptionBuilder.FILES_VALUE is not registered so it should just return the string
+ list.add(Arguments.of("some.files", PatternOptionBuilder.FILES_VALUE, "some.files"));
+
+ list.add(Arguments.of("just-a-string", Integer.class, ParseException.class));
+ list.add(Arguments.of("5", Integer.class, 5));
+ list.add(Arguments.of("5.5", Integer.class, ParseException.class));
+ list.add(Arguments.of(Long.toString(Long.MAX_VALUE), Integer.class, ParseException.class));
+
+ list.add(Arguments.of("just-a-string", Long.class, ParseException.class));
+ list.add(Arguments.of("5", Long.class, 5L));
+ list.add(Arguments.of("5.5", Long.class, ParseException.class));
+
+ list.add(Arguments.of("just-a-string", Short.class, ParseException.class));
+ list.add(Arguments.of("5", Short.class, (short) 5));
+ list.add(Arguments.of("5.5", Short.class, ParseException.class));
+ list.add(Arguments.of(Integer.toString(Integer.MAX_VALUE), Short.class, ParseException.class));
+
+ list.add(Arguments.of("just-a-string", Byte.class, ParseException.class));
+ list.add(Arguments.of("5", Byte.class, (byte) 5));
+ list.add(Arguments.of("5.5", Byte.class, ParseException.class));
+ list.add(Arguments.of(Short.toString(Short.MAX_VALUE), Byte.class, ParseException.class));
+
+ list.add(Arguments.of("just-a-string", Character.class, 'j'));
+ list.add(Arguments.of("5", Character.class, '5'));
+ list.add(Arguments.of("5.5", Character.class, '5'));
+ list.add(Arguments.of("\\u0124", Character.class, Character.toChars(0x0124)[0]));
+
+ list.add(Arguments.of("just-a-string", Double.class, ParseException.class));
+ list.add(Arguments.of("5", Double.class, 5d));
+ list.add(Arguments.of("5.5", Double.class, 5.5));
+
+ list.add(Arguments.of("just-a-string", Float.class, ParseException.class));
+ list.add(Arguments.of("5", Float.class, 5f));
+ list.add(Arguments.of("5.5", Float.class, 5.5f));
+ list.add(Arguments.of(Double.toString(Double.MAX_VALUE), Float.class, Float.POSITIVE_INFINITY));
+
+ list.add(Arguments.of("just-a-string", BigInteger.class, ParseException.class));
+ list.add(Arguments.of("5", BigInteger.class, new BigInteger("5")));
+ list.add(Arguments.of("5.5", BigInteger.class, ParseException.class));
+
+ list.add(Arguments.of("just-a-string", BigDecimal.class, ParseException.class));
+ list.add(Arguments.of("5", BigDecimal.class, new BigDecimal("5")));
+ list.add(Arguments.of("5.5", BigDecimal.class, new BigDecimal(5.5)));
+
+ list.add(Arguments.of("1.5", PatternOptionBuilder.NUMBER_VALUE, Double.valueOf(1.5)));
+ list.add(Arguments.of("15", PatternOptionBuilder.NUMBER_VALUE, Long.valueOf(15)));
+ list.add(Arguments.of("not a number", PatternOptionBuilder.NUMBER_VALUE, ParseException.class));
+
+ list.add(Arguments.of(Instantiable.class.getName(), PatternOptionBuilder.OBJECT_VALUE, new Instantiable()));
+ list.add(Arguments.of(NotInstantiable.class.getName(), PatternOptionBuilder.OBJECT_VALUE, ParseException.class));
+ list.add(Arguments.of("unknown", PatternOptionBuilder.OBJECT_VALUE, ParseException.class));
+
+ list.add(Arguments.of("String", PatternOptionBuilder.STRING_VALUE, "String"));
+
+ final String urlString = "https://commons.apache.org";
+ list.add(Arguments.of(urlString, PatternOptionBuilder.URL_VALUE, new URL(urlString)));
+ list.add(Arguments.of("Malformed-url", PatternOptionBuilder.URL_VALUE, ParseException.class));
+
+ return list.stream();
+
}
@Test
- public void testCreateValueFile() throws Exception {
- final File result = TypeHandler.createValue("some-file.txt", PatternOptionBuilder.FILE_VALUE);
- assertEquals("some-file.txt", result.getName());
+ void testCreateClass() throws ParseException {
+ final Class> cls = getClass();
+ assertEquals(cls, TypeHandler.createClass(cls.getName()));
}
- @Test(expected = UnsupportedOperationException.class)
- public void testCreateValueFiles() throws Exception {
- TypeHandler.createValue("some.files", PatternOptionBuilder.FILES_VALUE);
+ @ParameterizedTest
+ @MethodSource("createDateFixtures")
+ @DefaultLocale(language = "en", country = "US")
+ void testCreateDate(final Date date) {
+ assertEquals(date, TypeHandler.createDate(date.toString()));
}
- @Test(expected = ParseException.class)
- public void testCreateValueInteger_failure() throws Exception {
- TypeHandler.createValue("just-a-string", Integer.class);
+ @Test
+ void testCreateFile() {
+ final File file = FileUtils.current().getAbsoluteFile();
+ assertEquals(file, TypeHandler.createFile(file.toString()));
}
@Test
- public void testCreateValueNumber_Double() throws Exception {
- assertEquals(1.5d, TypeHandler.createValue("1.5", PatternOptionBuilder.NUMBER_VALUE));
+ void testCreateFiles() {
+ assertThrows(UnsupportedOperationException.class, () -> TypeHandler.createFiles(null));
}
@Test
- public void testCreateValueNumber_Long() throws Exception {
- assertEquals(Long.valueOf(15), TypeHandler.createValue("15", PatternOptionBuilder.NUMBER_VALUE));
+ void testCreateNumber() throws ParseException {
+ assertEquals(0L, TypeHandler.createNumber("0"));
+ assertEquals(0d, TypeHandler.createNumber("0.0"));
}
- @Test(expected = ParseException.class)
- public void testCreateValueNumber_noNumber() throws Exception {
- TypeHandler.createValue("not a number", PatternOptionBuilder.NUMBER_VALUE);
+ @Test
+ void testCreateObject() throws ParseException {
+ assertTrue(TypeHandler.createObject(Date.class.getName()) instanceof Date);
}
@Test
- public void testCreateValueObject_InstantiableClass() throws Exception {
- final Object result = TypeHandler.createValue(Instantiable.class.getName(), PatternOptionBuilder.OBJECT_VALUE);
- assertTrue(result instanceof Instantiable);
+ void testCreateURL() throws ParseException, MalformedURLException {
+ final URL file = Paths.get("").toAbsolutePath().toUri().toURL();
+ assertEquals(file, TypeHandler.createURL(file.toString()));
}
- @Test(expected = ParseException.class)
- public void testCreateValueObject_notInstantiableClass() throws Exception {
- TypeHandler.createValue(NotInstantiable.class.getName(), PatternOptionBuilder.OBJECT_VALUE);
+ @SuppressWarnings("unchecked")
+ @ParameterizedTest(name = "{0} as {1}")
+ @MethodSource("createValueTestParameters")
+ void testCreateValue(final String str, final Class> type, final Object expected) throws Exception {
+ @SuppressWarnings("cast")
+ final Object objectApiTest = type; // KEEP this cast
+ if (expected instanceof Class> && Throwable.class.isAssignableFrom((Class>) expected)) {
+ assertThrows((Class) expected, () -> TypeHandler.createValue(str, type));
+ assertThrows((Class) expected, () -> TypeHandler.createValue(str, objectApiTest));
+ } else {
+ assertEquals(expected, TypeHandler.createValue(str, type));
+ assertEquals(expected, TypeHandler.createValue(str, objectApiTest));
+ }
}
- @Test(expected = ParseException.class)
- public void testCreateValueObject_unknownClass() throws Exception {
- TypeHandler.createValue("unknown", PatternOptionBuilder.OBJECT_VALUE);
+ @Test
+ void testCreateValueExistingFile() throws Exception {
+ try (FileInputStream result = TypeHandler.createValue("src/test/resources/org/apache/commons/cli/existing-readable.file",
+ PatternOptionBuilder.EXISTING_FILE_VALUE)) {
+ assertNotNull(result);
+ }
}
+ /* proof of equality for later tests */
@Test
- public void testCreateValueString() throws Exception {
- assertEquals("String", TypeHandler.createValue("String", PatternOptionBuilder.STRING_VALUE));
+ void testnstantiableEquals() {
+ assertEquals(new Instantiable(), new Instantiable());
}
@Test
- public void testCreateValueURL() throws Exception {
- final String urlString = "https://commons.apache.org";
- final URL result = TypeHandler.createValue(urlString, PatternOptionBuilder.URL_VALUE);
- assertEquals(urlString, result.toString());
+ void testOpenFile() throws ParseException, IOException {
+ try (FileInputStream fis = TypeHandler.openFile("src/test/resources/org/apache/commons/cli/existing-readable.file")) {
+ IOUtils.consume(fis);
+ }
}
- @Test(expected = ParseException.class)
- public void testCreateValueURL_malformed() throws Exception {
- TypeHandler.createValue("malformed-url", PatternOptionBuilder.URL_VALUE);
+ @Test
+ void testRegister() {
+ final Map>, Converter, ? extends Throwable>> map = TypeHandler.createDefaultMap();
+ final TypeHandler typeHandler = new TypeHandler(map);
+ assertEquals(Converter.PATH, typeHandler.getConverter(Path.class));
+ try {
+ map.put(Path.class, PATH_CONVERTER);
+ assertEquals(PATH_CONVERTER, typeHandler.getConverter(Path.class));
+ } finally {
+ map.remove(Path.class);
+ assertEquals(Converter.DEFAULT, typeHandler.getConverter(Path.class));
+ }
}
+
}
diff --git a/src/test/java/org/apache/commons/cli/UnrecognizedOptionExceptionTest.java b/src/test/java/org/apache/commons/cli/UnrecognizedOptionExceptionTest.java
new file mode 100644
index 000000000..e05554464
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/UnrecognizedOptionExceptionTest.java
@@ -0,0 +1,35 @@
+/*
+ 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
+
+ https://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 static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link UnrecognizedOptionException}.
+ */
+class UnrecognizedOptionExceptionTest {
+
+ @Test
+ void testConstructor() {
+ assertEquals("a", new UnrecognizedOptionException("a").getMessage());
+ assertEquals("a", new UnrecognizedOptionException("a", "b").getMessage());
+ assertEquals("b", new UnrecognizedOptionException("a", "b").getOption());
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/UtilTest.java b/src/test/java/org/apache/commons/cli/UtilTest.java
index 66ca33e6c..b25ba62d8 100644
--- a/src/test/java/org/apache/commons/cli/UtilTest.java
+++ b/src/test/java/org/apache/commons/cli/UtilTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,14 +17,17 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+
+class UtilTest {
-public class UtilTest {
@Test
- public void testStripLeadingAndTrailingQuotes() {
+ void testStripLeadingAndTrailingQuotes() {
+ assertNull(Util.stripLeadingAndTrailingQuotes(null));
+ assertEquals("", Util.stripLeadingAndTrailingQuotes(""));
assertEquals("foo", Util.stripLeadingAndTrailingQuotes("\"foo\""));
assertEquals("foo \"bar\"", Util.stripLeadingAndTrailingQuotes("foo \"bar\""));
assertEquals("\"foo\" bar", Util.stripLeadingAndTrailingQuotes("\"foo\" bar"));
@@ -33,7 +36,7 @@ public void testStripLeadingAndTrailingQuotes() {
}
@Test
- public void testStripLeadingHyphens() {
+ void testStripLeadingHyphens() {
assertEquals("f", Util.stripLeadingHyphens("-f"));
assertEquals("foo", Util.stripLeadingHyphens("--foo"));
assertEquals("-foo", Util.stripLeadingHyphens("---foo"));
diff --git a/src/test/java/org/apache/commons/cli/ValueTest.java b/src/test/java/org/apache/commons/cli/ValueTest.java
index e54db88f6..81f020099 100644
--- a/src/test/java/org/apache/commons/cli/ValueTest.java
+++ b/src/test/java/org/apache/commons/cli/ValueTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,20 +17,33 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.junit.Before;
-import org.junit.Test;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class ValueTest {
- private CommandLine cl;
+class ValueTest {
+
+ private static final Option NULL_OPTION = null;
+ private static final String NULL_STRING = null;
+
+ protected static Stream parsers() {
+ return Stream.of(new DefaultParser(), new PosixParser());
+ }
+
private final Options opts = new Options();
- @Before
+ private CommandLine cl;
+
+ @BeforeEach
public void setUp() throws Exception {
opts.addOption("a", false, "toggle -a");
opts.addOption("b", true, "set -b");
@@ -43,41 +56,41 @@ public void setUp() throws Exception {
opts.addOption(OptionBuilder.hasOptionalArgs(2).withLongOpt("hide").create());
opts.addOption(OptionBuilder.hasOptionalArgs(2).create('i'));
opts.addOption(OptionBuilder.hasOptionalArgs().create('j'));
+ opts.addOption(Option.builder().option("v").hasArg().valueSeparator().get());
- final String[] args = {"-a", "-b", "foo", "--c", "--d", "bar"};
+ final String[] args = { "-a", "-b", "foo", "--c", "--d", "bar" };
- final Parser parser = new PosixParser();
- cl = parser.parse(opts, args);
+ cl = new PosixParser().parse(opts, args);
}
@Test
- public void testLongNoArg() {
+ void testLongNoArg() {
assertTrue(cl.hasOption("c"));
assertNull(cl.getOptionValue("c"));
}
@Test
- public void testLongNoArgWithOption() {
+ void testLongNoArgWithOption() {
assertTrue(cl.hasOption(opts.getOption("c")));
assertNull(cl.getOptionValue(opts.getOption("c")));
}
- @Test
- public void testLongOptionalArgValue() throws Exception {
- final String[] args = {"--fish", "face"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testLongOptionalArgValue(final CommandLineParser parser) throws Exception {
+ final String[] args = { "--fish", "face" };
final CommandLine cmd = parser.parse(opts, args);
assertTrue(cmd.hasOption("fish"));
assertEquals("face", cmd.getOptionValue("fish"));
}
- @Test
- public void testLongOptionalArgValues() throws Exception {
- final String[] args = {"--gravy", "gold", "garden"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testLongOptionalArgValues(final CommandLineParser parser) throws Exception {
+ final String[] args = { "--gravy", "gold", "garden" };
final CommandLine cmd = parser.parse(opts, args);
+ assertNull(cmd.getOptionValues(NULL_OPTION));
+ assertNull(cmd.getOptionValues(NULL_STRING));
assertTrue(cmd.hasOption("gravy"));
assertEquals("gold", cmd.getOptionValue("gravy"));
assertEquals("gold", cmd.getOptionValues("gravy")[0]);
@@ -85,12 +98,13 @@ public void testLongOptionalArgValues() throws Exception {
assertEquals(cmd.getArgs().length, 0);
}
- @Test
- public void testLongOptionalArgValuesWithOption() throws Exception {
- final String[] args = {"--gravy", "gold", "garden"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testLongOptionalArgValuesWithOption(final CommandLineParser parser) throws Exception {
+ final String[] args = { "--gravy", "gold", "garden" };
final CommandLine cmd = parser.parse(opts, args);
+ assertNull(cmd.getOptionValues(NULL_OPTION));
+ assertNull(cmd.getOptionValues(NULL_STRING));
assertTrue(cmd.hasOption(opts.getOption("gravy")));
assertEquals("gold", cmd.getOptionValue(opts.getOption("gravy")));
assertEquals("gold", cmd.getOptionValues(opts.getOption("gravy"))[0]);
@@ -98,22 +112,19 @@ public void testLongOptionalArgValuesWithOption() throws Exception {
assertEquals(cmd.getArgs().length, 0);
}
- @Test
- public void testLongOptionalArgValueWithOption() throws Exception {
- final String[] args = {"--fish", "face"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testLongOptionalArgValueWithOption(final CommandLineParser parser) throws Exception {
+ final String[] args = { "--fish", "face" };
final CommandLine cmd = parser.parse(opts, args);
assertTrue(cmd.hasOption(opts.getOption("fish")));
assertEquals("face", cmd.getOptionValue(opts.getOption("fish")));
}
- @Test
- public void testLongOptionalNArgValues() throws Exception {
- final String[] args = {"--hide", "house", "hair", "head"};
-
- final Parser parser = new PosixParser();
-
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testLongOptionalNArgValues(final CommandLineParser parser) throws Exception {
+ final String[] args = { "--hide", "house", "hair", "head" };
final CommandLine cmd = parser.parse(opts, args);
assertTrue(cmd.hasOption("hide"));
assertEquals("house", cmd.getOptionValue("hide"));
@@ -123,13 +134,12 @@ public void testLongOptionalNArgValues() throws Exception {
assertEquals("head", cmd.getArgs()[0]);
}
- @Test
- public void testLongOptionalNArgValuesWithOption() throws Exception {
- final String[] args = {"--hide", "house", "hair", "head"};
-
- final Parser parser = new PosixParser();
-
- final CommandLine cmd = parser.parse(opts, args);
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testLongOptionalNArgValuesWithOption(final CommandLineParser parser) throws Exception {
+ final CommandLine cmd = parser.parse(opts, new String[] { "--hide", "house", "hair", "head" });
+ assertNull(cmd.getOptionValues(NULL_OPTION));
+ assertNull(cmd.getOptionValues(NULL_STRING));
assertTrue(cmd.hasOption(opts.getOption("hide")));
assertEquals("house", cmd.getOptionValue(opts.getOption("hide")));
assertEquals("house", cmd.getOptionValues(opts.getOption("hide"))[0]);
@@ -138,87 +148,81 @@ public void testLongOptionalNArgValuesWithOption() throws Exception {
assertEquals("head", cmd.getArgs()[0]);
}
- @Test
- public void testLongOptionalNoValue() throws Exception {
- final String[] args = {"--fish"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testLongOptionalNoValue(final CommandLineParser parser) throws Exception {
+ final String[] args = { "--fish" };
final CommandLine cmd = parser.parse(opts, args);
assertTrue(cmd.hasOption("fish"));
assertNull(cmd.getOptionValue("fish"));
}
- @Test
- public void testLongOptionalNoValueWithOption() throws Exception {
- final String[] args = {"--fish"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testLongOptionalNoValueWithOption(final CommandLineParser parser) throws Exception {
+ final String[] args = { "--fish" };
final CommandLine cmd = parser.parse(opts, args);
assertTrue(cmd.hasOption(opts.getOption("fish")));
assertNull(cmd.getOptionValue(opts.getOption("fish")));
}
@Test
- public void testLongWithArg() {
+ void testLongWithArg() {
assertTrue(cl.hasOption("d"));
assertNotNull(cl.getOptionValue("d"));
assertEquals(cl.getOptionValue("d"), "bar");
}
@Test
- public void testLongWithArgWithOption() {
+ void testLongWithArgWithOption() {
assertTrue(cl.hasOption(opts.getOption("d")));
assertNotNull(cl.getOptionValue(opts.getOption("d")));
assertEquals(cl.getOptionValue(opts.getOption("d")), "bar");
}
@Test
- public void testShortNoArg() {
+ void testShortNoArg() {
assertTrue(cl.hasOption("a"));
assertNull(cl.getOptionValue("a"));
}
@Test
- public void testShortNoArgWithOption() {
+ void testShortNoArgWithOption() {
assertTrue(cl.hasOption(opts.getOption("a")));
assertNull(cl.getOptionValue(opts.getOption("a")));
}
- @Test
- public void testShortOptionalArgNoValue() throws Exception {
- final String[] args = {"-e"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testShortOptionalArgNoValue(final CommandLineParser parser) throws Exception {
+ final String[] args = { "-e" };
final CommandLine cmd = parser.parse(opts, args);
assertTrue(cmd.hasOption("e"));
assertNull(cmd.getOptionValue("e"));
}
- @Test
- public void testShortOptionalArgNoValueWithOption() throws Exception {
- final String[] args = {"-e"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testShortOptionalArgNoValueWithOption(final CommandLineParser parser) throws Exception {
+ final String[] args = { "-e" };
final CommandLine cmd = parser.parse(opts, args);
assertTrue(cmd.hasOption(opts.getOption("e")));
assertNull(cmd.getOptionValue(opts.getOption("e")));
}
- @Test
- public void testShortOptionalArgValue() throws Exception {
- final String[] args = {"-e", "everything"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testShortOptionalArgValue(final CommandLineParser parser) throws Exception {
+ final String[] args = { "-e", "everything" };
final CommandLine cmd = parser.parse(opts, args);
assertTrue(cmd.hasOption("e"));
assertEquals("everything", cmd.getOptionValue("e"));
}
- @Test
- public void testShortOptionalArgValues() throws Exception {
- final String[] args = {"-j", "ink", "idea"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testShortOptionalArgValues(final CommandLineParser parser) throws Exception {
+ final String[] args = { "-j", "ink", "idea" };
final CommandLine cmd = parser.parse(opts, args);
assertTrue(cmd.hasOption("j"));
assertEquals("ink", cmd.getOptionValue("j"));
@@ -227,12 +231,13 @@ public void testShortOptionalArgValues() throws Exception {
assertEquals(cmd.getArgs().length, 0);
}
- @Test
- public void testShortOptionalArgValuesWithOption() throws Exception {
- final String[] args = {"-j", "ink", "idea"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testShortOptionalArgValuesWithOption(final CommandLineParser parser) throws Exception {
+ final String[] args = { "-j", "ink", "idea" };
final CommandLine cmd = parser.parse(opts, args);
+ assertNull(cmd.getOptionValues(NULL_OPTION));
+ assertNull(cmd.getOptionValues(NULL_STRING));
assertTrue(cmd.hasOption(opts.getOption("j")));
assertEquals("ink", cmd.getOptionValue(opts.getOption("j")));
assertEquals("ink", cmd.getOptionValues(opts.getOption("j"))[0]);
@@ -240,22 +245,24 @@ public void testShortOptionalArgValuesWithOption() throws Exception {
assertEquals(cmd.getArgs().length, 0);
}
- @Test
- public void testShortOptionalArgValueWithOption() throws Exception {
- final String[] args = {"-e", "everything"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testShortOptionalArgValueWithOption(final CommandLineParser parser) throws Exception {
+ final String[] args = { "-e", "everything" };
final CommandLine cmd = parser.parse(opts, args);
+ assertNull(cmd.getOptionValues(NULL_OPTION));
+ assertNull(cmd.getOptionValues(NULL_STRING));
assertTrue(cmd.hasOption(opts.getOption("e")));
assertEquals("everything", cmd.getOptionValue(opts.getOption("e")));
}
- @Test
- public void testShortOptionalNArgValues() throws Exception {
- final String[] args = {"-i", "ink", "idea", "isotope", "ice"};
-
- final Parser parser = new PosixParser();
+ @ParameterizedTest
+ @MethodSource("parsers")
+ void testShortOptionalNArgValues(final CommandLineParser parser) throws Exception {
+ final String[] args = { "-i", "ink", "idea", "isotope", "ice" };
final CommandLine cmd = parser.parse(opts, args);
+ assertNull(cmd.getOptionValues(NULL_OPTION));
+ assertNull(cmd.getOptionValues(NULL_STRING));
assertTrue(cmd.hasOption("i"));
assertEquals("ink", cmd.getOptionValue("i"));
assertEquals("ink", cmd.getOptionValues("i")[0]);
@@ -266,11 +273,27 @@ public void testShortOptionalNArgValues() throws Exception {
}
@Test
- public void testShortOptionalNArgValuesWithOption() throws Exception {
- final String[] args = {"-i", "ink", "idea", "isotope", "ice"};
+ void testShortOptionalNArgValuesSeparated() throws Exception {
+ final String[] args = { "-v=ink", "-v=idea", "-v=isotope", "-v=ice" };
+ final CommandLineParser parser = new DefaultParser();
+ final CommandLine cmd = parser.parse(opts, args);
+ assertNull(cmd.getOptionValues(NULL_OPTION));
+ assertNull(cmd.getOptionValues(NULL_STRING));
+ assertTrue(cmd.hasOption("v"));
+ assertEquals("ink", cmd.getOptionValue("v"));
+ assertEquals("ink", cmd.getOptionValues("v")[0]);
+ assertEquals("idea", cmd.getOptionValues("v")[1]);
+ assertEquals("isotope", cmd.getOptionValues("v")[2]);
+ assertEquals("ice", cmd.getOptionValues("v")[3]);
+ }
- final Parser parser = new PosixParser();
+ @Test
+ void testShortOptionalNArgValuesWithOption() throws Exception {
+ final String[] args = { "-i", "ink", "idea", "isotope", "ice" };
+ final CommandLineParser parser = new PosixParser();
final CommandLine cmd = parser.parse(opts, args);
+ assertNull(cmd.getOptionValues(NULL_OPTION));
+ assertNull(cmd.getOptionValues(NULL_STRING));
assertTrue(cmd.hasOption("i"));
assertEquals("ink", cmd.getOptionValue(opts.getOption("i")));
assertEquals("ink", cmd.getOptionValues(opts.getOption("i"))[0]);
@@ -281,14 +304,14 @@ public void testShortOptionalNArgValuesWithOption() throws Exception {
}
@Test
- public void testShortWithArg() {
+ void testShortWithArg() {
assertTrue(cl.hasOption("b"));
assertNotNull(cl.getOptionValue("b"));
assertEquals(cl.getOptionValue("b"), "foo");
}
@Test
- public void testShortWithArgWithOption() {
+ void testShortWithArgWithOption() {
assertTrue(cl.hasOption(opts.getOption("b")));
assertNotNull(cl.getOptionValue(opts.getOption("b")));
assertEquals(cl.getOptionValue(opts.getOption("b")), "foo");
diff --git a/src/test/java/org/apache/commons/cli/ValuesTest.java b/src/test/java/org/apache/commons/cli/ValuesTest.java
index 2e0928b3c..9a3b684d9 100644
--- a/src/test/java/org/apache/commons/cli/ValuesTest.java
+++ b/src/test/java/org/apache/commons/cli/ValuesTest.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,19 +17,19 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class ValuesTest {
+class ValuesTest {
private CommandLine cmd;
- @Before
+ @BeforeEach
public void setUp() throws Exception {
final Options options = new Options();
@@ -73,65 +73,65 @@ public void setUp() throws Exception {
}
@Test
- public void testCharSeparator() {
+ void testCharSeparator() {
// tests the char methods of CommandLine that delegate to the String methods
- assertTrue("Option j is not set", cmd.hasOption("j"));
- assertTrue("Option j is not set", cmd.hasOption('j'));
+ assertTrue(cmd.hasOption("j"), "Option j is not set");
+ assertTrue(cmd.hasOption('j'), "Option j is not set");
assertArrayEquals(new String[] {"key", "value", "key", "value"}, cmd.getOptionValues("j"));
assertArrayEquals(new String[] {"key", "value", "key", "value"}, cmd.getOptionValues('j'));
- assertTrue("Option k is not set", cmd.hasOption("k"));
- assertTrue("Option k is not set", cmd.hasOption('k'));
+ assertTrue(cmd.hasOption("k"), "Option k is not set");
+ assertTrue(cmd.hasOption('k'), "Option k is not set");
assertArrayEquals(new String[] {"key1", "value1", "key2", "value2"}, cmd.getOptionValues("k"));
assertArrayEquals(new String[] {"key1", "value1", "key2", "value2"}, cmd.getOptionValues('k'));
- assertTrue("Option m is not set", cmd.hasOption("m"));
- assertTrue("Option m is not set", cmd.hasOption('m'));
+ assertTrue(cmd.hasOption("m"), "Option m is not set");
+ assertTrue(cmd.hasOption('m'), "Option m is not set");
assertArrayEquals(new String[] {"key", "value"}, cmd.getOptionValues("m"));
assertArrayEquals(new String[] {"key", "value"}, cmd.getOptionValues('m'));
}
@Test
- public void testComplexValues() {
- assertTrue("Option i is not set", cmd.hasOption("i"));
- assertTrue("Option h is not set", cmd.hasOption("h"));
+ void testComplexValues() {
+ assertTrue(cmd.hasOption("i"), "Option i is not set");
+ assertTrue(cmd.hasOption("h"), "Option h is not set");
assertArrayEquals(new String[] {"val1", "val2"}, cmd.getOptionValues("h"));
}
@Test
- public void testExtraArgs() {
- assertArrayEquals("Extra args", new String[] {"arg1", "arg2", "arg3"}, cmd.getArgs());
+ void testExtraArgs() {
+ assertArrayEquals(new String[] {"arg1", "arg2", "arg3"}, cmd.getArgs(), "Extra args");
}
@Test
- public void testMultipleArgValues() {
- assertTrue("Option e is not set", cmd.hasOption("e"));
+ void testMultipleArgValues() {
+ assertTrue(cmd.hasOption("e"), "Option e is not set");
assertArrayEquals(new String[] {"one", "two"}, cmd.getOptionValues("e"));
}
@Test
- public void testShortArgs() {
- assertTrue("Option a is not set", cmd.hasOption("a"));
- assertTrue("Option c is not set", cmd.hasOption("c"));
+ void testShortArgs() {
+ assertTrue(cmd.hasOption("a"), "Option a is not set");
+ assertTrue(cmd.hasOption("c"), "Option c is not set");
assertNull(cmd.getOptionValues("a"));
assertNull(cmd.getOptionValues("c"));
}
@Test
- public void testShortArgsWithValue() {
- assertTrue("Option b is not set", cmd.hasOption("b"));
+ void testShortArgsWithValue() {
+ assertTrue(cmd.hasOption("b"), "Option b is not set");
assertEquals("foo", cmd.getOptionValue("b"));
assertEquals(1, cmd.getOptionValues("b").length);
- assertTrue("Option d is not set", cmd.hasOption("d"));
+ assertTrue(cmd.hasOption("b"), "Option b is not set");
assertEquals("bar", cmd.getOptionValue("d"));
assertEquals(1, cmd.getOptionValues("d").length);
}
@Test
- public void testTwoArgValues() {
- assertTrue("Option g is not set", cmd.hasOption("g"));
+ void testTwoArgValues() {
+ assertTrue(cmd.hasOption("g"), "Option g is not set");
assertArrayEquals(new String[] {"val1", "val2"}, cmd.getOptionValues("g"));
}
@@ -140,16 +140,16 @@ public void testTwoArgValues() {
* in case I get a brainwave on how to resolve this.
*/
/*
- * public void testGetValue() { // the 'm' option assertTrue(_option.getValues().length == 2); assertEquals(
+ * void testGetValue() { // the 'm' option assertTrue(_option.getValues().length == 2); assertEquals(
* _option.getValue(), "key"); assertEquals(_option.getValue(0), "key"); assertEquals(_option.getValue(1),
* "value");
*
- * try { assertEquals(_option.getValue(2), "key"); fail("IndexOutOfBounds not caught"); } catch(
+ * try { assertEquals(_option.getValue(2), "key"); fail("IndexOutOfBounds not caught"); } catch (
* IndexOutOfBoundsException exp) {
*
* }
*
- * try { assertEquals(_option.getValue(-1), "key"); fail("IndexOutOfBounds not caught"); } catch(
+ * try { assertEquals(_option.getValue(-1), "key"); fail("IndexOutOfBounds not caught"); } catch (
* IndexOutOfBoundsException exp) {
*
* } }
diff --git a/src/test/java/org/apache/commons/cli/bug/BugCLI133Test.java b/src/test/java/org/apache/commons/cli/bug/BugCLI133Test.java
index 0e1ea7a4e..3d224138c 100644
--- a/src/test/java/org/apache/commons/cli/bug/BugCLI133Test.java
+++ b/src/test/java/org/apache/commons/cli/bug/BugCLI133Test.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,19 +17,19 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli.bug;
-import static org.junit.Assert.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class BugCLI133Test {
+class BugCLI133Test {
@Test
- public void testOrder() throws ParseException {
+ void testOrder() throws ParseException {
final Option optionA = new Option("a", "first");
final Options opts = new Options();
opts.addOption(optionA);
diff --git a/src/test/java/org/apache/commons/cli/bug/BugCLI13Test.java b/src/test/java/org/apache/commons/cli/bug/BugCLI13Test.java
index 14ffb2d80..fe43d9d2a 100644
--- a/src/test/java/org/apache/commons/cli/bug/BugCLI13Test.java
+++ b/src/test/java/org/apache/commons/cli/bug/BugCLI13Test.java
@@ -6,7 +6,7 @@
* (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
+ * https://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,
@@ -17,8 +17,8 @@
package org.apache.commons.cli.bug;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
@@ -26,27 +26,22 @@
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class BugCLI13Test {
+class BugCLI13Test {
@Test
- public void testCLI13() throws ParseException {
+ void testCLI13() throws ParseException {
final String debugOpt = "debug";
- @SuppressWarnings("static-access")
- //@formatter:off
- final Option debug = OptionBuilder
- .withArgName(debugOpt)
- .withDescription("turn on debugging")
- .withLongOpt(debugOpt)
- .hasArg()
- .create('d');
- //@formatter:on
+ OptionBuilder.withArgName(debugOpt);
+ OptionBuilder.withDescription("turn on debugging");
+ OptionBuilder.withLongOpt(debugOpt);
+ OptionBuilder.hasArg();
+ final Option debug = OptionBuilder.create('d');
final Options options = new Options();
options.addOption(debug);
- final CommandLine commandLine = new PosixParser().parse(options, new String[] {"-d", "true"});
-
+ final CommandLine commandLine = new PosixParser().parse(options, new String[] { "-d", "true" });
assertEquals("true", commandLine.getOptionValue(debugOpt));
assertEquals("true", commandLine.getOptionValue('d'));
assertTrue(commandLine.hasOption('d'));
diff --git a/src/test/java/org/apache/commons/cli/bug/BugCLI148Test.java b/src/test/java/org/apache/commons/cli/bug/BugCLI148Test.java
index 712c89057..17e36650e 100644
--- a/src/test/java/org/apache/commons/cli/bug/BugCLI148Test.java
+++ b/src/test/java/org/apache/commons/cli/bug/BugCLI148Test.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -17,24 +17,24 @@ Licensed to the Apache Software Foundation (ASF) under one or more
package org.apache.commons.cli.bug;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
/**
* https://issues.apache.org/jira/browse/CLI-148
*/
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class BugCLI148Test {
+class BugCLI148Test {
private Options options;
- @Before
+ @BeforeEach
public void setUp() throws Exception {
options = new Options();
options.addOption(OptionBuilder.hasArg().create('t'));
@@ -42,7 +42,7 @@ public void setUp() throws Exception {
}
@Test
- public void testWorkaround1() throws Exception {
+ void testWorkaround1() throws Exception {
final CommandLineParser parser = new PosixParser();
final String[] args = {"-t-something"};
@@ -51,7 +51,7 @@ public void testWorkaround1() throws Exception {
}
@Test
- public void testWorkaround2() throws Exception {
+ void testWorkaround2() throws Exception {
final CommandLineParser parser = new PosixParser();
final String[] args = {"-t", "\"-something\""};
diff --git a/src/test/java/org/apache/commons/cli/bug/BugCLI162Test.java b/src/test/java/org/apache/commons/cli/bug/BugCLI162Test.java
index 3123d902f..a1a0e9f06 100644
--- a/src/test/java/org/apache/commons/cli/bug/BugCLI162Test.java
+++ b/src/test/java/org/apache/commons/cli/bug/BugCLI162Test.java
@@ -6,7 +6,7 @@
* (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
+ * https://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,
@@ -17,7 +17,7 @@
package org.apache.commons.cli.bug;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -28,12 +28,12 @@
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
-public class BugCLI162Test {
+class BugCLI162Test {
/** Constant for the line separator. */
- private static final String CR = System.getProperty("line.separator");
+ private static final String CR = System.lineSeparator();
// Constants used for options
private static final String OPT = "-";
@@ -104,18 +104,78 @@ public class BugCLI162Test {
private static final String PMODES = PMODE_IN + ", " + PMODE_INOUT + ", " + PMODE_OUT + ", " + PMODE_UNK;
+ // @formatter:off
+ private static final String EXPECTED = "usage: org.apache.commons.cli.bug.BugCLI162Test" + CR +
+ " -2,--jdbc2sfmd Converts the JDBC file in the first argument" + CR +
+ " to an SMFD file specified in the second" + CR +
+ " argument." + CR +
+ " -a,--paramNames Parameter XML names; default names are" + CR +
+ " param1, param2, etc. Example: -a \"pname1" + CR +
+ " pname2\"" + CR +
+ " -b,--jdbc Writes a JDBC binding node file for the given" + CR +
+ " SQL" + CR +
+ " -c,--url Connection URL" + CR +
+ " -d,--driver JDBC driver class name" + CR +
+ " -e,--description SFMD description. A default description is" + CR +
+ " used if omited. Example: -e \"Runs such and" + CR +
+ " such\"" + CR +
+ " -f,--sfmd Writes a SFMD file for the given SQL" + CR +
+ " -g,--printTiming Prints timing information" + CR +
+ " -h,--help Prints help and quits" + CR +
+ " -i,--interactive Runs in interactive mode, reading and writing" + CR +
+ " from the console, 'go' or '/' sends a" + CR +
+ " statement" + CR +
+ " -j,--node Writes a JDBC node file for the given SQL" + CR +
+ " (internal debugging)" + CR +
+ " -l,--columnNames Column XML names; default names column" + CR +
+ " labels. Example: -l \"cname1 cname2\"" + CR +
+ " -m,--printMetaData Prints metadata information" + CR +
+ " -n,--info Prints driver information and properties. If" + CR +
+ " -c is not specified, all drivers on the" + CR +
+ " classpath are displayed." + CR +
+ " -o,--paramModes Parameters modes (1=IN, 2=INOUT, 4=OUT," + CR +
+ " 0=Unknown). -o and -O are mutually exclusive." + CR +
+ " Example for 2 parameters, OUT and IN: -o \"4" + CR +
+ " 1\"" + CR +
+ " -O,--paramModeNames Parameters mode names (IN, INOUT, OUT," + CR +
+ " Unknown). -o and -O are mutually exclusive." + CR +
+ " Example for 2 parameters, OUT and IN: -O \"OUT" + CR +
+ " IN\"" + CR +
+ " -p,--password The database password for the user specified" + CR +
+ " with the -u option. You can obfuscate the" + CR +
+ " password with" + CR +
+ " org.mortbay.jetty.security.Password, see" + CR +
+ " https://docs.codehaus.org/display/JETTY/Secur" + CR +
+ " ing+Passwords" + CR +
+ " -s,--sql Runs SQL or {call stored_procedure(?, ?)} or" + CR +
+ " {?=call function(?, ?)}" + CR +
+ " -t,--printStack Prints stack traces on errors" + CR +
+ " --trim Trims leading and trailing spaces from all" + CR +
+ " column values. Column XML names can be" + CR +
+ " optionally specified to set which columns to" + CR +
+ " trim." + CR +
+ " -u,--user A database user name" + CR +
+ " -w,--outfile Writes the SQL output to the given file" + CR +
+ " -y,--paramTypes Parameter types from java.sql.Types. -y and" + CR +
+ " -Y are mutually exclusive. Example: -y \"-10" + CR +
+ " 12\"" + CR +
+ " -Y,--paramTypeNames Parameter java.sql.Types names. -y and -Y are" + CR +
+ " mutually exclusive. Example: -Y \"CURSOR" + CR +
+ " VARCHAR\"" + CR;
+ // @formatter:on
+
private HelpFormatter formatter;
private StringWriter sw;
- @Before
+ @BeforeEach
public void setUp() {
formatter = new HelpFormatter();
sw = new StringWriter();
}
@Test
- public void testInfiniteLoop() {
+ void testInfiniteLoop() {
final Options options = new Options();
options.addOption("h", "help", false, "This is a looooong description");
// used to hang & crash
@@ -134,7 +194,7 @@ public void testInfiniteLoop() {
}
@Test
- public void testLongLineChunking() {
+ void testLongLineChunking() {
final Options options = new Options();
//@formatter:off
options.addOption("x", "extralongarg", false,
@@ -165,11 +225,11 @@ public void testLongLineChunking() {
" yes." + CR +
"Footer" + CR;
//@formatter:on
- assertEquals("Long arguments did not split as expected", expected, sw.toString());
+ assertEquals(expected, sw.toString(), "Long arguments did not split as expected");
}
@Test
- public void testLongLineChunkingIndentIgnored() {
+ void testLongLineChunkingIndentIgnored() {
final Options options = new Options();
options.addOption("x", "extralongarg", false, "This description is Long.");
formatter.printHelp(new PrintWriter(sw), 22, this.getClass().getName(), "Header", options, 0, 5, "Footer");
@@ -184,11 +244,11 @@ public void testLongLineChunkingIndentIgnored() {
" Long." + CR +
"Footer" + CR;
//@formatter:on
- assertEquals("Long arguments did not split as expected", expected, sw.toString());
+ assertEquals(expected, sw.toString(), "Long arguments did not split as expected");
}
@Test
- public void testPrintHelpLongLines() {
+ void testPrintHelpLongLines() {
// Options build
final Options commandLineOptions;
commandLineOptions = new Options();
@@ -212,7 +272,7 @@ public void testPrintHelpLongLines() {
+ OPT
+ OPT_USER
+ " option. You can obfuscate the password with org.mortbay.jetty.security.Password,"
- + " see http://docs.codehaus.org/display/JETTY/Securing+Passwords");
+ + " see https://docs.codehaus.org/display/JETTY/Securing+Passwords");
//@formatter:on
commandLineOptions.addOption(OPT_SQL, OPT_SQL_L, true, "Runs SQL or {call stored_procedure(?, ?)} or {?=call function(?, ?)}");
commandLineOptions.addOption(OPT_FILE_SFMD, "sfmd", true, "Writes a SFMD file for the given SQL");
@@ -241,14 +301,14 @@ public void testPrintHelpLongLines() {
//@formatter:on
commandLineOptions.addOption(option);
//
- final OptionGroup pOutTypesOptionGroup = new OptionGroup();
- final String pOutTypesOptionGroupDoc = OPT + OPT_PARAM_TYPES_INT + " and " + OPT + OPT_PARAM_TYPES_NAME + " are mutually exclusive.";
+ final OptionGroup outTypesOptionGroup = new OptionGroup();
+ final String outTypesOptionGroupDoc = OPT + OPT_PARAM_TYPES_INT + " and " + OPT + OPT_PARAM_TYPES_NAME + " are mutually exclusive.";
final String typesClassName = Types.class.getName();
//@formatter:off
option = new Option(OPT_PARAM_TYPES_INT, "paramTypes", true, "Parameter types from "
+ typesClassName
+ ". "
- + pOutTypesOptionGroupDoc
+ + outTypesOptionGroupDoc
+ " Example: "
+ OPT
+ OPT_PARAM_TYPES_INT
@@ -259,14 +319,14 @@ public void testPrintHelpLongLines() {
option = new Option(OPT_PARAM_TYPES_NAME, "paramTypeNames", true, "Parameter "
+ typesClassName
+ " names. "
- + pOutTypesOptionGroupDoc
+ + outTypesOptionGroupDoc
+ " Example: "
+ OPT
+ OPT_PARAM_TYPES_NAME
+ " \"CURSOR VARCHAR\"");
//@formatter:on
commandLineOptions.addOption(option);
- commandLineOptions.addOptionGroup(pOutTypesOptionGroup);
+ commandLineOptions.addOptionGroup(outTypesOptionGroup);
//
final OptionGroup modesOptionGroup = new OptionGroup();
final String modesOptionGroupDoc = OPT + OPT_PARAM_MODES_INT + " and " + OPT + OPT_PARAM_MODES_NAME + " are mutually exclusive.";
@@ -319,66 +379,8 @@ public void testPrintHelpLongLines() {
formatter.printHelp(new PrintWriter(sw), HelpFormatter.DEFAULT_WIDTH, this.getClass().getName(), null, commandLineOptions,
HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, null);
- //@formatter:off
- final String expected = "usage: org.apache.commons.cli.bug.BugCLI162Test" + CR +
- " -2,--jdbc2sfmd Converts the JDBC file in the first argument" + CR +
- " to an SMFD file specified in the second" + CR +
- " argument." + CR +
- " -a,--paramNames Parameter XML names; default names are" + CR +
- " param1, param2, etc. Example: -a \"pname1" + CR +
- " pname2\"" + CR +
- " -b,--jdbc Writes a JDBC binding node file for the given" + CR +
- " SQL" + CR +
- " -c,--url Connection URL" + CR +
- " -d,--driver JDBC driver class name" + CR +
- " -e,--description SFMD description. A default description is" + CR +
- " used if omited. Example: -e \"Runs such and" + CR +
- " such\"" + CR +
- " -f,--sfmd Writes a SFMD file for the given SQL" + CR +
- " -g,--printTiming Prints timing information" + CR +
- " -h,--help Prints help and quits" + CR +
- " -i,--interactive Runs in interactive mode, reading and writing" + CR +
- " from the console, 'go' or '/' sends a" + CR +
- " statement" + CR +
- " -j,--node Writes a JDBC node file for the given SQL" + CR +
- " (internal debugging)" + CR +
- " -l,--columnNames Column XML names; default names column" + CR +
- " labels. Example: -l \"cname1 cname2\"" + CR +
- " -m,--printMetaData Prints metadata information" + CR +
- " -n,--info Prints driver information and properties. If" + CR +
- " -c is not specified, all drivers on the" + CR +
- " classpath are displayed." + CR +
- " -o,--paramModes Parameters modes (1=IN, 2=INOUT, 4=OUT," + CR +
- " 0=Unknown). -o and -O are mutually exclusive." + CR +
- " Example for 2 parameters, OUT and IN: -o \"4" + CR +
- " 1\"" + CR +
- " -O,--paramModeNames Parameters mode names (IN, INOUT, OUT," + CR +
- " Unknown). -o and -O are mutually exclusive." + CR +
- " Example for 2 parameters, OUT and IN: -O \"OUT" + CR +
- " IN\"" + CR +
- " -p,--password The database password for the user specified" + CR +
- " with the -u option. You can obfuscate the" + CR +
- " password with" + CR +
- " org.mortbay.jetty.security.Password, see" + CR +
- " http://docs.codehaus.org/display/JETTY/Securi" + CR +
- " ng+Passwords" + CR +
- " -s,--sql Runs SQL or {call stored_procedure(?, ?)} or" + CR +
- " {?=call function(?, ?)}" + CR +
- " -t,--printStack Prints stack traces on errors" + CR +
- " --trim Trims leading and trailing spaces from all" + CR +
- " column values. Column XML names can be" + CR +
- " optionally specified to set which columns to" + CR +
- " trim." + CR +
- " -u,--user A database user name" + CR +
- " -w,--outfile Writes the SQL output to the given file" + CR +
- " -y,--paramTypes Parameter types from java.sql.Types. -y and" + CR +
- " -Y are mutually exclusive. Example: -y \"-10" + CR +
- " 12\"" + CR +
- " -Y,--paramTypeNames Parameter java.sql.Types names. -y and -Y are" + CR +
- " mutually exclusive. Example: -Y \"CURSOR" + CR +
- " VARCHAR\"" + CR;
//@formatter:on
- assertEquals(expected, sw.toString());
+ assertEquals(EXPECTED, sw.toString());
}
}
diff --git a/src/test/java/org/apache/commons/cli/bug/BugCLI18Test.java b/src/test/java/org/apache/commons/cli/bug/BugCLI18Test.java
index 7104b1c66..94f0fe818 100644
--- a/src/test/java/org/apache/commons/cli/bug/BugCLI18Test.java
+++ b/src/test/java/org/apache/commons/cli/bug/BugCLI18Test.java
@@ -6,7 +6,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
(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
+ https://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,
@@ -23,14 +23,14 @@ Licensed to the Apache Software Foundation (ASF) under one or more
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
/**
* https://issues.apache.org/jira/browse/CLI-18
*/
-public class BugCLI18Test {
+class BugCLI18Test {
@Test
- public void testCLI18() {
+ void testCLI18() {
final Options options = new Options();
options.addOption(new Option("a", "aaa", false, "aaaaaaa"));
options.addOption(new Option(null, "bbb", false, "bbbbbbb dksh fkshd fkhs dkfhsdk fhskd hksdks dhfowehfsdhfkjshf skfhkshf sf jkshfk sfh skfh skf f"));
diff --git a/src/test/java/org/apache/commons/cli/bug/BugCLI252Test.java b/src/test/java/org/apache/commons/cli/bug/BugCLI252Test.java
index e39ac52f9..7bef913bc 100644
--- a/src/test/java/org/apache/commons/cli/bug/BugCLI252Test.java
+++ b/src/test/java/org/apache/commons/cli/bug/BugCLI252Test.java
@@ -6,7 +6,7 @@
* (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
+ * https://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,
@@ -17,29 +17,31 @@
package org.apache.commons.cli.bug;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
import org.apache.commons.cli.AmbiguousOptionException;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-public class BugCLI252Test {
+class BugCLI252Test {
private Options getOptions() {
final Options options = new Options();
- options.addOption(Option.builder().longOpt("prefix").build());
- options.addOption(Option.builder().longOpt("prefixplusplus").build());
+ options.addOption(Option.builder().longOpt("prefix").get());
+ options.addOption(Option.builder().longOpt("prefixplusplus").get());
return options;
}
- @Test(expected = AmbiguousOptionException.class)
- public void testAmbiquousOptionName() throws ParseException {
- new DefaultParser().parse(getOptions(), new String[] {"--pref"});
+ @Test
+ void testAmbiquousOptionName() {
+ assertThrows(AmbiguousOptionException.class, () -> new DefaultParser().parse(getOptions(), new String[] { "--pref" }));
}
@Test
- public void testExactOptionNameMatch() throws ParseException {
+ void testExactOptionNameMatch() throws ParseException {
new DefaultParser().parse(getOptions(), new String[] {"--prefix"});
}
diff --git a/src/test/java/org/apache/commons/cli/bug/BugCLI265Test.java b/src/test/java/org/apache/commons/cli/bug/BugCLI265Test.java
index b1880ea4c..eda81f999 100644
--- a/src/test/java/org/apache/commons/cli/bug/BugCLI265Test.java
+++ b/src/test/java/org/apache/commons/cli/bug/BugCLI265Test.java
@@ -6,7 +6,7 @@
* (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
+ * https://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,
@@ -17,43 +17,43 @@
package org.apache.commons.cli.bug;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
/**
* Test for CLI-265.
*
* The issue is that a short option with an optional value will use whatever comes next as value.
*/
-public class BugCLI265Test {
+class BugCLI265Test {
private DefaultParser parser;
private Options options;
- @Before
+ @BeforeEach
public void setUp() {
parser = new DefaultParser();
- final Option optionT1 = Option.builder("t1").hasArg().numberOfArgs(1).optionalArg(true).argName("t1_path").build();
- final Option optionA = Option.builder("a").hasArg(false).build();
- final Option optionB = Option.builder("b").hasArg(false).build();
- final Option optionLast = Option.builder("last").hasArg(false).build();
+ final Option optionT1 = Option.builder("t1").hasArg().numberOfArgs(1).optionalArg(true).argName("t1_path").get();
+ final Option optionA = Option.builder("a").hasArg(false).get();
+ final Option optionB = Option.builder("b").hasArg(false).get();
+ final Option optionLast = Option.builder("last").hasArg(false).get();
options = new Options().addOption(optionT1).addOption(optionA).addOption(optionB).addOption(optionLast);
}
@Test
- public void shouldParseConcatenatedShortOptions() throws Exception {
+ void testShouldParseConcatenatedShortOptions() throws Exception {
final String[] concatenatedShortOptions = {"-t1", "-ab"};
final CommandLine commandLine = parser.parse(options, concatenatedShortOptions);
@@ -66,18 +66,18 @@ public void shouldParseConcatenatedShortOptions() throws Exception {
}
@Test
- public void shouldParseShortOptionWithoutValue() throws Exception {
+ void testShouldParseShortOptionWithoutValue() throws Exception {
final String[] twoShortOptions = {"-t1", "-last"};
final CommandLine commandLine = parser.parse(options, twoShortOptions);
assertTrue(commandLine.hasOption("t1"));
- assertNotEquals("Second option has been used as value for first option", "-last", commandLine.getOptionValue("t1"));
- assertTrue("Second option has not been detected", commandLine.hasOption("last"));
+ assertNotEquals(commandLine.getOptionValue("t1"), "Second option has been used as value for first option", "-last");
+ assertTrue(commandLine.hasOption("last"), "Second option has not been detected");
}
@Test
- public void shouldParseShortOptionWithValue() throws Exception {
+ void testShouldParseShortOptionWithValue() throws Exception {
final String[] shortOptionWithValue = {"-t1", "path/to/my/db"};
final CommandLine commandLine = parser.parse(options, shortOptionWithValue);
diff --git a/src/test/java/org/apache/commons/cli/bug/BugCLI266Test.java b/src/test/java/org/apache/commons/cli/bug/BugCLI266Test.java
index 885e28090..39cd2fd1e 100644
--- a/src/test/java/org/apache/commons/cli/bug/BugCLI266Test.java
+++ b/src/test/java/org/apache/commons/cli/bug/BugCLI266Test.java
@@ -6,7 +6,7 @@
* (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
+ * https://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,
@@ -17,6 +17,8 @@
package org.apache.commons.cli.bug;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -27,55 +29,48 @@
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
-import org.junit.Assert;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-public class BugCLI266Test {
+class BugCLI266Test {
private final List insertedOrder = Arrays.asList("h", "d", "f", "x", "s", "p", "t", "w", "o");
private final List sortOrder = Arrays.asList("d", "f", "h", "o", "p", "s", "t", "w", "x");
private void buildOptionsGroup(final Options options) {
- final OptionGroup firstGroup = new OptionGroup();
- final OptionGroup secondGroup = new OptionGroup();
- firstGroup.setRequired(true);
- secondGroup.setRequired(true);
+ final OptionGroup optionGroup1 = new OptionGroup();
+ final OptionGroup optionGroup2 = new OptionGroup();
+ optionGroup1.setRequired(true);
+ optionGroup2.setRequired(true);
//@formatter:off
- firstGroup.addOption(Option.builder("d")
- .longOpt("db")
- .hasArg()
- .argName("table-name")
- .build());
- firstGroup.addOption(Option.builder("f")
- .longOpt("flat-file")
- .hasArg()
- .argName("input.csv")
- .build());
+ optionGroup1.addOption(Option.builder("d")
+ .longOpt("db")
+ .hasArg()
+ .argName("table-name").get());
+ optionGroup1.addOption(Option.builder("f")
+ .longOpt("flat-file")
+ .hasArg()
+ .argName("input.csv").get());
//@formatter:on
- options.addOptionGroup(firstGroup);
+ options.addOptionGroup(optionGroup1);
//@formatter:off
- secondGroup.addOption(Option.builder("x")
- .hasArg()
- .argName("arg1")
- .build());
- secondGroup.addOption(Option.builder("s")
- .build());
- secondGroup.addOption(Option.builder("p")
- .hasArg()
- .argName("arg1")
- .build());
+ optionGroup2.addOption(Option.builder("x")
+ .hasArg()
+ .argName("arg1").get());
+ optionGroup2.addOption(Option.builder("s").get());
+ optionGroup2.addOption(Option.builder("p")
+ .hasArg()
+ .argName("arg1").get());
//@formatter:on
- options.addOptionGroup(secondGroup);
+ options.addOptionGroup(optionGroup2);
}
private Options getOptions() {
final Options options = new Options();
//@formatter:off
final Option help = Option.builder("h")
- .longOpt("help")
- .desc("Prints this help message")
- .build();
+ .longOpt("help")
+ .desc("Prints this help message").get();
//@formatter:on
options.addOption(help);
@@ -83,19 +78,16 @@ private Options getOptions() {
//@formatter:off
final Option t = Option.builder("t")
- .required()
- .hasArg()
- .argName("file")
- .build();
+ .required()
+ .hasArg()
+ .argName("file").get();
final Option w = Option.builder("w")
- .required()
- .hasArg()
- .argName("word")
- .build();
+ .required()
+ .hasArg()
+ .argName("word").get();
final Option o = Option.builder("o")
- .hasArg()
- .argName("directory")
- .build();
+ .hasArg()
+ .argName("directory").get();
//@formatter:on
options.addOption(t);
options.addOption(w);
@@ -104,23 +96,23 @@ private Options getOptions() {
}
@Test
- public void testOptionComparatorDefaultOrder() {
+ void testOptionComparatorDefaultOrder() {
final HelpFormatter formatter = new HelpFormatter();
final List options = new ArrayList<>(getOptions().getOptions());
Collections.sort(options, formatter.getOptionComparator());
int i = 0;
for (final Option o : options) {
- Assert.assertEquals(o.getOpt(), sortOrder.get(i));
+ assertEquals(o.getOpt(), sortOrder.get(i));
i++;
}
}
@Test
- public void testOptionComparatorInsertedOrder() {
+ void testOptionComparatorInsertedOrder() {
final Collection options = getOptions().getOptions();
int i = 0;
for (final Option o : options) {
- Assert.assertEquals(o.getOpt(), insertedOrder.get(i));
+ assertEquals(o.getOpt(), insertedOrder.get(i));
i++;
}
}
diff --git a/src/test/java/org/apache/commons/cli/bug/BugCLI312Test.java b/src/test/java/org/apache/commons/cli/bug/BugCLI312Test.java
new file mode 100644
index 000000000..47e3dcced
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/bug/BugCLI312Test.java
@@ -0,0 +1,113 @@
+/*
+ * 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
+ *
+ * https://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.bug;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.Properties;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.MissingArgumentException;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Demonstrates inconsistencies in parsing Java property-style options.
+ */
+class BugCLI312Test {
+ @Test
+ void testNoOptionValues() {
+ final Option o1 = Option.builder("A").get();
+ final Option o2 = Option.builder().option("D").longOpt("define").numberOfArgs(2).valueSeparator('=').get();
+ final Options options = new Options().addOption(o1).addOption(o2);
+
+ final CommandLineParser parser = new DefaultParser();
+
+ assertThrows(MissingArgumentException.class, () -> parser.parse(options, "-D -A".split(" ")));
+ }
+
+ @Test
+ void testPropertyStyleOption_withGetOptionProperties() throws ParseException {
+ final Option o1 = Option.builder().option("D").longOpt("define").numberOfArgs(2).valueSeparator('=').get();
+
+ final Options options = new Options();
+ options.addOption(o1);
+
+ final CommandLineParser parser = new DefaultParser();
+
+ final CommandLine cl = parser.parse(options, "-Dv -Dw=1 -D x=2 -D y -D z=3 other".split(" "));
+ assertArrayEquals(new String[] {"v", "w", "1", "x", "2", "y", "z", "3"}, cl.getOptionValues('D'));
+
+ final Properties properties = cl.getOptionProperties("D");
+ assertEquals("true", properties.getProperty("v"));
+ assertEquals("1", properties.getProperty("w"));
+ assertEquals("2", properties.getProperty("x"));
+ assertEquals("true", properties.getProperty("y"));
+ assertEquals("3", properties.getProperty("z"));
+ assertEquals(5, properties.size());
+ assertEquals("other", cl.getArgList().get(0));
+ }
+
+ @Test
+ void testPropertyStyleOption_withGetOptions() throws ParseException {
+ final Option o1 = Option.builder().option("D").longOpt("define").numberOfArgs(2).valueSeparator('=').get();
+
+ final Options options = new Options();
+ options.addOption(o1);
+
+ final CommandLineParser parser = new DefaultParser();
+
+ final CommandLine cl = parser.parse(options, "-Dv -Dw=1 -D x=2 -D y -D z=3 other".split(" "));
+ assertArrayEquals(new String[] {"v", "w", "1", "x", "2", "y", "z", "3"}, cl.getOptionValues('D'));
+
+ int defineOptionsFound = 0;
+ for (final Option o : cl.getOptions()) {
+ if ("D".equals(o.getOpt())) {
+ defineOptionsFound++;
+
+ switch (defineOptionsFound) {
+ case 1:
+ assertArrayEquals(new String[] {"v"}, o.getValues());
+ break;
+ case 2:
+ assertArrayEquals(new String[] {"w", "1"}, o.getValues());
+ break;
+ case 3:
+ assertArrayEquals(new String[] {"x", "2"}, o.getValues());
+ break;
+ case 4:
+ assertArrayEquals(new String[] {"y"}, o.getValues());
+ break;
+ case 5:
+ assertArrayEquals(new String[] {"z", "3"}, o.getValues());
+ break;
+ default:
+ fail("Didn't expect " + defineOptionsFound + " occurrences of -D");
+ break;
+ }
+ }
+ }
+ assertEquals("other", cl.getArgList().get(0));
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/bug/BugCLI325Test.java b/src/test/java/org/apache/commons/cli/bug/BugCLI325Test.java
new file mode 100644
index 000000000..58fd2c005
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/bug/BugCLI325Test.java
@@ -0,0 +1,48 @@
+/*
+ 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
+
+ https://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.bug;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Properties;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.junit.jupiter.api.Test;
+
+class BugCLI325Test {
+
+ @Test
+ void testCli325() throws ParseException {
+ // @formatter:off
+ final Option option = Option.builder("x")
+ .hasArgs()
+ .valueSeparator()
+ .desc("Multiple arg option with value separator.").get();
+ // @formatter:on
+ final String[] args = {"-x", "A=a", "B=b"};
+ final CommandLine cmdLine = DefaultParser.builder().get().parse(new Options().addOption(option), args);
+ final Properties props = cmdLine.getOptionProperties(option);
+ assertEquals(2, props.size());
+ assertEquals("a", props.get("A"));
+ assertEquals("b", props.get("B"));
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/bug/BugCLI71Test.java b/src/test/java/org/apache/commons/cli/bug/BugCLI71Test.java
index 8457c8097..d4edd48e1 100644
--- a/src/test/java/org/apache/commons/cli/bug/BugCLI71Test.java
+++ b/src/test/java/org/apache/commons/cli/bug/BugCLI71Test.java
@@ -6,7 +6,7 @@
* (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
+ * https://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,
@@ -17,8 +17,8 @@
package org.apache.commons.cli.bug;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
@@ -26,15 +26,15 @@
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class BugCLI71Test {
+class BugCLI71Test {
private Options options;
private CommandLineParser parser;
- @Before
+ @BeforeEach
public void setUp() {
options = new Options();
@@ -50,7 +50,7 @@ public void setUp() {
}
@Test
- public void testBasic() throws Exception {
+ void testBasic() throws Exception {
final String[] args = {"-a", "Caesar", "-k", "A"};
final CommandLine line = parser.parse(options, args);
assertEquals("Caesar", line.getOptionValue("a"));
@@ -58,7 +58,7 @@ public void testBasic() throws Exception {
}
@Test
- public void testGetsDefaultIfOptional() throws Exception {
+ void testGetsDefaultIfOptional() throws Exception {
final String[] args = {"-k", "-a", "Caesar"};
options.getOption("k").setOptionalArg(true);
final CommandLine line = parser.parse(options, args);
@@ -68,18 +68,14 @@ public void testGetsDefaultIfOptional() throws Exception {
}
@Test
- public void testLackOfError() throws Exception {
- final String[] args = {"-k", "-a", "Caesar"};
- try {
- parser.parse(options, args);
- fail("MissingArgumentException expected");
- } catch (final MissingArgumentException e) {
- assertEquals("option missing an argument", "k", e.getOption().getOpt());
- }
+ void testLackOfError() throws Exception {
+ final String[] args = { "-k", "-a", "Caesar" };
+ final MissingArgumentException e = assertThrows(MissingArgumentException.class, () -> parser.parse(options, args));
+ assertEquals("k", e.getOption().getOpt(), "option missing an argument");
}
@Test
- public void testMistakenArgument() throws Exception {
+ void testMistakenArgument() throws Exception {
String[] args = {"-a", "Caesar", "-k", "A"};
CommandLine line = parser.parse(options, args);
args = new String[] {"-a", "Caesar", "-k", "a"};
diff --git a/src/test/java/org/apache/commons/cli/bug/BugsTest.java b/src/test/java/org/apache/commons/cli/bug/BugsTest.java
index 2eb6ea73a..0d3aeb7fe 100644
--- a/src/test/java/org/apache/commons/cli/bug/BugsTest.java
+++ b/src/test/java/org/apache/commons/cli/bug/BugsTest.java
@@ -6,7 +6,7 @@
* (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
+ * https://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,
@@ -17,14 +17,17 @@
package org.apache.commons.cli.bug;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
+import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Properties;
@@ -40,13 +43,14 @@
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.Parser;
import org.apache.commons.cli.PosixParser;
-import org.junit.Test;
+import org.apache.commons.lang3.ArrayUtils;
+import org.junit.jupiter.api.Test;
@SuppressWarnings("deprecation") // tests some deprecated classes
-public class BugsTest {
+class BugsTest {
@Test
- public void test11456() throws Exception {
- // Posix
+ void test11456() throws Exception {
+ // POSIX
Options options = new Options();
options.addOption(OptionBuilder.hasOptionalArg().create('a'));
options.addOption(OptionBuilder.hasArg().create('b'));
@@ -70,7 +74,7 @@ public void test11456() throws Exception {
}
@Test
- public void test11457() throws Exception {
+ void test11457() throws Exception {
final Options options = new Options();
options.addOption(OptionBuilder.withLongOpt("verbose").create());
final String[] args = {"--verbose"};
@@ -82,7 +86,7 @@ public void test11457() throws Exception {
}
@Test
- public void test11458() throws Exception {
+ void test11458() throws Exception {
final Options options = new Options();
options.addOption(OptionBuilder.withValueSeparator('=').hasArgs().create('D'));
options.addOption(OptionBuilder.withValueSeparator(':').hasArgs().create('p'));
@@ -123,22 +127,32 @@ public void test11458() throws Exception {
}
@Test
- public void test11680() throws Exception {
+ void test11680() throws Exception {
final Options options = new Options();
- options.addOption("f", true, "foobar");
- options.addOption("m", true, "missing");
- final String[] args = {"-f", "foo"};
-
+ final Option optionF = options.addOption("f", true, "foobar").getOption("f");
+ final Option optionM = options.addOption("m", true, "missing").getOption("m");
+ final String[] args = { "-f", "foo" };
final CommandLineParser parser = new PosixParser();
-
final CommandLine cmd = parser.parse(options, args);
-
+ // 1.7.0 API:
+ cmd.getOptionValue(optionF, () -> "default f");
+ cmd.getOptionValue(optionM, () -> "default m");
+ // 1.7.0 API:
+ cmd.getOptionValue('f', () -> "default f");
+ cmd.getOptionValue('m', () -> "default m");
+ // 1.5.0 API:
+ cmd.getOptionValue(optionF, "default f");
+ cmd.getOptionValue(optionM, "default m");
+ // Original API:
cmd.getOptionValue("f", "default f");
cmd.getOptionValue("m", "default m");
+ //
+ assertNull(cmd.getOptionValue((String) null, (String) null));
+ assertEquals("default", cmd.getOptionValue((String) null, "default"));
}
@Test
- public void test12210() throws Exception {
+ void test12210() throws Exception {
// create the main options object which will handle the first parameter
final Options mainOptions = new Options();
// There can be 2 main exclusive options: -exec|-rep
@@ -146,13 +160,13 @@ public void test12210() throws Exception {
// Therefore, place them in an option group
String[] argv = {"-exec", "-exec_opt1", "-exec_opt2"};
- final OptionGroup grp = new OptionGroup();
+ final OptionGroup optionGroup = new OptionGroup();
- grp.addOption(new Option("exec", false, "description for this option"));
+ optionGroup.addOption(new Option("exec", false, "description for this option"));
- grp.addOption(new Option("rep", false, "description for this option"));
+ optionGroup.addOption(new Option("rep", false, "description for this option"));
- mainOptions.addOptionGroup(grp);
+ mainOptions.addOptionGroup(optionGroup);
// for the exec option, there are 2 options...
final Options execOptions = new Options();
@@ -190,7 +204,7 @@ public void test12210() throws Exception {
}
@Test
- public void test13425() throws Exception {
+ void test13425() throws Exception {
final Options options = new Options();
//@formatter:off
final Option oldpass = OptionBuilder.withLongOpt("old-password")
@@ -202,50 +216,44 @@ public void test13425() throws Exception {
.hasArg()
.create('n');
//@formatter:on
-
final String[] args = {"-o", "-n", "newpassword"};
-
options.addOption(oldpass);
options.addOption(newpass);
-
- final Parser parser = new PosixParser();
-
- try {
- parser.parse(options, args);
- fail("MissingArgumentException not caught.");
- } catch (final MissingArgumentException expected) {
- }
+ assertThrows(MissingArgumentException.class, () -> new PosixParser().parse(options, args));
}
@Test
- public void test13666() throws Exception {
+ void test13666() throws Exception {
final Options options = new Options();
- final Option dir = OptionBuilder.withDescription("dir").hasArg().create('d');
- options.addOption(dir);
-
+ final Option dirOption = OptionBuilder.withDescription("dir").hasArg().create('d');
+ options.addOption(dirOption);
final PrintStream oldSystemOut = System.out;
try {
- final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- final PrintStream print = new PrintStream(bytes);
-
- // capture this platform's eol symbol
- print.println();
- final String eol = bytes.toString();
- bytes.reset();
-
- System.setOut(new PrintStream(bytes));
-
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final String eol = System.lineSeparator();
+ System.setOut(new PrintStream(baos));
final HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("dir", options);
-
- assertEquals("usage: dir" + eol + " -d dir" + eol, bytes.toString());
+ assertEquals("usage: dir" + eol + " -d dir" + eol, baos.toString());
} finally {
System.setOut(oldSystemOut);
}
}
@Test
- public void test13935() throws Exception {
+ void test13666_Builder() throws Exception {
+ final Options options = new Options();
+ final Option dirOption = OptionBuilder.withDescription("dir").hasArg().create('d');
+ options.addOption(dirOption);
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final String eol = System.lineSeparator();
+ final HelpFormatter formatter = HelpFormatter.builder().setPrintWriter(new PrintWriter(baos)).get();
+ formatter.printHelp("dir", options);
+ assertEquals("usage: dir" + eol + " -d dir" + eol, baos.toString());
+ }
+
+ @Test
+ void test13935() throws Exception {
final OptionGroup directions = new OptionGroup();
final Option left = new Option("l", "left", false, "go left");
@@ -264,21 +272,10 @@ public void test13935() throws Exception {
final CommandLineParser parser = new PosixParser();
- String[] args = {};
- try {
- parser.parse(opts, args);
- fail("Expected ParseException");
- } catch (final ParseException expected) {
- }
-
- args = new String[] {"-s"};
- try {
- parser.parse(opts, args);
- fail("Expected ParseException");
- } catch (final ParseException expected) {
- }
+ assertThrows(ParseException.class, () -> parser.parse(opts, ArrayUtils.EMPTY_STRING_ARRAY));
+ assertThrows(ParseException.class, () -> parser.parse(opts, new String[] {"-s"}));
- args = new String[] {"-s", "-l"};
+ String[] args = {"-s", "-l"};
CommandLine line = parser.parse(opts, args);
assertNotNull(line);
@@ -289,7 +286,7 @@ public void test13935() throws Exception {
}
@Test
- public void test14786() throws Exception {
+ void test14786() throws Exception {
final Option o = OptionBuilder.isRequired().withDescription("test").create("test");
final Options opts = new Options();
opts.addOption(o);
@@ -304,7 +301,7 @@ public void test14786() throws Exception {
}
@Test
- public void test15046() throws Exception {
+ void test15046() throws Exception {
final CommandLineParser parser = new PosixParser();
final String[] cliArgs = {"-z", "c"};
@@ -321,7 +318,7 @@ public void test15046() throws Exception {
}
@Test
- public void test15648() throws Exception {
+ void test15648() throws Exception {
final CommandLineParser parser = new PosixParser();
final String[] args = {"-m", "\"Two Words\""};
final Option m = OptionBuilder.hasArgs().create("m");
@@ -332,7 +329,7 @@ public void test15648() throws Exception {
}
@Test
- public void test31148() throws ParseException {
+ void test31148() throws ParseException {
final Option multiArgOption = new Option("o", "option with multiple args");
multiArgOption.setArgs(1);
diff --git a/src/test/java/org/apache/commons/cli/example/AptHelpAppendable.java b/src/test/java/org/apache/commons/cli/example/AptHelpAppendable.java
new file mode 100644
index 000000000..cbedfe225
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/example/AptHelpAppendable.java
@@ -0,0 +1,149 @@
+/*
+ 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
+
+ https://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.example;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.cli.help.FilterHelpAppendable;
+import org.apache.commons.cli.help.TableDefinition;
+import org.apache.commons.cli.help.TextStyle;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.translate.CharSequenceTranslator;
+import org.apache.commons.text.translate.LookupTranslator;
+
+/**
+ * Appends APT formatted text to an {@link Appendable}.
+ */
+public class AptHelpAppendable extends FilterHelpAppendable {
+
+ /**
+ * Translator object for escaping APT codes
+ */
+ public static final CharSequenceTranslator ESCAPE_APT;
+
+ static {
+ final Map escapeAptMap = new HashMap<>();
+ escapeAptMap.put("\\", "\\\\");
+ escapeAptMap.put("\"", "\\\"");
+ escapeAptMap.put("*", "\\*");
+ escapeAptMap.put("+", "\\+");
+ escapeAptMap.put("|", "\\|");
+ ESCAPE_APT = new LookupTranslator(escapeAptMap);
+ }
+
+ /**
+ * Constructs an appendable filter built on top of the specified underlying appendable.
+ *
+ * @param output the underlying appendable to be assigned to the field {@code this.output} for later use, or {@code null} if this instance is to be created
+ * without an underlying stream.
+ */
+ public AptHelpAppendable(final Appendable output) {
+ super(output);
+ }
+
+ @Override
+ public void appendHeader(final int level, final CharSequence text) throws IOException {
+ if (StringUtils.isNotEmpty(text)) {
+ if (level < 1) {
+ throw new IllegalArgumentException("level must be at least 1");
+ }
+ for (int i = 0; i < level; i++) {
+ output.append("*");
+ }
+ appendFormat(" %s%n%n", ESCAPE_APT.translate(text));
+ }
+ }
+
+ @Override
+ public void appendList(final boolean ordered, final Collection list) throws IOException {
+ if (list != null) {
+ if (ordered) {
+ int idx = 1;
+ for (final CharSequence s : list) {
+ appendFormat(" [[%s]] %s%n", idx++, ESCAPE_APT.translate(s));
+ }
+ } else {
+ for (final CharSequence s : list) {
+ appendFormat(" * %s%n", ESCAPE_APT.translate(s));
+ }
+ }
+ output.append(System.lineSeparator());
+ }
+ }
+
+ @Override
+ public void appendParagraph(final CharSequence paragraph) throws IOException {
+ if (StringUtils.isNotEmpty(paragraph)) {
+ appendFormat(" %s%n%n", ESCAPE_APT.translate(paragraph));
+ }
+ }
+
+ @Override
+ public void appendTable(final TableDefinition table) throws IOException {
+ if (table != null) {
+ // create the row separator string
+ final StringBuilder sb = new StringBuilder("*");
+ for (int i = 0; i < table.headers().size(); i++) {
+ final String header = table.headers().get(i);
+ final TextStyle style = table.columnTextStyles().get(i);
+ sb.append(StringUtils.repeat('-', header.length() + 2));
+ switch (style.getAlignment()) {
+ case LEFT:
+ sb.append("+");
+ break;
+ case CENTER:
+ sb.append("*");
+ break;
+ case RIGHT:
+ sb.append(":");
+ break;
+ }
+ }
+ final String rowSeparator = System.lineSeparator() + sb.append(System.lineSeparator());
+ // output the header line.
+ output.append(sb.toString());
+ output.append("|");
+ for (final String header : table.headers()) {
+ appendFormat(" %s |", ESCAPE_APT.translate(header));
+ }
+ output.append(rowSeparator);
+ // write the table entries
+ for (final Collection row : table.rows()) {
+ output.append("|");
+ for (final String cell : row) {
+ appendFormat(" %s |", ESCAPE_APT.translate(cell));
+ }
+ output.append(rowSeparator);
+ }
+ // write the caption
+ if (StringUtils.isNotEmpty(table.caption())) {
+ appendFormat("%s%n", ESCAPE_APT.translate(table.caption()));
+ }
+ output.append(System.lineSeparator());
+ }
+ }
+
+ @Override
+ public void appendTitle(final CharSequence title) throws IOException {
+ if (StringUtils.isNotEmpty(title)) {
+ appendFormat(" -----%n %1$s%n -----%n%n%1$s%n%n", title);
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/example/AptHelpAppendableTest.java b/src/test/java/org/apache/commons/cli/example/AptHelpAppendableTest.java
new file mode 100644
index 000000000..b4a9408e9
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/example/AptHelpAppendableTest.java
@@ -0,0 +1,140 @@
+/*
+ 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
+
+ https://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.example;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.cli.help.TableDefinition;
+import org.apache.commons.cli.help.TextStyle;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link AptHelpAppendable}.
+ */
+class AptHelpAppendableTest {
+
+ private StringBuilder sb;
+ private AptHelpAppendable underTest;
+
+ @BeforeEach
+ public void beforeEach() {
+ sb = new StringBuilder();
+ underTest = new AptHelpAppendable(sb);
+ }
+
+ @Test
+ void testAppendFormatTest() throws IOException {
+ underTest.appendFormat("Big %s and Phantom %,d", "Joe", 309);
+ assertEquals(String.format("Big Joe and Phantom 309"), sb.toString());
+ }
+
+ @Test
+ void testAppendHeaderTest() throws IOException {
+ underTest.appendHeader(1, "Hello World");
+ assertEquals(String.format("* Hello World%n%n"), sb.toString());
+ sb.setLength(0);
+ underTest.appendHeader(2, "Hello World");
+ assertEquals(String.format("** Hello World%n%n"), sb.toString());
+ sb.setLength(0);
+ assertThrows(IllegalArgumentException.class, () -> underTest.appendHeader(0, "Hello World"));
+ }
+
+ @Test
+ void testAppendListTest() throws IOException {
+ final String[] entries = { "one", "two", "three" };
+ underTest.appendList(true, Arrays.asList(entries));
+ assertEquals(String.format(" [[1]] one%n [[2]] two%n [[3]] three%n%n"), sb.toString());
+ sb.setLength(0);
+ underTest.appendList(false, Arrays.asList(entries));
+ assertEquals(String.format(" * one%n * two%n * three%n%n"), sb.toString());
+ }
+
+ @Test
+ void testAppendParagraphFormatTest() throws IOException {
+ underTest.appendParagraphFormat("Hello %s World %,d", "Big Joe", 309);
+ assertEquals(String.format(" Hello Big Joe World 309%n%n"), sb.toString());
+ }
+
+ @Test
+ void testAppendParagraphTest() throws IOException {
+ underTest.appendParagraph("Hello World");
+ assertEquals(String.format(" Hello World%n%n"), sb.toString());
+ }
+
+ @Test
+ void testAppendTableTest() throws IOException {
+ final List styles = Arrays.asList(TextStyle.DEFAULT, TextStyle.DEFAULT, TextStyle.DEFAULT);
+ final String[] headers = { "one", "two", "three" };
+ // @formatter:off
+ final List> rows = Arrays.asList(
+ Arrays.asList(new String[]{"uno", "dos", "tres"}),
+ Arrays.asList(new String[]{"aon", "dhá", "trí"}),
+ Arrays.asList(new String[]{"واحد", "اثنين", "ثلاثة"})
+ );
+ // @formatter:on
+ List expected = new ArrayList<>();
+ expected.add("*-----+-----+-------+");
+ expected.add("| one | two | three |");
+ expected.add("*-----+-----+-------+");
+ expected.add("| uno | dos | tres |");
+ expected.add("*-----+-----+-------+");
+ expected.add("| aon | dhá | trí |");
+ expected.add("*-----+-----+-------+");
+ expected.add("| واحد | اثنين | ثلاثة |");
+ expected.add("*-----+-----+-------+");
+ expected.add("The caption");
+ expected.add("");
+ TableDefinition table = TableDefinition.from("The caption", styles, Arrays.asList(headers), rows);
+ underTest.appendTable(table);
+ List actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual, "full table failed");
+ table = TableDefinition.from(null, styles, Arrays.asList(headers), rows);
+ expected.remove(9);
+ sb.setLength(0);
+ underTest.appendTable(table);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+ table = TableDefinition.from(null, styles, Arrays.asList(headers), Collections.emptyList());
+ expected = new ArrayList<>();
+ expected.add("*-----+-----+-------+");
+ expected.add("| one | two | three |");
+ expected.add("*-----+-----+-------+");
+ expected.add("");
+ sb.setLength(0);
+ underTest.appendTable(table);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual, "no rows test failed");
+
+ }
+
+ @Test
+ void testAppendTitleTest() throws IOException {
+ sb.setLength(0);
+ underTest.appendTitle("Hello World");
+ assertEquals(String.format(" -----%n Hello World%n -----%n%nHello World%n%n"), sb.toString());
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/example/WeirdOptionFormat.java b/src/test/java/org/apache/commons/cli/example/WeirdOptionFormat.java
new file mode 100644
index 000000000..bccdd36c9
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/example/WeirdOptionFormat.java
@@ -0,0 +1,64 @@
+/*
+ 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
+
+ https://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.example;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.help.TableDefinition;
+import org.apache.commons.cli.help.TextStyle;
+import org.apache.commons.lang3.StringUtils;
+
+public class WeirdOptionFormat implements Function, TableDefinition> {
+ private final String[] headers = { "Opt", "Since", "Required", "LongOpt", "Deprecated", "Arg Name", "Type", "Description" };
+
+ private final List styles;
+
+ public WeirdOptionFormat() {
+ styles = new ArrayList<>();
+ final TextStyle.Builder builder = TextStyle.builder();
+ styles.add(builder.setLeftPad(1).setIndent(3).get());
+ styles.add(builder.setLeftPad(5).get());
+ styles.add(builder.get());
+ styles.add(builder.get());
+ styles.add(builder.get());
+ styles.add(builder.get());
+ styles.add(builder.get());
+ styles.add(builder.get());
+ }
+
+ @Override
+ public TableDefinition apply(final Iterable options) {
+ final List> rows = new ArrayList<>();
+ for (final Option option : options) {
+ final List row = new ArrayList<>();
+ row.add(option.getOpt());
+ row.add(StringUtils.defaultIfEmpty(option.getSince(), "--"));
+ row.add(option.isRequired() ? "T" : "F");
+ row.add(option.getLongOpt());
+ row.add(option.isDeprecated() ? "T" : "F");
+ row.add(option.hasArg() ? option.getArgName() : "--");
+ row.add(option.getType() == null ? "--" : option.getValue().toString());
+ row.add(option.getDescription());
+ rows.add(row);
+ }
+ return TableDefinition.from("", styles, Arrays.asList(headers), rows);
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/example/XhtmlHelpAppendable.java b/src/test/java/org/apache/commons/cli/example/XhtmlHelpAppendable.java
new file mode 100644
index 000000000..51ae058d0
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/example/XhtmlHelpAppendable.java
@@ -0,0 +1,105 @@
+/*
+ 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
+
+ https://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.example;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.cli.help.FilterHelpAppendable;
+import org.apache.commons.cli.help.TableDefinition;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.StringEscapeUtils;
+
+/**
+ * Appends XHTML formatted text to an {@link Appendable}.
+ */
+public class XhtmlHelpAppendable extends FilterHelpAppendable {
+
+ /**
+ * Constructs an appendable filter built on top of the specified underlying appendable.
+ *
+ * @param output the underlying appendable to be assigned to the field {@code this.output} for later use, or {@code null} if this instance is to be created
+ * without an underlying stream.
+ */
+ public XhtmlHelpAppendable(final Appendable output) {
+ super(output);
+ }
+
+ @Override
+ public void appendHeader(final int level, final CharSequence text) throws IOException {
+ if (StringUtils.isNotEmpty(text)) {
+ if (level < 1) {
+ throw new IllegalArgumentException("level must be at least 1");
+ }
+ appendFormat("%s %n", level, StringEscapeUtils.escapeHtml4(Objects.toString(text)));
+ }
+ }
+
+ @Override
+ public void appendList(final boolean ordered, final Collection list) throws IOException {
+ if (list != null) {
+ appendFormat("<%sl>%n", ordered ? "o" : "u");
+ for (final CharSequence line : list) {
+ appendFormat(" %s %n", StringEscapeUtils.escapeHtml4(StringUtils.defaultIfEmpty(line, "").toString()));
+ }
+ appendFormat("%sl>%n", ordered ? "o" : "u");
+ }
+ }
+
+ @Override
+ public void appendParagraph(final CharSequence paragraph) throws IOException {
+ if (StringUtils.isNotEmpty(paragraph)) {
+ appendFormat("%s
%n", StringEscapeUtils.escapeHtml4(Objects.toString(paragraph)));
+ }
+ }
+
+ @Override
+ public void appendTable(final TableDefinition table) throws IOException {
+ if (table != null) {
+ appendFormat("%n");
+ if (StringUtils.isNotEmpty(table.caption())) {
+ appendFormat(" %s %n", StringEscapeUtils.escapeHtml4(table.caption()));
+ }
+ // write the headers
+ if (!table.headers().isEmpty()) {
+ appendFormat(" %n");
+ for (final String header : table.headers()) {
+ appendFormat(" %s %n", StringEscapeUtils.escapeHtml4(header));
+ }
+ appendFormat(" %n");
+ }
+ // write the data
+ for (final List row : table.rows()) {
+ appendFormat(" %n");
+ for (final String column : row) {
+ appendFormat(" %s %n", StringEscapeUtils.escapeHtml4(column));
+ }
+ appendFormat(" %n");
+ }
+ appendFormat("
%n");
+ }
+ }
+
+ @Override
+ public void appendTitle(final CharSequence title) throws IOException {
+ if (StringUtils.isNotEmpty(title)) {
+ appendFormat("%s %n", StringEscapeUtils.escapeHtml4(Objects.toString(title)));
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/example/XhtmlHelpAppendableTest.java b/src/test/java/org/apache/commons/cli/example/XhtmlHelpAppendableTest.java
new file mode 100644
index 000000000..db78ee249
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/example/XhtmlHelpAppendableTest.java
@@ -0,0 +1,147 @@
+/*
+ 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
+
+ https://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.example;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.cli.help.TableDefinition;
+import org.apache.commons.cli.help.TextStyle;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link XhtmlHelpAppendable}.
+ */
+class XhtmlHelpAppendableTest {
+
+ private StringBuilder sb;
+ private XhtmlHelpAppendable underTest;
+
+ @BeforeEach
+ public void beforeEach() {
+ sb = new StringBuilder();
+ underTest = new XhtmlHelpAppendable(sb);
+ }
+
+ @Test
+ void testAppendHeaderTest() throws IOException {
+ underTest.appendHeader(1, "Hello World");
+ assertEquals(String.format("Hello World %n"), sb.toString());
+ sb.setLength(0);
+ underTest.appendHeader(2, "Hello World");
+ assertEquals(String.format("Hello World %n"), sb.toString());
+ sb.setLength(0);
+ assertThrows(IllegalArgumentException.class, () -> underTest.appendHeader(0, "Hello World"));
+ }
+
+ @Test
+ void testAppendListTest() throws IOException {
+ final String[] entries = { "one", "two", "three" };
+ underTest.appendList(true, Arrays.asList(entries));
+ assertEquals(String.format("%n one %n two %n three %n %n"), sb.toString());
+ sb.setLength(0);
+ underTest.appendList(false, Arrays.asList(entries));
+ assertEquals(String.format("%n"), sb.toString());
+ }
+
+ @Test
+ void testAppendParagraphFormatTest() throws IOException {
+ underTest.appendParagraphFormat("Hello %s World %,d", "Joe", 309);
+ assertEquals(String.format("Hello Joe World 309
%n"), sb.toString());
+ }
+
+ @Test
+ void testAppendParagraphTest() throws IOException {
+ underTest.appendParagraph("Hello World");
+ assertEquals(String.format("Hello World
%n"), sb.toString());
+ }
+
+ @Test
+ void testAppendTableTest() throws IOException {
+ final List styles = Arrays.asList(TextStyle.DEFAULT, TextStyle.DEFAULT, TextStyle.DEFAULT);
+ final String[] headers = { "one", "two", "three" };
+ // @formatter:off
+ final List> rows = Arrays.asList(
+ Arrays.asList(new String[]{"uno", "dos", "tres"}),
+ Arrays.asList(new String[]{"aon", "dhá", "trí"}),
+ Arrays.asList(new String[]{"واحد", "اثنين", "ثلاثة"})
+ );
+ // @formatter:on
+ List expected = new ArrayList<>();
+ expected.add("");
+ expected.add(" The caption ");
+ expected.add(" ");
+ expected.add(" one ");
+ expected.add(" two ");
+ expected.add(" three ");
+ expected.add(" ");
+ expected.add(" ");
+ expected.add(" uno ");
+ expected.add(" dos ");
+ expected.add(" tres ");
+ expected.add(" ");
+ expected.add(" ");
+ expected.add(" aon ");
+ expected.add(" dhá ");
+ expected.add(" trí ");
+ expected.add(" ");
+ expected.add(" ");
+ expected.add(" واحد ");
+ expected.add(" اثنين ");
+ expected.add(" ثلاثة ");
+ expected.add(" ");
+ expected.add("
");
+ TableDefinition table = TableDefinition.from("The caption", styles, Arrays.asList(headers), rows);
+ underTest.appendTable(table);
+ List actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual, "full table failed");
+ table = TableDefinition.from(null, styles, Arrays.asList(headers), rows);
+ expected.remove(1);
+ sb.setLength(0);
+ underTest.appendTable(table);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+ table = TableDefinition.from(null, styles, Arrays.asList(headers), Collections.emptyList());
+ expected = new ArrayList<>();
+ expected.add("");
+ expected.add(" ");
+ expected.add(" one ");
+ expected.add(" two ");
+ expected.add(" three ");
+ expected.add(" ");
+ expected.add("
");
+ sb.setLength(0);
+ underTest.appendTable(table);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual, "no rows test failed");
+ }
+
+ @Test
+ void testAppendTitleTest() throws IOException {
+ underTest.appendTitle("Hello World");
+ assertEquals(String.format("Hello World %n"), sb.toString());
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/help/HelpFormatterTest.java b/src/test/java/org/apache/commons/cli/help/HelpFormatterTest.java
new file mode 100644
index 000000000..41e9c08d0
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/help/HelpFormatterTest.java
@@ -0,0 +1,521 @@
+/*
+ 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
+
+ https://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.help;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionGroup;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.example.XhtmlHelpAppendable;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link HelpFormatter}.
+ */
+class HelpFormatterTest {
+
+ private Options getTestGroups() {
+ // @formatter:off
+ return new Options()
+ .addOptionGroup(new OptionGroup()
+ .addOption(Option.builder("1").longOpt("one").hasArg().desc("English one").get())
+ .addOption(Option.builder().longOpt("aon").hasArg().desc("Irish one").get())
+ .addOption(Option.builder().longOpt("uno").hasArg().desc("Spanish one").get())
+ )
+ .addOptionGroup(new OptionGroup()
+ .addOption(Option.builder().longOpt("two").hasArg().desc("English two").get())
+ .addOption(Option.builder().longOpt("dó").hasArg().desc("Irish twp").get())
+ .addOption(Option.builder().longOpt("dos").hasArg().desc("Spanish two").get())
+ )
+ .addOptionGroup(new OptionGroup()
+ .addOption(Option.builder().longOpt("three").hasArg().desc("English three").get())
+ .addOption(Option.builder().longOpt("trí").hasArg().desc("Irish three").get())
+ .addOption(Option.builder().longOpt("tres").hasArg().desc("Spanish three").get())
+ );
+ // @formatter:on
+ }
+
+ @Test
+ void testDefault() {
+ final StringBuilder sb = new StringBuilder();
+ final TextHelpAppendable serializer = new TextHelpAppendable(sb);
+ final HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
+ assertEquals(serializer, formatter.getSerializer(), "Unexpected helpAppendable tests may fail unexpectedly");
+ assertEquals(AbstractHelpFormatter.DEFAULT_COMPARATOR, formatter.getComparator(), "Unexpected comparator tests may fail unexpectedly");
+ assertEquals(AbstractHelpFormatter.DEFAULT_SYNTAX_PREFIX, formatter.getSyntaxPrefix(), "Unexpected syntax prefix tests may fail unexpectedly");
+ }
+
+ @Test
+ void testPrintHelp() throws IOException {
+ final StringBuilder sb = new StringBuilder();
+ final TextHelpAppendable serializer = new TextHelpAppendable(sb);
+ HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
+
+ final Options options = new Options().addOption(Option.builder("a").since("1853").hasArg().desc("aaaa aaaa aaaa aaaa aaaa").get());
+
+ List expected = new ArrayList<>();
+ expected.add(" usage: commandSyntax [-a ]");
+ expected.add("");
+ expected.add(" header");
+ expected.add("");
+ expected.add(" Options Since Description ");
+ expected.add(" -a 1853 aaaa aaaa aaaa aaaa aaaa");
+ expected.add("");
+ expected.add(" footer");
+ expected.add("");
+
+ formatter.printHelp("commandSyntax", "header", options, "footer", true);
+ List actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ formatter = HelpFormatter.builder().setShowSince(false).setHelpAppendable(serializer).get();
+ expected = new ArrayList<>();
+ expected.add(" usage: commandSyntax [-a ]");
+ expected.add("");
+ expected.add(" header");
+ expected.add("");
+ expected.add(" Options Description ");
+ expected.add(" -a aaaa aaaa aaaa aaaa aaaa");
+ expected.add("");
+ expected.add(" footer");
+ expected.add("");
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", "header", options, "footer", true);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", "header", options, "footer", false);
+ expected.set(0, " usage: commandSyntax");
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", "", options, "footer", false);
+ expected.remove(3);
+ expected.remove(2);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", null, options, "footer", false);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", null, options, "", false);
+ expected.remove(6);
+ expected.remove(5);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", null, options, null, false);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ final HelpFormatter fHelp = formatter;
+ assertThrows(IllegalArgumentException.class, () -> fHelp.printHelp("", "header", options, "footer", true));
+ assertEquals(0, sb.length(), "Should not write to output");
+ assertThrows(IllegalArgumentException.class, () -> fHelp.printHelp(null, "header", options, "footer", true));
+ assertEquals(0, sb.length(), "Should not write to output");
+ }
+
+ /**
+ * Tests example from the mailing list that caused an infinite loop.
+ *
+ * @see [CLI-351] Multiple traililng BREAK_CHAR_SET characters cause infinite loop in
+ * HelpFormatter
+ */
+ @Test
+ void testPrintHelpHeader() throws IOException {
+ HelpFormatter.builder().get().printHelp("CL syntax", "Header", Collections.emptyList(), "Footer", true);
+ HelpFormatter.builder().get().printHelp("CL syntax", "Header\n\n", // This makes printHelp enter into an infinite loop
+ Collections.emptyList(), "Footer", true);
+ }
+
+ @Test
+ public void testPrintHelpWithIterableOptions() throws IOException {
+ final StringBuilder sb = new StringBuilder();
+ final TextHelpAppendable serializer = new TextHelpAppendable(sb);
+ HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
+
+ final List options = new ArrayList<>();
+ options.add(Option.builder("a").since("1853").hasArg().desc("aaaa aaaa aaaa aaaa aaaa").build());
+
+ List expected = new ArrayList<>();
+ expected.add(" usage: commandSyntax [-a ]");
+ expected.add("");
+ expected.add(" header");
+ expected.add("");
+ expected.add(" Options Since Description ");
+ expected.add(" -a 1853 aaaa aaaa aaaa aaaa aaaa");
+ expected.add("");
+ expected.add(" footer");
+ expected.add("");
+
+ formatter.printHelp("commandSyntax", "header", options, "footer", true);
+ List actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ formatter = HelpFormatter.builder().setShowSince(false).setHelpAppendable(serializer).get();
+ expected = new ArrayList<>();
+ expected.add(" usage: commandSyntax [-a ]");
+ expected.add("");
+ expected.add(" header");
+ expected.add("");
+ expected.add(" Options Description ");
+ expected.add(" -a aaaa aaaa aaaa aaaa aaaa");
+ expected.add("");
+ expected.add(" footer");
+ expected.add("");
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", "header", options, "footer", true);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", "header", options, "footer", false);
+ expected.set(0, " usage: commandSyntax");
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", "", options, "footer", false);
+ expected.remove(3);
+ expected.remove(2);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", null, options, "footer", false);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", null, options, "", false);
+ expected.remove(6);
+ expected.remove(5);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ formatter.printHelp("commandSyntax", null, options, null, false);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ final HelpFormatter fHelp = formatter;
+ assertThrows(IllegalArgumentException.class, () -> fHelp.printHelp("", "header", options, "footer", true));
+ assertEquals(0, sb.length(), "Should not write to output");
+ assertThrows(IllegalArgumentException.class, () -> fHelp.printHelp(null, "header", options, "footer", true));
+ assertEquals(0, sb.length(), "Should not write to output");
+ }
+
+ @Test
+ void testPrintHelpXML() throws IOException {
+ final StringBuilder sb = new StringBuilder();
+ final XhtmlHelpAppendable serializer = new XhtmlHelpAppendable(sb);
+ final HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
+
+ final Options options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa");
+
+ final List expected = new ArrayList<>();
+ expected.add("usage: commandSyntax [-a]
");
+ expected.add("header
");
+ expected.add("");
+ expected.add(" ");
+ expected.add(" Options ");
+ expected.add(" Since ");
+ expected.add(" Description ");
+ expected.add(" ");
+ expected.add(" ");
+ expected.add(" -a ");
+ expected.add(" -- ");
+ expected.add(" aaaa aaaa aaaa aaaa aaaa ");
+ expected.add(" ");
+ expected.add("
");
+ expected.add("footer
");
+
+ formatter.printHelp("commandSyntax", "header", options, "footer", true);
+ final List actual = IOUtils.readLines(new StringReader(sb.toString()));
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ void testPrintOptions() throws IOException {
+ final StringBuilder sb = new StringBuilder();
+ final TextHelpAppendable serializer = new TextHelpAppendable(sb);
+ final HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).setShowSince(false).get();
+
+ // help format default column styles
+ // col options description helpAppendable
+ // styl FIXED VARIABLE VARIABLE
+ // LPad 0 5 1
+ // indent 1 1 3
+ //
+ // default helpAppendable
+
+ Options options;
+ List expected = new ArrayList<>();
+ expected.add(" Options Description ");
+ expected.add(" -a aaaa aaaa aaaa aaaa aaaa");
+ expected.add("");
+
+ options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa");
+
+ formatter.printOptions(options);
+ List actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ serializer.setMaxWidth(30);
+ expected = new ArrayList<>();
+ expected.add(" Options Description ");
+ expected.add(" -a aaaa aaaa aaaa ");
+ expected.add(" aaaa aaaa ");
+ expected.add("");
+ formatter.printOptions(options);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(31, actual.get(0).length());
+ assertEquals(expected, actual);
+
+ sb.setLength(0);
+ serializer.setLeftPad(5);
+ expected = new ArrayList<>();
+ expected.add(" Options Description ");
+ expected.add(" -a aaaa aaaa aaaa ");
+ expected.add(" aaaa aaaa ");
+ expected.add("");
+ formatter.printOptions(options);
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ void testSetOptionFormatBuilderTest() {
+ final HelpFormatter.Builder underTest = HelpFormatter.builder();
+ final OptionFormatter.Builder ofBuilder = OptionFormatter.builder().setOptPrefix("Just Another ");
+ underTest.setOptionFormatBuilder(ofBuilder);
+ final HelpFormatter formatter = underTest.get();
+ final OptionFormatter oFormatter = formatter.getOptionFormatter(Option.builder("thing").get());
+ assertEquals("Just Another thing", oFormatter.getOpt());
+
+ }
+
+ @Test
+ void testSetOptionGroupSeparatorTest() {
+ final HelpFormatter.Builder underTest = HelpFormatter.builder().setOptionGroupSeparator(" and ");
+ final HelpFormatter formatter = underTest.get();
+ final String result = formatter.toSyntaxOptions(new OptionGroup().addOption(Option.builder("this").get()).addOption(Option.builder("that").get()));
+ assertTrue(result.contains("-that and -this"));
+ }
+
+ @Test
+ void testSortOptionGroupsTest() {
+ final Options options = getTestGroups();
+ final List optList = new ArrayList<>(options.getOptions());
+ final HelpFormatter underTest = HelpFormatter.builder().get();
+ final List expected = new ArrayList<>();
+ expected.add(optList.get(0)); // because 1 sorts before all long values
+ expected.add(optList.get(1));
+ expected.add(optList.get(5));
+ expected.add(optList.get(4));
+ expected.add(optList.get(6));
+ expected.add(optList.get(8));
+ expected.add(optList.get(7));
+ expected.add(optList.get(3));
+ expected.add(optList.get(2));
+ assertEquals(expected, underTest.sort(options));
+ }
+
+ @Test
+ void testSortOptionsTest() {
+ // @formatter:off
+ final Options options = new Options()
+ .addOption(Option.builder("a").longOpt("optA").hasArg().desc("The description of A").get())
+ .addOption(Option.builder("b").longOpt("BOpt").hasArg().desc("B description").get())
+ .addOption(Option.builder().longOpt("COpt").hasArg().desc("A COpt description").get());
+ // @formatter:on
+
+ HelpFormatter underTest = HelpFormatter.builder().get();
+ final List expected = new ArrayList<>();
+ expected.add(options.getOption("a"));
+ expected.add(options.getOption("b"));
+ expected.add(options.getOption("COpt"));
+
+ assertEquals(expected, underTest.sort(options));
+
+ expected.set(0, expected.get(2));
+ expected.set(2, options.getOption("a"));
+ underTest = HelpFormatter.builder().setComparator(AbstractHelpFormatter.DEFAULT_COMPARATOR.reversed()).get();
+ assertEquals(expected, underTest.sort(options));
+
+ assertEquals(0, underTest.sort(Collections.emptyList()).size(), "empty colleciton should return empty list");
+ assertEquals(0, underTest.sort((Iterable ) null).size(), "null iterable should return empty list");
+ assertEquals(0, underTest.sort((Options) null).size(), "null Options should return empty list");
+ }
+
+ @Test
+ void testSyntaxPrefix() {
+ final StringBuilder sb = new StringBuilder();
+ final TextHelpAppendable serializer = new TextHelpAppendable(sb);
+ final HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
+ formatter.setSyntaxPrefix("Something new");
+ assertEquals("Something new", formatter.getSyntaxPrefix());
+ assertEquals(0, sb.length(), "Should not write to output");
+ }
+
+ @Test
+ void testToArgNameTest() {
+ final StringBuilder sb = new StringBuilder();
+ final TextHelpAppendable serializer = new TextHelpAppendable(sb);
+ final HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
+
+ assertEquals("", formatter.toArgName("some Arg"));
+ assertEquals("<>", formatter.toArgName(""));
+ assertEquals("<>", formatter.toArgName(null));
+ }
+
+ @Test
+ void testToSyntaxOptionGroupTest() {
+ final HelpFormatter underTest = HelpFormatter.builder().get();
+ // @formatter:off
+ final OptionGroup optionGroup = new OptionGroup()
+ .addOption(Option.builder().option("o").longOpt("one").hasArg().get())
+ .addOption(Option.builder().option("t").longOpt("two").hasArg().required().argName("other").get())
+ .addOption(Option.builder().option("th").longOpt("three").required().argName("other").get())
+ .addOption(Option.builder().option("f").argName("other").get())
+ .addOption(Option.builder().longOpt("five").hasArg().argName("other").get())
+ .addOption(Option.builder().longOpt("six").required().hasArg().argName("other").get())
+ .addOption(Option.builder().option("s").longOpt("sevem").hasArg().get());
+ // @formatter:on
+ assertEquals("[-f | --five | -o | -s | --six | -t | -th]", underTest.toSyntaxOptions(optionGroup));
+
+ optionGroup.setRequired(true);
+ assertEquals("-f | --five | -o | -s | --six | -t | -th", underTest.toSyntaxOptions(optionGroup));
+
+ assertEquals("", underTest.toSyntaxOptions(new OptionGroup()), "empty group should return empty string");
+ }
+
+ @Test
+ void testToSyntaxOptionIterableTest() {
+ final HelpFormatter underTest = HelpFormatter.builder().get();
+ final List options = new ArrayList<>();
+
+ options.add(Option.builder().option("o").longOpt("one").hasArg().get());
+ options.add(Option.builder().option("t").longOpt("two").hasArg().required().argName("other").get());
+ options.add(Option.builder().option("th").longOpt("three").required().argName("other").get());
+ options.add(Option.builder().option("f").argName("other").get());
+ options.add(Option.builder().longOpt("five").hasArg().argName("other").get());
+ options.add(Option.builder().longOpt("six").required().hasArg().argName("other").get());
+ options.add(Option.builder().option("s").longOpt("sevem").hasArg().get());
+ assertEquals("[-f] [--five ] [-o ] [-s ] --six -t -th", underTest.toSyntaxOptions(options));
+
+ }
+
+ @Test
+ void testToSyntaxOptionOptionsTest() {
+ final HelpFormatter underTest = HelpFormatter.builder().get();
+ Options options = getTestGroups();
+ assertEquals("[-1 | --aon | --uno ] [--dos | --dó | --two ] [--three | --tres | --trí ]",
+ underTest.toSyntaxOptions(options), "getTestGroup options failed");
+
+ // @formatter:off
+ options = new Options()
+ .addOption(Option.builder().option("o").longOpt("one").hasArg().get())
+ .addOption(Option.builder().option("t").longOpt("two").hasArg().required().argName("other").get())
+ .addOption(Option.builder().option("th").longOpt("three").required().argName("other").get())
+ .addOption(Option.builder().option("f").argName("other").get())
+ .addOption(Option.builder().longOpt("five").hasArg().argName("other").get())
+ .addOption(Option.builder().longOpt("six").required().hasArg().argName("other").get())
+ .addOption(Option.builder().option("s").longOpt("seven").hasArg().get());
+ // @formatter:on
+ assertEquals("[-f] [--five ] [-o ] [-s ] --six -t -th", underTest.toSyntaxOptions(options), "assorted options failed");
+ // @formatter:off
+ options = new Options()
+ .addOption(Option.builder().option("o").longOpt("one").hasArg().get())
+ .addOptionGroup(
+ new OptionGroup()
+ .addOption(Option.builder().option("t").longOpt("two").hasArg().required().argName("other").get())
+ .addOption(Option.builder().option("th").longOpt("three").required().argName("other").get()))
+ .addOption(Option.builder().option("f").argName("other").get())
+ .addOption(Option.builder().longOpt("five").hasArg().argName("other").get())
+ .addOption(Option.builder().longOpt("six").required().hasArg().argName("other").get())
+ .addOption(Option.builder().option("s").longOpt("seven").hasArg().get());
+ // @formatter:on
+ assertEquals("[-f] [--five ] [-o ] [-s ] --six [-t | -th]", underTest.toSyntaxOptions(options),
+ "option with group failed");
+
+ // @formatter:off
+ final OptionGroup group1 = new OptionGroup()
+ .addOption(Option.builder().option("t").longOpt("two").hasArg().required().argName("other").get())
+ .addOption(Option.builder().option("th").longOpt("three").required().argName("other").get());
+ // @formatter:on
+ group1.setRequired(true);
+ // @formatter:off
+ options = new Options()
+ .addOption(Option.builder().option("o").longOpt("one").hasArg().get())
+ .addOptionGroup(group1)
+ .addOption(Option.builder().option("f").argName("other").get())
+ .addOption(Option.builder().longOpt("five").hasArg().argName("other").get())
+ .addOption(Option.builder().longOpt("six").required().hasArg().argName("other").get())
+ .addOption(Option.builder().option("s").longOpt("seven").hasArg().get());
+ // @formatter:on
+ assertEquals("[-f] [--five ] [-o ] [-s ] --six -t | -th", underTest.toSyntaxOptions(options),
+ "options with required group failed");
+ }
+
+ @Test
+ void verifyOptionGroupingOutput() throws IOException {
+ // create options and groups
+ final Option o1 = new Option("o1", "Descr");
+ final Option o2 = new Option("o2", "Descr");
+
+ final Options options = new Options();
+ options.addOption(o1);
+ options.addOption(o2);
+
+ final OptionGroup group = new OptionGroup();
+ group.addOption(o1);
+ group.addOption(o2);
+ options.addOptionGroup(group);
+
+ final StringBuilder output = new StringBuilder();
+ //format options with new formatter
+ final org.apache.commons.cli.help.HelpFormatter newFormatter =
+ org.apache.commons.cli.help.HelpFormatter.builder().setShowSince(false)
+ .setHelpAppendable(new TextHelpAppendable(output)).get();
+ newFormatter.printHelp("Command", null, options, null, true);
+ assertTrue(output.toString().contains("[-o1 | -o2]"));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/cli/help/OptionFormatterTest.java b/src/test/java/org/apache/commons/cli/help/OptionFormatterTest.java
new file mode 100644
index 000000000..dbb59e3e5
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/help/OptionFormatterTest.java
@@ -0,0 +1,321 @@
+/*
+ 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
+
+ https://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.help;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import org.apache.commons.cli.DeprecatedAttributes;
+import org.apache.commons.cli.Option;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests {@link OptionFormatter}.
+ */
+class OptionFormatterTest {
+
+ public static Stream deprecatedAttributesData() {
+ final List lst = new ArrayList<>();
+
+ final DeprecatedAttributes.Builder daBuilder = DeprecatedAttributes.builder();
+ lst.add(Arguments.of(daBuilder.get(), "[Deprecated]"));
+
+ daBuilder.setSince("now");
+ lst.add(Arguments.of(daBuilder.get(), "[Deprecated since now]"));
+
+ daBuilder.setForRemoval(true);
+ lst.add(Arguments.of(daBuilder.get(), "[Deprecated for removal since now]"));
+
+ daBuilder.setSince(null);
+ lst.add(Arguments.of(daBuilder.get(), "[Deprecated for removal]"));
+
+ daBuilder.setForRemoval(false);
+ daBuilder.setDescription("Use something else");
+ lst.add(Arguments.of(daBuilder.get(), "[Deprecated. Use something else]"));
+
+ daBuilder.setForRemoval(true);
+ lst.add(Arguments.of(daBuilder.get(), "[Deprecated for removal. Use something else]"));
+
+ daBuilder.setForRemoval(false);
+ daBuilder.setSince("then");
+ lst.add(Arguments.of(daBuilder.get(), "[Deprecated since then. Use something else]"));
+
+ daBuilder.setForRemoval(true);
+ lst.add(Arguments.of(daBuilder.get(), "[Deprecated for removal since then. Use something else]"));
+
+ return lst.stream();
+ }
+
+ private void assertEquivalent(final OptionFormatter formatter, final OptionFormatter formatter2) {
+ assertEquals(formatter.toSyntaxOption(), formatter2.toSyntaxOption());
+ assertEquals(formatter.toSyntaxOption(true), formatter2.toSyntaxOption(true));
+ assertEquals(formatter.toSyntaxOption(false), formatter2.toSyntaxOption(false));
+ assertEquals(formatter.getOpt(), formatter2.getOpt());
+ assertEquals(formatter.getLongOpt(), formatter2.getLongOpt());
+ assertEquals(formatter.getBothOpt(), formatter2.getBothOpt());
+ assertEquals(formatter.getDescription(), formatter2.getDescription());
+ assertEquals(formatter.getArgName(), formatter2.getArgName());
+ assertEquals(formatter.toOptional("foo"), formatter2.toOptional("foo"));
+ }
+
+ @Test
+ void testAsOptional() {
+ OptionFormatter underTest;
+ final Option option = Option.builder().option("o").longOpt("opt").hasArg().get();
+
+ underTest = OptionFormatter.from(option);
+ assertEquals("[what]", underTest.toOptional("what"));
+ assertEquals("", underTest.toOptional(""), "enpty string should return empty string");
+ assertEquals("", underTest.toOptional(null), "null should return empty string");
+
+ underTest = OptionFormatter.builder().setOptionalDelimiters("-> ", " <-").build(option);
+ assertEquals("-> what <-", underTest.toOptional("what"));
+
+ }
+
+ @Test
+ void testAsSyntaxOption() {
+ OptionFormatter underTest;
+
+ Option option = Option.builder().option("o").longOpt("opt").hasArg().get();
+ underTest = OptionFormatter.from(option);
+ assertEquals("[-o ]", underTest.toSyntaxOption(), "optional arg failed");
+
+ option = Option.builder().option("o").longOpt("opt").hasArg().argName("other").get();
+ underTest = OptionFormatter.from(option);
+ assertEquals("[-o ]", underTest.toSyntaxOption(), "optional 'other' arg failed");
+
+ option = Option.builder().option("o").longOpt("opt").hasArg().required().argName("other").get();
+ underTest = OptionFormatter.from(option);
+ assertEquals("-o ", underTest.toSyntaxOption(), "required 'other' arg failed");
+
+ option = Option.builder().option("o").longOpt("opt").required().argName("other").get();
+ underTest = OptionFormatter.from(option);
+ assertEquals("-o", underTest.toSyntaxOption(), "required no arg failed");
+
+ option = Option.builder().option("o").argName("other").get();
+ underTest = OptionFormatter.from(option);
+ assertEquals("[-o]", underTest.toSyntaxOption(), "optional no arg arg failed");
+
+ option = Option.builder().longOpt("opt").hasArg().argName("other").get();
+ underTest = OptionFormatter.from(option);
+ assertEquals("[--opt ]", underTest.toSyntaxOption(), "optional longOpt 'other' arg failed");
+
+ option = Option.builder().longOpt("opt").required().hasArg().argName("other").get();
+ underTest = OptionFormatter.from(option);
+ assertEquals("--opt ", underTest.toSyntaxOption(), "required longOpt 'other' arg failed");
+
+ option = Option.builder().option("ot").longOpt("opt").hasArg().get();
+ underTest = OptionFormatter.from(option);
+ assertEquals("[-ot ]", underTest.toSyntaxOption(), "optional multi char opt arg failed");
+ }
+
+ @Test
+ void testCli343Part1() {
+ assertThrows(IllegalStateException.class, () -> Option.builder().required(false).build());
+ assertThrows(IllegalStateException.class, () -> Option.builder().required(false).get());
+ }
+
+ @Test
+ void testCli343Part2() {
+ assertThrows(IllegalStateException.class, () -> Option.builder().desc("description").build());
+ assertThrows(IllegalStateException.class, () -> Option.builder().desc("description").get());
+ }
+
+ @ParameterizedTest(name = "{index} {0}")
+ @MethodSource("deprecatedAttributesData")
+ void testComplexDeprecationFormat(final DeprecatedAttributes da, final String expected) {
+ final Option.Builder builder = Option.builder("o").deprecated(da);
+ final Option.Builder builderWithDesc = Option.builder("o").desc("The description").deprecated(da);
+
+ assertEquals(expected, OptionFormatter.COMPLEX_DEPRECATED_FORMAT.apply(builder.get()));
+ assertEquals(expected + " The description", OptionFormatter.COMPLEX_DEPRECATED_FORMAT.apply(builderWithDesc.get()));
+ }
+
+ @Test
+ void testCopyConstructor() {
+ final Function depFunc = o -> "Ooo Deprecated";
+ final BiFunction fmtFunc = (o, b) -> "Yep, it worked";
+ // @formatter:off
+ final OptionFormatter.Builder builder = OptionFormatter.builder()
+ .setLongOptPrefix("l")
+ .setOptPrefix("s")
+ .setArgumentNameDelimiters("{", "}")
+ .setDefaultArgName("Some Argument")
+ .setOptSeparator(" and ")
+ .setOptionalDelimiters("?>", "")
+ .setSyntaxFormatFunction(fmtFunc)
+ .setDeprecatedFormatFunction(depFunc);
+ // @formatter:on
+
+ Option option = Option.builder("o").longOpt("opt").get();
+
+ OptionFormatter formatter = builder.build(option);
+ OptionFormatter.Builder builder2 = new OptionFormatter.Builder(formatter);
+ assertEquivalent(formatter, builder2.build(option));
+
+ option = Option.builder("o").longOpt("opt").deprecated().required().get();
+ formatter = builder.build(option);
+ builder2 = new OptionFormatter.Builder(formatter);
+ assertEquivalent(formatter, builder2.build(option));
+ }
+
+ @Test
+ void testDefaultSyntaxFormat() {
+
+ Option option = Option.builder().option("o").longOpt("opt").hasArg().get();
+ OptionFormatter formatter = OptionFormatter.from(option);
+ assertEquals("[-o ]", formatter.toSyntaxOption());
+ assertEquals("-o ", formatter.toSyntaxOption(true));
+
+ option = Option.builder().option("o").longOpt("opt").hasArg().required().get();
+ formatter = OptionFormatter.from(option);
+ assertEquals("-o ", formatter.toSyntaxOption());
+ assertEquals("[-o ]", formatter.toSyntaxOption(false));
+ }
+
+ @Test
+ void testGetBothOpt() {
+ OptionFormatter underTest;
+
+ Option option = Option.builder().option("o").longOpt("opt").hasArg().get();
+ underTest = OptionFormatter.from(option);
+ assertEquals("-o, --opt", underTest.getBothOpt());
+
+ option = Option.builder().longOpt("opt").hasArg().get();
+ underTest = OptionFormatter.from(option);
+ assertEquals("--opt", underTest.getBothOpt());
+
+ option = Option.builder().option("o").hasArg().get();
+ underTest = OptionFormatter.from(option);
+ assertEquals("-o", underTest.getBothOpt());
+ }
+
+ @Test
+ void testGetDescription() {
+ final Option normalOption = Option.builder().option("o").longOpt("one").hasArg().desc("The description").get();
+
+ final Option deprecatedOption = Option.builder().option("o").longOpt("one").hasArg().desc("The description").deprecated().get();
+
+ final Option deprecatedOptionWithAttributes = Option.builder().option("o").longOpt("one").hasArg().desc("The description")
+ .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("now").setDescription("Use something else").get()).get();
+
+ assertEquals("The description", OptionFormatter.from(normalOption).getDescription(), "normal option failure");
+ assertEquals("The description", OptionFormatter.from(deprecatedOption).getDescription(), "deprecated option failure");
+ assertEquals("The description", OptionFormatter.from(deprecatedOptionWithAttributes).getDescription(), "complex deprecated option failure");
+
+ OptionFormatter.Builder builder = OptionFormatter.builder().setDeprecatedFormatFunction(OptionFormatter.SIMPLE_DEPRECATED_FORMAT);
+
+ assertEquals("The description", builder.build(normalOption).getDescription(), "normal option failure");
+ assertEquals("[Deprecated] The description", builder.build(deprecatedOption).getDescription(), "deprecated option failure");
+ assertEquals("[Deprecated] The description", builder.build(deprecatedOptionWithAttributes).getDescription(), "complex deprecated option failure");
+
+ builder = OptionFormatter.builder().setDeprecatedFormatFunction(OptionFormatter.COMPLEX_DEPRECATED_FORMAT);
+
+ assertEquals("The description", builder.build(normalOption).getDescription(), "normal option failure");
+ assertEquals("[Deprecated] The description", builder.build(deprecatedOption).getDescription(), "deprecated option failure");
+ assertEquals("[Deprecated for removal since now. Use something else] The description", builder.build(deprecatedOptionWithAttributes).getDescription(),
+ "complex deprecated option failure");
+ }
+
+ @Test
+ void testSetArgumentNameDelimiters() {
+ final Option option = Option.builder().option("o").longOpt("opt").hasArg().get();
+ OptionFormatter.Builder builder = OptionFormatter.builder().setArgumentNameDelimiters("with argument named ", ".");
+ assertEquals("with argument named arg.", builder.build(option).getArgName());
+
+ builder = OptionFormatter.builder().setArgumentNameDelimiters(null, "");
+ assertEquals("arg", builder.build(option).getArgName());
+
+ builder = OptionFormatter.builder().setArgumentNameDelimiters("", null);
+ assertEquals("arg", builder.build(option).getArgName());
+
+ }
+
+ @Test
+ void testSetDefaultArgName() {
+ final Option option = Option.builder().option("o").longOpt("opt").hasArg().get();
+ OptionFormatter.Builder builder = OptionFormatter.builder().setDefaultArgName("foo");
+ assertEquals("", builder.build(option).getArgName());
+
+ builder = OptionFormatter.builder().setDefaultArgName("");
+ assertEquals("", builder.build(option).getArgName());
+
+ builder = OptionFormatter.builder().setDefaultArgName(null);
+ assertEquals("", builder.build(option).getArgName());
+ }
+
+ @Test
+ void testSetLongOptPrefix() {
+ final Option option = Option.builder().option("o").longOpt("opt").hasArg().get();
+ OptionFormatter.Builder builder = OptionFormatter.builder().setLongOptPrefix("fo");
+ assertEquals("foopt", builder.build(option).getLongOpt());
+
+ builder = OptionFormatter.builder().setLongOptPrefix("");
+ assertEquals("opt", builder.build(option).getLongOpt());
+
+ builder = OptionFormatter.builder().setLongOptPrefix(null);
+ assertEquals("opt", builder.build(option).getLongOpt());
+ }
+
+ @Test
+ void testSetOptArgumentSeparator() {
+ final Option option = Option.builder().option("o").longOpt("opt").hasArg().get();
+ OptionFormatter.Builder builder = OptionFormatter.builder().setOptArgSeparator(" with argument named ");
+ assertEquals("[-o with argument named ]", builder.build(option).toSyntaxOption());
+
+ builder = OptionFormatter.builder().setOptArgSeparator(null);
+ assertEquals("[-o]", builder.build(option).toSyntaxOption());
+
+ builder = OptionFormatter.builder().setOptArgSeparator("=");
+ assertEquals("[-o=]", builder.build(option).toSyntaxOption());
+ }
+
+ @Test
+ void testSetOptSeparator() {
+ final Option option = Option.builder().option("o").longOpt("opt").hasArg().get();
+ OptionFormatter.Builder builder = OptionFormatter.builder().setOptSeparator(" and ");
+ assertEquals("-o and --opt", builder.build(option).getBothOpt());
+
+ builder = OptionFormatter.builder().setOptSeparator("");
+ assertEquals("-o--opt", builder.build(option).getBothOpt(), "Empty string should return default");
+
+ builder = OptionFormatter.builder().setOptSeparator(null);
+ assertEquals("-o--opt", builder.build(option).getBothOpt(), "null string should return default");
+ }
+
+ @Test
+ void testSetSyntaxFormatFunction() {
+ final BiFunction func = (o, b) -> "Yep, it worked";
+ final Option option = Option.builder().option("o").longOpt("opt").hasArg().get();
+
+ OptionFormatter.Builder builder = OptionFormatter.builder().setSyntaxFormatFunction(func);
+ assertEquals("Yep, it worked", builder.build(option).toSyntaxOption());
+
+ builder = OptionFormatter.builder().setSyntaxFormatFunction(null);
+ assertEquals("[-o ]", builder.build(option).toSyntaxOption());
+ }
+}
diff --git a/src/test/java/org/apache/commons/cli/help/TextHelpAppendableTest.java b/src/test/java/org/apache/commons/cli/help/TextHelpAppendableTest.java
new file mode 100644
index 000000000..d29fcddbc
--- /dev/null
+++ b/src/test/java/org/apache/commons/cli/help/TextHelpAppendableTest.java
@@ -0,0 +1,494 @@
+/*
+ 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
+
+ https://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.help;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests {@link TextHelpAppendable}.
+ */
+public final class TextHelpAppendableTest {
+
+ private StringBuilder sb;
+ private TextHelpAppendable underTest;
+
+ @BeforeEach
+ public void setUp() {
+ sb = new StringBuilder();
+ underTest = new TextHelpAppendable(sb);
+ }
+
+ @Test
+ void testAdjustTableFormat() {
+ // test width smaller than header
+ // @formatter:off
+ final TableDefinition tableDefinition = TableDefinition.from("Testing",
+ Collections.singletonList(TextStyle.builder().setMaxWidth(3).get()),
+ Collections.singletonList("header"),
+ // "data" shorter than "header"
+ Collections.singletonList(Collections.singletonList("data"))
+ );
+ // @formatter:on
+ final TableDefinition actual = underTest.adjustTableFormat(tableDefinition);
+ assertEquals("header".length(), actual.columnTextStyles().get(0).getMaxWidth());
+ assertEquals("header".length(), actual.columnTextStyles().get(0).getMinWidth());
+ }
+
+ @Test
+ void testAppend() throws IOException {
+ final char c = (char) 0x1F44D;
+ underTest.append(c);
+ assertEquals(1, sb.length());
+ assertEquals(String.valueOf(c), sb.toString());
+
+ sb.setLength(0);
+ underTest.append("Hello");
+ assertEquals("Hello", sb.toString());
+ }
+
+ @Test
+ void testAppendHeader() throws IOException {
+ final String[] expected = { " Hello World", " ===========", "" };
+
+ sb.setLength(0);
+ underTest.appendHeader(1, "Hello World");
+ List actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(Arrays.asList(expected), actual, "header 1 failed");
+
+ sb.setLength(0);
+ underTest.appendHeader(2, "Hello World");
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ expected[1] = " %%%%%%%%%%%";
+ assertEquals(Arrays.asList(expected), actual, "header 2 failed");
+
+ sb.setLength(0);
+ underTest.appendHeader(3, "Hello World");
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ expected[1] = " +++++++++++";
+ assertEquals(Arrays.asList(expected), actual, "header 3 failed");
+
+ sb.setLength(0);
+ underTest.appendHeader(4, "Hello World");
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ expected[1] = " ___________";
+ assertEquals(Arrays.asList(expected), actual, "header 4 failed");
+
+ sb.setLength(0);
+ underTest.appendHeader(5, "Hello World");
+ actual = IOUtils.readLines(new StringReader(sb.toString()));
+ assertEquals(Arrays.asList(expected), actual, "header 5 failed");
+
+ sb.setLength(0);
+ assertThrows(IllegalArgumentException.class, () -> underTest.appendHeader(0, "Hello World"));
+
+ sb.setLength(0);
+ underTest.appendHeader(5, "");
+ assertEquals(0, sb.length(), "empty string test failed");
+
+ sb.setLength(0);
+ underTest.appendHeader(5, null);
+ assertEquals(0, sb.length(), "null test failed");
+ }
+
+ @Test
+ void testAppendList() throws IOException {
+ final List expected = new ArrayList<>();
+ final String[] entries = { "one", "two", "three" };
+ for (int i = 0; i < entries.length; i++) {
+ expected.add(String.format(" %s. %s", i + 1, entries[i]));
+ }
+ expected.add("");
+
+ sb.setLength(0);
+ underTest.appendList(true, Arrays.asList(entries));
+ List