Short Instruction

BM ASN.1の簡単な使い方を、以下のASN.1定義を例に解説します。

FrightStatusTypes DEFINITIONS AUTOMATIC TAGS ::= BEGIN
	
	FrightNumber ::= PrintableString
	
	Airport ::= ENUMERATED {
		tokyo(0),
		osaka(1),
		nagoya(2),
		fukuoka(3)
	}
	
	FrightInformation ::= SEQUENCE {
		airport Airport,
		scheduled UTCTime,
		actual UTCTime OPTIONAL
	}
	
	Status ::= CHOICE {
		onTime NULL,
		delay INTEGER
	}
	
	FrightStatus ::= [APPLICATION 0] IMPLICIT SEQUENCE {
		frightNo FrightNumber,
		departure [0] IMPLICIT FrightInformation,
		arrival [1] IMPLICIT FrightInformation,
		status [2] Status DEFAULT onTime:NULL
	}
	
	AllFrights ::= [APPLICATION 1] SEQUENCE OF FrightStatus
	
END

ASN.1型の定義

各ASN.1型は、ASN1Typeクラスを継承したクラスとして定義します。ただし、ユーザー定義のASN.1型クラスがASN1Typeクラスを直接に継承することはなく、jp.bitmeister.asn1.type.builtinパッケージとjp.bitmeister.asn1.type.usefulパッケージに含まれているBuilt-In ASN.1型クラスを使用します。これらのASN.1型クラスには、jp.bitmeister.asn1.type.annotationパッケージに含まれているアノテーションで、ASN.1型に関連する各種の情報を付与することができます。

public class FrightNumber extends PrintableString {}

ENUMERATED型の要素は、@ASN1Enumerationを付与したpublic static finalフィールドとして定義します。

public class Airport extends ENUMERATED {
	
	@ASN1Enumeration
	public static final int tokyo = 0;
	
	@ASN1Enumeration
	public static final int osaka = 1;
	
	@ASN1Enumeration
	public static final int nagoya = 2;
	
	@ASN1Enumeration
	public static final int fukuoka = 3;
	
}

SEQUENCE型の要素は、SEQUENCE型を継承したクラスのフィールドに@ASN1Elementアノテーションを付与して定義します。@ASN1ElementのvalueパラメータはASN.1定義上での要素の登場順です。先頭の要素を0として、以下順番に番号付けします。SEQUENCE型の要素にできるのは、アクセス指定がpublicの変更可能なインスタンスの(つまり、staticおよびfinalではない)フィールドだけです。

public class FrightInformation extends SEQUENCE {
	
	@ASN1Element(0)
	public Airport airport;
	
	@ASN1Element(1)
	public UTCTime scheduled;
	
	@ASN1Element(value = 2, optional = true)
	public UTCTime actual;
	
}

CHOICE型の要素は、CHOICE型を継承したクラスのフィールドに@ASN1Alternativeアノテーションを付与して定義します。@ASN1Alternativeのvalueパラメータは、@ASN1Elementと同様にASN.1定義上での要素の登場順です。

public class Status extends CHOICE {
	
	@ASN1Alternative(0)
	public NULL onTime;
	
	@ASN1Alternative(1)
	public INTEGER delay;
	
}

ASN.1タグは、@ASN1Tagアノテーションで付与します。構造型の要素にデフォルト値がある場合は、@ASN1ElementアノテーションのhasDefaultパラメータをtrueに設定し、フィールドをデフォルト値で初期化します。

@ASN1Tag(value = 0, tagClass = ASN1TagClass.APPLICATION, tagMode = ASN1TagMode.IMPLICIT)
public class FrightStatus extends SEQUENCE {

	@ASN1Element(0)
	public FrightNumber frightNo;
	
	@ASN1Element(1)
	@ASN1Tag(value = 0, tagMode = ASN1TagMode.IMPLICIT)
	public Information departure;
	
	@ASN1Element(2)
	@ASN1Tag(value = 1, tagMode = ASN1TagMode.IMPLICIT)
	public Information arrival;
	
	@ASN1Element(value = 3, hasDefault = true)
	@ASN1Tag(value = 2, tagMode = ASN1TagMode.EXPLICIT)
	public Status status = new Status(ASN1TagClass.CONTEXT_SPECIFIC, 0, new NULL());
	
}

