Вступление
Picocli — это структура синтаксического анализа командной строки, состоящая из одного файла, которая позволяет создавать приложения командной строки практически без кода. Аннотируйте поля в вашем приложении с помощью @Option
или @Parameters
, и picocli заполнит эти поля параметрами командной строки и позиционными параметрами соответственно. Например:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@Command (name = "Greet" , header = "%n@|green Hello world demo|@" ) class Greet implements Runnable { @Option (names = { "-u" , "--user" }, required = true , description = "The user name." ) String userName; public void run() { System.out.println( "Hello, " + userName); } public static void main(String... args) { CommandLine.run( new Greet(), System.err, args); } } |
Когда мы выполняем эту программу, picocli анализирует командную строку и заполняет поле userName
перед вызовом метода run
:
1
2
3
|
$ java Greet -u picocli Hello, picocli |
Picocli создает справочные сообщения об использовании цветов и стилей Ansi . Если мы запустим вышеупомянутую программу с неверным вводом (пропуская обязательную опцию имени пользователя), picocli напечатает ошибку и сообщение помощи использования:
Picocli может сгенерировать сценарий автозаполнения, который позволяет конечным пользователям использовать завершение командной строки <TAB>
чтобы определить, какие параметры и подкоманды доступны. Вам также может понравиться поддержка picocli для подкоманд и вложенных подкоманд до любого уровня глубины.
Руководство пользователя подробно описывает функциональные возможности Picocli. В этой статье рассматриваются новые и заслуживающие внимания функции, представленные в выпуске picocli 2.0.
Параметры микширования с позиционными параметрами
Парсер был улучшен, и позиционные параметры теперь можно смешивать с опциями в командной строке.
Ранее позиционные параметры должны были следовать параметрам. В этом выпуске любой аргумент командной строки, который не является опцией или подкомандой, интерпретируется как позиционный параметр.
Например:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
class MixDemo implements Runnable { @Option (names = "-o" ) List<String> options; @Parameters List<String> positional; public void run() { System.out.println( "positional: " + positional); System.out.println( "options : " + options); } public static void main(String[] args) { CommandLine.run( new MixDemo(), System.err, args); } } |
Запуск вышеуказанного класса со смесью опций и позиционных параметров показывает, что неопционные параметры распознаются как позиционные параметры. Например:
1
2
3
4
|
$ java MixDemo param0 -o AAA param1 param2 -o BBB param3 positional: [param0, param1, param2, param3] options : [AAA, BBB] |
Для поддержки параметров смешивания с позиционными параметрами, синтаксический анализатор был изменен. В Picocli 2.0 многозначные параметры (поля массива, списка и карты) больше не являются жадными по умолчанию . Примечания к выпуску 2.0 подробно описывают это изменение и другие потенциальные критические изменения .
Открытие типов коллекций
Picocli выполняет автоматическое преобразование типов аргументов командной строки в тип аннотированного поля. И именованные параметры, и позиционные параметры могут быть строго типизированы.
До версии 2.0 Picocli требовал аннотирования полей « Collection
и « Map
type
атрибутом type
чтобы можно было выполнять преобразование типов. Для полей других типов, например полей массива и полей с одним значением, таких как поля int
или java.io.File
, picocli автоматически определяет тип цели по типу поля, но для коллекций и карт требуется более подробная аннотация. Например:
1
2
3
4
5
6
7
|
class Before { @Option (names = "-u" , type = {TimeUnit. class , Long. class }) Map<TimeUnit, Long> timeout; @Parameters (type = File. class ) List<File> files; } |
Начиная с версии 2.0, атрибут type
больше не требуется для полей Collection
и Map
: picocli будет выводить тип элемента коллекции из универсального типа. Атрибут type
прежнему работает, как и раньше, в большинстве случаев он не является обязательным.
Пропуск атрибута type
удаляет некоторое дублирование и приводит к более простому и чистому коду:
1
2
3
4
5
6
7
|
class Current { @Option (names = "-u" ) Map<TimeUnit, Long> timeout; @Parameters List<File> files; } |
В приведенном выше примере picocli 2.0 может автоматически обнаруживать, что аргументы командной строки необходимо преобразовать в File
перед добавлением их в список, а для карты эти ключи необходимо преобразовать в TimeUnit
а значения в Long
.
Автоматическая справка
Picocli предоставляет ряд удобных методов, таких как run
и call
которые анализируют аргументы командной строки, заботятся об обработке ошибок и вызывают интерфейсный метод для выполнения приложения.
В этом выпуске вспомогательные методы также автоматически usageHelp
об использовании и информацию о версии, когда пользователь указывает параметр, usageHelp
атрибутом usageHelp
или usageHelp
в командной строке.
Пример программы ниже демонстрирует автоматическую помощь:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
@Command (version = "Help demo v1.2.3" , header = "%nAutomatic Help Demo%n" , description = "Prints usage help and version help when requested.%n" ) class AutomaticHelpDemo implements Runnable { @Option (names = "--count" , description = "The number of times to repeat." ) int count; @Option (names = { "-h" , "--help" }, usageHelp = true , description = "Print usage help and exit." ) boolean usageHelpRequested; @Option (names = { "-V" , "--version" }, versionHelp = true , description = "Print version information and exit." ) boolean versionHelpRequested; public void run() { // NOTE: code like below is no longer required: // // if (usageHelpRequested) { // new CommandLine(this).usage(System.err); // } else if (versionHelpRequested) { // new CommandLine(this).printVersionHelp(System.err); // } else { ... the business logic for ( int i = 0 ; i < count; i++) { System.out.println( "Hello world" ); } } public static void main(String... args) { CommandLine.run( new AutomaticHelpDemo(), System.err, args); } } |
При выполнении с -h
или --help
программа выводит справку об использовании:
Аналогично, при выполнении с -V
или --version
программа выводит информацию о версии:
Методы, которые автоматически выводят справку:
- CommandLine :: вызов
- CommandLine :: бежать
- CommandLine :: parseWithHandler (со встроенными обработчиками Run…)
- CommandLine :: parseWithHandlers (со встроенными обработчиками Run…)
Методы, которые не выводят справку автоматически:
- CommandLine :: синтаксического
- CommandLine :: populateCommand
Лучшая поддержка подкоманд
В этом выпуске добавлены новые CommandLine::parseWithHandler
. Эти методы предлагают ту же простоту использования, что и методы run
и call
, но с большей гибкостью и лучшей поддержкой вложенных подкоманд.
Рассмотрим, что должно делать приложение с подкомандами:
- Разбор командной строки.
- Если пользовательский ввод был неверным, распечатайте сообщение об ошибке и справку по использованию для подкоманды, где анализ не удался.
- Если анализ выполнен успешно, проверьте, запрашивал ли пользователь справку об использовании или информацию о версии для команды верхнего уровня или подкоманды. Если это так, распечатайте запрошенную информацию и выйдите.
- В противном случае выполните бизнес-логику. Обычно это означает выполнение наиболее конкретной подкоманды.
Picocli предоставляет некоторые строительные блоки для достижения этой цели, но приложение должно было соединить их вместе. Эта проводка, по сути, является стандартной и очень похожа между приложениями. Например, ранее приложение с подкомандами обычно содержало такой код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public static void main() { // 1. parse the command line CommandLine top = new CommandLine( new YourApp()); List<CommandLine> parsedCommands; try { parsedCommands = top.parse(args); } catch (ParameterException ex) { // 2. handle incorrect user input for one of the subcommands System.err.println(ex.getMessage()); ex.getCommandLine().usage(System.err); return ; } // 3. check if the user requested help for (CommandLine parsed : parsedCommands) { if (parsed.isUsageHelpRequested()) { parsed.usage(System.err); return ; } else if (parsed.isVersionHelpRequested()) { parsed.printVersionHelp(System.err); return ; } } // 4. execute the most specific subcommand Object last = parsedCommands.get(parsedCommands.size() - 1 ).getCommand(); if (last instanceof Runnable) { ((Runnable) last).run(); } else if (last instanceof Callable) { Object result = ((Callable) last).call(); // ... do something with result } else { throw new ExecutionException( "Not a Runnable or Callable" ); } } |
Это довольно много стандартного кода. Picocli 2.0 предоставляет удобный метод, который позволяет вам свести все вышеперечисленное к одной строке кода, чтобы вы могли сосредоточиться на бизнес-логике вашего приложения:
1
2
3
4
5
6
7
8
|
public static void main() { // This handles all of the above in one line: // 1. parse the command line // 2. handle incorrect user input for one of the subcommands // 3. automatically print help if requested // 4. execute one or more subcommands new CommandLine( new YourApp()).parseWithHandler( new RunLast(), System.err, args); } |
Новый удобный метод — parseWithHandler
. Вы можете создать свой собственный обработчик или использовать один из встроенных обработчиков. Picocli предоставляет реализации обработчиков для некоторых распространенных случаев использования.
Встроенными обработчиками являются RunFirst
, RunLast
и RunAll
. Все они предоставляют автоматическую справку: если пользователь запрашивает UseHelp или VersionHelp, запрашиваемая информация печатается, и обработчик возвращается без дальнейшей обработки. Обработчики ожидают, что все команды реализуют либо java.lang.Runnable
либо java.util.concurrent.Callable
.
-
RunLast
выполняет наиболее конкретную команду или подкоманду. Например, если пользователь вызвалjava Git commit -m "commit message"
, picocli считаетGit
командой верхнего уровня и передаст подкоманду. В этом примере подкомандаcommit
является наиболее конкретной командой, поэтомуRunLast
будет выполнять только эту подкоманду. Если подкоманд нет, выполняется команда верхнего уровня.RunLast
теперь используется внутри Picocli для реализации существующихCommandLine::run
иCommandLine::call
удобства. -
RunFirst
выполняет только команду первого , верхнего уровня и игнорирует подкоманды. -
RunAll
выполняет команду верхнего уровня и все подкоманды, которые появились в командной строке.
Существует также метод parseWithHandlers
, который аналогичен, но дополнительно позволяет указать пользовательский обработчик для неправильного ввода пользователя.
Улучшены методы run
и call
CommandLine::call
и CommandLine::run
теперь поддерживают подкоманды и будут выполнять последнюю подкоманду, указанную пользователем. Ранее подкоманды были проигнорированы, и была выполнена только команда верхнего уровня.
Улучшенные исключения
Наконец, в этом выпуске все исключения picocli предоставляют метод getCommandLine
который возвращает команду или подкоманду, если синтаксический анализ или выполнение не удалось. Ранее, если пользователь предоставлял неверный ввод для приложений с подкомандами, было трудно точно определить, какая именно подкоманда не смогла проанализировать ввод.
Вывод
Если вы уже используете Picocli, v2.0 является существенным обновлением. Если вы ранее не пользовались picocli, надеюсь, вышеизложенное заинтересовало вас попробовать.
Многие из этих улучшений возникли в результате отзывов пользователей и последующих обсуждений. Пожалуйста, не стесняйтесь задавать вопросы, запрашивать функции или давать другие отзывы о системе отслеживания проблем picocli.
Пожалуйста, пометьте проект на GitHub, если вам это нравится, и расскажите об этом друзьям!
Смотрите оригинальную статью здесь: Picocli 2.0: больше с меньшими затратами
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |