• Главная
  • Контакты
Подписаться:
  • Twitter
  • Facebook
  • RSS
  • VK
  • PushAll
logo

logo

  • Все
    • Положительные
    • Отрицательные
  • За сегодня
    • Положительные
    • Отрицательные
  • За вчера
    • Положительные
    • Отрицательные
  • За 3 дня
    • Положительные
    • Отрицательные
  • За неделю
    • Положительные
    • Отрицательные
  • За месяц
    • Положительные
    • Отрицательные
  • За год
    • Положительные
    • Отрицательные
  • Сортировка
    • По дате (возр)
    • По дате (убыв)
    • По рейтингу (возр)
    • По рейтингу (убыв)
    • По комментам (возр)
    • По комментам (убыв)
    • По просмотрам (возр)
    • По просмотрам (убыв)
Главная
  • Все
    • Положительные
    • Отрицательные
  • За сегодня
    • Положительные
    • Отрицательные
  • За вчера
    • Положительные
    • Отрицательные
  • За 3 дня
    • Положительные
    • Отрицательные
  • За неделю
    • Положительные
    • Отрицательные
  • За месяц
    • Положительные
    • Отрицательные
  • Главная
  • Jerminal — эмулятор терминала для Java-программ

Jerminal — эмулятор терминала для Java-программ +9

24.04.2017 10:01
KarlKremen 12 3400 Источник
Java*

Вступление


Привет, хабраюзеры! Решил поведать вам о мини-библиотеке Jerminal. Я сейчас работаю над большим коммерческим проектом на Groovy/Java. Ну и мне пришло задание — написать консольку для приложения. К сожалению, было поставлено условие: никаких сторонних решений, все только свое. Недолго думая, я сел и написал ее. Подробнее — под катом.


Цели


В принципе, чего-то очень серьезного от меня не требовали. Вот, собственно, параметры:


  • Должна быть консолька, принимающая команды с текстовыми аргументами (без заморочек — просто строковый массив).
  • Должно быть приглашение которое вводу, которое можно будет менять в коде.
  • Можно привязывать методы к командам с помощью Reflection и лямбда-выражений.

В целом довольно простой список. Тем, кому не терпится посмотреть на результат, ссылку на репозиторий даю: kkremen/jerminal.


Обзор кода


Ну а теперь к делу. Немного почитав про Reflection и лямбды, я решил сделать "ход конем".
В первую очередь я создал интерфейс Executable:


Executable.java
package org.meinkopf.console;
import java.lang.reflect.InvocationTargetException;

public interface Executable {
    Object invoke(Object[] args) throws InvocationTargetException, IllegalAccessException;
}

Если кто не знает, то можно сделать так:


Executable ex = (args) -> {
    return null;
}
ex.invoke(someArgs);

Теперь при вызове ex.invoke(...) выполнится лямбда-выражение. Ну а для поддержки Reflection я создал класс BasicExecutable, наследующий Executable:


BasicExecutable.java
package org.meinkopf.console;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

@SuppressWarnings({ "WeakerAccess", "unused" })
public class BasicExecutable implements Executable {
    protected Method method;
    protected Object target;

    public BasicExecutable(Method method, Object target) {
        this.method = method;
        this.target = target;
    }

    /* Getters and Setters */

    public Object invoke(Object[] args) throws InvocationTargetException, IllegalAccessException {

        if (args.length < method.getParameterCount()) {
            return "Too few arguments!\n"; // если аргументов слишком мало, выводим на консоль предупреждение
        }

        return method.invoke(target, Arrays.copyOfRange(args, 0, method.getParameterCount())); // если аргументов слишком много, просто обрезаем ненужные
    }
}

Еще я написал пару интерфейс-класс для списка команд: CommandList и BasicCommandList соответственно. Базовый класс хранит команды в Map < String, Executable > и возвращает основному классу объекты типа Executable.


Скрытый текст
package org.meinkopf.console;

public interface CommandList {
    Executable get(String commandName);
}

Скрытый текст
package org.meinkopf.console;

import java.util.HashMap;
import java.util.Map;

@SuppressWarnings({ "unused", "WeakerAccess" })
public class BasicCommandList implements CommandList {

    protected Map < String, Executable > methodMap = new HashMap <>();

    @Override
    public Executable get(String command) {
        return methodMap.get(command);
    }

    public void register(@SuppressWarnings("SameParameterValue") String name, Executable command) {
        methodMap.put(name, command);
    }
}