コレクション型(SET OF, SEQUENCE OF)を定義する場合は、ジェネリックの型パラメータにコレクションの要素となる型を設定します。また、コンストラクタで要素型のクラスオブジェクトを、親クラスのコンストラクタに渡します。

public static class AllFrights extends SEQUENCE_OF<FrightStatus> {

	public AllFrights() {
		super(FrightStatus.class);
	}
	
}

ASN.1型クラスに引数付きのコンストラクタを定義しておくと、コード内でデータの初期化を簡単に行うことができます。引数付きのコンストラクタを定義する場合は、必ず引数無しのコンストラクタも定義します。

public class FrightNumber extends PrintableString {

	public FrightNumber() {}

	public FrightNumber(String value) {
		super(value);
	}
	
}

public class FrightStatus extends SEQUENCE {

	...
	
	public FrightStatus() {}
	
	public FrightStatus(FrightNumber frightNo, Information departure,
			Information arrival, Status status) {
		this.frightNo = frightNo;
		this.departure = departure;
		this.arrival = arrival;
		this.status = status;
	}
	
}

ASN.1モジュールの定義

ASN.1モジュールは、ASN1Moduleクラスを継承したクラスとして定義します。ASN.1モジュールクラスのメンバークラスとしてASN.1型クラスを定義すると、それらのクラスはそのモジュールに含まれるASN.1型として扱われます。また、ASN.1モジュールクラスのメンバークラスではないASN.1型クラスを、アノテーションによってモジュールに含めることもできます。ASN.1型クラスは、必ずいずれかのASN.1モジュールに含めなければなりません。

モジュール宣言のタグデフォルト指定は、@ASN1ModuleTagsアノテーションで付与します。モジュールに含まれる各ASN.1型クラスは、必ずpublic staticクラスとして定義します。

@ASN1ModuleTags(ASN1TagDefault.AUTOMATIC_TAGS)
public class FrightStatusTypes extends ASN1Module {

	public static class FrightNumber extends PrintableString {

		public FrightNumber() {}

		public FrightNumber(String value) {
			super(value);
		}
	}
	
	public static class Airport extends ENUMERATED {
		
		@ASN1Enumeration
		public static final int tokyo = 0;
		
	...

ASN.1モジュールクラスのメンバークラス以外のASN.1型クラスをモジュールに含める場合は、@ASN1ModuleRefアノテーションを使用します。

@ASN1ModuleRef(FrightStatusTypes.class)
@ASN1Tag(value = 0, tagClass = ASN1TagClass.APPLICATION, tagMode = ASN1TagMode.IMPLICIT)
public class FrightStatus extends SEQUENCE {

	@ASN1Element(0)
	public FrightNumber frightNo;
	
	...

@ASN1ModuleRefでモジュールを指定した場合、ASN.1モジュールクラス側では@ASN1DefinedTypesでそのクラスをモジュールに登録する必要があります。

@ASN1ModuleTags(ASN1DefaultTags.IMPLICIT_TAGS)
@ASN1DefinedTypes(FrightStatus.class)
public class FrightStatusTypes extends ASN1Module {
	
	public FrightStatusTypes() {
		register(FrightStatus.class);
	}
	
	...

ASN.1型データ値の設定

BOOLEANやINTEGERなどのプリミティブ型への値の設定は、コンストラクタまたはsetメソッドで行います。設定された値はvalue()メソッドで取り出すことができます。また、それぞれのプリミティブ型に固有のメソッドも実装されています。

	BOOLEAN bool = new BOOLEAN(true);
	if (bool.value()) {
		bool.set(false);
	}
	
	INTEGER number = new INTEGER(100);
	if (number.isIntValue()) {
		int value = number.intValue();
	}

ユーザー定義の単純型への値の設定は、setメソッドで行います。

	FrightNumber frightNo = new FrightNumber();
	frightNo.set("JP041");

初期化用のコンストラクタを定義すると、コードを短縮できます。

	FrightNumber frightNo = new FrightNumber("JP041");	

構造型への値の設定は、フィールドへのASN.1型データの代入で行います。

