Лично мне больше всего нравится ваше первое решение, оно простое и легкое в обслуживании. Возможно, добавьте вспомогательный метод для печати и форматирования, чтобы методы команд могли выглядеть следующим образом:
@CommandLine.Command
public String sayGoodbye()
{
return printValue("GoodBye");
}
Вы уже нашли метод CommandLine.getParseResult
; возможно, вспомогательный метод также может помочь с форматированием.
Существует третий вариант, но, к сожалению, он немного сложнее: вы можете создать пользовательский IExecutionStrategy
, который печатает результат каждой команды после ее выполнения. Он включает в себя копирование большого количества кода из внутренних компонентов picocli, и это не совсем реалистичное решение c; Я просто упоминаю это для полноты.
// extend RunLast to handle requests for help/version and exit code stuff
class PrintingExecutionStrategy extends CommandLine.RunLast {
@Override
protected List<Object> handle(ParseResult parseResult) throws ExecutionException {
// Simplified: executes only the last subcommand (so no repeating subcommands).
// Look at RunLast.executeUserObjectOfLastSubcommandWithSameParent if you need repeating subcommands.
List<CommandLine> parsedCommands = parseResult.asCommandLineList();
CommandLine last = parsedCommands.get(parsedCommands.size() - 1);
return execute(last, new ArrayList<Object>());
}
// copied from CommandLine.executeUserObject,
// modified to print the execution result
private List<Object> execute(CommandLine cmd, List<Object> executionResultList) throws Exception {
Object command = parsed.getCommand();
if (command instanceof Runnable) {
try {
((Runnable) command).run();
parsed.setExecutionResult(null); // 4.0
executionResultList.add(null); // for compatibility with picocli 2.x
return executionResultList;
} catch (ParameterException ex) {
throw ex;
} catch (ExecutionException ex) {
throw ex;
} catch (Exception ex) {
throw new ExecutionException(parsed, "Error while running command (" + command + "): " + ex, ex);
}
} else if (command instanceof Callable) {
try {
@SuppressWarnings("unchecked") Callable<Object> callable = (Callable<Object>) command;
Object executionResult = callable.call();
System.out.println(executionResult); <-------- print result
parsed.setExecutionResult(executionResult);
executionResultList.add(executionResult);
return executionResultList;
} catch (ParameterException ex) {
throw ex;
} catch (ExecutionException ex) {
throw ex;
} catch (Exception ex) {
throw new ExecutionException(parsed, "Error while calling command (" + command + "): " + ex, ex);
}
} else if (command instanceof Method) {
try {
Method method = (Method) command;
Object[] parsedArgs = parsed.getCommandSpec().argValues();
Object executionResult;
if (Modifier.isStatic(method.getModifiers())) {
executionResult = method.invoke(null, parsedArgs); // invoke static method
} else if (parsed.getCommandSpec().parent() != null) {
executionResult = method.invoke(parsed.getCommandSpec().parent().userObject(), parsedArgs);
} else {
executionResult = method.invoke(parsed.factory.create(method.getDeclaringClass()), parsedArgs);
}
System.out.println(executionResult); <-------- print result
parsed.setExecutionResult(executionResult);
executionResultList.add(executionResult);
return executionResultList;
} catch (InvocationTargetException ex) {
Throwable t = ex.getTargetException();
if (t instanceof ParameterException) {
throw (ParameterException) t;
} else if (t instanceof ExecutionException) {
throw (ExecutionException) t;
} else {
throw new ExecutionException(parsed, "Error while calling command (" + command + "): " + t, t);
}
} catch (Exception ex) {
throw new ExecutionException(parsed, "Unhandled error while calling command (" + command + "): " + ex, ex);
}
}
throw new ExecutionException(parsed, "Parsed command (" + command + ") is not a Method, Runnable or Callable");
}
}
Используйте это так:
public static void main(String... args) {
new CommandLine(new Foo())
.setExecutionStrategy(new PrintingExecutionStrategy())
.execute(args);
}
Я бы не рекомендовал выше.
Обновление: я подумал о другом, четвертом, варианте (на самом деле это вариант вашего второго решения). Вы можете указать пользовательский IExecutionExceptionHandler
, который не печатает трассировку стека, но вместо этого сохраняет исключение, чтобы вы могли распечатать трассировку стека после печати результатов команды. Примерно так:
class MyHandler extends IExecutionExceptionHandler() {
Exception exception;
public int handleExecutionException(Exception ex,
CommandLine commandLine,
ParseResult parseResult) {
//ex.printStackTrace(); // no stack trace
exception = ex;
}
}
Используйте это так:
public static void main(String... args) {
MyHandler handler = new MyHandler();
CommandLine cmd = new CommandLine(new Foo())
.setExecutionExceptionHandler(handler);
cmd.execute(args);
ParseResult parseResult = cmd.getParseResult();
for( ParseResult pr : parseResult.subcommands() )
{
System.out.println( pr.commandSpec().commandLine()
.getExecutionResult()
.toString() );
}
if (handler.exception != null) {
handler.exception.printStackTrace();
}
}