Служебный класс Command хранит разобранную парсером команду в виде строки-имени и ArrayList аргументов. Аргументы, кстати, принимаются только строковые, то есть числа тоже передаются в виде строки.


Основной класс реализует Runnable, но пока я это никак не использовал, и вызываю метод run напрямую.


Скрытый текст
package org.meinkopf.console;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

@SuppressWarnings({ "WeakerAccess", "unused", "SpellCheckingInspection" })
public class Jerminal implements Runnable {

    public static String PROMPT = " ~ $ ";
    public static String HEADER = "JConsole v0.0.0 for Java Apps\nAuthor: Karl Meinkopf\nBuilt especially for Protium project\n";
    protected ArrayList < Command > commandHistory = new ArrayList <>();
    protected CommandList commandList = null;
    private Scanner scanner;

    public JConsole(CommandList list) {
        commandList = list;
        scanner = new Scanner(System.in);
    }

    protected Command parse(String rawCommand) {
        rawCommand = rawCommand.replaceAll("(\"[\\s\\S]*?\"|[\\S]+)\\s*", "$1\u0001");
        rawCommand = rawCommand.replaceAll("\"([\\s\\S]*?)\"", "$1");
        String[] rawList = rawCommand.split("\u0001");

        ArrayList < String > args = new ArrayList <>(Arrays.asList(rawList));

        String command = args.remove(0).trim();

        return new Command(command, rawCommand, args);
    }

    protected Object execute(Command command) throws InvocationTargetException, IllegalAccessException {
        Executable executable = commandList.get(command.getName());

        if (executable == null) {
            return "Can't find command: '" + command.getName() + "'";
        }

        return executable.invoke(command.getArgs().toArray());
    }

    protected void prompt( ) {
        System.err.println();
        System.err.print(PROMPT);

        Command command = parse(getInputLine());

        Object result;
        try {
            result = execute(command);
        } catch (InvocationTargetException | IllegalAccessException e) {
            System.err.println("Can not execute command '" + command.getName() + "'!\n\tReason: " + e.getMessage());
            return;
        }

        if (result != null)
            System.err.println(result.toString());
    }

    protected String getInputLine( ) {
        return scanner.nextLine();
    }

    public void run( ) {
        System.err.println(HEADER);

        //noinspection InfiniteLoopStatement
        while (true) {
            prompt();
        }
    }
}

Заключение


Ну вот и вся библиотека. Пока маленькая, но я планирую ее развивать и улучшать. Если есть замечания и советы — буду рад услышать.

Поделиться с друзьями
-->

Комментарии (12)


  1. interprise
    24.04.2017 14:01
    #10188532

    для начала не плохо бы на гитхаб залить


    1. KarlKremen
      24.04.2017 18:55
      #10189054

      Репозиторий проекта есть.


  1. apangin
    24.04.2017 15:16
    #10188702
    +5

    Название не самое удачное. JConsole — это известный инструмент, входящий в стандартную поставку JDK.


    1. KarlKremen
      24.04.2017 18:54
      #10189050

      Спасибо за информацию, исправлю!


  1. AndreyRubankov
    24.04.2017 15:17
    #10188704
    +3

    А вас не смущает, что в пакете поставки JDK уже есть утилита JConsole, которая выполняет совершенно другую роль? Перед прочтением статьи, было мнение, что статья будет именно про эту утилиту.


    1. KarlKremen
      24.04.2017 18:54
      #10189052

      Спасибо за информацию, исправлю!


      1. AndreyRubankov
        25.04.2017 08:14
        #10189606

        Посмотрел код, думаю, Вам будет полезно почитать вот такой туториал по SPI:
        https://docs.oracle.com/javase/tutorial/ext/basics/spi.html «Система плагинов» из коробки =)

        Сама консоль — это будет ядро с какой-то функцией-заглушкой, которая будет вызываться, если команда не была найдена. А все остальные команды будут подкладываться к приложению отдельным jar, в котором будет реализация SPI команд-хендлера.

        Кода будет меньше, код будет чище, плагины и модульность проекта из коробки.


  1. sshikov
    24.04.2017 19:45
    #10189098

    К сожалению, было поставлено условие: никаких сторонних решений, все только свое.

    Никогда не понимал подобных условий.


    1. KarlKremen
      24.04.2017 19:56
      #10189110
      +1

      По-моему, их не поймет никто кроме тех, кто такие условия ставит.


  1. lxsmkv
    24.04.2017 19:47
    #10189106

    А контекстный диалог она поддерживает? Например: «создай», «что создать?» «игрок» «имя?» «Иван» «игрок Иван создан». Как я из кода понял что нет. Т.е параметры команды надо знать заранее. А вообще насколько сложнее сделать поддержку контекстного диалога? Может кто-нибудь может привести пример удачной реализации?


    1. KarlKremen
      24.04.2017 19:54
      #10189108

      В данном случае это лишь вопрос реализации Executable. То есть, теоретически это сделать можно как-нибудь вот так:


      Executable exec =  (args) -> {
          System.out.println("Prompt: ");
          String command = readLine(); // будем предполагать, что такая функция реализована.
          ...
      }

      Я подумаю, как добавить поддержку таких комманд "из коробки".


      1. lxsmkv
        24.04.2017 21:54
        #10189292

        мне представлялось это как-то так: каждая команда имеет диалоговые варианты, если пользователь дает один из приемлимых вариантов, обработка передается следующей команде, и так пока цепочка команд не будет выполнена. Если мы не даем команде подходящего уточнения, ввод зацикливается на команду, пока мы не дадим приемлимый параметр, либо не дадим отбой. Но все это в какие то синтаксические деревья уходит если вовремя не остановится. Так что я вообще не уверен в целесообразности такого подхода.

