Skip to content

Commit

Permalink
[#561][#650][#424][#541][#663][#672] fixes, improved documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
remkop committed Apr 24, 2019
1 parent 4d84544 commit b37681a
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 29 deletions.
15 changes: 8 additions & 7 deletions src/main/java/overview.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@
<p>
Picocli provides a number of <a href="http://picocli.info/#_less_boilerplate">convenience methods</a>
that allow you to omit error handling and other boilerplate code for common use cases.
Here is a small example application that uses the <code>CommandLine.call</code> convenience method
Here is a small example application that uses the <code>CommandLine.execute</code> convenience method
to do parsing and error handling in one line of code.
</p>
<p>
The full user manual is hosted at <a href="http://picocli.info/">http://picocli.info</a>.
</p>
<pre>
&#064;Command(name = "checksum", mixinStandardHelpOptions = true, version = "Checksum 2.0",
&#064;Command(name = "checksum", mixinStandardHelpOptions = true, version = "Checksum 4.0",
description = "Prints the checksum (MD5 by default) of a file to STDOUT.")
class CheckSum implements Callable&lt;Void&gt; {
class CheckSum implements Callable&lt;Integer&gt; {

&#064;Parameters(index = "0", description = "The file whose checksum to calculate.")
private File file;
Expand All @@ -35,16 +35,17 @@
public static void main(String[] args) throws Exception {
// CheckSum implements Callable, so parsing, error handling and handling user
// requests for usage help or version help can be done with one line of code.
CommandLine.call(new CheckSum(), args);
int exitCode = new CommandLine(new CheckSum()).execute(args);
// System.exit(exitCode);
}

&#064;Override
public Void call() throws Exception {
public Integer call() throws Exception {
// your business logic goes here
byte[] fileContents = Files.readAllBytes(file.toPath());
byte[] digest = MessageDigest.getInstance(algorithm).digest(fileContents);
System.out.println(javax.xml.bind.DatatypeConverter.printHexBinary(digest));
return null;
System.out.printf("%0" + (digest.length*2) + "x%n", new BigInteger(1, digest));
return 0;
}
}
</pre>
Expand Down
64 changes: 42 additions & 22 deletions src/main/java/picocli/CommandLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,12 @@
* -vooutfile in1 in2
* </pre>
* <p>
* Another example that implements {@code Callable} and uses the {@link #call(Callable, String...) CommandLine.call} convenience API to run in a single line of code:
* Another example that implements {@code Callable} and uses the {@link #execute(String...) CommandLine.execute} convenience API to run in a single line of code:
* </p>
* <pre>
* &#064;Command(description = "Prints the checksum (MD5 by default) of a file to STDOUT.",
* name = "checksum", mixinStandardHelpOptions = true, version = "checksum 3.0")
* class CheckSum implements Callable&lt;Void&gt; {
* name = "checksum", mixinStandardHelpOptions = true, version = "checksum 4.0")
* class CheckSum implements Callable&lt;Integer&gt; {
*
* &#064;Parameters(index = "0", description = "The file whose checksum to calculate.")
* private File file;
Expand All @@ -117,16 +117,17 @@
* public static void main(String[] args) throws Exception {
* // CheckSum implements Callable, so parsing, error handling and handling user
* // requests for usage help or version help can be done with one line of code.
* CommandLine.call(new CheckSum(), args);
* int exitCode = new CommandLine(new CheckSum()).execute(args);
* // System.exit(exitCode);
* }
*
* &#064;Override
* public Void call() throws Exception {
* public Integer call() throws Exception {
* // your business logic goes here...
* byte[] fileContents = Files.readAllBytes(file.toPath());
* byte[] digest = MessageDigest.getInstance(algorithm).digest(fileContents);
* System.out.println(javax.xml.bind.DatatypeConverter.printHexBinary(digest));
* return null;
* System.out.printf("%0" + (digest.length*2) + "x%n", new BigInteger(1,digest));
* return 0;
* }
* }
* </pre>
Expand Down Expand Up @@ -784,10 +785,10 @@ public CommandLine setUnmatchedArgumentsAllowed(boolean newValue) {
public List<String> getUnmatchedArguments() {
return interpreter.parseResultBuilder == null ? Collections.<String>emptyList() : UnmatchedArgumentException.stripErrorMessage(interpreter.parseResultBuilder.unmatched);
}
interface IExitCodeGenerator {
public interface IExitCodeGenerator {
int getExitCode();
}
interface IExitCodeExceptionMapper {
public interface IExitCodeExceptionMapper {
int getExitCode(Throwable exception);
}
private PrintWriter out;
Expand Down Expand Up @@ -847,7 +848,7 @@ public CommandLine setOut(PrintWriter out) {
/** Returns the stream to print diagnostic messages to. Defaults to a PrintWriter wrapper around {@code System.err},
* unless {@link #setErr(PrintWriter)} was called with a different stream.
* <p>This method is used by {@link #execute(String...)}.
* {@code IParameterExceptionHandler} and {@link IExecutionExceptionHandler} implementations
* {@code IParameterExceptionHandler} and {@link CommandLine.IExecutionExceptionHandler} implementations
* should use this stream to print error messages (which may include a usage help message) when an unexpected error occurs.
* </p>
* @since 4.0 */
Expand Down Expand Up @@ -1035,8 +1036,27 @@ public static interface IParseResultHandler2<R> {
R handleParseResult(ParseResult parseResult) throws ExecutionException;
}

interface IExecutionStrategy {
int execute(ParseResult parseResult) throws ExecutionException;
/**
* Implementations are responsible for "executing" the user input and returning an exit code.
* The {@link #execute(String...)} and {@link #tryExecute(String...)} methods delegate to a {@linkplain #setExecutionStrategy(IExecutionStrategy) configured} execution strategy.
* <p>Implementers responsibilities are:</p>
* <ul>
* <li>From the {@code ParseResult}, select which {@code CommandSpec} should be executed. This is especially important for commands that have subcommands.</li>
* <li>"Execute" the selected {@code CommandSpec}. Often this means invoking a method on the spec's {@linkplain CommandSpec#userObject() user object}.</li>
* <li>Return an exit code. Common sources of exit values are the invoked method's return value, or the user object if it implements {@link IExitCodeGenerator}.</li>
* </ul>
* This interface supercedes {@link IParseResultHandler2}
* @since 4.0 */
public interface IExecutionStrategy {
/**
* "Executes" the user input and returns an exit code.
* Execution often means invoking a method on the selected CommandSpec's {@linkplain CommandSpec#userObject() user object}.
* @param parseResult the parse result from which to select one or more {@code CommandSpec} instances to execute.
* @return an exit code
* @throws ParameterException if the invoked method on the CommandSpec's user object threw a ParameterException to signify invalid user input.
* @throws ExecutionException if any problem occurred while executing the command. Any exceptions (other than ParameterException) should be wrapped in a ExecutionException and not thrown as is.
*/
int execute(ParseResult parseResult) throws ExecutionException, ParameterException;
}

/**
Expand Down Expand Up @@ -1093,10 +1113,10 @@ public static interface IExceptionHandler2<R> {
*/
R handleExecutionException(ExecutionException ex, ParseResult parseResult);
}
interface IParameterExceptionHandler {
public interface IParameterExceptionHandler {
int handleParseException(ParameterException ex, String[] args) throws Exception;
}
interface IExecutionExceptionHandler {
public interface IExecutionExceptionHandler {
int handleExecutionException(ExecutionException ex, ParseResult parseResult) throws Exception;
}

Expand Down Expand Up @@ -1388,7 +1408,7 @@ private static List<Object> executeUserObject(CommandLine parsed, List<Object> e
* </p><p>
* A second difference is that this method provides exit code support.
* If the user object {@code Callable} or {@code Method} returns an {@code int} or {@code Integer},
* this will be used as the exit code. Additionally, if the user object implements {@link IExitCodeGenerator IExitCodeGenerator},
* this will be used as the exit code. Additionally, if the user object implements {@link CommandLine.IExitCodeGenerator IExitCodeGenerator},
* an exit code is obtained by calling its {@code getExitCode()} method (after invoking the user object).
* </p><p>
* In the case of multiple exit codes the highest value will be used (or if all values are negative, the lowest value will be used).
Expand Down Expand Up @@ -1418,7 +1438,7 @@ private static List<Object> executeUserObject(CommandLine parsed, List<Object> e
* The default {@code IExecutionExceptionHandler} will rethrow the <em>cause</em> Exception.
* This method will catch any exceptions thrown by the execution exception handler and return an exit code.
* </p><p>
* If an {@link IExitCodeExceptionMapper IExitCodeExceptionMapper} is {@linkplain #setExitCodeExceptionMapper(IExitCodeExceptionMapper) configured},
* If an {@link CommandLine.IExitCodeExceptionMapper IExitCodeExceptionMapper} is {@linkplain #setExitCodeExceptionMapper(IExitCodeExceptionMapper) configured},
* this mapper is used to determine the exit code based on the exception.
* </p><p>
* If no {@code IExitCodeExceptionMapper} is set, by default this method will return the {@code @Command} annotation's
Expand Down Expand Up @@ -1455,7 +1475,7 @@ public int execute(String... args) {
* </p><p>
* A second difference is that this method provides exit code support.
* If the user object {@code Callable} or {@code Method} returns an {@code int} or {@code Integer},
* this will be used as the exit code. Additionally, if the user object implements {@link IExitCodeGenerator IExitCodeGenerator},
* this will be used as the exit code. Additionally, if the user object implements {@link CommandLine.IExitCodeGenerator IExitCodeGenerator},
* an exit code is obtained by calling its {@code getExitCode()} method (after invoking the user object).
* </p><p>
* In the case of multiple exit codes the highest value will be used (or if all values are negative, the lowest value will be used).
Expand Down Expand Up @@ -1547,7 +1567,7 @@ public abstract static class AbstractParseResultHandler<R> extends AbstractHandl
* Finally, either a list of result objects is returned, or the JVM is terminated if an exit code {@linkplain #andExit(int) was set}.
*
* @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments
* @return the result of {@link #handle(ParseResult) processing parse results}
* @return the result of {@link #handle(CommandLine.ParseResult) processing parse results}
* @throws ParameterException if the {@link HelpCommand HelpCommand} was invoked for an unknown subcommand. Any {@code ParameterExceptions}
* thrown from this method are treated as if this exception was thrown during parsing and passed to the {@link IExceptionHandler2}
* @throws ExecutionException if a problem occurred while processing the parse results; client code can use
Expand Down Expand Up @@ -1602,7 +1622,7 @@ private int resolveExitCode(int exitCodeOnSuccess, R executionResult, List<IExit
*/
protected abstract R handle(ParseResult parseResult) throws ExecutionException;

protected abstract List<IExitCodeGenerator> extractExitCodeGenerators(ParseResult parseResult);
protected List<IExitCodeGenerator> extractExitCodeGenerators(ParseResult parseResult) { return Collections.emptyList(); }
}
/**
* Command line parse result handler that prints help if requested, and otherwise executes the top-level
Expand Down Expand Up @@ -2240,7 +2260,7 @@ public static <C extends Callable<T>, T> T call(Class<C> callableClass, IFactory
/**
* Convenience method to allow command line application authors to avoid some boilerplate code in their application.
* The specified {@linkplain IFactory factory} will create an instance of the specified {@code callableClass};
* use this method instead of {@link #call(Callable, PrintStream, PrintStream, Help.ColorScheme, String...) call(Callable, ...)}
* use this method instead of {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...) call(Callable, ...)}
* if you want to use a factory that performs Dependency Injection.
* The annotated class needs to implement {@link Callable}.
* <p>Consider using the {@link #execute(String...)} method instead:</p>
Expand Down Expand Up @@ -3678,12 +3698,12 @@ private static class NoCompletionCandidates implements Iterable<String> {
*/
int usageHelpWidth() default 80;

/** Exit code for successful termination. {@value CommandSpec#DEFAULT_EXIT_CODE_OK} by default, following C/C++ <a href="https://www.freebsd.org/cgi/man.cgi?query=sysexits&sektion=3">conventions</a>.
/** Exit code for successful termination. {@value picocli.CommandLine.Model.CommandSpec#DEFAULT_EXIT_CODE_OK} by default, following C/C++ <a href="https://www.freebsd.org/cgi/man.cgi?query=sysexits&sektion=3">conventions</a>.
* @see #execute(String...)
* @since 4.0 */
int exitCodeOnSuccess() default CommandSpec.DEFAULT_EXIT_CODE_OK;

/** Exit code for successful termination after printing usage help on user request. {@value CommandSpec#DEFAULT_EXIT_CODE_OK} by default, following unix C/C++ <a href="https://www.freebsd.org/cgi/man.cgi?query=sysexits&sektion=3">conventions</a>.
/** Exit code for successful termination after printing usage help on user request. {@value Model.CommandSpec#DEFAULT_EXIT_CODE_OK} by default, following unix C/C++ <a href="https://www.freebsd.org/cgi/man.cgi?query=sysexits&sektion=3">conventions</a>.
* @see #execute(String...)
* @since 4.0 */
int exitCodeOnUsageHelp() default CommandSpec.DEFAULT_EXIT_CODE_OK;
Expand Down

0 comments on commit b37681a

Please sign in to comment.