Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[CLI-329] Support "Deprecated" CLI Options
- HelpFormatter does not print deprecated options by default
- HelpFormatter can show deprecated options:
HelpFormatter.builder().setShowDeprecated(true).get();
  • Loading branch information
garydgregory committed Mar 27, 2024
commit a37841b558199347a34b640f3a97033b0ce28171
72 changes: 72 additions & 0 deletions src/main/java/org/apache/commons/cli/HelpFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
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.
Expand Down Expand Up @@ -63,6 +64,38 @@ Licensed to the Apache Software Foundation (ASF) under one or more
*/
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.
*/
Expand Down Expand Up @@ -113,6 +146,16 @@ public int compare(final Option opt1, final Option opt2) {
/** 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
*
Expand Down Expand Up @@ -184,11 +227,34 @@ public int compare(final Option opt1, final Option opt2) {
*/
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.
*
Expand Down Expand Up @@ -686,7 +752,13 @@ protected StringBuffer renderOptions(final StringBuffer sb, final int width, fin
}
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());
Expand Down
162 changes: 162 additions & 0 deletions src/test/java/org/apache/commons/cli/SolrCliTest.java
Original file line number Diff line number Diff line change
@@ -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());
}
}
106 changes: 106 additions & 0 deletions src/test/java/org/apache/commons/cli/SolrCreateToolTest.java
Original file line number Diff line number Diff line change
@@ -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"));
}
}