OSDN Git Service

ゼロチョップモード追加
authorOlyutorskii <olyutorskii@users.osdn.me>
Tue, 24 May 2011 04:32:42 +0000 (13:32 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Tue, 24 May 2011 04:32:42 +0000 (13:32 +0900)
src/main/java/jp/sourceforge/mikutoga/parser/TextDecoder.java
src/test/java/jp/sourceforge/mikutoga/parser/TextDecoderTest.java

index 08f3821..0b8994e 100644 (file)
@@ -17,6 +17,14 @@ import java.nio.charset.CodingErrorAction;
 
 /**
  * 文字デコーダー。
+ * <p>あらかじめ長さが既知であるバイト列をMMD入力ソースから読み取り、
+ * デコーディング結果を返す。
+ * <p>デコード対象のバイト列が全てメモリ上に展開されるので、
+ * 巨大なテキストのデコードには不適当。
+ * <p>入力バイト値0x00以降をデコーディングの対象から外す
+ * 「ゼロチョップモード」を備える。
+ * デフォルトではゼロチョップモードはオフ。
+ * ゼロチョップモードはUTF16などのデコーディング時に使っても意味が無い。
  */
 public class TextDecoder {
 
@@ -29,6 +37,8 @@ public class TextDecoder {
 
     private final CharsetDecoder decoder;
 
+    private boolean chopZero = false;
+
     private byte[] byteArray;
     private ByteBuffer byteBuffer;  // byteArrayの別ビュー
     private CharBuffer charBuffer;
@@ -48,7 +58,7 @@ public class TextDecoder {
      * コンストラクタ。
      * @param decoder デコーダ
      */
-    protected TextDecoder(CharsetDecoder decoder){
+    public TextDecoder(CharsetDecoder decoder){
         super();
         this.decoder = decoder;
         this.decoder.onMalformedInput(CodingErrorAction.REPORT);
@@ -82,10 +92,50 @@ public class TextDecoder {
     }
 
     /**
+     * ゼロチョップモードを設定する。
+     * ゼロチョップモードをオンにすると、
+     * 入力バイト値0x00以降はデコード対象外となる。
+     * @param chop trueならゼロチョップモードオン
+     */
+    public void setZeroChopMode(boolean chop){
+        this.chopZero = chop;
+        return;
+    }
+
+    /**
+     * ゼロチョップモードか否か判定する。
+     * @return ゼロチョップモードならtrue
+     */
+    public boolean isZeroChopMode(){
+        return this.chopZero;
+    }
+
+    /**
+     * 入力バイト列のバイト値'0'出現以降をチョップする。
+     * ゼロチョップモードでなければ何もしない。
+     */
+    protected void chopZeroTermed(){
+        if( ! this.chopZero ) return;
+
+        int limit = this.byteBuffer.limit();
+
+        for(int idx = 0; idx < limit; idx++){
+            byte bVal = this.byteArray[idx];
+            if(bVal == 0x00){
+                this.byteBuffer.limit(idx);
+                break;
+            }
+        }
+
+        return;
+    }
+
+    /**
      * バイト列を読み込み文字列へデコーディングする。
      * @param source 入力ソース
      * @param byteSize 読み込みバイトサイズ
-     * @return 文字へのデコード結果。
+     * @return 内部に保持されるデコード結果。
+     * 次回呼び出しまでに結果の適切なコピーがなされなければならない。
      * @throws MmdEofException 意図しないファイル末端
      * @throws MmdFormatException 矛盾したバイトシーケンス
      * もしくは未定義文字
@@ -97,6 +147,7 @@ public class TextDecoder {
 
         source.parseByteArray(this.byteArray, 0, byteSize);
         this.byteBuffer.rewind().limit(byteSize);
+        chopZeroTermed();
 
         this.charBuffer.clear();
 
@@ -104,8 +155,13 @@ public class TextDecoder {
         CoderResult decResult =
                 this.decoder.decode(this.byteBuffer, this.charBuffer, true);
         if(decResult.isError()){
-            throw new MmdFormatException("illegal character encoding",
-                                         source.getPosition() );
+            if(decResult.isUnmappable()){
+                throw new MmdFormatException("unmapped character",
+                                             source.getPosition() );
+            }else{
+                throw new MmdFormatException("illegal character encoding",
+                                             source.getPosition() );
+            }
         }else if(decResult.isOverflow()){
             assert false;
         }
index 4ad63d2..da4709a 100644 (file)
@@ -103,11 +103,32 @@ public class TextDecoderTest {
     }
 
     /**
+     * Test of setChopMode, getChopMode method, of class TextDecoder.
+     */
+    @Test
+    public void testChopMode() throws Exception {
+        System.out.println("chopMode");
+
+        TextDecoder decoder;
+
+        decoder = new TextDecoder(CS_WIN31J);
+        assertFalse(decoder.isZeroChopMode());
+
+        decoder.setZeroChopMode(true);
+        assertTrue(decoder.isZeroChopMode());
+
+        decoder.setZeroChopMode(false);
+        assertFalse(decoder.isZeroChopMode());
+
+        return;
+    }
+
+    /**
      * Test of parseString method, of class TextDecoder.
      */
     @Test
-    public void testParseString() throws Exception {
-        System.out.println("parseString");
+    public void testParseStringChop() throws Exception {
+        System.out.println("parseString(Chop)");
 
         TextDecoder decoder;
         byte[] bdata;
@@ -116,37 +137,135 @@ public class TextDecoderTest {
         CharBuffer cb;
 
         decoder = new TextDecoder(CS_WIN31J);
+        decoder.setZeroChopMode(true);
 
-        bdata = byteArray("41:42");
-        istream = new ByteArrayInputStream(bdata);
-        source = new MmdSource(istream);
-        cb =decoder.parseString(source, 2);
-        assertEquals("AB", cb.toString());
+        assertDecoded("41:42:00", "AB", decoder);
+        assertDecoded("41:00:42", "A", decoder);
+        assertDecoded("00:41:42", "", decoder);
+        assertDecoded("41:00:88", "A", decoder);
 
+        bdata = byteArray("41:00:42:43");
         istream = new ByteArrayInputStream(bdata);
         source = new MmdSource(istream);
-        cb =decoder.parseString(source, 1);
+        cb =decoder.parseString(source, 3);
         assertEquals("A", cb.toString());
+        cb =decoder.parseString(source, 1);
+        assertEquals("C", cb.toString());
 
-        bdata = byteArray("88:9F");
-        istream = new ByteArrayInputStream(bdata);
-        source = new MmdSource(istream);
-        cb =decoder.parseString(source, 2);
-        assertEquals("亜", cb.toString());
+        return;
+    }
+
+    /**
+     * Test of parseString method, of class TextDecoder.
+     */
+    @Test
+    public void testParseStringWin31J() throws Exception {
+        System.out.println("parseString(Win31J)");
+
+        TextDecoder decoder;
+
+        decoder = new TextDecoder(CS_WIN31J);
+
+        assertDecoded("41:42", "AB", decoder);
+        assertDecoded("41:42", "A", decoder, 1);
+        assertDecoded("88:9F", "亜", decoder);
+        assertDecoded("88:9F:88:A0", "亜唖", decoder);
+        assertDecoded("88:9F:41:88:A0", "亜A唖", decoder);
+        assertDecoded("00", "\u0000", decoder);
+
+        assertFormatError("88:9F:88:A0", decoder, 3);
+
+
+        byte[] bdata;
+        InputStream istream;
+        MmdSource source;
+        CharBuffer cb;
 
         bdata = byteArray("88:9F:88:A0");
         istream = new ByteArrayInputStream(bdata);
         source = new MmdSource(istream);
-        cb =decoder.parseString(source, 4);
-        assertEquals("亜唖", cb.toString());
+        try{
+            cb =decoder.parseString(source, 5);
+            fail();
+        }catch(MmdEofException e){
+            // OK
+        }
+
+        return;
+    }
+
+    /**
+     * Test of parseString method, of class TextDecoder.
+     */
+    @Test
+    public void testParseStringUTF8() throws Exception {
+        System.out.println("parseString(UTF8)");
+
+        TextDecoder decoder;
+
+        decoder = new TextDecoder(CS_UTF8);
 
-        bdata = byteArray("88:9F:41:88:A0");
+        assertDecoded("41:42", "AB", decoder);
+        assertDecoded("41:42", "A", decoder, 1);
+        assertDecoded("E4:BA:9C", "亜", decoder);
+        assertDecoded("E4:BA:9C:E5:94:96", "亜唖", decoder);
+        assertDecoded("E4:BA:9C:41:E5:94:96", "亜A唖", decoder);
+        assertDecoded("00", "\u0000", decoder);
+        assertDecoded("EF:BF:BF", "\uffff", decoder);
+
+        assertFormatError("E4:BA:9C:E5:94:96", decoder, 5);
+
+
+        byte[] bdata;
+        InputStream istream;
+        MmdSource source;
+        CharBuffer cb;
+
+        bdata = byteArray("E4:BA:9C:E5:94:96");
         istream = new ByteArrayInputStream(bdata);
         source = new MmdSource(istream);
-        cb =decoder.parseString(source, 5);
-        assertEquals("亜A唖", cb.toString());
+        try{
+            cb =decoder.parseString(source, 7);
+            fail();
+        }catch(MmdEofException e){
+            // OK
+        }
 
-        bdata = byteArray("88:9F:88:A0");
+        return;
+    }
+
+    /**
+     * Test of parseString method, of class TextDecoder.
+     */
+    @Test
+    public void testParseStringUTF16LE() throws Exception {
+        System.out.println("parseString(UTF16LE)");
+
+        TextDecoder decoder;
+
+        decoder = new TextDecoder(CS_UTF16LE);
+
+        assertDecoded("41:00:42:00", "AB", decoder);
+        assertDecoded("41:00:42:00", "A", decoder, 2);
+        assertDecoded("9C:4E", "亜", decoder);
+        assertDecoded("9C:4E:16:55", "亜唖", decoder);
+        assertDecoded("9C:4E:41:00:16:55", "亜A唖", decoder);
+        assertDecoded("00:00", "\u0000", decoder);
+        assertDecoded("FF:FF", "\uffff", decoder);
+
+        assertDecoded("60:08", "\u0860", decoder);
+
+        assertDecoded("FF:FE:9C:4E", "\ufeff亜", decoder);
+        // not BOM, ZERO WIDTH NO-BREAK SPACE
+
+        assertFormatError("9C:4E:16:55", decoder, 3);
+
+
+        byte[] bdata;
+        InputStream istream;
+        MmdSource source;
+        CharBuffer cb;
+        bdata = byteArray("9C:4E:16:55");
         istream = new ByteArrayInputStream(bdata);
         source = new MmdSource(istream);
         try{
@@ -156,11 +275,104 @@ public class TextDecoderTest {
             // OK
         }
 
-        bdata = byteArray("88:9F:88:A0");
+        return;
+    }
+
+    /**
+     * Test of Yen(U+00A5) & Backslash(U+005C) encoding, of class TextDecoder.
+     */
+    @Test
+    public void testYenAndBackslash() throws Exception {
+        System.out.println("Yen & Backslash");
+
+        TextDecoder decoder;
+
+        decoder = new TextDecoder(CS_WIN31J);
+        assertDecoded("5C", "\u005c\u005c", decoder);
+
+        decoder = new TextDecoder(CS_UTF8);
+        assertDecoded("5C", "\u005c\u005c", decoder);
+        assertDecoded("C2:A5", "\u00a5", decoder);
+
+        decoder = new TextDecoder(CS_UTF16LE);
+        assertDecoded("5C:00", "\u005c\u005c", decoder);
+        assertDecoded("A5:00", "\u00a5", decoder);
+
+        return;
+    }
+
+    /**
+     * Test of unmapped char, of class TextDecoder.
+     */
+    @Test
+    public void testUnmapChar() throws Exception {
+        System.out.println("unmap char");
+
+        TextDecoder decoder;
+
+        decoder = new TextDecoder(CS_WIN31J);
+        assertFormatError("FF:FF", decoder, 2);
+
+
+        // Unicode2.0の時点でU+0860は未定義文字
+
+        decoder = new TextDecoder(CS_UTF8);
+        assertFormatError("FF:FF:FF", decoder, 3);
+        assertDecoded("E0:A1:A0", "\u0860", decoder);
+
+        decoder = new TextDecoder(CS_UTF16LE);
+        assertDecoded("60:08", "\u0860", decoder);
+
+        return;
+    }
+
+    public void assertDecoded(String bin, String desired,
+                                TextDecoder decoder)
+            throws Exception{
+        byte[] bdata = byteArray(bin);
+        assertDecoded(bin, desired, decoder, bdata.length);
+        return;
+    }
+
+    public void assertDecoded(String bin, String desired,
+                                TextDecoder decoder, int len)
+            throws Exception{
+        byte[] bdata;
+        InputStream istream;
+        MmdSource source;
+        CharBuffer cb;
+
+        bdata = byteArray(bin);
+        istream = new ByteArrayInputStream(bdata);
+        source = new MmdSource(istream);
+
+        assertDecoded(source, desired, decoder, len);
+
+        return;
+    }
+
+    public void assertDecoded(MmdSource source, String desired,
+                                TextDecoder decoder, int len)
+            throws Exception{
+        CharBuffer cb;
+        cb =decoder.parseString(source, len);
+        assertEquals(desired, cb.toString());
+        return;
+    }
+
+    public void assertFormatError(String bin,
+                                    TextDecoder decoder, int len)
+            throws Exception{
+        byte[] bdata;
+        InputStream istream;
+        MmdSource source;
+
+        bdata = byteArray(bin);
         istream = new ByteArrayInputStream(bdata);
         source = new MmdSource(istream);
+
         try{
-            cb =decoder.parseString(source, 3);
+            decoder.parseString(source, len);
             fail();
         }catch(MmdFormatException e){
             // OK