	FrightStatus status = new FrightStatus();
	status.frightNo = new FrightNumber("JP041");
	status.departure = new Information();
	status.departure.airport = new Airport(Airport.tokyo);
	...

CHOICE型の場合も、同じくフィールドへのデータ代入で値を設定できます。値が設定されているフィールドが、そのCHOICE型データの選択値として扱われます。フィールドの2つ以上に値が設定されている場合の動作は不定です。clearメソッドを使用すると、現在の選択値をクリアすることができます。

	Status status = new Status();
	status.onTime = new NULL();
	status.clear();
	status.delay = new INTEGER(10);

各ASN.1型クラスに初期化用のコンストラクタを定義している場合は、以下の形で初期化が行えます。

	FrightStatus status = new FrightStatus(
			new FrightNumber("JP041"),
			new Information(
					new Airport(Airport.tokyo),
					new UTCTime("110627073000"),
					new UTCTime("110627073500")
					),
			new Information(
					new Airport(Airport.fukuoka),
					new UTCTime("110627090000"),
					null
					),
			new Status(
					ASN1TagClass.CONTEXT_SPECIFIC, 1, new INTEGER(5))
			);

Encode/Decode

現バージョンのBM ASN.1は、BER(Basic Encoding Rules)およびDER(Distinguished Encoding Rules)エンコード、XER(XML Encoding Rules)エンコードとBERおよびXERデコードに対応しています。DERエンコードの結果のbyte列も、BerDecoderでデコードすることができます。BERとDERの違いについては、こちらを参照してください。

BerEncoderDerEncoderおよびXerEncoderのencodeメソッドはASN.1型データを引数にとり、エンコードした結果をOutputStreamに書き込みます。

	ByteArrayOutputStream bo = new ByteArrayOutputStream();
	DerEncoder enc = new DerEncoder(bo);
	enc.encode(status);

BerDecoderおよびXerDecoderは、InputStreamからバイト列を読み取ってASN.1型データにデコードします。BerDecoderのdecodeメソッドにASN.1型クラスオブジェクトを渡すと、BerDecoderはデコード結果をそのASN.1型クラスのインスタンスに設定して返します。バイト列からデコードされたASN.1タグまたはXMLタグ名が引数のASN.1型と一致していない場合は、例外が発生します。

	ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
	BerDecoder dec = new BerDecoder(bi);
	FrightStatus result = dec.decode(FrightStatus.class);

引数なしのdecodeメソッドが呼ばれると、XerDecoderおよびBerDecoderはバイト列からASN.1タグまたはXMLタグ名をデコードし、デコーダーの生成時に指定されたASN.1モジュール型に登録されている型の中からデコードされたタグに一致するASN.1型クラスを探し出してデコードを行います。

	ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
	BerDecoder dec = new BerDecoder(FrightStatusTypes.class, bi);
	ASN1Type result = dec.decode();

toString

BM ASN.1の全てのASN.1型クラスはtoStringメソッドを実装しているので、System.out.println()メソッドなどにASN.1型データを渡すことで、データ内容を文字列化して表示することができます。

次のコードを実行すると、

	System.out.println(result);

コンソールに以下の出力を得ることができます。

FrightStatus ::= [APPLICATION 0] IMPLICIT SEQUENCE {
	frightNo	FrightNumber ::= PrintableString	"JP041",
	departure	[0] IMPLICIT	Information ::= SEQUENCE {
		airport	[0] IMPLICIT	Airport ::= ENUMERATED	tokyo(0),
		scheduled	[1] IMPLICIT	UTCTime	"110626223000Z" [2011/06/27 07:30:00.0 JST],
		actual	[2] IMPLICIT	UTCTime	"110626223500Z" [2011/06/27 07:35:00.0 JST] OPTIONAL },
	arrival	[1] IMPLICIT	Information ::= SEQUENCE {
		airport	[0] IMPLICIT	Airport ::= ENUMERATED	fukuoka(3),
		scheduled	[1] IMPLICIT	UTCTime	"110627000000Z" [2011/06/27 09:00:00.0 JST],
		actual	[2] IMPLICIT	<No value> OPTIONAL },
	status	[2] Status ::= CHOICE {
		delay	[1] IMPLICIT	INTEGER	(5) } }