МЕТКИ

  • Хабы
  • Теги

JAVA

java

java 8

console

terminal

cli

library

консоль

консольная утилита

библиотека

СЕРВИСЫ
  • logo

    CloudLogs.ru - Облачное логирование

    • Храните логи вашего сервиса или приложения в облаке. Удобно просматривайте и анализируйте их.
Все публикации автора
  • Jerminal — эмулятор терминала для Java-программ +9

    • 24.04.2017 10:01

Подписка


ЛУЧШЕЕ

  • Сегодня
  • Вчера
  • Позавчера
05:00

Менталитет старой школы: инженерные привычки программиста 70–90-х и как их применять сегодня +31

07:09

Как мы запустили свой спутник. Разбираю процесс по шагам +24

05:56

Адский эксперимент: личный сайт на нищих микросервисах +22

09:01

Google Cloud уже в третий раз блокирует аккаунт моей компании +19

13:47

Почему природа до сих пор не породила колесо +16

05:16

Кино, которого нет: опыт работы над ИИ-клипом +15

12:10

Планковский масштаб: от математического курьёза к горизонту познания +14

08:01

UMPC возвращаются? Три прототипа, которые пытаются оживить забытый класс мини-компьютеров +14

13:01

Рейтинг контента и пользователей на основе офелократии. Часть 2. Реализация на SQL +11

07:38

Работа с JDK Flight Recorder (JFR) из командной строки: инструмент для профилирования без графического интерфейса +11

10:05

Как превратить телефон в портативную консоль +10

09:15

Парсим XML и JSON на ассемблере +10

12:38

Пузырь доткомов (1995-2000) очень похож на пузырь криптовалюты и Искусственного интеллекта +9

09:09

«Отучаем» WinFXNet от жадности (часть 2 и заключительная) +9

11:20

Верите ли вы в случайность? +6

07:32

Как with делает ваш Python-код безопаснее и читабельнее +6

15:16

Хайп vs реальность: что tech-медиа пишут об ИИ и кто реально лучший в 2025? +5

13:30

Как я начал создавать медицинские изделия, ч.3 +5

13:20

На сопках Манчжурии: авария в бухте Чажма +5

13:08

AI-драгдизайн: первая молекула прошла Фазу II +5

08:00

/e/OS 3.2: приватный Android без слежки, который только что стал еще лучше +40

11:15

Как я заменил саппорт-команду ChatGPT и потом неделю разбирался с жалобами +39

04:18

Как я получаю зарплату от зарубежных IT-компаний в 2025 году, живя в России +36

09:01

Люди Х против Железной Няни в космической Матрице: загадочная (анти)утопия 1949 года +32

12:17

В процессе обучения нейронных сетей получаются красивые фракталы +27

06:18

«План любой ценой»: Почему российский менеджмент превратил работу в выживание и можно ли с этим бороться +26

13:19

Климат в эпоху динозавров +22

14:24

Подключение PlayStation2 Джойстика к Микроконтроллеру (или Переходник между человеком и компьютером) +21

13:02

Ультрадешёвая гитарная квакушка ZORY DF2210 +21

18:00

