From f408f0febe113a91cec5341099d0f6c49ad5494d Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sat, 12 Dec 2015 03:05:53 +0300 Subject: [PATCH] Recode --- build.gradle | 2 +- .../ru/dmitriymx/shell/CommandCompleter.java | 33 --- .../ru/dmitriymx/shell/IShellCommand.java | 8 - src/main/java/ru/dmitriymx/shell/Shell.java | 228 ++++++++++-------- .../shell/commands/AbstractCommand.java | 21 ++ .../ru/dmitriymx/shell/commands/Command.java | 17 ++ .../dmitriymx/shell/commands/ExitCommand.java | 18 ++ 7 files changed, 191 insertions(+), 136 deletions(-) delete mode 100644 src/main/java/ru/dmitriymx/shell/CommandCompleter.java delete mode 100644 src/main/java/ru/dmitriymx/shell/IShellCommand.java create mode 100644 src/main/java/ru/dmitriymx/shell/commands/AbstractCommand.java create mode 100644 src/main/java/ru/dmitriymx/shell/commands/Command.java create mode 100644 src/main/java/ru/dmitriymx/shell/commands/ExitCommand.java diff --git a/build.gradle b/build.gradle index ecd8f8e..f6581d9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ group = 'ru.dmitriymx' -version = '1.0' +version = '2.0-SNAPSHOT' apply plugin: 'java' apply plugin: 'idea' diff --git a/src/main/java/ru/dmitriymx/shell/CommandCompleter.java b/src/main/java/ru/dmitriymx/shell/CommandCompleter.java deleted file mode 100644 index 7943a58..0000000 --- a/src/main/java/ru/dmitriymx/shell/CommandCompleter.java +++ /dev/null @@ -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 commandList) { - commandNamesCompleter = new StringsCompleter(commandList); - } - - @Override - public int complete(String buffer, int cursor, List 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); - } -} \ No newline at end of file diff --git a/src/main/java/ru/dmitriymx/shell/IShellCommand.java b/src/main/java/ru/dmitriymx/shell/IShellCommand.java deleted file mode 100644 index 7187ffa..0000000 --- a/src/main/java/ru/dmitriymx/shell/IShellCommand.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.dmitriymx.shell; - -public interface IShellCommand { - - public String getName(); - - public void execute(final String[] args); -} diff --git a/src/main/java/ru/dmitriymx/shell/Shell.java b/src/main/java/ru/dmitriymx/shell/Shell.java index 9be3f84..c764d9c 100644 --- a/src/main/java/ru/dmitriymx/shell/Shell.java +++ b/src/main/java/ru/dmitriymx/shell/Shell.java @@ -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 + * 2015 */ -public class Shell implements Runnable { - private Thread shellThread; - private String prompt; - private Map 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 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; - try { - while (!currentThread.isInterrupted() && (line = cReader.readLine()) != null) { - String[] parseLine = commandCompleter.parseLine(line, cReader.getCursorBuffer().cursor).getArguments(); - if (parseLine.length == 0) { - continue; + console.setPrompt(ConsoleReader.RESET_LINE + "> "); + + while (run && !loopThread.isInterrupted()) { + try { + String line; + if ((line = console.readLine()) != null) { + String[] parseLine = DELIMITER.delimit(line.trim(), console.getCursorBuffer().cursor).getArguments(); + if (parseLine.length == 0) { + continue; + } + + String comandName = parseLine[0].toLowerCase(); + if (commandMap.containsKey(comandName)) { + Command command = commandMap.get(comandName); + + if (parseLine.length == 1) { + command.execute(EMPTY_ARGS); + } else { + String[] args = new String[parseLine.length - 1]; + System.arraycopy(parseLine, 1, args, 0, args.length); + command.execute(args); + } + } else { + println(RED + "Unknown command \"" + comandName + "\"" + Ansi.ansi().reset()); + } } - - String commandName = parseLine[0].toLowerCase(); - if (commandList.containsKey(commandName)) { - IShellCommand command = commandList.get(commandName); - - if (parseLine.length == 1) { - command.execute(emptyArray); - } 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(); - } + } catch (IOException e) { + e.printStackTrace(out); } - } catch (IOException e) { - System.err.println("Shell exception"); - e.printStackTrace(); + + // чтобы не нагружать процессор + safeSleep(1); } - cReader.removeCompleter(commandCompleter); + 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 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 + } } } diff --git a/src/main/java/ru/dmitriymx/shell/commands/AbstractCommand.java b/src/main/java/ru/dmitriymx/shell/commands/AbstractCommand.java new file mode 100644 index 0000000..45c50f1 --- /dev/null +++ b/src/main/java/ru/dmitriymx/shell/commands/AbstractCommand.java @@ -0,0 +1,21 @@ +package ru.dmitriymx.shell.commands; + +import ru.dmitriymx.shell.Shell; + +/** + * @author DmitriyMX + * 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; + } +} diff --git a/src/main/java/ru/dmitriymx/shell/commands/Command.java b/src/main/java/ru/dmitriymx/shell/commands/Command.java new file mode 100644 index 0000000..8e53429 --- /dev/null +++ b/src/main/java/ru/dmitriymx/shell/commands/Command.java @@ -0,0 +1,17 @@ +package ru.dmitriymx.shell.commands; + +import ru.dmitriymx.shell.Shell; + +/** + * @author DmitriyMX + * 2015 + */ +public interface Command { + String getName(); + + Shell getShell(); + + void setShell(Shell shell); + + void execute(String[] args); +} diff --git a/src/main/java/ru/dmitriymx/shell/commands/ExitCommand.java b/src/main/java/ru/dmitriymx/shell/commands/ExitCommand.java new file mode 100644 index 0000000..73bb3ea --- /dev/null +++ b/src/main/java/ru/dmitriymx/shell/commands/ExitCommand.java @@ -0,0 +1,18 @@ +package ru.dmitriymx.shell.commands; + +/** + * @author DmitriyMX + * 2015 + */ +public class ExitCommand extends AbstractCommand { + + @Override + public String getName() { + return "exit"; + } + + @Override + public void execute(String[] args) { + getShell().shutdown(); + } +}