Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
50 changes: 31 additions & 19 deletions src/main/java/org/apache/commons/cli/HelpFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Supplier;

/**
Expand Down Expand Up @@ -75,9 +76,14 @@ public static final class Builder implements Supplier<HelpFormatter> {
// Make HelpFormatter immutable for 2.0

/**
* Whether to show deprecated options.
* A function to convert a description (not null) and a deprecated Option (not null) to help description
*/
private boolean showDeprecated;
private static final BiFunction<String, Option, String> DEFAULT_DEPRECATED_FORMAT = (d, o) -> "[Deprecated] " + d;

/**
* Formatter for deprecated options.
*/
private BiFunction<String, Option, String> deprecatedFormatFunc;

/**
* The output PrintWriter, defaults to wrapping {@link System#out}.
Expand All @@ -86,7 +92,7 @@ public static final class Builder implements Supplier<HelpFormatter> {

@Override
public HelpFormatter get() {
return new HelpFormatter(showDeprecated, printStream);
return new HelpFormatter(deprecatedFormatFunc, printStream);
}

/**
Expand All @@ -103,14 +109,24 @@ public Builder setPrintWriter(final PrintWriter printWriter) {
/**
* Sets whether to show deprecated options.
*
* @param showDeprecated Whether to show deprecated options.
* @param useDefaultFormat if {@code true} use the default format, otherwise clear the formatter.
* @return this.
*/
public Builder setShowDeprecated(final boolean showDeprecated) {
this.showDeprecated = showDeprecated;
return this;
public Builder setShowDeprecated(final boolean useDefaultFormat) {
return setShowDeprecated(useDefaultFormat ? DEFAULT_DEPRECATED_FORMAT : null);
}

/**
* Sets whether to show deprecated options.
*
* @param showDeprecatedFunc Specify the format for the deprecated options.
* @return this.
* @since 1.8.0
*/
public Builder setShowDeprecated(final BiFunction<String, Option, String> showDeprecatedFunc) {

@garydgregory garydgregory May 11, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new public or protected API needs a @since tag, which for semantic versioning would be 1.8.0.

this.deprecatedFormatFunc = showDeprecatedFunc;
return this;
}
}

/**
Expand Down Expand Up @@ -250,9 +266,9 @@ private static PrintWriter createDefaultPrintWriter() {
protected Comparator<Option> optionComparator = new OptionComparator();

/**
* Whether to show deprecated options.
* BiFunction to format the description for a deprecated option.
*/
private final boolean showDeprecated;
private final BiFunction<String, Option, String> deprecatedFormatFunc;

/**
* Where to print help.
Expand All @@ -268,17 +284,17 @@ private static PrintWriter createDefaultPrintWriter() {
* Constructs a new instance.
*/
public HelpFormatter() {
this(false, createDefaultPrintWriter());
this(null, createDefaultPrintWriter());
}

/**
* Constructs a new instance.
* @param printStream TODO
*/
private HelpFormatter(final boolean showDeprecated, final PrintWriter printStream) {
private HelpFormatter(final BiFunction<String, Option, String> deprecatedFormatFunc, final PrintWriter printStream) {
// TODO All other instance HelpFormatter instance variables.
// Make HelpFormatter immutable for 2.0
this.showDeprecated = showDeprecated;
this.deprecatedFormatFunc = deprecatedFormatFunc;
this.printWriter = printStream;
}

Expand Down Expand Up @@ -778,13 +794,9 @@ 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(' ');
}
if (deprecatedFormatFunc != null && option.isDeprecated()) {
optBuf.append(deprecatedFormatFunc.apply(option.getDescription() == null ? "" : option.getDescription(), option).trim());
} else if (option.getDescription() != null) {
optBuf.append(option.getDescription());
}
renderWrappedText(sb, width, nextLineTabStop, optBuf.toString());
Expand Down
59 changes: 59 additions & 0 deletions src/test/java/org/apache/commons/cli/HelpFormatterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ Licensed to the Apache Software Foundation (ASF) under one or more
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
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;

/**
* Test case for the HelpFormatter class.
Expand Down Expand Up @@ -484,6 +490,59 @@ public void testPrintOptions() {
assertEquals(expected, sb.toString(), "multiple wrapped options");
}

@ParameterizedTest
@MethodSource("deprecatedOptionsProvider")
public 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;
String expected = lpad + "-a,--aaa";

options = new Options().addOption(option);
if (expectedTxt.length() > 0) {
expected = expected + dpad + expectedTxt;
}
hf.renderOptions(sb, 160, options, leftPad, descPad);
assertEquals(expected, sb.toString());
}

static Stream<Arguments> deprecatedOptionsProvider() {
List<Arguments> 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())
.build();

HelpFormatter 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((d, o) -> String.format("%s [%s]", d, 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())
.build();

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((d, o) -> String.format("%s [%s]", d, o.getDeprecated())).get();
lst.add(Arguments.of(hf, option, "[Deprecated for removal since now: Why why why]"));

return lst.stream();
}

@Test
public void testPrintOptionWithEmptyArgNameUsage() {
final Option option = new Option("f", true, null);
Expand Down