Archived
0

Полностью переписан алгоритм

This commit is contained in:
2015-12-14 11:24:10 +03:00
parent b790462aba
commit 23a4660730
8 changed files with 266 additions and 125 deletions

View File

@@ -5,29 +5,23 @@ import jline.console.completer.Completer;
import jline.console.completer.StringsCompleter;
import java.util.List;
import java.util.Set;
/**
* @author DmitriyMX <mail@dmitriymx.ru>
* 2015
*/
public class CommandCompleter implements Completer {
private ArgumentCompleter.ArgumentDelimiter delimiter = new ArgumentCompleter.WhitespaceArgumentDelimiter();
private Completer commandNamesCompleter;
public CommandCompleter(Set<String> commandList) {
commandNamesCompleter = new StringsCompleter(commandList);
}
protected StringsCompleter stringsCompleter = new StringsCompleter();
@Override
public int complete(String buffer, int cursor, List<CharSequence> candidates) {
ArgumentCompleter.ArgumentList parseCommandLine = parseLine(buffer, cursor);
int cursorArgument = parseCommandLine.getCursorArgumentIndex();
ArgumentCompleter.ArgumentList parseLine = Shell.DELIMITER.delimit(buffer, cursor);
int cursorArgumentIndex = parseLine.getCursorArgumentIndex();
if (cursorArgument < 0) {
if (cursorArgumentIndex < 0) {
return -1;
} else {
return commandNamesCompleter.complete(buffer, cursor, candidates);
return stringsCompleter.complete(buffer, cursor, candidates);
}
}
public ArgumentCompleter.ArgumentList parseLine(String buffer, int cursor) {
return delimiter.delimit(buffer, cursor);
}
}

View File

@@ -0,0 +1,75 @@
package ru.dmitriymx.shell;
import jline.console.ConsoleReader;
import org.fusesource.jansi.Ansi;
import ru.dmitriymx.shell.commands.Command;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author DmitriyMX <mail@dmitriymx.ru>
* 2015
*/
public class CommandLoop implements Runnable {
private static final String[] EMPTY_ARGS = new String[0];
private static final String RED = Ansi.ansi().fgBright(Ansi.Color.RED).toString();
private ConsoleReader console;
private boolean run;
protected Map<String, Command> commandMap = new HashMap<>();
public CommandLoop(ConsoleReader consoleReader) {
console = consoleReader;
}
@Override
public void run() {
Thread loop = Thread.currentThread();
run = true;
while (run && !loop.isInterrupted()) {
try {
String line;
if ((line = console.readLine()) != null) {
String[] parseLine = Shell.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 {
System.err.println(RED + "Unknown command \"" + comandName + "\"" + Ansi.ansi().reset());
}
}
} catch (IOException e) {
e.printStackTrace();
}
// чтобы не нагружать процессор
safeSleep(1);
}
}
public void shutdown() {
run = false;
}
private void safeSleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
//ignore
}
}
}

View File

@@ -1,8 +0,0 @@
package ru.dmitriymx.shell;
public interface IShellCommand {
public String getName();
public void execute(final String[] args);
}

View File

