Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix UsageFormatter: inherit usage formatter for subcommands #423

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion doc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ class ParameterNamesUsageFormatter implements IUsageFormatter {
// Extend other required methods as seen in DefaultUsageFormatter

// This is the method which does the actual output formatting
public void usage(StringBuilder out, String indent) {
public void usage(JCommander commander, StringBuilder out, String indent) {
Copy link
Owner

Choose a reason for hiding this comment

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

This is a breaking change (and there might be more in this PR, haven't fully reviewed yet).

Not a deal breaker, but if this gets merged, the major version will have to go up.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@cbeust I have started a v2 branch to collect things that could go into JCommander 2.0.0. Did you find the time to finish your review, or should I start a review from scratch?

Copy link
Owner

Choose a reason for hiding this comment

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

Let's start from scratch.

if (commander.getDescriptions() == null) {
commander.createDescriptions();
}
Expand Down
200 changes: 107 additions & 93 deletions src/main/java/com/beust/jcommander/DefaultUsageFormatter.java

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions src/main/java/com/beust/jcommander/IUsageFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,30 @@ public interface IUsageFormatter {
/**
* Display the usage for this command.
*/
void usage(String commandName);
void usage(JCommander commander, String commandName);

/**
* Store the help for the command in the passed string builder.
*/
void usage(String commandName, StringBuilder out);
void usage(JCommander commander, String commandName, StringBuilder out);

/**
* Store the help in the passed string builder.
*/
void usage(StringBuilder out);
void usage(JCommander commander, StringBuilder out);

/**
* Store the help for the command in the passed string builder, indenting every line with "indent".
*/
void usage(String commandName, StringBuilder out, String indent);
void usage(JCommander commander, String commandName, StringBuilder out, String indent);

/**
* Stores the help in the passed string builder, with the argument indentation.
*/
void usage(StringBuilder out, String indent);
void usage(JCommander commander, StringBuilder out, String indent);

/**
* @return the description of the argument command
*/
String getCommandDescription(String commandName);
String getCommandDescription(JCommander commander, String commandName);
}
40 changes: 37 additions & 3 deletions src/main/java/com/beust/jcommander/JCommander.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public void addValue(Object convertedValue) {
/**
* The usage formatter to use in {@link #usage()}.
*/
private IUsageFormatter usageFormatter = new DefaultUsageFormatter(this);
private IUsageFormatter usageFormatter = new DefaultUsageFormatter();

private MainParameter mainParameter = null;

Expand Down Expand Up @@ -1020,12 +1020,42 @@ public void setProgramName(String name, String... aliases) {
*/
public void usage() {
StringBuilder sb = new StringBuilder();
usageFormatter.usage(sb);
usageFormatter.usage(this, sb);
getConsole().println(sb.toString());
}

/**
* Sets the usage formatter.
* Prints the usage to given {@link StringBuilder} using the underlying {@link #usageFormatter}
* @param sb {@link StringBuilder} to append
*/
void usage(StringBuilder sb) {
// package access, for tests
usageFormatter.usage(this, sb);
}

/**
* Prints the usage of subcommand on {@link #getConsole()} using the underlying {@link #usageFormatter}.
*/
public void usage(String commandName) {
StringBuilder sb = new StringBuilder();
usageFormatter.usage(this, commandName, sb);
getConsole().println(sb.toString());
}

void usage(String commandName, StringBuilder sb) {
// package access, for tests
usageFormatter.usage(this, commandName, sb);
}

/**
* @return Get the description of the argument command using the underlying {@link #usageFormatter}
*/
public String getCommandDescription(String commandName) {
return usageFormatter.getCommandDescription(this, commandName);
}

/**
* Sets the usage formatter. This will set usage formatter for all subcommands recursively.
*
* @param usageFormatter the usage formatter
* @throws IllegalArgumentException if the argument is <tt>null</tt>
Expand All @@ -1034,6 +1064,9 @@ public void setUsageFormatter(IUsageFormatter usageFormatter) {
if (usageFormatter == null)
throw new IllegalArgumentException("Argument UsageFormatter must not be null");
this.usageFormatter = usageFormatter;
for (Map.Entry<ProgramName, JCommander> entry : commands.entrySet()) {
entry.getValue().setUsageFormatter(usageFormatter);
}
}

/**
Expand Down Expand Up @@ -1393,6 +1426,7 @@ public void addCommand(String name, Object object, String... aliases) {
jc.addObject(object);
jc.createDescriptions();
jc.setProgramName(name, aliases);
jc.setUsageFormatter(usageFormatter);
ProgramName progName = jc.programName;
commands.put(progName, jc);

Expand Down
14 changes: 7 additions & 7 deletions src/main/java/com/beust/jcommander/UnixStyleUsageFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,26 @@

/**
* A unix-style usage formatter. This works by overriding and modifying the output of
* {@link #appendAllParametersDetails(StringBuilder, int, String, List)} which is inherited from
* {@link #appendAllParametersDetails(JCommander, StringBuilder, int, String, List)} which is inherited from
* {@link DefaultUsageFormatter}.
*/
public class UnixStyleUsageFormatter extends DefaultUsageFormatter {

public UnixStyleUsageFormatter(JCommander commander) {
super(commander);
}
public UnixStyleUsageFormatter() { }

/**
* Appends the details of all parameters in the given order to the argument string builder, indenting every
* line with <tt>indentCount</tt>-many <tt>indent</tt>.
*
* @param commander the commander
* @param out the builder to append to
* @param indentCount the amount of indentation to apply
* @param indent the indentation
* @param sortedParameters the parameters to append to the builder
*/
public void appendAllParametersDetails(StringBuilder out, int indentCount, String indent,
List<ParameterDescription> sortedParameters) {
@Override
protected void appendAllParametersDetails(JCommander commander, StringBuilder out, int indentCount, String indent,
List<ParameterDescription> sortedParameters) {
if (sortedParameters.size() > 0) {
out.append(indent).append(" Options:\n");
}
Expand Down Expand Up @@ -101,7 +101,7 @@ public void appendAllParametersDetails(StringBuilder out, int indentCount, Strin
// Append description
// The magic value 3 is the number of spaces between the name of the option and its description
// in DefaultUsageFormatter#appendCommands(..)
wrapDescription(out, indentCount + prefixIndent - 3, initialLinePrefixLength, description);
wrapDescription(commander, out, indentCount + prefixIndent - 3, initialLinePrefixLength, description);
out.append("\n");
}
}
Expand Down
38 changes: 19 additions & 19 deletions src/test/java/com/beust/jcommander/DefaultUsageFormatterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class ArgsTemplate {
.build();

// action
jc.getUsageFormatter().usage(sb);
jc.usage(sb);

// verify
String expected = "Usage: <main class> [options]\n"
Expand Down Expand Up @@ -104,7 +104,7 @@ public void testLongMainParameterDescription() {
StringBuilder sb = new StringBuilder();

//action
jc.getUsageFormatter().usage(sb);
jc.usage(sb);

//verify
for (String line : sb.toString().split("\n")) {
Expand All @@ -121,7 +121,7 @@ public void testLongCommandDescription() throws Exception {
StringBuilder sb = new StringBuilder();

//action
jc.getUsageFormatter().usage(sb);
jc.usage(sb);

//verify
for (String line : sb.toString().split("\n")) {
Expand All @@ -138,7 +138,7 @@ public void testDescriptionWrappingLongWord() {
.build();

//action
jc.getUsageFormatter().usage(sb);
jc.usage(sb);

//verify
for (String line : sb.toString().split("\n")) {
Expand All @@ -152,7 +152,7 @@ public void programName() {
String programName = "main";
jcommander.setProgramName(programName);
StringBuilder sb = new StringBuilder();
jcommander.getUsageFormatter().usage(sb);
jcommander.usage(sb);

Assert.assertTrue(sb.toString().contains(programName));
Assert.assertEquals(jcommander.getProgramName(), programName);
Expand All @@ -171,7 +171,7 @@ class CommandTemplate {
.build();
jcommander.setProgramName("main");
StringBuilder sb = new StringBuilder();
jcommander.getUsageFormatter().usage(sb);
jcommander.usage(sb);
Assert.assertEquals(sb.toString().indexOf("options"), -1);
}

Expand All @@ -188,7 +188,7 @@ class DSimple {
JCommander jc = JCommander.newBuilder()
.addObject(new DSimple())
.build();
jc.getUsageFormatter().usage(new StringBuilder());
jc.usage(new StringBuilder());
}

/**
Expand All @@ -199,7 +199,7 @@ public void nonexistentCommandShouldThrow() {
String[] argv = {};
JCommander jc = JCommander.newBuilder().addObject(new Object()).build();
jc.parse(argv);
jc.getUsageFormatter().getCommandDescription("foo");
jc.getCommandDescription("foo");
}

@Test
Expand All @@ -210,7 +210,7 @@ public void i18MissingKeyForCommand() {
.build();
jc.addCommand(new ArgsLongCommandDescription());
StringBuilder sb = new StringBuilder();
jc.getUsageFormatter().usage(sb);
jc.usage(sb);
String usage = sb.toString();
Assert.assertTrue(usage.contains("text"));
}
Expand All @@ -220,7 +220,7 @@ public void noParseConstructor() {
JCommander jCommander = JCommander.newBuilder()
.addObject(new ArgsMainParameter1())
.build();
jCommander.getUsageFormatter().usage(new StringBuilder());
jCommander.usage(new StringBuilder());
// Before fix, this parse would throw an exception, because it calls createDescription, which
// was already called by usage(), and can only be called once.
jCommander.parse();
Expand All @@ -238,7 +238,7 @@ public void usageWithRequiredArgsAndResourceBundle() {
.resourceBundle(getResourceBundle())
.build();
// Should be able to display usage without triggering validation
jc.getUsageFormatter().usage(new StringBuilder());
jc.usage(new StringBuilder());
try {
jc.parse("-h");
Assert.fail("Should have thrown a required parameter exception");
Expand All @@ -253,11 +253,11 @@ public void usageShouldNotChange() {
JCommander jc = JCommander.newBuilder().addObject(new Args1()).build();
jc.parse("-log", "1");
StringBuilder sb = new StringBuilder();
jc.getUsageFormatter().usage(sb);
jc.usage(sb);
String expected = sb.toString();

sb = new StringBuilder();
jc.getUsageFormatter().usage(sb);
jc.usage(sb);
String actual = sb.toString();
Assert.assertEquals(actual, expected);
}
Expand All @@ -267,7 +267,7 @@ public void oom() {
JCommander jc = JCommander.newBuilder()
.addObject(new ArgsOutOfMemory())
.build();
jc.getUsageFormatter().usage(new StringBuilder());
jc.usage(new StringBuilder());
}

@Test
Expand All @@ -283,7 +283,7 @@ class Arg {

StringBuilder sb = new StringBuilder();

jc.getUsageFormatter().usage(sb);
jc.usage(sb);

Assert.assertFalse(sb.toString().contains("Default"));
}
Expand Down Expand Up @@ -312,7 +312,7 @@ class ArgCommandB {
c.addCommand("b", new ArgCommandB());

StringBuilder sb = new StringBuilder();
c.getUsageFormatter().usage(sb);
c.usage(sb);
Assert.assertTrue(sb.toString().contains("[command options]\n Commands:"));
}

Expand Down Expand Up @@ -340,7 +340,7 @@ class ArgCommandB {
c.addCommand("b", new ArgCommandB());

StringBuilder sb = new StringBuilder();
c.getUsageFormatter().usage(sb);
c.usage(sb);
Assert.assertTrue(sb.toString().contains("command a parameters\n\n b"));
}

Expand Down Expand Up @@ -372,7 +372,7 @@ class ArgCommandB {
aCommand.addCommand("b", new ArgCommandB());

StringBuilder sb = new StringBuilder();
c.getUsageFormatter().usage(sb);
c.usage(sb);
Assert.assertTrue(sb.toString().contains("command a parameters\n Commands:"));
Assert.assertTrue(sb.toString().contains("command b\n Usage:"));
}
Expand All @@ -388,7 +388,7 @@ class Arg {
JCommander c = JCommander.newBuilder()
.addObject(a)
.build();
c.getUsageFormatter().usage(sb);
c.usage(sb);
Assert.assertTrue(sb.toString().contains("Default: <empty string>"));
}
}
2 changes: 1 addition & 1 deletion src/test/java/com/beust/jcommander/ParameterOrderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void testOrder(Object cmd, String ... expected) {
JCommander commander = new JCommander(cmd);

StringBuilder out = new StringBuilder();
commander.getUsageFormatter().usage(out);
commander.usage(out);
Copy link
Owner

Choose a reason for hiding this comment

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

Isn't this change a step back?

Copy link
Contributor

Choose a reason for hiding this comment

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

It is, however it does read nicer.

String output = out.toString();
List<String> order = new ArrayList<>();
for (String line : output.split("[\\n\\r]+")) {
Expand Down
Loading