This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-cli.git
The following commit(s) were added to refs/heads/master by this push: new d9a9433 [CLI-329] Support "Deprecated" CLI Options (#252) d9a9433 is described below commit d9a9433131d8c821278af738deaf6146f11a25b9 Author: Gary Gregory <garydgreg...@users.noreply.github.com> AuthorDate: Fri Mar 29 12:15:41 2024 -0400 [CLI-329] Support "Deprecated" CLI Options (#252) * [CLI-329] Support "Deprecated" CLI Options * [CLI-329] Support "Deprecated" CLI Options - HelpFormatter does not print deprecated options by default - HelpFormatter can show deprecated options: HelpFormatter.builder().setShowDeprecated(true).get(); * [CLI-329] Support "Deprecated" CLI Options Deprecated string looks like: "Option 'c': Deprecated for removal since 2.0: Use X." --- .../java/org/apache/commons/cli/CommandLine.java | 70 ++++++-- .../java/org/apache/commons/cli/DefaultParser.java | 46 +++-- .../apache/commons/cli/DeprecatedAttributes.java | 186 +++++++++++++++++++++ .../java/org/apache/commons/cli/HelpFormatter.java | 72 ++++++++ src/main/java/org/apache/commons/cli/Option.java | 162 ++++++++++++------ .../org/apache/commons/cli/CommandLineTest.java | 17 ++ .../org/apache/commons/cli/DefaultParserTest.java | 37 ++++ .../commons/cli/DeprecatedAttributesTest.java | 75 +++++++++ .../java/org/apache/commons/cli/OptionTest.java | 64 +++++-- .../java/org/apache/commons/cli/OptionsTest.java | 20 ++- .../java/org/apache/commons/cli/SolrCliTest.java | 162 ++++++++++++++++++ .../org/apache/commons/cli/SolrCreateToolTest.java | 106 ++++++++++++ 12 files changed, 923 insertions(+), 94 deletions(-) diff --git a/src/main/java/org/apache/commons/cli/CommandLine.java b/src/main/java/org/apache/commons/cli/CommandLine.java index bbcc540..afe8461 100644 --- a/src/main/java/org/apache/commons/cli/CommandLine.java +++ b/src/main/java/org/apache/commons/cli/CommandLine.java @@ -26,6 +26,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Properties; +import java.util.function.Consumer; import java.util.function.Supplier; /** @@ -47,12 +48,22 @@ public class CommandLine implements Serializable { */ public static final class Builder { + /** + * Prints an Option to {@link System#out}. + */ + static final Consumer<Option> DEPRECATED_HANDLER = o -> System.out.println(o.toDeprecatedString()); + /** The unrecognized options/arguments */ private final List<String> args = new LinkedList<>(); /** The processed options */ private final List<Option> options = new ArrayList<>(); + /** + * Deprecated Option handler. + */ + private Consumer<Option> deprecatedHandler = DEPRECATED_HANDLER; + /** * Adds left-over unrecognized option/argument. * @@ -82,15 +93,30 @@ public class CommandLine implements Serializable { } /** - * Returns the new instance. + * Creates the new instance. * * @return the new instance. */ public CommandLine build() { - return new CommandLine(args, options); + return new CommandLine(args, options, deprecatedHandler); + } + + /** + * Sets the deprecated option handler. + * + * @param deprecatedHandler the deprecated option handler. + * @return this. + * @since 1.7.0 + */ + public Builder setDeprecatedHandler(final Consumer<Option> deprecatedHandler) { + this.deprecatedHandler = deprecatedHandler; + return this; } } + /** The serial version UID. */ + private static final long serialVersionUID = 1L; + /** * Creates a new builder. * @@ -101,28 +127,34 @@ public class CommandLine implements Serializable { return new Builder(); } - /** The serial version UID. */ - private static final long serialVersionUID = 1L; - /** The unrecognized options/arguments */ private final List<String> args; /** The processed options */ private final List<Option> options; + /** + * The deprecated option handler. + * <p> + * If you want to serialize this field, use a serialization proxy. + * </p> + */ + private final transient Consumer<Option> deprecatedHandler; + /** * Creates a command line. */ protected CommandLine() { - this(new LinkedList<>(), new ArrayList<>()); + this(new LinkedList<>(), new ArrayList<>(), Builder.DEPRECATED_HANDLER); } /** * Creates a command line. */ - private CommandLine(final List<String> args, final List<Option> options) { + private CommandLine(final List<String> args, final List<Option> options, final Consumer<Option> deprecatedHandler) { this.args = Objects.requireNonNull(args, "args"); this.options = Objects.requireNonNull(options, "options"); + this.deprecatedHandler = deprecatedHandler; } /** @@ -530,13 +562,14 @@ public class CommandLine implements Serializable { } /** - * Tests to see if an option has been set. + * Handles deprecated options. * - * @param opt character name of the option. - * @return true if set, false if not. + * @param option a deprecated option. */ - public boolean hasOption(final char opt) { - return hasOption(String.valueOf(opt)); + private void handleDeprecated(final Option option) { + if (deprecatedHandler != null) { + deprecatedHandler.accept(option); + } } /** @@ -557,6 +590,16 @@ public class CommandLine implements Serializable { * return buf.toString(); } */ + /** + * Tests to see if an option has been set. + * + * @param opt character name of the option. + * @return true if set, false if not. + */ + public boolean hasOption(final char opt) { + return hasOption(String.valueOf(opt)); + } + /** * Tests to see if an option has been set. * @@ -615,6 +658,9 @@ public class CommandLine implements Serializable { if (actual != null) { for (final Option option : options) { if (actual.equals(option.getOpt()) || actual.equals(option.getLongOpt())) { + if (option.isDeprecated()) { + handleDeprecated(option); + } return option; } } diff --git a/src/main/java/org/apache/commons/cli/DefaultParser.java b/src/main/java/org/apache/commons/cli/DefaultParser.java index 077a1a6..4737596 100644 --- a/src/main/java/org/apache/commons/cli/DefaultParser.java +++ b/src/main/java/org/apache/commons/cli/DefaultParser.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Properties; +import java.util.function.Consumer; /** * Default parser. @@ -48,6 +49,14 @@ public class DefaultParser implements CommandLineParser { /** Flag indicating if partial matching of long options is supported. */ private boolean allowPartialMatching = true; + /** + * The deprecated option handler. + * <p> + * If you want to serialize this field, use a serialization proxy. + * </p> + */ + private Consumer<Option> deprecatedHandler = CommandLine.Builder.DEPRECATED_HANDLER; + /** Flag indicating if balanced leading and trailing double quotes should be stripped from option arguments. */ private Boolean stripLeadingAndTrailingQuotes; @@ -67,7 +76,7 @@ public class DefaultParser implements CommandLineParser { * @since 1.5.0 */ public DefaultParser build() { - return new DefaultParser(allowPartialMatching, stripLeadingAndTrailingQuotes); + return new DefaultParser(allowPartialMatching, stripLeadingAndTrailingQuotes, deprecatedHandler); } /** @@ -97,6 +106,18 @@ public class DefaultParser implements CommandLineParser { return this; } + /** + * Sets the deprecated option handler. + * + * @param deprecatedHandler the deprecated option handler. + * @return this. + * @since 1.7.0 + */ + public Builder setDeprecatedHandler(final Consumer<Option> deprecatedHandler) { + this.deprecatedHandler = deprecatedHandler; + return this; + } + /** * Sets if balanced leading and trailing double quotes should be stripped from option arguments. * @@ -160,6 +181,14 @@ public class DefaultParser implements CommandLineParser { * null represents the historic arbitrary behavior */ private final Boolean stripLeadingAndTrailingQuotes; + /** + * The deprecated option handler. + * <p> + * If you want to serialize this field, use a serialization proxy. + * </p> + */ + private final Consumer<Option> deprecatedHandler; + /** * Creates a new DefaultParser instance with partial matching enabled. * @@ -182,6 +211,7 @@ public class DefaultParser implements CommandLineParser { public DefaultParser() { this.allowPartialMatching = true; this.stripLeadingAndTrailingQuotes = null; + this.deprecatedHandler = CommandLine.Builder.DEPRECATED_HANDLER; } /** @@ -208,6 +238,7 @@ public class DefaultParser implements CommandLineParser { public DefaultParser(final boolean allowPartialMatching) { this.allowPartialMatching = allowPartialMatching; this.stripLeadingAndTrailingQuotes = null; + this.deprecatedHandler = CommandLine.Builder.DEPRECATED_HANDLER; } /** @@ -217,10 +248,10 @@ public class DefaultParser implements CommandLineParser { * @param allowPartialMatching if partial matching of long options shall be enabled * @param stripLeadingAndTrailingQuotes if balanced outer double quoutes should be stripped */ - private DefaultParser(final boolean allowPartialMatching, - final Boolean stripLeadingAndTrailingQuotes) { + private DefaultParser(final boolean allowPartialMatching, final Boolean stripLeadingAndTrailingQuotes, final Consumer<Option> deprecatedHandler) { this.allowPartialMatching = allowPartialMatching; this.stripLeadingAndTrailingQuotes = stripLeadingAndTrailingQuotes; + this.deprecatedHandler = deprecatedHandler; } /** @@ -671,28 +702,21 @@ public class DefaultParser implements CommandLineParser { skipParsing = false; currentOption = null; expectedOpts = new ArrayList<>(options.getRequiredOptions()); - // clear the data from the groups for (final OptionGroup group : options.getOptionGroups()) { group.setSelected(null); } - - cmd = CommandLine.builder().build(); - + cmd = CommandLine.builder().setDeprecatedHandler(deprecatedHandler).build(); if (arguments != null) { for (final String argument : arguments) { handleToken(argument); } } - // check the arguments of the last option checkRequiredArgs(); - // add the default options handleProperties(properties); - checkRequiredOptions(); - return cmd; } diff --git a/src/main/java/org/apache/commons/cli/DeprecatedAttributes.java b/src/main/java/org/apache/commons/cli/DeprecatedAttributes.java new file mode 100644 index 0000000..bb4521d --- /dev/null +++ b/src/main/java/org/apache/commons/cli/DeprecatedAttributes.java @@ -0,0 +1,186 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +package org.apache.commons.cli; + +import java.util.function.Supplier; + +/** + * Deprecated attributes. + * <p> + * Note: This class isn't called "Deprecated" to avoid clashing with "java.lang.Deprecated". + * </p> + * <p> + * If you want to serialize this class, use a serialization proxy. + * </p> + * + * @since 1.7.0 + * @see Deprecated + */ +public final class DeprecatedAttributes { + + /** + * Builds {@link DeprecatedAttributes}. + */ + public static class Builder implements Supplier<DeprecatedAttributes> { + + /** The description. */ + private String description; + + /** + * Whether this option is subject to removal in a future version. + * + * @see <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Deprecated.html#forRemoval()">Deprecated.forRemoval</a> + */ + private boolean forRemoval; + + /** + * The version in which the option became deprecated. + * + * @see <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Deprecated.html#forRemoval()">Deprecated.since</a> + */ + private String since; + + @Override + public DeprecatedAttributes get() { + return new DeprecatedAttributes(description, since, forRemoval); + } + + /** + * Sets the description. + * + * @param description the description. + * @return this. + */ + public Builder setDescription(final String description) { + this.description = description; + return this; + } + + /** + * Whether this option is subject to removal in a future version. + * + * @param forRemoval whether this is subject to removal in a future version. + * @return this. + * @see <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Deprecated.html#forRemoval()">Deprecated.forRemoval</a> + */ + public Builder setForRemoval(final boolean forRemoval) { + this.forRemoval = forRemoval; + return this; + } + + /** + * Sets the version in which the option became deprecated. + * + * @param since the version in which the option became deprecated. + * @return this. + */ + public Builder setSince(final String since) { + this.since = since; + return this; + } + } + + /** + * The default value for a DeprecatedAttributes. + */ + static final DeprecatedAttributes DEFAULT = new DeprecatedAttributes("", "", false); + + /** + * The empty string. + */ + private static final String EMPTY_STRING = ""; + + /** + * Creates a new builder. + * + * @return a new builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** The description. */ + private final String description; + + /** Whether this option will be removed. */ + private final boolean forRemoval; + + /** The version label for removal. */ + private final String since; + + /** + * Constructs a new instance. + * + * @param description The description. + * @param since The version label for removal. + * @param forRemoval Whether this option will be removed. + */ + private DeprecatedAttributes(final String description, final String since, final boolean forRemoval) { + this.description = toEmpty(description); + this.since = toEmpty(since); + this.forRemoval = forRemoval; + } + + /** + * Gets the descriptions. + * + * @return the descriptions. + */ + public String getDescription() { + return description; + } + + /** + * Gets version in which the option became deprecated. + * + * @return the version in which the option became deprecated. + */ + public String getSince() { + return since; + } + + /** + * Tests whether this option is subject to removal in a future version. + * + * @return whether this option is subject to removal in a future version. + */ + public boolean isForRemoval() { + return forRemoval; + } + + private String toEmpty(final String since) { + return since != null ? since : EMPTY_STRING; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder("Deprecated"); + if (forRemoval) { + builder.append(" for removal"); + } + if (!since.isEmpty()) { + builder.append(" since "); + builder.append(since); + } + if (!description.isEmpty()) { + builder.append(": "); + builder.append(description); + } + return builder.toString(); + } +} diff --git a/src/main/java/org/apache/commons/cli/HelpFormatter.java b/src/main/java/org/apache/commons/cli/HelpFormatter.java index e8faece..2137efe 100644 --- a/src/main/java/org/apache/commons/cli/HelpFormatter.java +++ b/src/main/java/org/apache/commons/cli/HelpFormatter.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; +import java.util.function.Supplier; /** * A formatter of help messages for command line options. @@ -63,6 +64,38 @@ import java.util.List; */ public class HelpFormatter { + /** + * Builds {@link HelpFormatter}. + * + * @since 1.7.0 + */ + public static final class Builder implements Supplier<HelpFormatter> { + // TODO All other instance HelpFormatter instance variables. + // Make HelpFormatter immutable for 2.0 + + /** + * Whether to show deprecated options. + */ + private boolean showDeprecated; + + @Override + public HelpFormatter get() { + return new HelpFormatter(showDeprecated); + } + + /** + * Sets whether to show deprecated options. + * + * @param showDeprecated Whether to show deprecated options. + * @return this. + */ + public Builder setShowDeprecated(final boolean showDeprecated) { + this.showDeprecated = showDeprecated; + return this; + } + + } + /** * This class implements the {@code Comparator} interface for comparing Options. */ @@ -113,6 +146,16 @@ public class HelpFormatter { /** Default name for an argument */ public static final String DEFAULT_ARG_NAME = "arg"; + /** + * Creates a new builder. + * + * @return a new builder. + * @since 1.7.0 + */ + public static Builder builder() { + return new Builder(); + } + /** * Number of characters per line * @@ -184,11 +227,34 @@ public class HelpFormatter { */ protected Comparator<Option> optionComparator = new OptionComparator(); + /** + * Whether to show deprecated options. + */ + private final boolean showDeprecated; + /** * The separator displayed between the long option and its value. */ private String longOptSeparator = DEFAULT_LONG_OPT_SEPARATOR; + /** + * Constructs a new instance. + */ + public HelpFormatter() { + super(); + this.showDeprecated = false; + } + + /** + * Constructs a new instance. + */ + private HelpFormatter(final boolean showDeprecated) { + // TODO All other instance HelpFormatter instance variables. + // Make HelpFormatter immutable for 2.0 + super(); + this.showDeprecated = showDeprecated; + } + /** * Appends the usage clause for an Option to a StringBuffer. * @@ -686,7 +752,13 @@ public class HelpFormatter { } optBuf.append(dpad); final int nextLineTabStop = max + descPad; + if (showDeprecated && option.isDeprecated()) { + optBuf.append("[Deprecated]"); + } if (option.getDescription() != null) { + if (showDeprecated && option.isDeprecated()) { + optBuf.append(' '); + } optBuf.append(option.getDescription()); } renderWrappedText(sb, width, nextLineTabStop, optBuf.toString()); diff --git a/src/main/java/org/apache/commons/cli/Option.java b/src/main/java/org/apache/commons/cli/Option.java index b6f378f..8c54ce2 100644 --- a/src/main/java/org/apache/commons/cli/Option.java +++ b/src/main/java/org/apache/commons/cli/Option.java @@ -25,16 +25,13 @@ import java.util.List; import java.util.Objects; /** - * Describes a single command-line option. It maintains information regarding the short-name of the option, the - * long-name, if any exists, a flag indicating if an argument is required for this option, and a self-documenting - * description of the option. + * Describes a single command-line option. It maintains information regarding the short-name of the option, the long-name, if any exists, a flag indicating if + * an argument is required for this option, and a self-documenting description of the option. * <p> - * An Option is not created independently, but is created through an instance of {@link Options}. An Option is required - * to have at least a short or a long-name. + * An Option is not created independently, but is created through an instance of {@link Options}. An Option is required to have at least a short or a long-name. * </p> * <p> - * <b>Note:</b> once an {@link Option} has been added to an instance of {@link Options}, its required flag cannot be - * changed. + * <b>Note:</b> once an {@link Option} has been added to an instance of {@link Options}, its required flag cannot be changed. * </p> * * @see org.apache.commons.cli.Options @@ -68,6 +65,9 @@ public class Option implements Cloneable, Serializable { /** The name of the argument for this option. */ private String argName; + /** Specifies whether this option is deprecated. */ + private DeprecatedAttributes deprecated; + /** Specifies whether this option is required to be present. */ private boolean required; @@ -123,7 +123,9 @@ public class Option implements Cloneable, Serializable { /** * Sets the converter for the option. * - * <p>Note: see {@link TypeHandler} for serialization discussion.</p> + * <p> + * Note: see {@link TypeHandler} for serialization discussion. + * </p> * * @param converter the Converter to use. * @return this builder, to allow method chaining. @@ -134,6 +136,28 @@ public class Option implements Cloneable, Serializable { return this; } + /** + * Marks this Option as deprecated. + * + * @return this builder. + * @since 1.7.0 + */ + public Builder deprecated() { + return deprecated(DeprecatedAttributes.DEFAULT); + } + + /** + * Sets whether the Option is deprecated. + * + * @param deprecated specifies whether the Option is deprecated. + * @return this builder. + * @since 1.7.0 + */ + public Builder deprecated(final DeprecatedAttributes deprecated) { + this.deprecated = deprecated; + return this; + } + /** * Sets the description for this option. * @@ -235,9 +259,9 @@ public class Option implements Cloneable, Serializable { } /** - * Sets whether the Option is mandatory. + * Sets whether the Option is required. * - * @param required specifies whether the Option is mandatory. + * @param required specifies whether the Option is required. * @return this builder. */ public Builder required(final boolean required) { @@ -275,7 +299,7 @@ public class Option implements Cloneable, Serializable { * Option opt = Option.builder("D").hasArgs().valueSeparator('=').build(); * Options options = new Options(); * options.addOption(opt); - * String[] args = {"-Dkey=value"}; + * String[] args = { "-Dkey=value" }; * CommandLineParser parser = new DefaultParser(); * CommandLine line = parser.parse(options, args); * String propertyName = line.getOptionValues("D")[0]; // will be "key" @@ -338,6 +362,14 @@ public class Option implements Cloneable, Serializable { /** Description of the option. */ private String description; + /** + * Specifies whether this option is deprecated, may be null. + * <p> + * If you want to serialize this field, use a serialization proxy. + * </p> + */ + private final transient DeprecatedAttributes deprecated; + /** Specifies whether this option is required to be present. */ private boolean required; @@ -356,7 +388,7 @@ public class Option implements Cloneable, Serializable { /** The character that is the value separator. */ private char valuesep; - /** The explicit converter for this option. May be null. */ + /** The explicit converter for this option. May be null. */ private transient Converter<?, ?> converter; /** @@ -371,6 +403,7 @@ public class Option implements Cloneable, Serializable { this.argCount = builder.argCount; this.option = builder.option; this.optionalArg = builder.optionalArg; + this.deprecated = builder.deprecated; this.required = builder.required; this.type = builder.type; this.valuesep = builder.valueSeparator; @@ -380,8 +413,8 @@ public class Option implements Cloneable, Serializable { /** * Creates an Option using the specified parameters. * - * @param option short representation of the option. - * @param hasArg specifies whether the Option takes an argument or not. + * @param option short representation of the option. + * @param hasArg specifies whether the Option takes an argument or not. * @param description describes the function of the option. * * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}. @@ -393,7 +426,7 @@ public class Option implements Cloneable, Serializable { /** * Creates an Option using the specified parameters. The option does not take an argument. * - * @param option short representation of the option. + * @param option short representation of the option. * @param description describes the function of the option. * * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}. @@ -405,23 +438,22 @@ public class Option implements Cloneable, Serializable { /** * Creates an Option using the specified parameters. * - * @param option short representation of the option. - * @param longOption the long representation of the option. - * @param hasArg specifies whether the Option takes an argument or not. + * @param option short representation of the option. + * @param longOption the long representation of the option. + * @param hasArg specifies whether the Option takes an argument or not. * @param description describes the function of the option. * * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}. */ public Option(final String option, final String longOption, final boolean hasArg, final String description) throws IllegalArgumentException { // ensure that the option is valid + this.deprecated = null; this.option = OptionValidator.validate(option); this.longOption = longOption; - // if hasArg is set then the number of arguments is 1 if (hasArg) { this.argCount = 1; } - this.description = description; } @@ -436,8 +468,8 @@ public class Option implements Cloneable, Serializable { } /** - * Adds the value to this Option. If the number of arguments is greater than zero and there is enough space in the list - * then add the value. Otherwise, throw a runtime exception. + * Adds the value to this Option. If the number of arguments is greater than zero and there is enough space in the list then add the value. Otherwise, throw + * a runtime exception. * * @param value The value to be added to this Option. * @@ -452,8 +484,7 @@ public class Option implements Cloneable, Serializable { } /** - * This method is not intended to be used. It was a piece of internal API that was made public in 1.0. It currently - * throws an UnsupportedOperationException. + * This method is not intended to be used. It was a piece of internal API that was made public in 1.0. It currently throws an UnsupportedOperationException. * * @param value the value to add. * @return always throws an {@link UnsupportedOperationException}. @@ -463,7 +494,7 @@ public class Option implements Cloneable, Serializable { @Deprecated public boolean addValue(final String value) { throw new UnsupportedOperationException( - "The addValue method is not intended for client use. " + "Subclasses should use the addValueForProcessing method instead. "); + "The addValue method is not intended for client use. " + "Subclasses should use the addValueForProcessing method instead. "); } /** @@ -479,8 +510,7 @@ public class Option implements Cloneable, Serializable { } /** - * Clears the Option values. After a parse is complete, these are left with data in them and they need clearing if - * another parse is done. + * Clears the Option values. After a parse is complete, these are left with data in them and they need clearing if another parse is done. * * See: <a href="https://issues.apache.org/jira/browse/CLI-71">CLI-71</a> */ @@ -533,9 +563,8 @@ public class Option implements Cloneable, Serializable { * Gets the number of argument values this Option can take. * * <p> - * A value equal to the constant {@link #UNINITIALIZED} (= -1) indicates the number of arguments has not been specified. - * A value equal to the constant {@link #UNLIMITED_VALUES} (= -2) indicates that this options takes an unlimited amount - * of values. + * A value equal to the constant {@link #UNINITIALIZED} (= -1) indicates the number of arguments has not been specified. A value equal to the constant + * {@link #UNLIMITED_VALUES} (= -2) indicates that this options takes an unlimited amount of values. * </p> * * @return num the number of argument values. @@ -556,6 +585,16 @@ public class Option implements Cloneable, Serializable { return converter == null ? TypeHandler.getConverter(type) : converter; } + /** + * Gets deprecated attributes if any. + * + * @return boolean deprecated attributes or null. + * @since 1.7.0 + */ + public DeprecatedAttributes getDeprecated() { + return deprecated; + } + /** * Gets the self-documenting description of this Option. * @@ -566,8 +605,7 @@ public class Option implements Cloneable, Serializable { } /** - * Gets the id of this Option. This is only set when the Option shortOpt is a single character. This is used for - * switch statements. + * Gets the id of this Option. This is only set when the Option shortOpt is a single character. This is used for switch statements. * * @return the id of this Option. */ @@ -576,8 +614,7 @@ public class Option implements Cloneable, Serializable { } /** - * Gets the 'unique' Option identifier. This is the option value if set or the long value - * if the options value is not set. + * Gets the 'unique' Option identifier. This is the option value if set or the long value if the options value is not set. * * @return the 'unique' Option identifier. * @since 1.7.0 @@ -599,8 +636,8 @@ public class Option implements Cloneable, Serializable { /** * Gets the name of this Option. * - * It is this String which can be used with {@link CommandLine#hasOption(String opt)} and - * {@link CommandLine#getOptionValue(String opt)} to check for existence and argument. + * It is this String which can be used with {@link CommandLine#hasOption(String opt)} and {@link CommandLine#getOptionValue(String opt)} to check for + * existence and argument. * * @return The name of this option. */ @@ -748,19 +785,28 @@ public class Option implements Cloneable, Serializable { return valuesep > 0; } + /** + * Tests whether this Option is deprecated. + * + * @return boolean flag indicating whether this Option is deprecated. + * @since 1.7.0 + */ + public boolean isDeprecated() { + return deprecated != null; + } + /** * Tests whether this Option is required. * - * @return boolean flag indicating whether this Option is mandatory. + * @return boolean flag indicating whether this Option is required. */ public boolean isRequired() { return required; } /** - * Processes the value. If this Option has a value separator the value will have to be parsed into individual tokens. - * When n-1 tokens have been processed and there are more value separators in the value, parsing is ceased and the - * remaining characters are added as a single token. + * Processes the value. If this Option has a value separator the value will have to be parsed into individual tokens. When n-1 tokens have been processed + * and there are more value separators in the value, parsing is ceased and the remaining characters are added as a single token. * * @param value The String to be processed. * @@ -892,8 +938,7 @@ public class Option implements Cloneable, Serializable { /** * Sets the type of this Option. * <p> - * <b>Note:</b> this method is kept for binary compatibility and the input type is supposed to be a {@link Class} - * object. + * <b>Note:</b> this method is kept for binary compatibility and the input type is supposed to be a {@link Class} object. * </p> * * @param type the type of this Option. @@ -913,6 +958,21 @@ public class Option implements Cloneable, Serializable { this.valuesep = sep; } + String toDeprecatedString() { + if (!isDeprecated()) { + return ""; + } + final StringBuilder buf = new StringBuilder().append("Option '"); + buf.append(option).append('\''); + if (longOption != null) { + buf.append('\'').append(longOption).append('\''); + } + if (isDeprecated()) { + buf.append(": ").append(deprecated); + } + return buf.toString(); + } + /** * Creates a String suitable for debugging. * @@ -920,30 +980,26 @@ public class Option implements Cloneable, Serializable { */ @Override public String toString() { - final StringBuilder buf = new StringBuilder().append("[ option: "); - + final StringBuilder buf = new StringBuilder().append("[ "); + buf.append("Option "); buf.append(option); - if (longOption != null) { - buf.append(" ").append(longOption); + buf.append(' ').append(longOption); + } + if (isDeprecated()) { + buf.append(' '); + buf.append(deprecated.toString()); } - - buf.append(" "); - if (hasArgs()) { buf.append("[ARG...]"); } else if (hasArg()) { buf.append(" [ARG]"); } - buf.append(" :: ").append(description); - if (type != null) { buf.append(" :: ").append(type); } - buf.append(" ]"); - return buf.toString(); } } diff --git a/src/test/java/org/apache/commons/cli/CommandLineTest.java b/src/test/java/org/apache/commons/cli/CommandLineTest.java index 32f3fad..7a6b2b7 100644 --- a/src/test/java/org/apache/commons/cli/CommandLineTest.java +++ b/src/test/java/org/apache/commons/cli/CommandLineTest.java @@ -20,8 +20,10 @@ 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 static org.junit.jupiter.api.Assertions.assertSame; import java.util.Properties; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; import org.junit.jupiter.api.Test; @@ -29,6 +31,21 @@ import org.junit.jupiter.api.Test; @SuppressWarnings("deprecation") // tests some deprecated classes public class CommandLineTest { + @Test + public void testDeprecatedOption() { + final CommandLine.Builder builder = new CommandLine.Builder(); + builder.addArg("foo").addArg("bar"); + final Option opt = Option.builder().option("T").deprecated().build(); + builder.addOption(opt); + final AtomicReference<Option> handler = new AtomicReference<>(); + final CommandLine cmd = builder.setDeprecatedHandler(handler::set).build(); + cmd.getOptionValue(opt.getOpt()); + assertSame(opt, handler.get()); + handler.set(null); + cmd.getOptionValue("Nope"); + assertNull(handler.get()); + } + @Test public void testBuilder() { final CommandLine.Builder builder = new CommandLine.Builder(); diff --git a/src/test/java/org/apache/commons/cli/DefaultParserTest.java b/src/test/java/org/apache/commons/cli/DefaultParserTest.java index cf6c083..31d4805 100644 --- a/src/test/java/org/apache/commons/cli/DefaultParserTest.java +++ b/src/test/java/org/apache/commons/cli/DefaultParserTest.java @@ -18,6 +18,11 @@ 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.assertTrue; + +import java.util.HashSet; +import java.util.Set; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -33,13 +38,45 @@ public class DefaultParserTest extends AbstractParserTestCase { @Test public void testBuilder() { + // @formatter:off parser = DefaultParser.builder() .setStripLeadingAndTrailingQuotes(false) .setAllowPartialMatching(false) + .setDeprecatedHandler(null) .build(); + // @formatter:on assertEquals(DefaultParser.class, parser.getClass()); } + @Test + public void testDeprecated() throws ParseException { + final Set<Option> handler = new HashSet<>(); + parser = DefaultParser.builder().setDeprecatedHandler(handler::add).build(); + final Option opt1 = Option.builder().option("d1").deprecated().build(); + // @formatter:off + final Option opt2 = Option.builder().option("d2").deprecated(DeprecatedAttributes.builder() + .setForRemoval(true) + .setSince("1.0") + .setDescription("Do this instead.").get()).build(); + // @formatter:on + final Option opt3 = Option.builder().option("a").build(); + // @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 testLongOptionQuoteHandlingWithoutStrip() throws Exception { parser = DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).build(); 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 0000000..c518035 --- /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 + + 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.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class DeprecatedAttributesTest { + + @Test + public 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 + public 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 + public 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 + public void testDefaultToString() { + assertEquals("Deprecated", DeprecatedAttributes.DEFAULT.toString()); + } +} diff --git a/src/test/java/org/apache/commons/cli/OptionTest.java b/src/test/java/org/apache/commons/cli/OptionTest.java index 4d5783d..68ec905 100644 --- a/src/test/java/org/apache/commons/cli/OptionTest.java +++ b/src/test/java/org/apache/commons/cli/OptionTest.java @@ -66,7 +66,8 @@ public class OptionTest { } 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()); @@ -77,6 +78,15 @@ public class OptionTest { assertEquals(optionalArg, option.hasOptionalArg()); 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()); + } } private Option roundTrip(final Option o) throws IOException, ClassNotFoundException { @@ -122,31 +132,52 @@ public class OptionTest { public 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").build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class, null, + null, null); + checkOption(Option.builder("a").desc("desc").build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class, null, + null, null); 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); + String.class, null, null, null); + checkOption(Option.builder("a").desc("desc").hasArg(true).build(), "a", "desc", null, 1, null, false, false, defaultSeparator, String.class, null, null, + null); 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); + String.class, null, null, null); + checkOption(Option.builder("a").desc("desc").hasArg(true).build(), "a", "desc", null, 1, null, false, false, defaultSeparator, String.class, null, null, + null); + checkOption(Option.builder("a").desc("desc").numberOfArgs(3).build(), "a", "desc", null, 3, null, false, false, defaultSeparator, String.class, null, + null, null); checkOption(Option.builder("a").desc("desc").required(true).build(), "a", "desc", null, Option.UNINITIALIZED, null, true, false, defaultSeparator, - String.class); + String.class, null, null, null); checkOption(Option.builder("a").desc("desc").required(false).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, - String.class); + String.class, null, null, null); checkOption(Option.builder("a").desc("desc").argName("arg1").build(), "a", "desc", null, Option.UNINITIALIZED, "arg1", false, false, defaultSeparator, - String.class); + String.class, null, null, null); 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, 1, null, false, true, defaultSeparator, String.class); + String.class, null, null, null); + checkOption(Option.builder("a").desc("desc").optionalArg(true).build(), "a", "desc", null, 1, null, false, true, defaultSeparator, String.class, null, + null, null); checkOption(Option.builder("a").desc("desc").valueSeparator(':').build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, ':', - String.class); + String.class, null, null, null); checkOption(Option.builder("a").desc("desc").type(Integer.class).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, - Integer.class); + Integer.class, null, null, null); checkOption(Option.builder().option("a").desc("desc").type(Integer.class).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, - defaultSeparator, Integer.class); + defaultSeparator, Integer.class, null, null, null); + // Deprecated + checkOption(Option.builder().option("a").desc("desc").type(Integer.class).deprecated().build(), "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()).build(), "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()).build(), + "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()).build(), + "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()).build(), + "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, Integer.class, "X", true, "2.0"); } @Test @@ -235,7 +266,6 @@ public class OptionTest { @Test public void testSerialization() throws IOException, ClassNotFoundException { - final Option o = Option.builder("o").type(TypeHandlerTest.Instantiable.class).build(); assertEquals(Converter.DEFAULT, o.getConverter()); Option o2 = roundTrip(o); diff --git a/src/test/java/org/apache/commons/cli/OptionsTest.java b/src/test/java/org/apache/commons/cli/OptionsTest.java index 18fc74f..7b7b382 100644 --- a/src/test/java/org/apache/commons/cli/OptionsTest.java +++ b/src/test/java/org/apache/commons/cli/OptionsTest.java @@ -17,8 +17,10 @@ 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -129,12 +131,28 @@ public class OptionsTest { assertEquals("toggle -a*", opts.getOption("a").getDescription(), "last one in wins"); } + @Test + public void testDeprecated() { + final Options opts = new Options(); + opts.addOption(Option.builder().option("a").build()); + opts.addOption(Option.builder().option("b").deprecated().build()); + opts.addOption(Option.builder().option("c") + .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("2.0").setDescription("Use X.").get()).build()); + // toString() + assertTrue(opts.getOption("a").toString().startsWith("[ Option a")); + assertTrue(opts.getOption("b").toString().startsWith("[ Option b")); + assertTrue(opts.getOption("c").toString().startsWith("[ Option c")); + // toDeprecatedString() + assertFalse(opts.getOption("a").toDeprecatedString().startsWith("Option a")); + assertEquals("Option 'b': Deprecated", opts.getOption("b").toDeprecatedString()); + assertEquals("Option 'c': Deprecated for removal since 2.0: Use X.", opts.getOption("c").toDeprecatedString()); + } + @Test public void testDuplicateSimple() { final Options opts = new Options(); opts.addOption("a", false, "toggle -a"); opts.addOption("a", true, "toggle -a*"); - assertEquals("toggle -a*", opts.getOption("a").getDescription(), "last one in wins"); } 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 0000000..0500100 --- /dev/null +++ b/src/test/java/org/apache/commons/cli/SolrCliTest.java @@ -0,0 +1,162 @@ +/* + 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.jupiter.api.Assertions.assertNotNull; + +import java.util.Locale; + +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 + + '.') + .build(); + // @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 + + '.') + .build(); + // @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() + + '.') + .build(); + // @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() + + '.') + .build(); + // @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.") + .build(); + // @formatter:on + + public static final Option OPTION_HELP = + // @formatter:off + Option.builder("h") + .longOpt("help") + .required(false) + .desc("Print this message.") + .build(); + // @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.") + .build(); + // @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") + .build(); + // @formatter:on + + public static String getDefaultSolrUrl() { + final String scheme = "http"; + final String host = "localhost"; + final String port = "8983"; + return String.format(Locale.ROOT, "%s://%s:%s", scheme.toLowerCase(Locale.ROOT), host, port); + } + + @Test + public 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 0000000..6dcc70d --- /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 + + 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.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; + +public class SolrCreateToolTest { + + public List<Option> 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.") + .build(), + Option.builder("s") + .longOpt("shards") + .argName("#") + .hasArg() + .required(false) + .desc("Number of shards; default is 1.") + .build(), + 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.") + .build(), + 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 + + '.') + .build(), + Option.builder("n") + .longOpt("confname") + .argName("NAME") + .hasArg() + .required(false) + .desc("Configuration name; default is the collection name.") + .build(), + 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 <HOST> Zookeeper connection string; unnecessary")); + return actual; + } + + @Test + public void testHelpFormatter() { + final HelpFormatter formatter = new HelpFormatter(); + final String actual = printHelp(formatter); + assertFalse(actual.contains("Deprecated")); + } + + @Test + public void testHelpFormatterDeprecated() { + final HelpFormatter formatter = HelpFormatter.builder().setShowDeprecated(true).get(); + final String actual = printHelp(formatter); + assertTrue(actual.contains("-zkHost,--zkHost <HOST> [Deprecated] Zookeeper connection")); + } +}