|
|
|
|
@@ -15,9 +15,14 @@
|
|
|
|
|
*/
|
|
|
|
|
package org.springframework.shell.jline;
|
|
|
|
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.function.Function;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
import org.jline.reader.ParsedLine;
|
|
|
|
|
import org.jline.reader.Parser;
|
|
|
|
|
import org.jline.reader.impl.DefaultParser;
|
|
|
|
|
|
|
|
|
|
import org.springframework.boot.ApplicationArguments;
|
|
|
|
|
import org.springframework.core.annotation.Order;
|
|
|
|
|
@@ -25,9 +30,9 @@ import org.springframework.shell.Input;
|
|
|
|
|
import org.springframework.shell.InputProvider;
|
|
|
|
|
import org.springframework.shell.Shell;
|
|
|
|
|
import org.springframework.shell.ShellRunner;
|
|
|
|
|
import org.springframework.shell.Utils;
|
|
|
|
|
import org.springframework.shell.context.InteractionMode;
|
|
|
|
|
import org.springframework.shell.context.ShellContext;
|
|
|
|
|
import org.springframework.util.StringUtils;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A {@link ShellRunner} that executes commands without entering interactive shell mode.
|
|
|
|
|
@@ -38,66 +43,106 @@ import org.springframework.util.StringUtils;
|
|
|
|
|
* @author Janne Valkealahti
|
|
|
|
|
* @author Chris Bono
|
|
|
|
|
*/
|
|
|
|
|
@Order(InteractiveShellRunner.PRECEDENCE - 50)
|
|
|
|
|
@Order(NonInteractiveShellRunner.PRECEDENCE)
|
|
|
|
|
public class NonInteractiveShellRunner implements ShellRunner {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The precedence at which this runner is ordered by the DefaultApplicationRunner - which also controls
|
|
|
|
|
* the order it is consulted on the ability to handle the current shell.
|
|
|
|
|
*/
|
|
|
|
|
public static final int PRECEDENCE = InteractiveShellRunner.PRECEDENCE - 50;
|
|
|
|
|
|
|
|
|
|
private final Shell shell;
|
|
|
|
|
|
|
|
|
|
private final ShellContext shellContext;
|
|
|
|
|
|
|
|
|
|
private Function<ApplicationArguments, List<String>> argsToShellCommand = (args) -> Arrays.asList(args.getSourceArgs());
|
|
|
|
|
private Parser lineParser;
|
|
|
|
|
|
|
|
|
|
private Function<ApplicationArguments, List<String>> commandsFromInputArgs;
|
|
|
|
|
|
|
|
|
|
public NonInteractiveShellRunner(Shell shell, ShellContext shellContext) {
|
|
|
|
|
this.shell = shell;
|
|
|
|
|
this.shellContext = shellContext;
|
|
|
|
|
this.lineParser = new DefaultParser();
|
|
|
|
|
this.commandsFromInputArgs = (args) ->
|
|
|
|
|
Collections.singletonList(String.join(" ", args.getSourceArgs()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setArgsToShellCommand(Function<ApplicationArguments, List<String>> argsToShellCommand) {
|
|
|
|
|
this.argsToShellCommand = argsToShellCommand;
|
|
|
|
|
/**
|
|
|
|
|
* Sets the function that creates the command() to run from the input application arguments.
|
|
|
|
|
*
|
|
|
|
|
* @param commandsFromInputArgs function that takes input application arguments and creates zero or more commands
|
|
|
|
|
* where each command is a string that specifies the command and options
|
|
|
|
|
* (eg. 'history --file myHistory.txt')
|
|
|
|
|
*/
|
|
|
|
|
public void setCommandsFromInputArgs(Function<ApplicationArguments, List<String>> commandsFromInputArgs) {
|
|
|
|
|
this.commandsFromInputArgs = commandsFromInputArgs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets the line parser used to parse commands.
|
|
|
|
|
*
|
|
|
|
|
* @param lineParser the line parser used to parse commands
|
|
|
|
|
*/
|
|
|
|
|
public void setLineParser(Parser lineParser) {
|
|
|
|
|
this.lineParser = lineParser;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean canRun(ApplicationArguments args) {
|
|
|
|
|
return !argsToShellCommand.apply(args).isEmpty();
|
|
|
|
|
return !commandsFromInputArgs.apply(args).isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void run(ApplicationArguments args) throws Exception {
|
|
|
|
|
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
|
|
|
|
|
List<String> commands = this.argsToShellCommand.apply(args);
|
|
|
|
|
InputProvider inputProvider = new StringInputProvider(commands);
|
|
|
|
|
List<String> commands = this.commandsFromInputArgs.apply(args);
|
|
|
|
|
List<ParsedLine> parsedLines = commands.stream()
|
|
|
|
|
.map(rawCommandLine -> lineParser.parse(rawCommandLine, rawCommandLine.length() + 1))
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
MultiParsedLineInputProvider inputProvider = new MultiParsedLineInputProvider(parsedLines);
|
|
|
|
|
shell.run(inputProvider);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class StringInputProvider implements InputProvider {
|
|
|
|
|
/**
|
|
|
|
|
* An {@link InputProvider} that returns an input for each entry in a list of {@link ParsedLine parsed lines}.
|
|
|
|
|
*/
|
|
|
|
|
static class MultiParsedLineInputProvider implements InputProvider {
|
|
|
|
|
|
|
|
|
|
private final List<String> commands;
|
|
|
|
|
private final List<ParsedLineInput> parsedLineInputs;
|
|
|
|
|
private int inputIdx;
|
|
|
|
|
|
|
|
|
|
private boolean done;
|
|
|
|
|
|
|
|
|
|
StringInputProvider(List<String> commands) {
|
|
|
|
|
this.commands = commands;
|
|
|
|
|
MultiParsedLineInputProvider(List<ParsedLine> parsedLines) {
|
|
|
|
|
this.parsedLineInputs = parsedLines.stream()
|
|
|
|
|
.map(ParsedLineInput::new)
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Input readInput() {
|
|
|
|
|
if (!done) {
|
|
|
|
|
done = true;
|
|
|
|
|
return new Input() {
|
|
|
|
|
@Override
|
|
|
|
|
public List<String> words() {
|
|
|
|
|
return commands;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String rawText() {
|
|
|
|
|
return StringUtils.collectionToDelimitedString(commands, " ");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (inputIdx == parsedLineInputs.size()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return parsedLineInputs.get(inputIdx++);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class ParsedLineInput implements Input {
|
|
|
|
|
|
|
|
|
|
private final ParsedLine parsedLine;
|
|
|
|
|
|
|
|
|
|
ParsedLineInput(ParsedLine parsedLine) {
|
|
|
|
|
this.parsedLine = parsedLine;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String rawText() {
|
|
|
|
|
return parsedLine.line();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<String> words() {
|
|
|
|
|
return Utils.sanitizeInput(parsedLine.words());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|