Archived
0
This commit is contained in:
2015-12-12 03:05:53 +03:00
parent b790462aba
commit f408f0febe
7 changed files with 191 additions and 136 deletions

View File

@@ -1,5 +1,5 @@
group = 'ru.dmitriymx' group = 'ru.dmitriymx'
version = '1.0' version = '2.0-SNAPSHOT'
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'idea' apply plugin: 'idea'

View File

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

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,164 @@
package ru.dmitriymx.shell; package ru.dmitriymx.shell;
import jline.console.ConsoleReader; 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.IOException;
import java.io.PrintWriter;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Командная оболочка * @author DmitriyMX <mail@dmitriymx.ru>
* 2015
*/ */
public class Shell implements Runnable { public class Shell extends Thread implements Completer {
private Thread shellThread; private final ArgumentCompleter.ArgumentDelimiter DELIMITER = new ArgumentCompleter.WhitespaceArgumentDelimiter();
private String prompt; private final String[] EMPTY_ARGS = new String[0];
private Map<String, IShellCommand> commandList = new HashMap<>(); private final String RED = Ansi.ansi().fgBright(Ansi.Color.RED).toString();
private CommandCompleter commandCompleter;
private final String[] emptyArray = new String[0]; private ConsoleReader console;
protected ConsoleReader cReader; private PrintWriter out;
protected boolean runned = false; 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 { public Shell() throws IOException {
cReader = new ConsoleReader(System.in, System.out); console = new ConsoleReader(System.in, System.err);
cReader.setExpandEvents(false); console.addCompleter(this);
out = new PrintWriter(console.getOutput());
} }
/** @Override
* Установить текст приглашения public synchronized void start() {
* @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");
try { try {
runned = true; this.join();
shellThread.join();
shellThread.start();
} catch (InterruptedException e) { } 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 PrintWriter getOut() {
* Остановка командной оболочки return out;
*/
public void stop() {
shellThread.interrupt();
} }
/**
* Обработчик входящих комманд
*/
@Override @Override
public void run() { public void run() {
cReader.setPrompt(prompt); loopThread = Thread.currentThread();
commandCompleter = new CommandCompleter(commandList.keySet());
cReader.addCompleter(commandCompleter);
Thread currentThread = Thread.currentThread();
String line; console.setPrompt(ConsoleReader.RESET_LINE + "> ");
try {
while (!currentThread.isInterrupted() && (line = cReader.readLine()) != null) { while (run && !loopThread.isInterrupted()) {
String[] parseLine = commandCompleter.parseLine(line, cReader.getCursorBuffer().cursor).getArguments(); try {
if (parseLine.length == 0) { String line;
continue; 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());
}
} }
} catch (IOException e) {
String commandName = parseLine[0].toLowerCase(); e.printStackTrace(out);
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) {
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<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
}
} }
} }

View File

@@ -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;
}
}

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,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();
}
}