しいしせねっとわーく
[映像編] [色規格] [JPEG] [QRコード]

Javaで使えるABNF

JavaでABNFを扱うためのライブラリをなんとなく作ってみたので使えるように公開? JSONもABNFでできているので作ってみました。簡易マニュアル的なページです。

ABNFとは

RFC でよく使われるバッカス・ナウア記法(Backus-Naur form : BNF)のひとつ(Augmented Backus-Naur form)でIETFがRFCを記述する際によく利用される。BNFは、メタ言語などといわれていて、言語を作るための言語のようなものです。他にはEBNFなどが一般的ですがEBNFにもいろいろあるようです。RFC 5234 とRFC 7405で定義されています。RFC 7405の拡張はあまり使われていません。

ABNF(Augmented Backus-Naur form)
定義
RFC 5234, RFC 7405
他の実装
少数?
使用例
IMF,URL,URI,URN,IRI,HTTP.IPv6
ダウンロード
SoftLibABNF (GitHub JDK8,Mavenくらい想定), SoftLibが必要

ABNFはメール(CMS)、HTML、IPv6アドレスなどの定義に使われています。XMLなども別のBNFで定義されています。

自作ツールはSoftLibABNFという名で、各種定義はSoftLibRFCとして別途まとめています。しばらく更新せず安定版ですが、現状バージョン付与していないソースコードのみです。

JSONとは

XMLに代わってRESTだとかWeb APIの主役になっているあれです。JavaScript Object Notation という名のとおりJavaScriptではよく使われている形式がWebの標準的なものとして拡大したものですが、Javaで直接扱うのは難しそうなので作ってみました。JavaでJSONを扱えるのは、他に標準API、Jakson、Json in Javaなどがあるようですが、いろいろ機能を盛っているので標準APIよりは便利かどうか。RFC 8259とECMA-404 2nd Editionが最終的なJSONの標準として落ち着いているようです。

JSON(JavaScript Object Notation)
定義
RFC 8259, ECMA-404 2nd Edition
他の実装
Java, Jakson, Json in Java,Gson など各種
使用例
REST
ダウンロード
SoftLibJSON (GitHub JDK8,Mavenくらい想定), SoftLibABNFが必要

自作ツールはSoftLibJSONという名です。RFCの例はSoftLibRFCです。現状バージョン付与していないソースコードのみです。

ABNF作り方

ABNFの説明はどこかのを見てください。SoftLibABNFではRFC 5234とRFC 7405に対応しています。

SoftLibABNFでは、定義をABNFReg というクラスをひとつの名前空間として構築します。class中にstaticで定義して、下に1行ずつ並べてもいいですし、テキスト形式で流し込んでもかまいません。

class Test {
    static ABNFReg REG = new ABNFReg();
}

classひとつでひとつの名前空間を作るくらいの感じで利用しています。

ABNFReg();

ABNFReg(引き継ぐ名前空間);

ABNFReg(引き継ぐ名前空間,ABNF定義);

初期化パラメータは3種類、ABNFにはALPHA,DIGITなどのcore rulesがありますが、それを引き継ぐにはnew ABNFReg(ABNFReg.BASE) とします。他のABNFRegを引き継ぐのも同様です。

2つめのパラメータはABNFパーサ自体のABNFで、差し替えが可能です。RFC 7405やHTTPなどABNF構文自体に特殊な定義がある場合に差し替えます。普段は気にしなくてもいいかもしれません。

static ABNF a = REG.rule("a","b");
static ABNF b = REG.rule("b := c");
static ABNF c = REG.rule("c", b.or(a));

登録と参照

ABNFの rule をひとつ作るのは、名前空間(ABNFReg)からrule(name,value) か、rule(rule) で可能です。ABNF自身の定義では厳密にはruleには改行を含まなくてはいけませんが、rule()では最後の改行は省略できます。

一括登録するには、rulelist(定義一覧) が使えます。参照には ref(name) または href(name) が使えます。

定義にABNF変数を使うこともできます。書式ではなくオブジェクトで構築することもできます。(後述)

ref(name)では未定義のルールを参照してもかまいません。循環ルールの定義も可能です。

文字コードはutf-8、改行コードはCRLFです。CRのみやLFのみでは読み込めません。

書式の代用

書式で書けるものは、オブジェクトの形で記述することもできます。

a := b
a = b;
名前
a = b.name("a");
択一 a / b / c
rule = a.or(b,c)
連結 a b c
rule = a.pl(b,c)
選択肢追加 b / c / d
a = b.or(c,d)
値の範囲指定 %##-##
ABNF.range(32,64)
反復 a := 2*3b
a = b.x(2,3)
a := 2b
a = b.x(2)
a := *b
a = b.x()
a := 1*b
a = b.ix()
オプション a := b [ d ]
a = b.pl(d.c())
文字
a = ABNF.text('a')
テキスト
a = ABNF.text("text")
バイナリ
a = ABNF.bin(int) または ABNF.bin(文字列)
text列 "a" / "b" / "c" / "d"
a = ABNF.list("abcd")

だいたいこんな感じでABNFをJavaの構文で構築できます。x()やix(),c()などに慣れるとABNFから気軽に書き換えられるはずです。

ABNFReg REGの使い方

登録はREG.rule() または REG.rulelist() で。

参照は REG.ref(name) または REG.href(name) で。

name() は、REGへの登録には反映されない?のでruleで名付けた方がいいかもしれません。

比較

各ABNFは、is() と eq() という比較演算子を用意してみました。Packetというクラスを使うと便利ですが、Stringでもかまいません。

で、trueまたはfalseを返します。 a.is("data") というふうな。Packetの場合は一致した長さが返せたりします。

パースとマッピングがつよいといろいろ作れる

一般的なABNFパーサの機能はここで終わりですが、ABNFの定義にいろいろなパーサを埋め込むことができるので以下略。