@@ -1,124 +1,78 @@
package ru.dmitriymx.shell;
import jline.console.ConsoleReader;
import jline.console.completer.ArgumentCompleter;
import ru.dmitriymx.shell.commands.Command;
import ru.dmitriymx.shell.commands.ExitCommand;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.io.PrintStream;
/**
* Командная оболочка
* @author DmitriyMX <mail@dmitriymx.ru>
* 2015
*/
public class Shell implements Runnable {
private Thread shellThread;
private String prompt;
private Map<String, IShellCommand> commandList = new HashMap<>();
public class Shell {
public static final ArgumentCompleter.ArgumentDelimiter DELIMITER = new ArgumentCompleter.WhitespaceArgumentDelimiter();
private PrintStream sysOut, sysErr;
private ShellPrintStream newErr;
private String promt;
private ConsoleReader console;
private CommandLoop commandLoop;
private CommandCompleter commandCompleter;
private final String[] emptyArray = new String[0];
protected ConsoleReader cReader;
protected boolean runned = false;
/**
* Создание командной оболочки
* @throws IOException
*/
public Shell() throws IOException {
cReader = new ConsoleReader(System.in, System.out);
cReader.setExpandEvents(false);
public void start() throws IOException, InterruptedException {
overrideSysErr();
console = new ConsoleReader(System.in, sysErr);
if (promt == null) promt = ":";
console.setPrompt(ConsoleReader.RESET_LINE + promt);
console.addCompleter((commandCompleter = new CommandCompleter()));
newErr.setConsoleReader(console);
commandLoop = new CommandLoop(console);
if (!commandLoop.commandMap.containsKey("exit")) {
addCommand(new ExitCommand());
}
/**
* Установить текст приглашения
* @param prompt
*/
public void setPrompt(String prompt) {
this.prompt = prompt;
Thread loopCommandReader = new Thread(commandLoop, "Command reader loop");
loopCommandReader.join();
loopCommandReader.start();
}
/**
* Добавить команду
* @param commandName имя команды
* @param command команда
*/
public void addCommand(String commandName, IShellCommand command) {
commandList.put(commandName.toLowerCase(), command);
public void shutdown() {
commandLoop.shutdown();
newErr.setConsoleReader(null);
console.shutdown();
System.setOut(sysOut);
System.setErr(sysErr);
}
/**
* Удаление команды
* @param command удаляемая команда
*/
public void removeCommand(String commandName, IShellCommand command) {
commandList.remove(commandName);
}
/**
* Запуск командной оболочки
*/
public void start() {
shellThread = new Thread(this, "Shell Thread");
try {
runned = true;
shellThread.join();
shellThread.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* Остановка командной оболочки
*/
public void stop() {
shellThread.interrupt();
}
/**
* Обработчик входящих комманд
*/
@Override
public void run() {
cReader.setPrompt(prompt);
commandCompleter = new CommandCompleter(commandList.keySet());
cReader.addCompleter(commandCompleter);
Thread currentThread = 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;
}
String commandName = parseLine[0].toLowerCase();
if (commandList.containsKey(commandName)) {
IShellCommand command = commandList.get(commandName);
if (parseLine.length == 1) {
command.execute(emptyArray);
public void setPromt(String promt) { //FIXME коостыли!!
if (console == null) {
this.promt = promt;
} else {
String[] args = new String[parseLine.length - 1];
System.arraycopy(parseLine, 1, args, 0, args.length);
command.execute(args);
console.setPrompt(ConsoleReader.RESET_LINE + promt);
}
}
continue;
public void addCommand(Command command) {
command.setShell(this);
String name = command.getName().toLowerCase();
commandLoop.commandMap.put(name, command);
commandCompleter.stringsCompleter.getStrings().add(name);
}
System.err.println(String.format("Unknow command \"%s\"", commandName));
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
System.err.println("Shell exception");
e.printStackTrace();
}
cReader.removeCompleter(commandCompleter);
/**
* Подмена стандартных SysErr и SysOut
*/
private void overrideSysErr() {
sysOut = System.out;
sysErr = System.err;
newErr = new ShellPrintStream(sysErr);
System.setErr(newErr);
System.setOut(newErr);
}
}

View File

@@ -0,0 +1,67 @@
package ru.dmitriymx.shell;
import jline.console.ConsoleReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* Обертка над SysOut и SysErr
*
* @author DmitriyMX <mail@dmitriymx.ru>
* 2015
*/
public class ShellPrintStream extends PrintStream {
private ConsoleReader consoleReader;
private PrintWriter writer;
public ShellPrintStream(OutputStream outputStream) {
super(outputStream, true);
}
public void setConsoleReader(ConsoleReader consoleReader) {
this.consoleReader = consoleReader;
if (consoleReader != null) {
this.writer = new PrintWriter(consoleReader.getOutput());
}
}
@Override
public void print(String s) {
println(s);
}
@Override
public void println(String s) {
if (consoleReader != null) {
writer.print(ConsoleReader.RESET_LINE);
writer.print(s);
cleanTrashLine(s);
writer.println();
try {
consoleReader.drawLine();
} catch (IOException e) {
// ignore
}
writer.flush();
} else {
super.print(ConsoleReader.RESET_LINE);
super.print(s);
}
}
/**
* Очистка печатной строки от мусора
*/
private void cleanTrashLine(String string) {
// очищает полностью строку
if (consoleReader.getCursorBuffer().buffer.length() + consoleReader.getPrompt().length() > string.length()) {
for (int i = string.length(); i <= consoleReader.getCursorBuffer().buffer.length() + 2; i++) {
writer.print(' ');
}
}
}
}

View File

@@ -0,0 +1,23 @@
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;
}
}

View 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);
}

View File

@@ -0,0 +1,19 @@
package ru.dmitriymx.shell.commands;
/**
* Выход из Shell
*
* @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();
}
}