commit cc44493bbf6aaeadd5355510d6b4a543a07d22bb Author: DmitriyMX Date: Tue Feb 18 15:30:08 2014 +0400 Init project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..84203dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +## General ## +lib/ +target/ + +## IDEA ## +.idea/ +out/ +*.iml diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..870748c --- /dev/null +++ b/build.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ru/dmitriymx/shell/CommandCompleter.java b/src/ru/dmitriymx/shell/CommandCompleter.java new file mode 100644 index 0000000..81e4244 --- /dev/null +++ b/src/ru/dmitriymx/shell/CommandCompleter.java @@ -0,0 +1,41 @@ +package ru.dmitriymx.shell; + +import jline.console.completer.ArgumentCompleter; +import jline.console.completer.Completer; +import jline.console.completer.StringsCompleter; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CommandCompleter implements Completer { + private ArgumentCompleter.ArgumentDelimiter delimiter = new ArgumentCompleter.WhitespaceArgumentDelimiter(); + private Map commandMap = new HashMap<>(); + private Completer commandNamesCompleter; + + public CommandCompleter(List commandList) { + for (IShellCommand command : commandList) { + commandMap.put(command.getName(), command); + } + + String[] commandNames = new String[commandMap.size()]; + commandNames = commandMap.keySet().toArray(commandNames); + commandNamesCompleter = new StringsCompleter(commandNames); + } + + @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/ru/dmitriymx/shell/IShellCommand.java b/src/ru/dmitriymx/shell/IShellCommand.java new file mode 100644 index 0000000..0c102dc --- /dev/null +++ b/src/ru/dmitriymx/shell/IShellCommand.java @@ -0,0 +1,8 @@ +package ru.dmitriymx.shell; + +public interface IShellCommand { + + public String getName(); + + public void execute(final String[] args); +} diff --git a/src/ru/dmitriymx/shell/LogAgent.java b/src/ru/dmitriymx/shell/LogAgent.java new file mode 100644 index 0000000..cb4f9f0 --- /dev/null +++ b/src/ru/dmitriymx/shell/LogAgent.java @@ -0,0 +1,79 @@ +package ru.dmitriymx.shell; + +import java.io.File; +import java.util.logging.*; + +public class LogAgent { + private Logger logger; + + public LogAgent() { + this(new LogFormatter(), new ConsoleHandler(), null); + } + + public LogAgent(Formatter formatter) { + this(formatter, new ConsoleHandler(), null); + } + + public LogAgent(ConsoleHandler consoleHandler) { + this(new LogFormatter(), consoleHandler, null); + } + + public LogAgent(File logFile) { + this(new LogFormatter(), new ConsoleHandler(), logFile); + } + + public LogAgent(Formatter formatter, ConsoleHandler consoleHandler, File logFile) { + logger = Logger.getLogger("Shell"); + logger.setUseParentHandlers(false); + + Handler[] handlers = logger.getHandlers(); + for (Handler handler : handlers) { + logger.removeHandler(handler); + } + + consoleHandler.setFormatter(formatter); + logger.addHandler(consoleHandler); + + if (logFile != null) { + try { + FileHandler fHandler = new FileHandler(logFile.getAbsolutePath(), true); + fHandler.setFormatter(formatter); + logger.addHandler(fHandler); + } catch (Exception e) { + logger.log(Level.WARNING, "Failed write log to " + logFile.getAbsolutePath(), e); + } + } + } + + public void info(String message) { + logger.log(Level.INFO, message); + } + + public void info(String message, Object... params) { + logger.log(Level.INFO, String.format(message, params)); + } + + public void warning(String message) { + logger.log(Level.WARNING, message); + } + + public void warning(String message, Object... params) { + logger.log(Level.WARNING, String.format(message, params)); + } + + public void warning(String message, Throwable trow) { + logger.log(Level.WARNING, message, trow); + } + + public void severe(String message) { + logger.log(Level.SEVERE, message); + } + + public void severe(String message, Object... params) { + logger.log(Level.SEVERE, String.format(message, params)); + } + + public void severe(String message, Throwable trow) { + logger.log(Level.SEVERE, message, trow); + } +} \ No newline at end of file diff --git a/src/ru/dmitriymx/shell/LogFormatter.java b/src/ru/dmitriymx/shell/LogFormatter.java new file mode 100644 index 0000000..282d23a --- /dev/null +++ b/src/ru/dmitriymx/shell/LogFormatter.java @@ -0,0 +1,28 @@ +package ru.dmitriymx.shell; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.SimpleDateFormat; +import java.util.logging.Formatter; +import java.util.logging.LogRecord; + +public class LogFormatter extends Formatter { + private final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss "); + + @Override + public String format(LogRecord record) { + StringBuilder sb = new StringBuilder(); + sb.append(SDF.format(record.getMillis())) + .append("[").append(record.getLevel().getName()).append("] ") + .append(record.getMessage()).append("\n"); + + Throwable thrown = record.getThrown(); + if (thrown != null) { + StringWriter sw = new StringWriter(); + thrown.printStackTrace(new PrintWriter(sw)); + sb.append(sw.toString()); + } + + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/ru/dmitriymx/shell/Shell.java b/src/ru/dmitriymx/shell/Shell.java new file mode 100644 index 0000000..7f07908 --- /dev/null +++ b/src/ru/dmitriymx/shell/Shell.java @@ -0,0 +1,172 @@ +package ru.dmitriymx.shell; + +import jline.console.ConsoleReader; + +import java.io.File; +import java.io.IOException; +import java.util.LinkedList; +import java.util.logging.Formatter; + +/** + * Командная оболочка + */ +public class Shell implements Runnable { + private LogAgent log; + private Thread shellThread; + private String prompt; + private LinkedList commandList = new LinkedList<>(); + private CommandCompleter commandCompleter; + protected ConsoleReader cReader; + protected boolean runned = false; + + /** + * Создание командной оболочки + * @throws IOException + */ + public Shell() throws IOException { + this(new LogFormatter(), null); + } + + /** + * Создание командной оболочки + * @param logFile файл журналирования + * @throws IOException + */ + public Shell(File logFile) throws IOException { + this(new LogFormatter(), logFile); + } + + /** + * Создание командной оболочки + * @param logFormatter свой вариант форматирования вывода + * @throws IOException + */ + public Shell(Formatter logFormatter) throws IOException { + this(logFormatter, null); + } + + /** + * Создание командной оболочки + * @param logFormatter свой вариант форматирования вывода + * @param logFile файл журналирования + * @throws IOException + */ + public Shell(Formatter logFormatter, File logFile) throws IOException { + cReader = new ConsoleReader(System.in, System.out); + cReader.setExpandEvents(false); + log = new LogAgent(logFormatter, new ShellConsoleHandler(this), logFile); + } + + /** + * Получить объект журналирования + * @return + */ + public LogAgent getLog() { + return log; + } + + /** + * Установить текст приглашения + * @param prompt + */ + public void setPrompt(String prompt) { + this.prompt = prompt; + } + + /** + * Добавить команду + * @param command команда + */ + public void addCommand(IShellCommand command) { + commandList.add(command); + } + + /** + * Удаление команды + * @param command удаляемая команда + */ + public void removeCommand(IShellCommand command) { + commandList.remove(command); + } + + /** + * Удаление команды по её имени + * @param commandName имя команды + */ + public void removeCommand(String commandName) { + IShellCommand foundCmd = null; + + for (IShellCommand command : commandList) { + if(command.getName().equalsIgnoreCase(commandName)) { + foundCmd = command; + break; + } + } + + if (foundCmd != null) { + commandList.remove(foundCmd); + } + } + + /** + * Запуск командной оболочки + */ + public void start() { + shellThread = new Thread(this, "Shell Thread"); + try { + runned = true; + shellThread.join(); + shellThread.start(); + } catch (InterruptedException e) { + log.severe("Shell thread exception: ", e); + } + } + + /** + * Остановка командной оболочки + */ + public void stop() { + shellThread.interrupt(); + } + + /** + * Обработчик входящих комманд + */ + @Override + public void run() { + cReader.setPrompt(prompt); + commandCompleter = new CommandCompleter(commandList); + cReader.addCompleter(commandCompleter); + Thread currentThread = Thread.currentThread(); + + String line; + try { + readerLoop: + 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]; + for (IShellCommand command : commandList) { + if (command.getName().equalsIgnoreCase(commandName)) { + if (parseLine.length == 1) { + command.execute(null); + } else { + String[] args = new String[parseLine.length - 1]; + System.arraycopy(parseLine, 1, args, 0, args.length); + command.execute(args); + } + continue readerLoop; + } + } + log.warning("Unknow command \"%s\"", commandName); + } + } catch (IOException e) { + log.severe("Shell exception:", e); + } + + cReader.removeCompleter(commandCompleter); + } +} diff --git a/src/ru/dmitriymx/shell/ShellConsoleHandler.java b/src/ru/dmitriymx/shell/ShellConsoleHandler.java new file mode 100644 index 0000000..29ff7e9 --- /dev/null +++ b/src/ru/dmitriymx/shell/ShellConsoleHandler.java @@ -0,0 +1,27 @@ +package ru.dmitriymx.shell; + +import jline.console.ConsoleReader; + +import java.io.IOException; +import java.util.logging.ConsoleHandler; + +public class ShellConsoleHandler extends ConsoleHandler { + private Shell shell; + + public ShellConsoleHandler(Shell shell) { + super(); + this.shell = shell; + } + + @Override + public synchronized void flush() { + try { + shell.cReader.print(ConsoleReader.RESET_LINE + ""); + shell.cReader.flush(); + super.flush(); + } catch (IOException e) { + e.printStackTrace(); + super.flush(); + } + } +} \ No newline at end of file