トップ «前の日記(2009-09-30) 最新 次の日記(2009-10-02)» 編集

日々の破片

著作一覧

2009-10-01

_ データフォーマットとアプリケーションからの扱い方(続)

昨日のエントリーの続き。

たとえば、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として実装することによるメリットとは何だろうか? 続く。


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|12|

ジェズイットを見習え