Recode
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
group = 'ru.dmitriymx'
|
||||
version = '1.0'
|
||||
version = '2.0-SNAPSHOT'
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'idea'
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package ru.dmitriymx.shell;
|
||||
|
||||
import jline.console.completer.ArgumentCompleter;
|
||||
import jline.console.completer.Completer;
|
||||
import jline.console.completer.StringsCompleter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class CommandCompleter implements Completer {
|
||||
private ArgumentCompleter.ArgumentDelimiter delimiter = new ArgumentCompleter.WhitespaceArgumentDelimiter();
|
||||
private Completer commandNamesCompleter;
|
||||
|
||||
public CommandCompleter(Set<String> commandList) {
|
||||
commandNamesCompleter = new StringsCompleter(commandList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int complete(String buffer, int cursor, List<CharSequence> candidates) {
|
||||
ArgumentCompleter.ArgumentList parseCommandLine = parseLine(buffer, cursor);
|
||||
int cursorArgument = parseCommandLine.getCursorArgumentIndex();
|
||||
|
||||
if (cursorArgument < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return commandNamesCompleter.complete(buffer, cursor, candidates);
|
||||
}
|
||||
}
|
||||
|
||||
public ArgumentCompleter.ArgumentList parseLine(String buffer, int cursor) {
|
||||
return delimiter.delimit(buffer, cursor);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package ru.dmitriymx.shell;
|
||||
|
||||
public interface IShellCommand {
|
||||
|
||||
public String getName();
|
||||
|
||||
public void execute(final String[] args);
|
||||
}
|
||||
@@ -1,124 +1,164 @@
|
||||
package ru.dmitriymx.shell;
|
||||
|
||||
import jline.console.ConsoleReader;
|
||||
import jline.console.completer.ArgumentCompleter;
|
||||
import jline.console.completer.Completer;
|
||||
import jline.console.completer.StringsCompleter;
|
||||
import org.fusesource.jansi.Ansi;
|
||||
import ru.dmitriymx.shell.commands.Command;
|
||||
import ru.dmitriymx.shell.commands.ExitCommand;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Командная оболочка
|
||||
* @author DmitriyMX <mail@dmitriymx.ru>
|
||||
* 2015
|
||||
*/
|
||||
public class Shell implements Runnable {
|
||||
private Thread shellThread;
|
||||
private String prompt;
|
||||
private Map<String, IShellCommand> commandList = new HashMap<>();
|
||||
private CommandCompleter commandCompleter;
|
||||
private final String[] emptyArray = new String[0];
|
||||
protected ConsoleReader cReader;
|
||||
protected boolean runned = false;
|
||||
public class Shell extends Thread implements Completer {
|
||||
private final ArgumentCompleter.ArgumentDelimiter DELIMITER = new ArgumentCompleter.WhitespaceArgumentDelimiter();
|
||||
private final String[] EMPTY_ARGS = new String[0];
|
||||
private final String RED = Ansi.ansi().fgBright(Ansi.Color.RED).toString();
|
||||
|
||||
private ConsoleReader console;
|
||||
private PrintWriter out;
|
||||
private Thread loopThread;
|
||||
private boolean run = false;
|
||||
private Map<String, Command> commandMap = new HashMap<>();
|
||||
private StringsCompleter stringsCompleter = new StringsCompleter();
|
||||
|
||||
/**
|
||||
* Создание командной оболочки
|
||||
* @throws IOException
|
||||
*/
|
||||
public Shell() throws IOException {
|
||||
cReader = new ConsoleReader(System.in, System.out);
|
||||
cReader.setExpandEvents(false);
|
||||
console = new ConsoleReader(System.in, System.err);
|
||||
console.addCompleter(this);
|
||||
out = new PrintWriter(console.getOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* Установить текст приглашения
|
||||
* @param prompt
|
||||
*/
|
||||
public void setPrompt(String prompt) {
|
||||
this.prompt = prompt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить команду
|
||||
* @param commandName имя команды
|
||||
* @param command команда
|
||||
*/
|
||||
public void addCommand(String commandName, IShellCommand command) {
|
||||
commandList.put(commandName.toLowerCase(), command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление команды
|
||||
* @param command удаляемая команда
|
||||
*/
|
||||
public void removeCommand(String commandName, IShellCommand command) {
|
||||
commandList.remove(commandName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Запуск командной оболочки
|
||||
*/
|
||||
public void start() {
|
||||
shellThread = new Thread(this, "Shell Thread");
|
||||
@Override
|
||||
public synchronized void start() {
|
||||
try {
|
||||
runned = true;
|
||||
shellThread.join();
|
||||
shellThread.start();
|
||||
this.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(out);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!commandMap.containsKey("exit")) {
|
||||
registerCommand(new ExitCommand());
|
||||
}
|
||||
|
||||
run = true;
|
||||
super.start();
|
||||
}
|
||||
|
||||
public synchronized void shutdown() {
|
||||
out.print(ConsoleReader.RESET_LINE);
|
||||
out.flush();
|
||||
|
||||
if (loopThread != null) {
|
||||
loopThread.interrupt();
|
||||
run = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Остановка командной оболочки
|
||||
*/
|
||||
public void stop() {
|
||||
shellThread.interrupt();
|
||||
public PrintWriter getOut() {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик входящих комманд
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
cReader.setPrompt(prompt);
|
||||
commandCompleter = new CommandCompleter(commandList.keySet());
|
||||
cReader.addCompleter(commandCompleter);
|
||||
Thread currentThread = Thread.currentThread();
|
||||
loopThread = Thread.currentThread();
|
||||
|
||||
String line;
|
||||
console.setPrompt(ConsoleReader.RESET_LINE + "> ");
|
||||
|
||||
while (run && !loopThread.isInterrupted()) {
|
||||
try {
|
||||
while (!currentThread.isInterrupted() && (line = cReader.readLine()) != null) {
|
||||
String[] parseLine = commandCompleter.parseLine(line, cReader.getCursorBuffer().cursor).getArguments();
|
||||
String line;
|
||||
if ((line = console.readLine()) != null) {
|
||||
String[] parseLine = DELIMITER.delimit(line.trim(), console.getCursorBuffer().cursor).getArguments();
|
||||
if (parseLine.length == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String commandName = parseLine[0].toLowerCase();
|
||||
if (commandList.containsKey(commandName)) {
|
||||
IShellCommand command = commandList.get(commandName);
|
||||
String comandName = parseLine[0].toLowerCase();
|
||||
if (commandMap.containsKey(comandName)) {
|
||||
Command command = commandMap.get(comandName);
|
||||
|
||||
if (parseLine.length == 1) {
|
||||
command.execute(emptyArray);
|
||||
command.execute(EMPTY_ARGS);
|
||||
} else {
|
||||
String[] args = new String[parseLine.length - 1];
|
||||
System.arraycopy(parseLine, 1, args, 0, args.length);
|
||||
command.execute(args);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
System.err.println(String.format("Unknow command \"%s\"", commandName));
|
||||
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} else {
|
||||
println(RED + "Unknown command \"" + comandName + "\"" + Ansi.ansi().reset());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("Shell exception");
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
|
||||
cReader.removeCompleter(commandCompleter);
|
||||
// чтобы не нагружать процессор
|
||||
safeSleep(1);
|
||||
}
|
||||
|
||||
console.setPrompt("");
|
||||
run = false;
|
||||
}
|
||||
|
||||
public void registerCommand(Command command) {
|
||||
if (command.getShell() == null) {
|
||||
command.setShell(this);
|
||||
}
|
||||
|
||||
String name = command.getName().toLowerCase();
|
||||
commandMap.put(name, command);
|
||||
stringsCompleter.getStrings().add(name);
|
||||
}
|
||||
|
||||
private synchronized void println(String string) {
|
||||
out.print(ConsoleReader.RESET_LINE);
|
||||
out.print(string);
|
||||
|
||||
// очищает полностью строку
|
||||
// 2 - это длинна promt'а
|
||||
if (console.getCursorBuffer().buffer.length() + 2 > string.length()) {
|
||||
for (int i = string.length(); i <= console.getCursorBuffer().buffer.length() + 2; i++) {
|
||||
out.print(' ');
|
||||
}
|
||||
}
|
||||
|
||||
out.println();
|
||||
out.flush();
|
||||
|
||||
try {
|
||||
console.drawLine();
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int complete(String buffer, int cursor, List<CharSequence> candidates) {
|
||||
ArgumentCompleter.ArgumentList parseLine = DELIMITER.delimit(buffer, cursor);
|
||||
int cursorArgumentIndex = parseLine.getCursorArgumentIndex();
|
||||
|
||||
if (cursorArgumentIndex < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return stringsCompleter.complete(buffer, cursor, candidates);
|
||||
}
|
||||
}
|
||||
|
||||
private void safeSleep(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.dmitriymx.shell.commands;
|
||||
|
||||
import ru.dmitriymx.shell.Shell;
|
||||
|
||||
/**
|
||||
* @author DmitriyMX <mail@dmitriymx.ru>
|
||||
* 2015
|
||||
*/
|
||||
public abstract class AbstractCommand implements Command {
|
||||
private Shell shell;
|
||||
|
||||
@Override
|
||||
public Shell getShell() {
|
||||
return shell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShell(Shell shell) {
|
||||
this.shell = shell;
|
||||
}
|
||||
}
|
||||
17
src/main/java/ru/dmitriymx/shell/commands/Command.java
Normal file
17
src/main/java/ru/dmitriymx/shell/commands/Command.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package ru.dmitriymx.shell.commands;
|
||||
|
||||
import ru.dmitriymx.shell.Shell;
|
||||
|
||||
/**
|
||||
* @author DmitriyMX <mail@dmitriymx.ru>
|
||||
* 2015
|
||||
*/
|
||||
public interface Command {
|
||||
String getName();
|
||||
|
||||
Shell getShell();
|
||||
|
||||
void setShell(Shell shell);
|
||||
|
||||
void execute(String[] args);
|
||||
}
|
||||
18
src/main/java/ru/dmitriymx/shell/commands/ExitCommand.java
Normal file
18
src/main/java/ru/dmitriymx/shell/commands/ExitCommand.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package ru.dmitriymx.shell.commands;
|
||||
|
||||
/**
|
||||
* @author DmitriyMX <mail@dmitriymx.ru>
|
||||
* 2015
|
||||
*/
|
||||
public class ExitCommand extends AbstractCommand {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "exit";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String[] args) {
|
||||
getShell().shutdown();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user