Статистика под капотом LinearRegression: почему мы минимизируем именно квадрат ошибки? +19

15:15

Что не так с ИИ-«искусством» +19

10:05

Кэширование и всё, что с ним связано +16

15:44

Карьерный фест: идея, которая пролежала год и все-таки стала большим проектом +11

15:44

Карьерный фест: идея, которая пролежала год и все-таки стала большим проектом +11

14:54

Гений маркетинга А.Белла и Г.Хаббарда или как продать то, что никто не понимает +11

07:00

Киберспорт: что нужно, чтобы стать профессионалом +11

07:22

VL53L0X что это такое и с чем это едят +9

22:36

Как я потратил почти месяц, чтобы НЕ запустить AI-стартап. История одной-двух-трёх гипотез +7

18:20

Разведочный анализ текстовых данных (EDA for text data) +7

21:03

Развитие Telegram-бота для VPS: Docker, i18n и планы на будущее +5

18:15

Путеводитель по матанализу, который скрывали от вас в вузе +121

09:01

Тетрод, пентод — зачем триоду дополнительные сетки? +75

14:52

Как решать LeetCode? Легко! Нужно просто… +47

08:06

Оживляем самый массовый карточный таксофон двухтысячных +47

13:01

Как менялись фотокамеры в телефонах и почему мегапиксели — не главное +45

07:05

Что известно о межзвёздной комете 3I/ATLAS, которую можно увидеть сейчас? +45

10:24

Войти в айти, выйти из айти: если защемило карьерным турникетом +41

19:06

От слов к делу: как Postgres Pro строит будущее в Академгородке +31

16:05

Судно на воздушной подушке — насколько оно реально для самостоятельной постройки (мини модель)? +29

06:24

Настраиваем nano под себя +27

16:05

История игровой журналистики в России. Часть 10. Другие журналы о компьютерных играх +26

10:02

Почему я выбрал Warp, а не Cursor или Claude Code: мои инструменты, MCP, подход и конкретные приёмы разработки с LLM +26

13:05

На заводе проекты идут по два года, а команда выгорает через полтора. Вот как я с этим справляюсь +25

12:00

PHP-веб-панель для Amnezia VPN: ускоряем корпоративную автоматизацию +24

08:34

Security by design на практике: проектирование безопасной инфраструктуры +24

13:57

Книга «Современный C#. Разработка настольных, облачных, мобильных и веб-приложений». Работа со встроенными массивами +21

13:59

Rise of RAG: от плоских векторов к темпоральным графам в юридическом домене +18

07:05

В защиту «обычных» разработчиков +18

07:23

Наследие кода: разбор С и С++ модулей Erlang, которые работают десятилетиями +17

08:59

Разработка с AI в 2025: от идеи до продакшена с Claude Code +16

ОБСУЖДАЕМОЕ

  • Путеводитель по матанализу, который скрывали от вас в вузе +121

    • 138   18000

    «План любой ценой»: Почему российский менеджмент превратил работу в выживание и можно ли с этим бороться +26

    • 120   33000

    Как я получаю зарплату от зарубежных IT-компаний в 2025 году, живя в России +36

    • 112   38000

    Что не так с ИИ-«искусством» +19

    • 89   5800

    Как менялись фотокамеры в телефонах и почему мегапиксели — не главное +45

    • 80   9500

    Как я заменил саппорт-команду ChatGPT и потом неделю разбирался с жалобами +39

    • 59   7400

    Что известно о межзвёздной комете 3I/ATLAS, которую можно увидеть сейчас? +45

    • 53   46000

    Тетрод, пентод — зачем триоду дополнительные сетки? +75

    • 49   3700

    По следам AerynOS: как выглядит современный дистрибутив +5

    • 46   3400

    Почему я выбрал Warp, а не Cursor или Claude Code: мои инструменты, MCP, подход и конкретные приёмы разработки с LLM +26

    • 42   18000

    Климат в эпоху динозавров +22

    • 41   3100

    VL53L0X что это такое и с чем это едят +9

    • 39   4300

    Оживляем самый массовый карточный таксофон двухтысячных +47

    • 36   7900

    PHP-веб-панель для Amnezia VPN: ускоряем корпоративную автоматизацию +24

    • 35   8700

    Менталитет старой школы: инженерные привычки программиста 70–90-х и как их применять сегодня +31

    • 34   7400
  • Главная
  • Контакты
© 2025. Все публикации принадлежат авторам.