Васильев Андрей Михайлович
Версии презентации
Официальная документация: https://jcommander.org/
JavaDoc-документация: https://javadoc.io/doc/org.jcommander/jcommander/latest/index.html
Альтернативное Java-решение: https://picocli.info/
Для работы с библиотекой, её необходимо добавить в список зависимостей приложения
Gradle-описание зависимости для подключения:
"org.jcommander:jcommander:3.0"
При использовании Gradle без дополнений её необходимо добавить в файл
build.gradle.kts в раздел dependencies:
dependencies {
implementation("org.jcommander:jcommander:3.0")
}При использовании шаблона лабораторной добавьте строку в файл
build.properties.json в список dependencies
@Parameters(separators = "=")
class Args {
@Parameter
var targets: List<String> = arrayListOf()
@Parameter(names = ["-bf", "--buildFile"],
description = "The build file")
var buildFile: String? = null
@Parameter(names = ["--checkVersions"],
description = "Check if there are any newer versions")
var checkVersions = false
}separators = " " — аргументы разбираются только в формате
ключ значениеseparators="=" включается разбор в формате
ключ=значение; при этом разбор через пробел отключаетсяnamesval commander: JCommander =
JCommander
.newBuilder()
.addObject(commonParameters)
.build()newBuilder()addObject()build()parse(), принимающего произвольное
количество строкcommander.parse("--enable", "--size", "15")
val arguments: Array<String> = ...
commander.parse(*arguments)commonParametersJCommander постарается автоматически преобразовать данные из строкового представления в тип данных свойства класса для: логических полей, целых чисел, вещественных чисел и строк
Для других сложных типов данных, классов можно предоставить класс-преобразователь, который должен реализовывать следующий Java-интерфейс:
public interface IStringConverter<T> {
T convert(String value);
}Например для UUID на Kotlin такой класс может выглядеть следующим образом:
class UUIDConverter : IStringConverter<UUID> {
override fun convert(parameter: String): UUID =
UUID.fromString(parameter)
}В целевом объекте-хранителе аргументов необходимо сослаться на данный преобразователь следующим образом:
@Parameter(names=["--id"], converter = UUIDConverter::class)
var someId: UUID? = nullАннотация Parameter позволяет указать аргумент как обязательный:
@Parameter(names=["--data-one"], required = true)
var dataOne: Int = 0JCommander проверяет обязательные аргументы не по значению поля после обработки, а по паре «значение по умолчанию + тип поля»:
var dataOne: String = "default"), проверка обязательности
пропускается, и при отсутствии аргумента исключение не выбрасывается.null (например, var dataOne: Int? = null),
тип примитивный (var dataOne: Int = 0) или значение по умолчанию
пустое, при отсутствии аргумента выбрасывается ParameterException.Поэтому обязательные аргументы рекомендуется описывать так:
@Parameter(names=["--data-one"], required = true)
var dataOne: Int? = nullФакт отсутствия исключения не убеждает компилятор Kotlin в наличие
не-null-значения в свойстве объекта.
Приложения с командным интерфейсом зачастую предоставляют ряд связных между собой действий, каждое из которых имеет свой собственный набор аргументов
git clone <REPOSITORY>git switch --create <BRANCH>Названием команды обычно выступает первый позиционный аргумент приложения
Согласно концепции JCommander
parsedCommandОписание не отличается от обычного объекта-хранителя:
@Parameters(separators = "=",
commandDescription = "List application entries")
class ListCommand {
@Parameter(names = ["-s", "--size"],
description = "Amount of items to display")
var size: Int = 10
}Единственным отличием является возможность использования аргумента
commandDescription у аннотации Parameters, которая позволяет добавить описание
назначения команды
val commander: JCommander =
JCommander
.newBuilder()
.addCommand("list", listCommand)
.addCommand("build", buildCommand)
.build()newBuilder()commander.parse("list", "--size=15")
val arguments: Array<String> = ...
commander.parse(*arguments)
val command: String? = commander.parsedCommand
if (command == null) {
// Команда не передана — сообщение об ошибке и ненулевой код возврата
} else if (command == "list") {
... // Обрабатываем данные внутри listCommand
} else if (command == "build") {
... // Обрабатываем данные внутри buildCommand
}Общие для всех команд параметры описываются отдельным объектом-хранителем и
добавляются в построитель методом addObject(). Общие параметры указываются
в командной строке до имени команды
val commonParameters = CommonParameters()
val commander: JCommander =
JCommander
.newBuilder()
.addObject(commonParameters)
.addCommand("list", listCommand)
.addCommand("build", buildCommand)
.build()
// --triangles-file="data.csv" list
commander.parse(*arguments)Если команда не передана, parse() не выбрасывает исключение, а
parsedCommand остаётся равным null. Такой случай необходимо проверять
явно и завершать приложение с ненулевым кодом возврата
JCommander способен сформировать справку по использованию всех доступных аргументов, их значений по умолчанию и т.д.
Для вывода этой информации на стандартный поток вывода, вызовите метод usage()
на объекте JCommander после добавления к нему объектов и команд
Usage: <main class> [command] [command options]
Commands:
list List application entries
Usage: list [options]
Options:
-s, --size
Amount of items to display
Default: 10
update Update application entry
Usage: update [options]
Options:
* -i, --id
Id of the entry to updateАргументы, помеченные *, являются обязательными
При разработке приложения с большим количеством аргументов надо иметь возможность удобным образом переключаться между разными наборами аргументов
Помимо использования модульных тестов JCommander предоставляет возможность
считывания списка аргументов из файла с помощью @-синтаксиса
commander.parse("@params.txt")Из файла params.txt, который находится в текущем рабочем каталоге:
#, будут проигнорированы@-аргументыДанный подход удобнее постоянного редактирования конфигурации запуска Gradle-задачи