picocliでJavaの引数処理を手軽に行う話
あいさつに代えて
今回の画像は、先日自宅近くで撮影した朝の空です。まさに雲一つない空ですね。まるで壁紙の様です。
picocliとは
picocliとは、Javaの引数処理を少ない手間で行うことができるライブラリです。実は前回の記事でも利用していました。
主な機能・特徴としては、次の通りです。
1ファイルで他のライブラリに依存しない
いろんな構文スタイルに対応
サブコマンドに対応
その他、豊富な機能
引数をファイルで指定する@-filesに対応
ヘルプの色表示に対応
BashやZSHのTAB補完を行う為のスクリプト生成に対応
GraalVM Native Image用の設定の生成に対応(picocli-codegen)
文字列リソースのI18Nに対応
機能が豊富で全部に触れるのは難しいので、今回はある程度の内容を簡単に使ってみたいと思います。
使ってみる
依存関係
記載時の最新バージョンは4.7.1です。
Gradleでは、次の様に記載します。
dependencies {
implementation 'info.picocli:picocli:4.7.1'
}
Mavenでは、次の様に記載します。
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>4.7.1</version>
</dependency>
コマンド例の概要
例として、こんなコマンドを作ります。
コマンド表示名:file-tool
指定したフォルダ内のファイルに関する情報を出力するツール
複数のサブコマンドを持つ共通オプション
-d, --delimiter:出力時の区切り文字(デフォルトはタブ)
--header:先頭行にヘッダ文字列を出力するかどうか(デフォルトは出力しない)
-h, --help:コマンド全体のヘルプ表示
-V, --version:バージョン表示
サブコマンド
サブコマンドの指定が無い場合はバージョン表示size:指定したフォルダ内の全ファイルについてバイト数を表示
フォルダはパラメータで指定、複数指定を許可
パラメータが無い場合は現在のフォルダ(システムプロパティuser.dir)
type:指定したフォルダ内の全ファイルについてContent-Typeを表示
Content-Typeの取得はFiles.probeContentType(Path)を使用フォルダはパラメータで指定、複数指定を許可
パラメータが無い場合は現在のフォルダ(システムプロパティuser.dir)
help:指定したサブコマンドのヘルプ表示
picocliが提供するpicocli.CommandLine.HelpCommandクラスを利用サブコマンドの指定が無い場合はコマンド全体のヘルプ表示
コマンドの定義と実行
コマンドを定義する為には、RunnableもしくはCallableインターフェースを実装したクラスを作成します。今回はRunnableインターフェースを使います。
このクラスのインスタンスを、picocli.CommandLineクラスのコンストラクタに指定して、execute()メソッドを呼び出します。
public class FileTool implements Runnable {
...
public static void main(String... args) {
int exitCode = new CommandLine(new FileTool()).execute(args);
System.exit(exitCode);
}
...
}
コマンド名・説明など
コマンドの説明などは、クラス定義に@picocli.CommandLine.Commandアノテーションで指定します。
@Command(name = "file-tool", version = "file-tool 0.1", mixinStandardHelpOptions = true,
description = "File tool", subcommands = HelpCommand.class, scope = ScopeType.INHERIT)
public class FileTool implements Runnable {
...
name:ヘルプで表示するコマンド名
description:ヘルプで表示するコマンドの説明文(複数行指定可能)
version:バージョンで表示するバージョン情報(複数行指定可能)
mixinStandardHelpOptions:自動で標準のヘルプオプションを追加するかどうか(デフォルトfalse)
次のオプションが自動で追加-h,--help:ヘルプ表示
-V,--version:バージョン表示
subcommands:サブコマンドで使用するクラス(複数指定可能)
サブコマンドの指定は後で説明scope:オプション等の表示範囲
picocli.CommandLine.ScopeType.INHERITを指定するとサブコマンドでも表示
コマンドのオプション
コマンドのオプションは、変数やセッターメソッドに@picocli.CommandLine.Optionアノテーションで指定します。
...
@Option(names = {"--header"}, negatable = true,
description = "Show header.",
scope = ScopeType.INHERIT)
boolean header;
@Option(names = {"-d", "--delimiter"}, defaultValue = "\t",
description = "Set delimiter (default: \\t).", paramLabel = "DELIMITER",
scope = ScopeType.INHERIT)
private String delimiter;
...
names:オプション名(複数指定可能)
オプション名の先頭を"-"1文字から始める("-d")と、POSIXオプションの様に複数のオプションをまとめて(例えば"-abcd=,"の様に)指定可能description:オプションの説明文(複数指定可能)
paramLabel:オプションの値で表示するラベル
defaultValue:オプションの初期値
negatable:値が真偽値の場合、反対の値を指定するオプションを自動的に追加
"--header"オプションに対する"--no-header"オプションが追加scope:このオプションの表示範囲
picocli.CommandLine.ScopeType.INHERITを指定するとこのオプションはサブコマンドでも表示
サブコマンド
サブコマンドの指定方法には2種類あります。
@picocli.CommandLine.Commandアノテーションを付けたコマンド定義と同様のクラスを作成し、親コマンドのsubcommandsに指定
helpサブコマンドの指定はこちらを使用(picocli.CommandLine.HelpCommandクラスを利用する為)@picocli.CommandLine.Commandアノテーションを付けたメソッドを作成
size,typeサブコマンドはこちらを使用
@Command(name = "file-tool", version = "file-tool 0.1", mixinStandardHelpOptions = true,
description = "File tool", subcommands = HelpCommand.class, scope = ScopeType.INHERIT)
public class FileTool implements Runnable {
...
@Command(name = "size", description = "Print file size.")
void size(@Parameters(paramLabel = "PATH",
description = {"File path(s).",
"The default is the current directory (${DEFAULT-VALUE})."},
defaultValue = "${sys:user.dir}") List<Path> paths) {
...
@Command(name = "type", description = "Print file content type.")
void contentType(@Parameters(paramLabel = "PATH",
description = {"File path(s).",
"The default is the current directory (${DEFAULT-VALUE})."},
defaultValue = "${sys:user.dir}") List<Path> paths) {
...
name:サブコマンド名
パラメータ
パラメータの指定方法は2種類あります。
変数やセッターメソッドに@picocli.CommandLine.Parametersアノテーションで指定
サブコマンドのメソッド仮引数に@picocli.CommandLine.Parametersアノテーションで指定
今回のサンプルプログラムでは、サブコマンドのメソッド仮引数に@picocli.CommandLine.Parametersアノテーションで指定します。
...
@Command(name = "size", description = "Print file size.")
void size(@Parameters(paramLabel = "PATH",
description = {"File path(s).",
"The default is the current directory (${DEFAULT-VALUE})."},
defaultValue = "${sys:user.dir}") List<Path> paths) {
...
@Command(name = "type", description = "Print file content type.")
void contentType(@Parameters(paramLabel = "PATH",
description = {"File path(s).",
"The default is the current directory (${DEFAULT-VALUE})."},
defaultValue = "${sys:user.dir}") List<Path> paths) {
...
description:パラメータの説明文(複数指定可能)
paramLabel:パラメータの値で表示するラベル
defaultValue:パラメータの初期値
index:パラメータの位置指定
未指定の場合、引数の型がリストや配列なので全てのパラメータを設定
コマンドモデル
バージョン表示を明示的に行う為には、picocli.CommandLineクラスのprintVersionHelp(PrintStream)メソッドが利用できます。
@Specアノテーションでpicocli.CommandLine.Model.CommandSpecクラスのインスタンスをバインドし、commandLine()メソッドでpicocli.CommandLineクラスのインスタンスを取得します。
...
@Spec
CommandSpec commandSpec;
...
public void run() {
commandSpec.commandLine().printVersionHelp(System.out);
}
...
変数
picocliでは、アノテーションの属性値に定義された変数を指定する事ができます。
...
void size(@Parameters(paramLabel = "PATH",
description = {"File path(s).",
"The default is the current directory (${DEFAULT-VALUE})."},
defaultValue = "${sys:user.dir}") List<Path> paths) {
...
void contentType(@Parameters(paramLabel = "PATH",
description = {"File path(s).",
"The default is the current directory (${DEFAULT-VALUE})."},
defaultValue = "${sys:user.dir}") List<Path> paths) {
...
指定できる変数の種類はこちらにありますが、サンプルプログラムで使用しているものを挙げます。
${DEFAULT-VALUE}:defaultValueの値
${sys:key}:システムプロパティkeyの値
サンプルプログラム
今回説明したサンプルプログラムの全体は、次になります。
おまけ
picocliの作者Remko Popmaさんは、Apache Log4J2やApache GroovyのPMC Memberです。
日本在住なので、Java関連のイベントで会えるかもしれません。