著作一覧 |
昨日のエントリーの続き。
たとえば、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として実装することによるメリットとは何だろうか? 続く。
ジェズイットを見習え |