From: Olyutorskii Date: Tue, 24 May 2011 04:32:42 +0000 (+0900) Subject: ゼロチョップモード追加 X-Git-Tag: fromMercurial~70 X-Git-Url: http://git.osdn.net/view?p=mikutoga%2FTogaGem.git;a=commitdiff_plain;h=d50ab7b6950537c1b0c282bda4d18fa3b8c311fb ゼロチョップモード追加 --- diff --git a/src/main/java/jp/sourceforge/mikutoga/parser/TextDecoder.java b/src/main/java/jp/sourceforge/mikutoga/parser/TextDecoder.java index 08f3821..0b8994e 100644 --- a/src/main/java/jp/sourceforge/mikutoga/parser/TextDecoder.java +++ b/src/main/java/jp/sourceforge/mikutoga/parser/TextDecoder.java @@ -17,6 +17,14 @@ import java.nio.charset.CodingErrorAction; /** * 文字デコーダー。 + *

あらかじめ長さが既知であるバイト列をMMD入力ソースから読み取り、 + * デコーディング結果を返す。 + *

デコード対象のバイト列が全てメモリ上に展開されるので、 + * 巨大なテキストのデコードには不適当。 + *

入力バイト値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; } diff --git a/src/test/java/jp/sourceforge/mikutoga/parser/TextDecoderTest.java b/src/test/java/jp/sourceforge/mikutoga/parser/TextDecoderTest.java index 4ad63d2..da4709a 100644 --- a/src/test/java/jp/sourceforge/mikutoga/parser/TextDecoderTest.java +++ b/src/test/java/jp/sourceforge/mikutoga/parser/TextDecoderTest.java @@ -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