| Графический интерфейс не всегда бывает наиболее удобным способом взаимодействия с программой. Зачастую приложение, запускаемое в командной строке с некоторым набором команд, гораздо удобнее в эксплуатации. При разработке такой программы, встает задача разбора аргументов ее запуска, что само по себе не всегда бывает тривиальной задачей. К счастью, как и большинство других распространенных задач, задача разбора аргументов командной строки уже решена во множестве библиотек. В этой статье я кратко опишу две популярные библиотеки, в основе которых лежат два, несколько отличающихся, подхода к решению этой задачи. |
Небольшое введение
Прежде всего необходимо определиться с терминами. Для этого воспользуемся простым примером:
POSIX (tar -zxvf foo.tar.gz), GNU (du --human-readable --max-depth=1) или Java (java -Djava.awt.headless=true Foo). Каким пользоваться, выбор за Вами.
hg log -l 3
это команда для вывода последних трех сообщений из истории системы контроля версий Mercurial.- hg - имя приложения
- log - команда, в данном случае, на вывод сообщений из истории
- l - параметр (или опция), в данном примере, уточняющий количество выводимых сообщений
POSIX (tar -zxvf foo.tar.gz), GNU (du --human-readable --max-depth=1) или Java (java -Djava.awt.headless=true Foo). Каким пользоваться, выбор за Вами.
Commons CLI
Первая библиотека, Apache Commons CLI, предлагает следующий подход:
Каждый параметр описывается как объект Option:
Для параметра указывается краткое и полное имя, логический ключ и описание. Логический ключ говорит о том, имеет ли параметр аргументы или нет. Объекты, описывающие параметры, объединяются в набор параметров Options:
Сама строка запуска представляется классом CommandLine. У объекта этого класса можно узнать был ли указан параметр и если был, то с каким значением:
А вот процесс получения объекта CommandLine представляет отдельный интерес. Для получения CommandLine применяются парсеры - объекты, реализующие интерфейс CommandLineParser:
Реализаций данного интерфейса библиотека предлагает два: PosixParser и GnuParser.
Каждый парсер реализует подход к форматированию параметров из одноименного стандарта. Такое решение позволяет без труда менять подход к форматированию параметров.
Помимо разбора аргументов строки запуска, Commons CLI позволяет генерировать справку, опираясь на описанные параметры:
Результат:
HelpFormatter достаточно гибко настраивается. Более полный пример его использования можно посмотреть на официальном сайте.
К сожалению, в Commons CLI не предусмотрено понятия команда, что сильно ограничивает возможности этой библиотеки.
Каждый параметр описывается как объект Option:
Option count = new Option("l", "limit", true, "ограничивает количество отображаемых изменений");
Options options = new Options();
options.addOption(count);
if( line.hasOption( "l" ) ) {
String count = line.getOptionValue( "l" );
}
CommandLine line = parser.parse( options, args );
CommandLineParser parser = new GnuParser();
CommandLineParser parser = new PosixParser();
Помимо разбора аргументов строки запуска, Commons CLI позволяет генерировать справку, опираясь на описанные параметры:
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp( "hg", options );
usage: hg -l,--limit <arg> ограничивает количество отображаемых изменений
К сожалению, в Commons CLI не предусмотрено понятия команда, что сильно ограничивает возможности этой библиотеки.
JCommander
В библиотеке JCommander предлагается иной подход к решению задачи разбора командной строки. Автор предлагает сопоставлять каждому параметру поле класса:
Обратите внимание, что в данном решении префиксы в имени параметра указываются в ручную и вам не удастся так просто сменить подход к форматированию параметров. Но на самом деле, кому это надо? :)
Зато, библиотека поддерживает различные типы параметров. Из коробки идут как минимум стандартные типы, вроде Integer, String и Boolean. И предусмотрена гибкая система для расширения этого списка через свои конверторы:
Помимо этого, есть механизм предварительной проверки параметров:
После того, как необходимые параметры описаны, создается объект JCommander и ему указывается массив с аргументами командной строки:
После этого, все поля, помеченные тегом Parameter в объекте hgLog, будут инициализированы соответствующими проверенными значениями из командной строки.
Самое главное, что в этой библиотеке аннотации предусмотрены не только для полей класса, но и для самого класса тоже. Аннотация Parameters позволяет объявить класс, как реализацию команды, не двусмысленно призывая следовать паттерну Команда:
Пример использования:
Результат:
JCommander так же позволяет выводить справку, но в кастомизации форматирования эта библиотека уступает предыдущей:
Зато, есть возможность выводить справку только по конкретной команде:
import com.beust.jcommander.Parameter;
public class HgLogExample {
@Parameter(names = { "-l", "--limit" }, arity = 1,
description = "ограничивает количество отображаемых изменений")
private Integer limit = 1;
}
Обратите внимание, что в данном решении префиксы в имени параметра указываются в ручную и вам не удастся так просто сменить подход к форматированию параметров. Но на самом деле, кому это надо? :)
Зато, библиотека поддерживает различные типы параметров. Из коробки идут как минимум стандартные типы, вроде Integer, String и Boolean. И предусмотрена гибкая система для расширения этого списка через свои конверторы:
public class FileConverter implements IStringConverter<File> {
@Override
private File convert(String value) {
return new File(value);
}
}
/* Конвертор указывается в аннотации к параметру */
@Parameter(names = "-file", converter = FileConverter.class)
File file;
Помимо этого, есть механизм предварительной проверки параметров:
public class PositiveInteger implements IParameterValidator {
public void validate(String name, String value)
throws ParameterException {
int n = Integer.parseInt(value);
if (n < 0) {
throw new ParameterException("Parameter " + name
+ " should be positive (found " + value +")");
}
}
}
/* Тем временем в классе HgLogExample...*/
@Parameter(names = { "-l", "--limit" }, arity = 1, validateWith = PositiveInteger.class,
description = "ограничивает количество отображаемых изменений")
private Integer limit = 1;
После того, как необходимые параметры описаны, создается объект JCommander и ему указывается массив с аргументами командной строки:
HgLogExample hgLog = new HgLogExample();
JCommander jc = new JCommander(hgLog);
jc.parse(args);
После этого, все поля, помеченные тегом Parameter в объекте hgLog, будут инициализированы соответствующими проверенными значениями из командной строки.
Самое главное, что в этой библиотеке аннотации предусмотрены не только для полей класса, но и для самого класса тоже. Аннотация Parameters позволяет объявить класс, как реализацию команды, не двусмысленно призывая следовать паттерну Команда:
@Parameters(commandNames = { "log" },
commandDescription = "показывает историю ревизий всего репозитория или файлов")
public class HgLogExample implements Runnable {
public void run() {
System.out.println("Hello world!");
}
@Parameter(names = { "-l", "--limit" }, arity = 1,
description = "ограничивает количество отображаемых изменений")
private Integer limit;
}
public static void main(String[] args) {
/* Создается карта имя-команда */
Map<String, Runnable> map = new HashMap<String, Runnable>();
HgLogExample hgLog = new HgLogExample();
map.put("log", hgLog);
/* Разбираются аргументы запуска */
JCommander jc = new JCommander();
jc.addCommand(hgLog);
args = new String[] { "log", "-l", "3" };
jc.parse(args);
/* Выполняется указанная в аргументах команда */
String name = jc.getParsedCommand();
map.get(name).run();
}
Hello world!
JCommander так же позволяет выводить справку, но в кастомизации форматирования эта библиотека уступает предыдущей:
jc.usage();
Usage: <main class> [options] [command] [command options] Commands: log показывает историю ревизий всего репозитория или файлов Usage: log [options] Options: -l, --limit ограничивает количество отображаемых изменений
Зато, есть возможность выводить справку только по конкретной команде:
jc.usage("log");
показывает историю ревизий всего репозитория или файлов Usage: log [options] Options: -l, --limit ограничивает количество отображаемых изменений
ИМХО:
Если в вашем приложении нет того, что в начале статьи было определено как команда, то скорее всего вам стоит выбрать common-cli. Но тот факт, что jcommander берет на себя вопрос инициализации полей(присвоение им значения из командной строки) и проверку значений аргументов, скорее всего даже в простом случае заставит вас сделать выбор в пользу JCommander,
Комментариев нет:
Отправить комментарий