| 著作一覧 |
昨日のエントリーの続き。
たとえば、Javaであれば、以下のような実装になる。
// BitmapBuilder.java
package com.example.bitmapparser;
public class BitmapBuilder {
public static Bitmap define(int bits) {
return new Bitmap(bits);
}
}
BitmapBuilderは、Builderとは名ばかりで、単にDSLとしてオブジェクトを返すだけのクラス。
それに対して、実際の処理を受け持つBitmapクラスの実装はたとえば次のようになる。
// Bitamp.java
package com.example.bitmapparser;
import java.util.*;
public class Bitmap {
public interface Validator {
boolean validate(int bit, char type, String value);
}
static final String CHAR_TYPE = "AN";
static class Field {
int bit;
String name;
int length;
char type;
Validator validator;
Field(int b, String n, int len, char t) {
this(b, n, len, t, null);
}
Field(int b, String n, int len, char t, Validator v) {
if (CHAR_TYPE.indexOf(t) < 0) {
throw new IllegalArgumentException("'" + type + "' is not acceptable");
}
bit = b;
name = n;
length = len;
type = t;
validator = v;
}
int parse(byte[] data, int offset, HashMap<Integer, String> map) {
store(new String(data, offset, length), map);
return offset + length;
}
void store(String value, HashMap<Integer, String> map) {
if (validator != null && !validator.validate(bit, type, value)) {
throw new IllegalArgumentException("bit:" + bit + "(" + name
+ ") \"" + value + "\"");
}
map.put(bit, value);
}
}
static class VariableLengthField extends Field {
int vlength;
VariableLengthField(int b, String n, int maxlen, char t, int vlen) {
this(b, n, maxlen, t, vlen, null);
}
VariableLengthField(int b, String n, int maxlen, char t, int vlen, Validator v) {
super(b, n, maxlen, t, v);
vlength = vlen;
}
int parse(byte[] data, int offset, HashMap<Integer, String> map) {
int len = Integer.parseInt(new String(data, offset, vlength));
if (len > length) {
throw new IllegalArgumentException("bit:" + bit + "(" + name
+ ") " + length + " but " + len);
}
offset += vlength;
store(new String(data, offset, len), map);
return offset + len;
}
}
HashMap<Integer, String> data;
Field[] fields;
int bits;
Bitmap(int initBits) {
bits = initBits;
fields = new Field[initBits * 8];
}
public Bitmap FIX(int bit, String name, int len, char type, Validator v) {
fields[bit - 1] = new Field(bit, name, len, type, v);
return this;
}
public Bitmap L(int bit, String name, int len, char type, Validator v) {
fields[bit - 1] = new VariableLengthField(bit, name, len, type, 1, v);
return this;
}
public Bitmap LL(int bit, String name, int len, char type, Validator v) {
fields[bit - 1] = new VariableLengthField(bit, name, len, type, 2, v);
return this;
}
public Bitmap FIX(int bit, String name, int len, char type) {
return FIX(bit, name, len, type, null);
}
public Bitmap L(int bit, String name, int len, char type) {
return L(bit, name, len, type, null);
}
public Bitmap LL(int bit, String name, int len, char type) {
return LL(bit, name, len, type, null);
}
public String bit(int bit) {
assert data != null;
return data.get(bit);
}
public void parse(byte[] blk) {
if (blk.length < bits) {
throw new IllegalArgumentException("length must be greater than " + bits
+ ", but " + blk.length);
}
data = new HashMap<Integer, String>();
int offset = bits;
for (int i = 0; i < bits; i++) {
for (int j = 0; j < 8; j++) {
int bit = i * 8 + j;
if ((blk[i] >>> 7 - j & 1) == 1 && fields[bit] != null) {
offset = fields[bit].parse(blk, offset, data);
}
}
}
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < fields.length; i++) {
if (fields[i] != null) {
sb.append(i + 1).append('(').append(fields[i].name).append(")=>\"")
.append((data == null) ? null : data.get(i + 1))
.append("\", ");
}
}
if (sb.length() > 0) {
sb.setLength(sb.length() - 2);
}
return sb.toString();
}
}
次のように呼び出す。
// BitmapTest.java
package com.example.bitmapparser;
import junit.framework.*;
public class BitmapTest extends TestCase {
Bitmap bmp;
protected void setUp() throws Exception {
bmp = BitmapBuilder
.define(4)
.LL(1, "id", 16, 'N')
.LL(2, "name", 64, 'A')
.FIX(3, "nationality", 3, 'A');
}
public void testParse() throws Exception {
bmp.parse("\u00e0\u0000\u0000\u000001114YAMADA KAKASHIJPN".getBytes("ISO-8859-1"));
assertEquals("1", bmp.bit(1));
assertEquals("YAMADA KAKASHI", bmp.bit(2));
assertEquals("JPN", bmp.bit(3));
}
public void testToString() throws Exception {
bmp.parse("\u00e0\u0000\u0000\u000001114YAMADA KAKASHIJPN".getBytes("ISO-8859-1"));
assertEquals("1(id)=>\"1\", 2(name)=>\"YAMADA KAKASHI\", 3(nationality)=>\"JPN\"", bmp.toString());
}
public void testParseWithoutField() throws Exception {
bmp.parse("\u00a0\u0000\u0000\u0000012CHN".getBytes("ISO-8859-1"));
assertEquals("2", bmp.bit(1));
assertEquals(null, bmp.bit(2));
assertEquals("CHN", bmp.bit(3));
}
public static void main(String[] args) {
junit.textui.TestRunner.run(new TestSuite(BitmapTest.class));
}
}
このタイプのデータフォーマットをDSLとして実装することによるメリットとは何だろうか? 続く。
ジェズイットを見習え |