From: Olyutorskii Date: Mon, 31 Dec 2012 16:48:22 +0000 (+0900) Subject: XML出力改善 X-Git-Tag: fromMercurial~33 X-Git-Url: http://git.osdn.net/view?p=mikutoga%2FTogaGem.git;a=commitdiff_plain;h=ee0c02cdf4bdec2209c356bec07bd873d5f093f2;ds=sidebyside XML出力改善 --- diff --git a/src/main/java/jp/sourceforge/mikutoga/parser/CommonParser.java b/src/main/java/jp/sourceforge/mikutoga/parser/CommonParser.java index 0c9dcce..8ee1a7c 100644 --- a/src/main/java/jp/sourceforge/mikutoga/parser/CommonParser.java +++ b/src/main/java/jp/sourceforge/mikutoga/parser/CommonParser.java @@ -7,68 +7,69 @@ package jp.sourceforge.mikutoga.parser; +import java.io.EOFException; import java.io.IOException; -import java.nio.CharBuffer; -import java.nio.charset.Charset; +import java.io.InputStream; +import java.io.PushbackInputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; /** * 各種パーサの共通実装。 */ public class CommonParser { - /** - * PMDで用いられる文字エンコーディング(windows-31j)。 - * ほぼShift_JISのスーパーセットと思ってよい。 - * デコード結果はUCS-2集合に収まるはず。 - */ - public static final Charset CS_WIN31J = Charset.forName("windows-31j"); - - /** PMXで用いられる文字エンコーディング(UTF-8)。 */ - public static final Charset CS_UTF8 = Charset.forName("UTF-8"); - - /** PMXで用いられる文字エンコーディング(UTF-16のリトルエンディアン)。 */ - public static final Charset CS_UTF16LE = Charset.forName("UTF-16LE"); + private static final int BYTES_SHORT = Short .SIZE / Byte.SIZE; + private static final int BYTES_INT = Integer.SIZE / Byte.SIZE; + private static final int BYTES_FLOAT = Float .SIZE / Byte.SIZE; + private static final int BYTES_PRIM = 4; private static final int MASK_8BIT = 0xff; private static final int MASK_16BIT = 0xffff; + static{ + assert BYTES_PRIM >= BYTES_FLOAT; + assert BYTES_PRIM >= BYTES_INT; + assert BYTES_PRIM >= BYTES_SHORT; + } + + + private final PushbackInputStream is; + + private final byte[] readBuffer; + private final ByteBuffer beBuf; + private final ByteBuffer leBuf; - private final MmdInputStream is; + private long position = 0L; - private final TextDecoder decoderWin31j = new TextDecoder(CS_WIN31J); - private final TextDecoder decoderUTF8 = new TextDecoder(CS_UTF8); - private final TextDecoder decoderUTF16LE = new TextDecoder(CS_UTF16LE); /** * コンストラクタ。 * @param source 入力ソース */ - public CommonParser(MmdInputStream source){ + public CommonParser(InputStream source){ super(); - this.is = source; + this.is = new PushbackInputStream(source, 1); - this.decoderWin31j .setZeroChopMode(true); - this.decoderUTF8 .setZeroChopMode(false); - this.decoderUTF16LE.setZeroChopMode(false); + this.readBuffer = new byte[BYTES_PRIM]; + + this.beBuf = ByteBuffer.wrap(this.readBuffer); + this.leBuf = ByteBuffer.wrap(this.readBuffer); + + this.beBuf.order(ByteOrder.BIG_ENDIAN); + this.leBuf.order(ByteOrder.LITTLE_ENDIAN); return; } - /** - * 入力ソースを返す。 - * @return 入力ソース - */ - protected MmdInputStream getSource(){ - return this.is; - } /** * 入力ソースの読み込み位置を返す。 * @return 入力ソースの読み込み位置。単位はbyte。 */ protected long getPosition(){ - long result = this.is.getPosition(); + long result = this.position; return result; } @@ -76,11 +77,23 @@ public class CommonParser { * 入力ソースにまだデータが残っているか判定する。 * @return まだ読み込んでいないデータが残っていればtrue * @throws IOException IOエラー - * @see MmdInputStream#hasMore() */ - protected boolean hasMore() throws IOException{ - boolean result = this.is.hasMore(); - return result; + public boolean hasMore() throws IOException{ + int bVal; + + try{ + bVal = this.is.read(); + }catch(EOFException e){ // ありえない? + return false; + } + + if(bVal < 0){ + return false; + } + + this.is.unread(bVal); + + return true; } /** @@ -88,28 +101,81 @@ public class CommonParser { * @param skipLength 読み飛ばすバイト数。 * @throws IOException IOエラー * @throws MmdEofException 読み飛ばす途中でストリーム終端に達した。 - * @see MmdInputStream#skip(long) + * @see InputStream#skip(long) */ protected void skip(long skipLength) throws IOException, MmdEofException { - long result = this.is.skipRepeat(skipLength); - if(result != skipLength){ - throw new MmdEofException(this.is.getPosition()); + long remain = skipLength; + + while(remain > 0L){ + long txSize = this.is.skip(remain); + if(txSize <= 0L){ + throw new MmdEofException(this.position); + } + remain -= txSize; + this.position += txSize; } return; } /** - * 入力ソースを読み飛ばす。 - * @param skipLength 読み飛ばすバイト数。 + * byte配列を読み込む。 + * @param dst 格納先配列 + * @param off 読み込み開始オフセット + * @param length 読み込みバイト数 * @throws IOException IOエラー - * @throws MmdEofException 読み飛ばす途中でストリーム終端に達した。 - * @see MmdInputStream#skip(long) + * @throws NullPointerException 配列がnull + * @throws IndexOutOfBoundsException 引数が配列属性と矛盾 + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * @see InputStream#read(byte[], int, int) */ - protected void skip(int skipLength) + protected void parseByteArray(byte[] dst, int off, int length) + throws IOException, + NullPointerException, + IndexOutOfBoundsException, + MmdEofException { + int remain = length; + int offset = off; + + while(remain > 0){ + int txSize = this.is.read(dst, offset, remain); + if(txSize <= 0){ + throw new MmdEofException(this.position); + } + remain -= txSize; + offset += txSize; + this.position += txSize; + } + + return; + } + + /** + * byte配列を読み込む。 + * 配列要素全ての読み込みが試みられる。 + * @param dst 格納先配列 + * @throws IOException IOエラー + * @throws NullPointerException 配列がnull + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * @see InputStream#read(byte[]) + */ + protected void parseByteArray(byte[] dst) + throws IOException, NullPointerException, MmdEofException{ + parseByteArray(dst, 0, dst.length); + return; + } + + /** + * 内部バッファへ指定バイト数だけ読み込む。 + * @param fillSize + * @throws IOException + * @throws MmdEofException + */ + private void fillBuffer(int fillSize) throws IOException, MmdEofException { - skip((long) skipLength); + parseByteArray(this.readBuffer, 0, fillSize); + return; } /** @@ -121,7 +187,15 @@ public class CommonParser { */ protected byte parseByte() throws IOException, MmdEofException{ - return this.is.parseByte(); + int bData = this.is.read(); + if(bData < 0){ + throw new MmdEofException(this.position); + } + + byte result = (byte) bData; + this.position++; + + return result; } /** @@ -147,7 +221,9 @@ public class CommonParser { */ protected boolean parseBoolean() throws IOException, MmdEofException{ - return this.is.parseBoolean(); + byte result = parseByte(); + if(result == 0x00) return false; + return true; } /** @@ -160,7 +236,9 @@ public class CommonParser { */ protected short parseLeShort() throws IOException, MmdEofException{ - return this.is.parseLeShort(); + fillBuffer(BYTES_SHORT); + short result = this.leBuf.getShort(0); + return result; } /** @@ -187,7 +265,9 @@ public class CommonParser { */ protected int parseLeInt() throws IOException, MmdEofException{ - return this.is.parseLeInt(); + fillBuffer(BYTES_INT); + int result = this.leBuf.getInt(0); + return result; } /** @@ -200,114 +280,26 @@ public class CommonParser { */ protected float parseLeFloat() throws IOException, MmdEofException{ - return this.is.parseLeFloat(); - } - - /** - * byte配列を読み込む。 - * @param dst 格納先配列 - * @param offset 読み込み開始オフセット - * @param length 読み込みバイト数 - * @throws IOException IOエラー - * @throws NullPointerException 配列がnull - * @throws IndexOutOfBoundsException 引数が配列属性と矛盾 - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 - * @see MmdInputStream#parseByteArray(byte[], int, int) - */ - protected void parseByteArray(byte[] dst, int offset, int length) - throws IOException, - NullPointerException, - IndexOutOfBoundsException, - MmdEofException { - int readSize = this.is.read(dst, offset, length); - if(readSize != length){ - throw new MmdEofException(this.is.getPosition()); - } - - return; - } - - /** - * byte配列を読み込む。 - * 配列要素全ての読み込みが試みられる。 - * @param dst 格納先配列 - * @throws IOException IOエラー - * @throws NullPointerException 配列がnull - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 - * @see MmdInputStream#parseByteArray(byte[]) - */ - protected void parseByteArray(byte[] dst) - throws IOException, NullPointerException, MmdEofException{ - parseByteArray(dst, 0, dst.length); - return; - } - - /** - * 指定された最大バイト長に収まるゼロ終端(0x00)文字列を読み込む。 - * 入力バイト列はwindows-31jエンコーディングとして解釈される。 - * ゼロ終端以降のデータは無視されるが、 - * IO入力は指定バイト数だけ読み進められる。 - * ゼロ終端が見つからないまま指定バイト数が読み込み終わった場合、 - * そこまでのデータから文字列を構成する。 - * @param maxlen 読み込みバイト数 - * @return デコードされた文字列 - * @throws IOException IOエラー - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 - * @throws MmdFormatException 不正な文字エンコーディングが検出された。 - */ - protected String parseZeroTermWin31J(int maxlen) - throws IOException, - MmdEofException, - MmdFormatException { - CharBuffer encoded = - this.decoderWin31j.parseString(this.is, maxlen); - - String result = encoded.toString(); - + fillBuffer(BYTES_FLOAT); + float result = this.leBuf.getFloat(0); return result; } /** - * 4byte整数によるバイト列長とそれに続くUTF8バイト列を - * 文字にデコードする。 - * @return デコードされた文字列。 - * @throws IOException IOエラー - * @throws MmdEofException 予期せぬ入力終端 - * @throws MmdFormatException 不正な文字エンコーディングが検出された。 + * 固定バイト長の文字列を読み込む。 + * @param decoder 文字デコーダ + * @param byteLen 読み込む固定バイト長 + * @return 文字列 + * @throws IOException 入力エラー + * @throws MmdEofException 固定長バイト列を読む前に末端に達した。 + * @throws MmdFormatException 文字エンコーディングに関するエラー */ - protected String parseHollerithUtf8() - throws IOException, - MmdEofException, - MmdFormatException { - int byteLen = this.is.parseLeInt(); - - CharBuffer encoded = - this.decoderUTF8.parseString(this.is, byteLen); - - String result = encoded.toString(); - - return result; - } - - /** - * 4byte整数によるバイト列長とそれに続くUTF16-LEバイト列を - * 文字にデコードする。 - * @return デコードされた文字列。 - * @throws IOException IOエラー - * @throws MmdEofException 予期せぬ入力終端 - * @throws MmdFormatException 不正な文字エンコーディングが検出された。 - */ - protected String parseHollerithUtf16LE() - throws IOException, - MmdEofException, - MmdFormatException { - int byteLen = this.is.parseLeInt(); - - CharBuffer encoded = - this.decoderUTF16LE.parseString(this.is, byteLen); - - String result = encoded.toString(); - + protected String parseString(TextDecoder decoder, int byteLen) + throws IOException, MmdEofException, MmdFormatException { + byte[] buf = decoder.prepareBuffer(byteLen); + parseByteArray(buf, 0, byteLen); + long basePos = getPosition(); + String result= decoder.decode(basePos, byteLen); return result; } diff --git a/src/main/java/jp/sourceforge/mikutoga/parser/MmdInputStream.java b/src/main/java/jp/sourceforge/mikutoga/parser/MmdInputStream.java deleted file mode 100644 index 0082516..0000000 --- a/src/main/java/jp/sourceforge/mikutoga/parser/MmdInputStream.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * MMD file input stream - * - * License : The MIT License - * Copyright(c) 2012 MikuToga Partners - */ - -package jp.sourceforge.mikutoga.parser; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * MMD各種バイナリデータ用の入力ストリーム。 - * リトルエンディアンデータおよびEOFの扱い方で - * java.io.DataInputStreamと差別化される。 - */ -public class MmdInputStream extends SpottedInputStream { - - private static final int BYTES_SHORT = Short .SIZE / Byte.SIZE; - private static final int BYTES_INT = Integer.SIZE / Byte.SIZE; - private static final int BYTES_FLOAT = Float .SIZE / Byte.SIZE; - private static final int BUF_SZ = 4; - - - private final byte[] readArray; - private final ByteBuffer beBuf; - private final ByteBuffer leBuf; - - - /** - * コンストラクタ。 - * @param is 入力ストリーム - */ - public MmdInputStream(InputStream is){ - super(is); - - this.readArray = new byte[BUF_SZ]; - - this.beBuf = ByteBuffer.wrap(this.readArray); - this.leBuf = ByteBuffer.wrap(this.readArray); - - this.beBuf.order(ByteOrder.BIG_ENDIAN); - this.leBuf.order(ByteOrder.LITTLE_ENDIAN); - - return; - } - - - /** - * 入力ストリームを読み飛ばす。 - * なるべく指定したバイト数全てが読み飛ばされるよう、 - * 読み飛ばし処理が繰り返される。 - * @param skipLength 読み飛ばすバイト数。 - * @return 実際に読み飛ばしたバイト数。 - * @throws IOException IOエラー - * @see java.io.InputStream#skip(long) - */ - public long skipRepeat(long skipLength) - throws IOException{ - if(skipLength <= 0L) return 0L; - - long remain = skipLength; - while(remain > 0L){ // BufferedInputStream対策 - long result = skip(remain); - if(result <= 0L) break; - remain -= result; - } - - return skipLength - remain; - } - - /** - * 指定したバイト数だけ内部バッファに読み込む。 - * @param fillSize 読み込むバイト数 - * @throws IOException IOエラー - * @throws IndexOutOfBoundsException 引数がバッファサイズと矛盾。 - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 - */ - private void fillBuffer(int fillSize) - throws IOException, IndexOutOfBoundsException, MmdEofException{ - int result = read(this.readArray, 0, fillSize); - - if(result != fillSize){ - long pos = getPosition(); - throw new MmdEofException(pos); - } - - return; - } - - /** - * byte値を読み込む。 - * @return 読み込んだbyte値 - * @throws IOException IOエラー - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 - */ - public byte parseByte() throws IOException, MmdEofException{ - int bData = read(); - if(bData < 0){ - long pos = getPosition(); - throw new MmdEofException(pos); - } - - byte result = (byte) bData; - return result; - } - - /** - * byte値を読み込み、boolean型に変換して返す。 - * 0x00は偽、それ以外は真と解釈される。 - * @return 読み込まれた値のboolean値 - * @throws IOException IOエラー - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 - */ - public boolean parseBoolean() throws IOException, MmdEofException{ - byte result = parseByte(); - if(result == 0x00) return false; - return true; - } - - /** - * short値を読み込む。 - * @param buf オーダー指定されたバッファ - * @return short値 - * @throws IOException 入力エラー - * @throws MmdEofException 途中でストリーム終端に達した。 - */ - private short parseShort(ByteBuffer buf) - throws IOException, MmdEofException{ - fillBuffer(BYTES_SHORT); - short result = buf.getShort(0); - - return result; - } - - /** - * ビッグエンディアンでshort値を読み込む。 - * @return short値 - * @throws IOException 入力エラー - * @throws MmdEofException 途中でストリーム終端に達した。 - */ - public short parseBeShort() throws IOException, MmdEofException{ - short result = parseShort(this.beBuf); - return result; - } - - /** - * リトルエンディアンでshort値を読み込む。 - * @return short値 - * @throws IOException 入力エラー - * @throws MmdEofException 途中でストリーム終端に達した。 - */ - public short parseLeShort() throws IOException, MmdEofException{ - short result = parseShort(this.leBuf); - return result; - } - - /** - * int値を読み込む。 - * @param buf オーダー指定されたバッファ - * @return int値 - * @throws IOException 入力エラー - * @throws MmdEofException 途中でストリーム終端に達した。 - */ - private int parseInt(ByteBuffer buf) - throws IOException, MmdEofException{ - fillBuffer(BYTES_INT); - int result = buf.getInt(0); - - return result; - } - - /** - * ビッグエンディアンでint値を読み込む。 - * @return int値 - * @throws IOException 入力エラー - * @throws MmdEofException 途中でストリーム終端に達した。 - */ - public int parseBeInt() throws IOException, MmdEofException{ - int result = parseInt(this.beBuf); - return result; - } - - /** - * リトルエンディアンでint値を読み込む。 - * @return int値 - * @throws IOException 入力エラー - * @throws MmdEofException 途中でストリーム終端に達した。 - */ - public int parseLeInt() throws IOException, MmdEofException{ - int result = parseInt(this.leBuf); - return result; - } - - /** - * float値を読み込む。 - * @param buf オーダー指定されたバッファ - * @return float値 - * @throws IOException 入力エラー - * @throws MmdEofException 途中でストリーム終端に達した。 - */ - private float parseFloat(ByteBuffer buf) - throws IOException, MmdEofException{ - fillBuffer(BYTES_FLOAT); - float result = buf.getFloat(0); - return result; - } - - /** - * ビッグエンディアンでfloat値を読み込む。 - * @return float値 - * @throws IOException 入力エラー - * @throws MmdEofException 途中でストリーム終端に達した。 - */ - public float parseBeFloat() throws IOException, MmdEofException{ - float result = parseFloat(this.beBuf); - return result; - } - - /** - * リトルエンディアンでfloat値を読み込む。 - * @return float値 - * @throws IOException 入力エラー - * @throws MmdEofException 途中でストリーム終端に達した。 - */ - public float parseLeFloat() throws IOException, MmdEofException{ - float result = parseFloat(this.leBuf); - return result; - } - -} diff --git a/src/main/java/jp/sourceforge/mikutoga/parser/ParseStage.java b/src/main/java/jp/sourceforge/mikutoga/parser/ParseStage.java index ba433f7..ffa1e14 100644 --- a/src/main/java/jp/sourceforge/mikutoga/parser/ParseStage.java +++ b/src/main/java/jp/sourceforge/mikutoga/parser/ParseStage.java @@ -7,18 +7,34 @@ package jp.sourceforge.mikutoga.parser; +import java.util.concurrent.atomic.AtomicInteger; + /** * パース処理の進行ステージ種別を表す。 * ループ構造の識別に用いられる。 */ public class ParseStage { + private static final AtomicInteger lastNo = new AtomicInteger(0); + + private final int no; + /** * コンストラクタ。 */ public ParseStage(){ super(); + this.no = lastNo.getAndIncrement(); + assert this.no >= 0; return; } + /** + * 各インスタンスに割り当てられたユニークな通し番号を返す。 + * @return + */ + public int getNo(){ + return this.no; + } + } diff --git a/src/main/java/jp/sourceforge/mikutoga/parser/SpottedInputStream.java b/src/main/java/jp/sourceforge/mikutoga/parser/SpottedInputStream.java deleted file mode 100644 index 8933584..0000000 --- a/src/main/java/jp/sourceforge/mikutoga/parser/SpottedInputStream.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * position spotted input stream - * - * License : The MIT License - * Copyright(c) 2012 MikuToga Partners - */ - -package jp.sourceforge.mikutoga.parser; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.PushbackInputStream; - -/** - * エラー報告用のバイト位置管理、 - * およびストリーム末端の判定 - * のための機能を含む入力ストリーム。 - */ -class SpottedInputStream extends InputStream { - - private final PushbackInputStream pin; - private long position = 0L; - - - /** - * コンストラクタ。 - * @param is 入力ストリーム - */ - SpottedInputStream(InputStream is){ - super(); - this.pin = new PushbackInputStream(is, 1); - return; - } - - - /** - * {@inheritDoc} - * @return {@inheritDoc} - * @throws IOException {@inheritDoc} - */ - @Override - public int read() throws IOException{ - int result = this.pin.read(); - if(result >= 0) this.position++; - return result; - } - - /** - * {@inheritDoc} - * @param b {@inheritDoc} - * @return {@inheritDoc} - * @throws IOException {@inheritDoc} - */ - @Override - public int read(byte[] b) throws IOException{ - int result = this.pin.read(b); - if(result >= 0) this.position += result; - return result; - } - - /** - * {@inheritDoc} - * @param b {@inheritDoc} - * @param off {@inheritDoc} - * @param len {@inheritDoc} - * @return {@inheritDoc} - * @throws IOException {@inheritDoc} - */ - @Override - public int read(byte[] b, int off, int len) throws IOException{ - int result = this.pin.read(b, off, len); - if(result >= 0) this.position += result; - return result; - } - - /** - * {@inheritDoc} - * @param skipLength {@inheritDoc} - * @return {@inheritDoc} - * @throws IOException {@inheritDoc} - */ - @Override - public long skip(long skipLength) throws IOException{ - long result = this.pin.skip(skipLength); - if(result >= 0L) this.position += result; - return result; - } - - /** - * {@inheritDoc} - * @throws IOException {@inheritDoc} - */ - @Override - public void close() throws IOException{ - this.pin.close(); - return; - } - - /** - * 読み込み済みバイト数を返す。 - * @return 読み込み済みバイト数 - */ - public long getPosition(){ - return this.position; - } - - /** - * まだ入力が残っているか判定する。 - * @return 残っていればtrue - * @throws IOException 入力エラー。java.io.EOFException ではないはず。 - */ - public boolean hasMore() throws IOException{ - int bVal; - - try{ - bVal = this.pin.read(); - }catch(EOFException e){ // ありえない? - return false; - } - - if(bVal < 0){ - return false; - } - - this.pin.unread(bVal); - - return true; - } - -} diff --git a/src/main/java/jp/sourceforge/mikutoga/parser/TextDecoder.java b/src/main/java/jp/sourceforge/mikutoga/parser/TextDecoder.java index 1a8044a..1b3419b 100644 --- a/src/main/java/jp/sourceforge/mikutoga/parser/TextDecoder.java +++ b/src/main/java/jp/sourceforge/mikutoga/parser/TextDecoder.java @@ -7,7 +7,6 @@ package jp.sourceforge.mikutoga.parser; -import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; @@ -17,32 +16,28 @@ import java.nio.charset.CodingErrorAction; /** * 文字デコーダー。 - *

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

あらかじめ長さが既知であるバイト列を読み取り、 + * 文字列へのデコード結果を返す。 *

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

入力バイト値0x00以降をデコーディングの対象から外す + *

入力バイト値0x00以降をデコード処理の対象から外す * 「ゼロチョップモード」を備える。 * デフォルトではゼロチョップモードはオフ。 - * ゼロチョップモードはUTF16などのデコーディング時に使っても意味が無い。 + * ゼロチョップモードはUTF16などのデコーディング時に使ってはならない。 */ public class TextDecoder { /** デコード作業用入力バッファ長のデフォルト。バイト単位。 */ - public static final int BYTEBUF_SZ = 512; - - /** バッファ成長率。 */ - private static final double WIDEN_RATE = 1.5; + public static final int DEF_BUFSZ = 512; private final CharsetDecoder decoder; - private boolean chopZero = false; - private byte[] byteArray; private ByteBuffer byteBuffer; // byteArrayの別ビュー private CharBuffer charBuffer; - private CharBuffer roBuffer; // charBufferの閲覧用ビュー + + private boolean chopZero = false; /** @@ -60,36 +55,40 @@ public class TextDecoder { */ public TextDecoder(CharsetDecoder decoder){ super(); + + if(decoder == null) throw new NullPointerException(); + this.decoder = decoder; - this.decoder.onMalformedInput(CodingErrorAction.REPORT); + this.decoder.onMalformedInput (CodingErrorAction.REPORT); this.decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + return; } /** - * 指定されたサイズで文字デコード用バッファを用意する。 + * 指定されたバイト長を満たす、デコード用入力バッファを用意する。 * 既存バッファで足りなければ新たに確保し直す。 - * @param newSize バッファ長さ。単位はバイト数。 + *

内部用出力用バッファも同時に適切な長さで確保される。 + * @param newSize 新たなバッファ長。単位はバイト数。 + * @return 入力バッファ。指定バイト長より長いかもしれない。他用厳禁 */ - protected void prepareBuffer(int newSize){ + public byte[] prepareBuffer(int newSize){ if(this.byteArray != null && this.byteArray.length >= newSize){ - return; + return this.byteArray; } - int rounded = (int)( newSize * WIDEN_RATE ); - if(rounded < BYTEBUF_SZ) rounded = BYTEBUF_SZ; + int rounded = newSize; + if(rounded < DEF_BUFSZ) rounded = DEF_BUFSZ; this.byteArray = new byte[rounded]; this.byteBuffer = ByteBuffer.wrap(this.byteArray); float maxCharsPerByte = this.decoder.maxCharsPerByte(); int maxChars = - (int)( this.byteBuffer.capacity() * maxCharsPerByte ) + 1; + (int)( this.byteArray.length * maxCharsPerByte ) + 1; this.charBuffer = CharBuffer.allocate(maxChars); - this.roBuffer = this.charBuffer.asReadOnlyBuffer(); - - return; + return this.byteArray; } /** @@ -132,26 +131,51 @@ public class TextDecoder { } /** - * バイト列を読み込み文字列へデコーディングする。 - * @param is 入力ストリーム - * @param byteSize 読み込みバイトサイズ - * @return 内部に保持されるデコード結果。 - * 次回呼び出しまでに結果の適切なコピーがなされなければならない。 - * @throws MmdEofException 意図しないファイル末端 - * @throws MmdFormatException 矛盾したバイトシーケンス - * もしくは未定義文字 - * @throws IOException 入力エラー + * 指定配列を内部にコピーした後、デコード処理を行う。 + * @param basePos エラー情報に含まれるストリーム位置 + * @param buf 入力バッファ + * @return デコードされた文字列 + * @throws MmdFormatException デコード異常 */ - public CharBuffer parseString(MmdInputStream is, int byteSize) - throws MmdEofException, MmdFormatException, IOException{ - prepareBuffer(byteSize); + public String decode(long basePos, byte[] buf) + throws MmdFormatException { + String result = decode(basePos, buf, 0, buf.length); + return result; + } - int readSize = is.read(this.byteArray, 0, byteSize); - if(readSize != byteSize){ - throw new MmdEofException(is.getPosition()); + /** + * 指定配列の一部を内部にコピーした後、デコード処理を行う。 + * @param basePos エラー情報に含まれるストリーム位置 + * @param buf 入力バッファ + * @param off 位置オフセット + * @param byteLen バイト長 + * @return デコードされた文字列 + * @throws MmdFormatException デコード異常 + * @throws IndexOutOfBoundsException 不正な位置指定。 + */ + public String decode(long basePos, byte[] buf, int off, int byteLen) + throws MmdFormatException, IndexOutOfBoundsException { + prepareBuffer(byteLen); + System.arraycopy(buf, off, this.byteArray, 0, byteLen); + String result = decode(basePos, byteLen); + return result; + } + + /** + * 内部バッファのデコード処理を行う。 + * @param basePos エラー情報に含まれるストリーム位置 + * @param byteLen バイト長 + * @return デコードされた文字列 + * @throws MmdFormatException デコード異常 + * @throws IndexOutOfBoundsException 不正なバイト長。 + */ + public String decode(long basePos, int byteLen) + throws MmdFormatException, IndexOutOfBoundsException { + if(this.byteArray.length < byteLen){ + throw new IndexOutOfBoundsException(); } - this.byteBuffer.rewind().limit(byteSize); + this.byteBuffer.rewind().limit(byteLen); chopZeroTermed(); this.charBuffer.clear(); @@ -159,21 +183,22 @@ public class TextDecoder { this.decoder.reset(); CoderResult decResult = this.decoder.decode(this.byteBuffer, this.charBuffer, true); + if(decResult.isError()){ + String errMsg; if(decResult.isUnmappable()){ - throw new MmdFormatException("unmapped character", - is.getPosition() ); + errMsg = "unmapped character"; }else{ - throw new MmdFormatException("illegal character encoding", - is.getPosition() ); + errMsg = "illegal character encoding"; } - }else if(decResult.isOverflow()){ - assert false; + long errPos = basePos + decResult.length(); + throw new MmdFormatException(errMsg, errPos); } - this.roBuffer.rewind().limit(this.charBuffer.position()); + assert ! decResult.isOverflow(); - return this.roBuffer; + String result = this.charBuffer.flip().toString(); + return result; } } diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParser.java b/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParser.java index d67fa99..68f9ec0 100644 --- a/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParser.java +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParser.java @@ -7,7 +7,7 @@ package jp.sourceforge.mikutoga.pmd.parser; -import jp.sourceforge.mikutoga.parser.MmdInputStream; +import java.io.InputStream; /** * PMDモデルファイルのパーサ最新版。 @@ -21,7 +21,7 @@ public class PmdParser extends PmdParserExt3{ * コンストラクタ。 * @param source 入力ソース */ - public PmdParser(MmdInputStream source){ + public PmdParser(InputStream source){ super(source); return; } diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserBase.java b/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserBase.java index 54f69ac..e92f0fa 100644 --- a/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserBase.java +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserBase.java @@ -8,15 +8,26 @@ package jp.sourceforge.mikutoga.pmd.parser; import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Arrays; import jp.sourceforge.mikutoga.parser.CommonParser; +import jp.sourceforge.mikutoga.parser.MmdEofException; import jp.sourceforge.mikutoga.parser.MmdFormatException; -import jp.sourceforge.mikutoga.parser.MmdInputStream; +import jp.sourceforge.mikutoga.parser.TextDecoder; /** * PMDモデルファイルのパーサ基本部。 */ public class PmdParserBase extends CommonParser { + /** + * PMDで用いられる文字エンコーディング(windows-31j)。 + * ほぼShift_JISのスーパーセットと思ってよい。 + * デコード結果はUCS-2集合に収まるはず。 + */ + public static final Charset CS_WIN31J = Charset.forName("windows-31j"); + /** 改行文字列 CR。 */ protected static final String CR = "\r"; // 0x0d /** 改行文字列 LF。 */ @@ -46,6 +57,8 @@ public class PmdParserBase extends CommonParser { } + private final TextDecoder decoderWin31j = new TextDecoder(CS_WIN31J); + private PmdBasicHandler basicHandler = null; private PmdShapeHandler shapeHandler = null; private PmdMaterialHandler materialHandler = null; @@ -61,8 +74,9 @@ public class PmdParserBase extends CommonParser { * コンストラクタ。 * @param source 入力ソース */ - public PmdParserBase(MmdInputStream source){ + public PmdParserBase(InputStream source){ super(source); + this.decoderWin31j.setZeroChopMode(true); return; } @@ -183,6 +197,27 @@ public class PmdParserBase extends CommonParser { } /** + * 指定されたバイト長に収まるゼロ終端(0x00)文字列を読み込む。 + * 入力バイト列はwindows-31jエンコーディングとして解釈される。 + * ゼロ終端以降のデータは無視されるが、 + * IO入力は指定バイト数だけ読み進められる。 + * ゼロ終端が見つからないまま指定バイト数が読み込み終わった場合、 + * そこまでのデータから文字列を構成する。 + * @param byteLen 読み込みバイト数 + * @return デコードされた文字列 + * @throws IOException IOエラー + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * @throws MmdFormatException 不正な文字エンコーディングが検出された。 + */ + protected String parsePmdText(int byteLen) + throws IOException, + MmdEofException, + MmdFormatException { + String result = parseString(this.decoderWin31j, byteLen); + return result; + } + + /** * PMDファイルのパースを開始する。 * @throws IOException IOエラー * @throws MmdFormatException フォーマットエラー @@ -234,16 +269,14 @@ public class PmdParserBase extends CommonParser { byte[] header = new byte[HEADER_LENGTH]; parseByteArray(header); - for(int idx = 0; idx < MAGIC_BYTES.length; idx++){ - if(header[idx] != MAGIC_BYTES[idx]){ - throw new MmdFormatException("unknown PMD-header type"); - } + if( ! Arrays.equals(header, MAGIC_BYTES) ){ + throw new MmdFormatException("unknown PMD-header type"); } String modelName = - parseZeroTermWin31J(PmdLimits.MAXBYTES_MODELNAME); + parsePmdText(PmdLimits.MAXBYTES_MODELNAME); String description = - parseZeroTermWin31J(PmdLimits.MAXBYTES_MODELDESC); + parsePmdText(PmdLimits.MAXBYTES_MODELDESC); description = description.replace(CRLF, LF); if(this.basicHandler != null){ @@ -375,7 +408,7 @@ public class PmdParserBase extends CommonParser { boolean hasEdge = parseBoolean(); int surfaceCount = parseLeInt(); String shadingFile = - parseZeroTermWin31J(PmdLimits.MAXBYTES_TEXTUREFILENAME); + parsePmdText(PmdLimits.MAXBYTES_TEXTUREFILENAME); String[] splitted = splitShadingFileInfo(shadingFile); String textureFile = splitted[0]; String sphereFile = splitted[1]; @@ -409,7 +442,7 @@ public class PmdParserBase extends CommonParser { for(int ct = 0; ct < this.boneCount; ct++){ String boneName = - parseZeroTermWin31J(PmdLimits.MAXBYTES_BONENAME); + parsePmdText(PmdLimits.MAXBYTES_BONENAME); int parentId = parseLeUShortAsInt(); int tailId = parseLeUShortAsInt(); byte boneKind = parseByte(); @@ -509,7 +542,7 @@ public class PmdParserBase extends CommonParser { for(int ct = 0; ct < this.morphCount; ct++){ String morphName = - parseZeroTermWin31J(PmdLimits.MAXBYTES_MORPHNAME); + parsePmdText(PmdLimits.MAXBYTES_MORPHNAME); int vertexCount = parseLeInt(); byte morphType = parseByte(); @@ -609,7 +642,7 @@ public class PmdParserBase extends CommonParser { for(int ct = 0; ct < this.boneGroupCount; ct++){ String groupName = - parseZeroTermWin31J(PmdLimits.MAXBYTES_BONEGROUPNAME); + parsePmdText(PmdLimits.MAXBYTES_BONEGROUPNAME); groupName = chopLastLF(groupName); this.boneHandler.pmdBoneGroupInfo(groupName); diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt1.java b/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt1.java index ce329ec..a72e97d 100644 --- a/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt1.java +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt1.java @@ -8,8 +8,8 @@ package jp.sourceforge.mikutoga.pmd.parser; import java.io.IOException; +import java.io.InputStream; import jp.sourceforge.mikutoga.parser.MmdFormatException; -import jp.sourceforge.mikutoga.parser.MmdInputStream; /** * PMDモデルファイルのパーサ拡張その1。 @@ -24,7 +24,7 @@ public class PmdParserExt1 extends PmdParserBase { * コンストラクタ。 * @param source 入力ソース */ - public PmdParserExt1(MmdInputStream source){ + public PmdParserExt1(InputStream source){ super(source); return; } @@ -75,9 +75,9 @@ public class PmdParserExt1 extends PmdParserBase { if( ! this.hasEnglishInfo ) return; String modelName = - parseZeroTermWin31J(PmdLimits.MAXBYTES_MODELNAME); + parsePmdText(PmdLimits.MAXBYTES_MODELNAME); String description = - parseZeroTermWin31J(PmdLimits.MAXBYTES_MODELDESC); + parsePmdText(PmdLimits.MAXBYTES_MODELDESC); description = description.replace(CRLF, LF); if(this.engHandler != null){ @@ -105,7 +105,7 @@ public class PmdParserExt1 extends PmdParserBase { for(int ct = 0; ct < boneNum; ct++){ String boneName = - parseZeroTermWin31J(PmdLimits.MAXBYTES_BONENAME); + parsePmdText(PmdLimits.MAXBYTES_BONENAME); this.engHandler.pmdEngBoneInfo(boneName); this.engHandler.loopNext(PmdEngHandler.ENGBONE_LIST); @@ -134,7 +134,7 @@ public class PmdParserExt1 extends PmdParserBase { for(int ct = 0; ct < morphNum; ct++){ String morphName = - parseZeroTermWin31J(PmdLimits.MAXBYTES_MORPHNAME); + parsePmdText(PmdLimits.MAXBYTES_MORPHNAME); this.engHandler.pmdEngMorphInfo(morphName); this.engHandler.loopNext(PmdEngHandler.ENGMORPH_LIST); @@ -163,7 +163,7 @@ public class PmdParserExt1 extends PmdParserBase { for(int ct = 0; ct < groupNum; ct++){ String boneGroupName = - parseZeroTermWin31J(PmdLimits.MAXBYTES_BONEGROUPNAME); + parsePmdText(PmdLimits.MAXBYTES_BONEGROUPNAME); this.engHandler.pmdEngBoneGroupInfo(boneGroupName); this.engHandler.loopNext(PmdEngHandler.ENGBONEGROUP_LIST); diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt2.java b/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt2.java index be15890..6824a85 100644 --- a/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt2.java +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt2.java @@ -8,8 +8,8 @@ package jp.sourceforge.mikutoga.pmd.parser; import java.io.IOException; +import java.io.InputStream; import jp.sourceforge.mikutoga.parser.MmdFormatException; -import jp.sourceforge.mikutoga.parser.MmdInputStream; /** * PMDモデルファイルのパーサ拡張その2。 @@ -23,7 +23,7 @@ public class PmdParserExt2 extends PmdParserExt1 { * コンストラクタ。 * @param source 入力ソース */ - public PmdParserExt2(MmdInputStream source){ + public PmdParserExt2(InputStream source){ super(source); return; } @@ -70,7 +70,7 @@ public class PmdParserExt2 extends PmdParserExt1 { for(int ct = 0; ct < PmdLimits.TOON_FIXEDNUM; ct++){ String toonName = - parseZeroTermWin31J(PmdLimits.MAXBYTES_TOONFILENAME); + parsePmdText(PmdLimits.MAXBYTES_TOONFILENAME); this.toonHandler.pmdToonFileInfo(toonName); this.toonHandler.loopNext(PmdToonHandler.TOON_LIST); diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt3.java b/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt3.java index 2fa93c0..a5483d7 100644 --- a/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt3.java +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/parser/PmdParserExt3.java @@ -8,8 +8,8 @@ package jp.sourceforge.mikutoga.pmd.parser; import java.io.IOException; +import java.io.InputStream; import jp.sourceforge.mikutoga.parser.MmdFormatException; -import jp.sourceforge.mikutoga.parser.MmdInputStream; /** * PMDモデルファイルのパーサ拡張その3。 @@ -27,7 +27,7 @@ public class PmdParserExt3 extends PmdParserExt2 { * コンストラクタ。 * @param source 入力ソース */ - public PmdParserExt3(MmdInputStream source){ + public PmdParserExt3(InputStream source){ super(source); return; } @@ -85,7 +85,7 @@ public class PmdParserExt3 extends PmdParserExt2 { for(int ct = 0; ct < rigidNum; ct++){ String rigidName = - parseZeroTermWin31J(PmdLimits.MAXBYTES_RIGIDNAME); + parsePmdText(PmdLimits.MAXBYTES_RIGIDNAME); this.rigidHandler.pmdRigidName(rigidName); int linkedBoneId = parseLeUShortAsInt(); @@ -146,7 +146,7 @@ public class PmdParserExt3 extends PmdParserExt2 { for(int ct = 0; ct < jointNum; ct++){ String jointName = - parseZeroTermWin31J(PmdLimits.MAXBYTES_JOINTNAME); + parsePmdText(PmdLimits.MAXBYTES_JOINTNAME); this.jointHandler.pmdJointName(jointName); int rigidIdA = parseLeInt(); diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdBasicParser.java b/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdBasicParser.java index b15f211..5d8a983 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdBasicParser.java +++ b/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdBasicParser.java @@ -8,9 +8,12 @@ package jp.sourceforge.mikutoga.vmd.parser; import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; import jp.sourceforge.mikutoga.parser.CommonParser; +import jp.sourceforge.mikutoga.parser.MmdEofException; import jp.sourceforge.mikutoga.parser.MmdFormatException; -import jp.sourceforge.mikutoga.parser.MmdInputStream; +import jp.sourceforge.mikutoga.parser.TextDecoder; import jp.sourceforge.mikutoga.vmd.VmdConst; /** @@ -19,6 +22,13 @@ import jp.sourceforge.mikutoga.vmd.VmdConst; */ class VmdBasicParser extends CommonParser{ + /** + * VMDで用いられる文字エンコーディング(windows-31j)。 + * ほぼShift_JISのスーパーセットと思ってよい。 + * デコード結果はUCS-2集合に収まるはず。 + */ + public static final Charset CS_WIN31J = Charset.forName("windows-31j"); + private static final int BZ_SIZE = 4; // 4byte Bezier parameter private static final int BZXYZR_SIZE = BZ_SIZE * 4; // XYZR Bezier private static final int BZ_REDUNDANT = 4; // redundant spare @@ -29,6 +39,8 @@ class VmdBasicParser extends CommonParser{ +"(Strict-mode)"; + private final TextDecoder decoderWin31j = new TextDecoder(CS_WIN31J); + private final byte[] motionIntplt = new byte[BZTOTAL_SIZE]; private VmdBasicHandler handler = null; @@ -41,8 +53,9 @@ class VmdBasicParser extends CommonParser{ * コンストラクタ。 * @param source 入力ソース */ - VmdBasicParser(MmdInputStream source){ + VmdBasicParser(InputStream source){ super(source); + this.decoderWin31j.setZeroChopMode(true); return; } @@ -77,6 +90,27 @@ class VmdBasicParser extends CommonParser{ } /** + * 指定された最大バイト長に収まるゼロ終端(0x00)文字列を読み込む。 + * 入力バイト列はwindows-31jエンコーディングとして解釈される。 + * ゼロ終端以降のデータは無視されるが、 + * IO入力は指定バイト数だけ読み進められる。 + * ゼロ終端が見つからないまま指定バイト数が読み込み終わった場合、 + * そこまでのデータから文字列を構成する。 + * @param byteLen 読み込みバイト数 + * @return デコードされた文字列 + * @throws IOException IOエラー + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * @throws MmdFormatException 不正な文字エンコーディングが検出された。 + */ + protected String parseVmdText(int byteLen) + throws IOException, + MmdEofException, + MmdFormatException { + String result = parseString(this.decoderWin31j, byteLen); + return result; + } + + /** * VMDファイル基本部のパースを開始する。 * @throws IOException IOエラー * @throws MmdFormatException フォーマットエラー @@ -118,7 +152,7 @@ class VmdBasicParser extends CommonParser{ * @throws MmdFormatException フォーマットエラー */ private void parseVmdModelName() throws IOException, MmdFormatException{ - String modelName = parseZeroTermWin31J(VmdConst.MODELNAME_MAX); + String modelName = parseVmdText(VmdConst.MODELNAME_MAX); if(VmdConst.isStageActName(modelName)){ this.hasStageActName = true; @@ -149,7 +183,7 @@ class VmdBasicParser extends CommonParser{ VmdBasicHandler.BONEMOTION_LIST, boneMotionNo); for(int ct = 0; ct < boneMotionNo; ct++){ - String boneName = parseZeroTermWin31J(VmdConst.BONENAME_MAX); + String boneName = parseVmdText(VmdConst.BONENAME_MAX); int keyFrameNo = parseLeInt(); this.handler.vmdBoneMotion(boneName, keyFrameNo); @@ -281,7 +315,7 @@ class VmdBasicParser extends CommonParser{ VmdBasicHandler.MORPH_LIST, morphMotionNo); for(int ct = 0; ct < morphMotionNo; ct++){ - String morphName = parseZeroTermWin31J(VmdConst.MORPHNAME_MAX); + String morphName = parseVmdText(VmdConst.MORPHNAME_MAX); int keyFrameNo = parseLeInt(); float flex = parseLeFloat(); this.handler.vmdMorphMotion(morphName, keyFrameNo, flex); diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdCameraParser.java b/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdCameraParser.java index 8e1634a..bf63851 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdCameraParser.java +++ b/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdCameraParser.java @@ -8,9 +8,9 @@ package jp.sourceforge.mikutoga.vmd.parser; import java.io.IOException; +import java.io.InputStream; import jp.sourceforge.mikutoga.parser.CommonParser; import jp.sourceforge.mikutoga.parser.MmdFormatException; -import jp.sourceforge.mikutoga.parser.MmdInputStream; import jp.sourceforge.mikutoga.vmd.VmdConst; /** @@ -33,7 +33,7 @@ class VmdCameraParser extends CommonParser{ * コンストラクタ。 * @param source 入力ソース */ - VmdCameraParser(MmdInputStream source){ + VmdCameraParser(InputStream source){ super(source); return; } diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdLightingParser.java b/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdLightingParser.java index 9115c87..000f0f8 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdLightingParser.java +++ b/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdLightingParser.java @@ -8,9 +8,9 @@ package jp.sourceforge.mikutoga.vmd.parser; import java.io.IOException; +import java.io.InputStream; import jp.sourceforge.mikutoga.parser.CommonParser; import jp.sourceforge.mikutoga.parser.MmdFormatException; -import jp.sourceforge.mikutoga.parser.MmdInputStream; import jp.sourceforge.mikutoga.vmd.VmdConst; /** @@ -27,7 +27,7 @@ class VmdLightingParser extends CommonParser { * コンストラクタ。 * @param source 入力ソース */ - VmdLightingParser(MmdInputStream source){ + VmdLightingParser(InputStream source){ super(source); return; } diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdParser.java b/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdParser.java index f19e141..1d0718c 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdParser.java +++ b/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdParser.java @@ -8,15 +8,15 @@ package jp.sourceforge.mikutoga.vmd.parser; import java.io.IOException; +import java.io.InputStream; import jp.sourceforge.mikutoga.parser.MmdFormatException; -import jp.sourceforge.mikutoga.parser.MmdInputStream; /** * VMDモーションファイルのパーサ。 */ public class VmdParser { - private final MmdInputStream source; + private final InputStream source; private final VmdBasicParser basicParser; private final VmdCameraParser cameraParser; @@ -31,7 +31,7 @@ public class VmdParser { * @param source 入力ソース * @throws NullPointerException 引数がnull */ - public VmdParser(MmdInputStream source) throws NullPointerException{ + public VmdParser(InputStream source) throws NullPointerException{ super(); if(source == null) throw new NullPointerException(); @@ -49,7 +49,7 @@ public class VmdParser { * 入力ソースを返す。 * @return 入力ソース */ - public MmdInputStream getSource(){ + public InputStream getSource(){ return this.source; } @@ -109,7 +109,7 @@ public class VmdParser { parseBody(); - boolean hasMoreData = this.source.hasMore(); + boolean hasMoreData = this.lightingParser.hasMore(); if(this.basicHandler != null){ this.basicHandler.vmdParseEnd(hasMoreData); } diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java b/src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java index 33ca677..92cab4c 100644 --- a/src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java +++ b/src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java @@ -30,11 +30,21 @@ public class BasicXmlExporter { /** デフォルトのインデント単位。 */ private static final String DEFAULT_INDENT_UNIT = "\u0020\u0020"; + private static final char CH_SP = '\u0020'; // + private static final char CH_YEN = '\u00a5'; // ¥ + private static final char CH_BSLASH = '\u005c\u005c'; // \ + private static final char[] HEXCHAR_TABLE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', }; + private static final String COMM_START = ""; + + private static final int MASK_BIT8 = 0x000f; + private static final int MASK_BIT16 = 0x00ff; + static{ assert HEXCHAR_TABLE.length == 16; } @@ -89,7 +99,8 @@ public class BasicXmlExporter { * @return Basic-Latin文字ならtrue */ public static boolean isBasicLatin(char ch){ - if(ch <= 0x7f) return true; + Character.UnicodeBlock block = Character.UnicodeBlock.of(ch); + if(block == Character.UnicodeBlock.BASIC_LATIN) return true; return false; } @@ -245,7 +256,7 @@ public class BasicXmlExporter { * @throws IOException 出力エラー */ public BasicXmlExporter sp() throws IOException{ - this.appendable.append(" "); + this.appendable.append(CH_SP); return this; } @@ -257,7 +268,7 @@ public class BasicXmlExporter { */ public BasicXmlExporter sp(int count) throws IOException{ for(int ct = 1; ct <= count; ct++){ - this.appendable.append(" "); + this.appendable.append(CH_SP); } return this; } @@ -301,15 +312,12 @@ public class BasicXmlExporter { * @throws IOException 出力エラー */ public BasicXmlExporter putCharRef2Hex(char ch) throws IOException{ - if(ch > 0xff) return putCharRef4Hex(ch); + if(ch > MASK_BIT16) return putCharRef4Hex(ch); - char hex3 = HEXCHAR_TABLE[(ch >> 4) & 0x000f]; - char hex4 = HEXCHAR_TABLE[(ch ) & 0x000f]; + char hex3 = HEXCHAR_TABLE[(ch >> 4) & MASK_BIT8]; + char hex4 = HEXCHAR_TABLE[(ch >> 0) & MASK_BIT8]; - this.appendable.append("&#x"); - this.appendable.append(hex3); - this.appendable.append(hex4); - this.appendable.append(';'); + put("&#x").put(hex3).put(hex4).put(';'); return this; } @@ -322,25 +330,21 @@ public class BasicXmlExporter { * @throws IOException 出力エラー */ public BasicXmlExporter putCharRef4Hex(char ch) throws IOException{ - char hex1 = HEXCHAR_TABLE[(ch >> 12) & 0x000f]; - char hex2 = HEXCHAR_TABLE[(ch >> 8) & 0x000f]; - char hex3 = HEXCHAR_TABLE[(ch >> 4) & 0x000f]; - char hex4 = HEXCHAR_TABLE[(ch ) & 0x000f]; - - this.appendable.append("&#x"); - this.appendable.append(hex1); - this.appendable.append(hex2); - this.appendable.append(hex3); - this.appendable.append(hex4); - this.appendable.append(';'); + char hex1 = HEXCHAR_TABLE[(ch >> 12) & MASK_BIT8]; + char hex2 = HEXCHAR_TABLE[(ch >> 8) & MASK_BIT8]; + char hex3 = HEXCHAR_TABLE[(ch >> 4) & MASK_BIT8]; + char hex4 = HEXCHAR_TABLE[(ch >> 0) & MASK_BIT8]; + + put("&#x").put(hex1).put(hex2).put(hex3).put(hex4).put(';'); return this; } /** * 要素の中身および属性値中身を出力する。 - * 必要に応じてXML定義済み実体文字が割り振られた文字、 + *

必要に応じてXML定義済み実体文字が割り振られた文字、 * コントロールコード、および非BasicLatin文字がエスケープされる。 + *

半角通貨記号U+00A5はバックスラッシュU+005Cに置換される。 * @param content 内容 * @return this本体 * @throws IOException 出力エラー @@ -349,23 +353,37 @@ public class BasicXmlExporter { throws IOException{ int length = content.length(); + char prev = '\0'; for(int pos = 0; pos < length; pos++){ char ch = content.charAt(pos); + if(Character.isISOControl(ch)){ putCharRef2Hex(ch); }else if( ! isBasicLatin(ch) && isBasicLatinOnlyOut()){ putCharRef4Hex(ch); + }else if(ch == CH_SP){ + if(prev == CH_SP){ + putCharRef2Hex(ch); + }else{ + put(ch); + } + }else if(Character.isSpaceChar(ch)){ + // 全角スペースその他 + putCharRef2Hex(ch); + }else if(ch == CH_YEN){ + put(CH_BSLASH); }else{ switch(ch){ - case '&': this.appendable.append("&"); break; - case '<': this.appendable.append("<"); break; - case '>': this.appendable.append(">"); break; - case '"': this.appendable.append("""); break; - case '\'': this.appendable.append("'"); break; - case '\u00a5': this.appendable.append('\u005c\u005c'); break; - default: this.appendable.append(ch); break; + case '&': put("&"); break; + case '<': put("<"); break; + case '>': put(">"); break; + case '"': put("""); break; + case '\'': put("'"); break; + default: put(ch); break; } } + + prev = ch; } return this; @@ -415,11 +433,12 @@ public class BasicXmlExporter { /** * コメントの内容を出力する。 - * コメント中の\n記号出現に伴い、 + *

コメント中の'\n'記号出現に伴い、 * あらかじめ指定された改行文字が出力される。 - * \n以外のコントロールコード各種、 - * 及び非BasicLatin文字はそのまま出力される。 - * 連続するハイフン(-)記号間には強制的にスペースが挿入される。 + *

コメント中の'\n'以外のコントロールコードは + * Control Pictures(U+2400〜)で代替される。 + *

それ以外の非BasicLatin文字はそのまま出力される。 + *

連続するハイフン(-)記号間には強制的にスペースが挿入される。 * @param comment コメント内容 * @return this本体 * @throws IOException 出力エラー @@ -431,13 +450,19 @@ public class BasicXmlExporter { char prev = '\0'; for(int pos = 0; pos < length; pos++){ char ch = comment.charAt(pos); + if(ch == '\n'){ ln(); - prev = ch; - continue; + }else if('\u0000' <= ch && ch <= '\u001f'){ + put((char)('\u2400' + ch)); + }else if(ch == '\u007f'){ + put('\u2421'); + }else if(prev == '-' && ch == '-'){ + sp().put(ch); + }else{ + put(ch); } - if(prev == '-' && ch == '-') put(' '); - put(ch); + prev = ch; } @@ -446,47 +471,44 @@ public class BasicXmlExporter { /** * 1行コメントを出力する。 - * コメント中の\n記号出現に伴い、 - * あらかじめ指定された改行文字が出力される。 - * \n以外のコントロールコード各種、 - * 及び非BasicLatin文字はそのまま出力される。 - * 連続するハイフン(-)記号間には強制的にスペースが挿入される。 + * コメント内部の頭及び末尾に空白が1つ挿入される。 * @param comment コメント内容 * @return this本体 * @throws IOException 出力エラー */ public BasicXmlExporter putLineComment(CharSequence comment) throws IOException{ - put(""); + sp().put(COMM_END); return this; } /** * ブロックコメントを出力する。 - * コメント中の\n記号出現に伴い、 - * あらかじめ指定された改行文字が出力される。 - * \n以外のコントロールコード各種、 - * 及び非BasicLatin文字はそのまま出力される。 - * 連続するハイフン(-)記号間には強制的にスペースが挿入される。 + *

コメント内部の頭の前に改行が出力される。 + *

コメント内部の末尾が改行でない場合、改行が挿入される。 + *

ブロックコメント末尾は改行で終わる。 + *

インデント設定は無視される。 * @param comment コメント内容 * @return this本体 * @throws IOException 出力エラー */ public BasicXmlExporter putBlockComment(CharSequence comment) throws IOException{ - put("").ln(); + put(COMM_END).ln(); return this; } diff --git a/src/test/java/jp/sourceforge/mikutoga/parser/DummyInputStream.java b/src/test/java/jp/sourceforge/mikutoga/parser/DummyInputStream.java new file mode 100644 index 0000000..0c80c37 --- /dev/null +++ b/src/test/java/jp/sourceforge/mikutoga/parser/DummyInputStream.java @@ -0,0 +1,272 @@ +/* + */ + +package jp.sourceforge.mikutoga.parser; + +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedList; +import java.util.Queue; + +/** + * テスト用入力ストリーム。 + * これはユニットテストではない。 + */ +public class DummyInputStream extends InputStream { + + private static final int MASK_08BIT = 0xff; + + + private final Queue queue = new LinkedList(); + + private boolean closed = false; + + private long errPos = -1L; + private IOException errEx = null; + + + public DummyInputStream(byte[] bArray, int offset, int len){ + super(); + queueByteArrayImpl(bArray, offset, len); + return; + } + + public DummyInputStream(byte... bArray){ + this(bArray, 0, bArray.length); + return; + } + + public DummyInputStream(int... iArray) throws IllegalArgumentException{ + this(toBarray(iArray)); + return; + } + + + /** + * int配列からbyte配列への変換を行う。 + * bitが欠損した場合は異常系を投げる。 + * @param iArray int配列 + * @return byte配列 + * @throws IllegalArgumentException bit欠損が発生 + */ + protected static byte[] toBarray(int[] iArray) + throws IllegalArgumentException{ + int length = iArray.length; + byte[] result = new byte[length]; + + for(int pos = 0; pos < length; pos++){ + int iVal = iArray[pos]; + if(iVal < 0x00 || 0xff < iVal){ + throw new IllegalArgumentException(); + } + byte bVal = (byte) iVal; + result[pos] = bVal; + } + + return result; + } + + + /** + * 指定バイト数読み込んだ後に入力例外を意図的に発生させるよう設定する。 + * @param pos 正常に読める残りバイト数。負の値なら設定解除。 + * @param ex 入力例外。解除時に限ってnullも可。 + */ + public void setErrorPosition(long pos, IOException ex){ + this.errPos = pos; + + if(this.errPos >= 0L){ + if(ex == null) throw new NullPointerException(); + this.errEx = ex; + }else{ + this.errEx = null; + } + + return; + } + + /** + * 意図的な入力例外を起こすまで、 + * あと何バイト正常に読めるか設定値を返す。 + * @return 正常に読めるバイト数。 + */ + public long getErrorPosition(){ + long result = this.errPos; + return result; + } + + /** + * 意図した入力エラー報告を投げる状況かチェックする。 + * @throws IOException 意図した入力エラー + */ + protected void checkIOEx() throws IOException { + if(this.errPos < 0L) return; + if(this.errPos > 0L) return; + + IOException ex = this.errEx; + this.errPos = -1L; + this.errEx = null; + + throw ex; + } + + /** + * {@inheritDoc} + * クローズの有無は外部からの観察が可能。 + * @throws IOException {@inheritDoc} + * 設定状況によっては意図的な入力例外を投げる。 + */ + @Override + public void close() throws IOException { + checkIOEx(); + this.closed = true; + return; + } + + /** + * クローズされた状況か判定する。 + * @return クローズ済みであればtrue + */ + public boolean isClosed(){ + boolean result = this.closed; + return result; + } + + /** + * {@inheritDoc} + * @param bArray {@inheritDoc} + * @param offset {@inheritDoc} + * @param len {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public int read(byte[] bArray, int offset, int len) throws IOException { + int result = 0; + + for(int pos = 0; pos < len; pos++){ + int iVal = read(); + if(iVal < 0){ + if(result == 0) result = -1; + break; + } + bArray[offset + pos] = (byte)iVal; + result++; + } + + return result; + } + + /** + * {@inheritDoc} + * @param bArray {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public int read(byte[] bArray) throws IOException { + int result = read(bArray, 0, bArray.length); + return result; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public int read() throws IOException { + checkIOEx(); + + Byte val = this.queue.poll(); + if(val == null) return -1; + + int iVal = (int)( val.byteValue() ); + int result = iVal & MASK_08BIT; + + if(this.errPos > 0){ + this.errPos--; + } + + return result; + } + + /** + * 未出力のバイト列長を返す。 + * @return バイト列長 + */ + public int getQueueLength(){ + int result = this.queue.size(); + return result; + } + + /** + * 未出力バイト列を空にする。 + */ + public void clearQueue(){ + this.queue.clear(); + return; + } + + /** + * 未出力バイト列にデータを追加する。 + * @param bArray 配列 + * @param offset 開始オフセット + * @param len バイト長 + */ + private void queueByteArrayImpl(byte[] bArray, int offset, int len){ + for(int pos = 0; pos < len; pos++){ + byte bVal = bArray[offset + pos]; + this.queue.add(bVal); + } + return; + } + + /** + * 未出力バイト列にデータを追加する。 + * @param bArray 配列 + * @param offset 開始オフセット + * @param len バイト長 + */ + public void queueByteArray(byte[] bArray, int offset, int len){ + queueByteArrayImpl(bArray, offset, len); + return; + } + + /** + * 未出力バイト列にデータを追加する。 + * @param bArray 配列 + */ + public void queueByteArray(byte[] bArray){ + for(byte bVal : bArray){ + this.queue.add(bVal); + } + return; + } + + /** + * 未出力バイト列にデータを追加する。 + * @param bArray byte型可変引数 + */ + public void queueByte(byte... bArray){ + queueByteArray(bArray); + return; + } + + /** + * 未出力バイト列にデータを追加する。 + * @param iArray int型可変引数。byte値に収まる要素値でなければならない。 + * @throws IllegalArgumentException byte値に収まらない値が指定された。 + */ + public void queueByte(int... iArray) throws IllegalArgumentException{ + for(int iVal : iArray){ + if(iVal < 0x00 || 0xff < iVal){ + throw new IllegalArgumentException(); + } + byte bVal = (byte) iVal; + this.queue.add(bVal); + } + return; + } + +} diff --git a/src/test/java/jp/sourceforge/mikutoga/parser/MmdInputStreamTest.java b/src/test/java/jp/sourceforge/mikutoga/parser/MmdInputStreamTest.java deleted file mode 100644 index 11435c9..0000000 --- a/src/test/java/jp/sourceforge/mikutoga/parser/MmdInputStreamTest.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - */ - -package jp.sourceforge.mikutoga.parser; - -import java.io.ByteArrayInputStream; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; - -/** - * - */ -public class MmdInputStreamTest { - - public MmdInputStreamTest() { - } - - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() { - } - - @After - public void tearDown() { - } - - /** - * Test of parseByte method, of class MmdInputStream. - */ - @Test - public void testParseByte() throws Exception { - System.out.println("parseByte"); - - MmdInputStream mis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream(new byte[]{0x01, 0x02}); - mis = new MmdInputStream(bis); - - byte result; - - result = mis.parseByte(); - assertEquals(result, (byte)0x01); - - result = mis.parseByte(); - assertEquals(result, (byte)0x02); - - try{ - mis.parseByte(); - fail(); - }catch(MmdEofException e){ - // GOOD - } - - assertEquals(-1, mis.read()); - - return; - } - - /** - * Test of parseBoolean method, of class MmdInputStream. - */ - @Test - public void testParseBoolean() throws Exception { - System.out.println("parseBoolean"); - - MmdInputStream mis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream(new byte[]{0x00, 0x01, 0x02}); - mis = new MmdInputStream(bis); - - boolean result; - - result = mis.parseBoolean(); - assertFalse(result); - - result = mis.parseBoolean(); - assertTrue(result); - - result = mis.parseBoolean(); - assertTrue(result); - - try{ - mis.parseBoolean(); - fail(); - }catch(MmdEofException e){ - // GOOD - } - - assertEquals(-1, mis.read()); - - return; - } - - /** - * Test of parseBeShort method, of class MmdInputStream. - */ - @Test - public void testParseBeShort() throws Exception { - System.out.println("parseBeShort"); - - MmdInputStream mis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream(new byte[]{0x01, 0x02, 0x03}); - mis = new MmdInputStream(bis); - - short result; - - result = mis.parseBeShort(); - assertEquals((short)0x0102, result); - - try{ - mis.parseBeShort(); - fail(); - }catch(MmdEofException e){ - // GOOD - } - - assertEquals(-1, mis.read()); - - return; - } - - /** - * Test of parseLeShort method, of class MmdInputStream. - */ - @Test - public void testParseLeShort() throws Exception { - System.out.println("parseLeShort"); - - MmdInputStream mis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream(new byte[]{0x01, 0x02, 0x03}); - mis = new MmdInputStream(bis); - - short result; - - result = mis.parseLeShort(); - assertEquals((short)0x0201, result); - - try{ - mis.parseLeShort(); - fail(); - }catch(MmdEofException e){ - // GOOD - } - - assertEquals(-1, mis.read()); - - return; - } - - /** - * Test of parseBeInt method, of class MmdInputStream. - */ - @Test - public void testParseBeInt() throws Exception { - System.out.println("parseBeInt"); - - MmdInputStream mis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream( - new byte[]{0x01, 0x02, 0x03, 0x04, 0x05}); - mis = new MmdInputStream(bis); - - int result; - - result = mis.parseBeInt(); - assertEquals(0x01020304, result); - - try{ - mis.parseBeInt(); - fail(); - }catch(MmdEofException e){ - // GOOD - } - - assertEquals(-1, mis.read()); - - return; - } - - /** - * Test of parseLeInt method, of class MmdInputStream. - */ - @Test - public void testParseLeInt() throws Exception { - System.out.println("parseLeInt"); - - MmdInputStream mis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream( - new byte[]{0x01, 0x02, 0x03, 0x04, 0x05}); - mis = new MmdInputStream(bis); - - int result; - - result = mis.parseLeInt(); - assertEquals(0x04030201, result); - - try{ - mis.parseLeInt(); - fail(); - }catch(MmdEofException e){ - // GOOD - } - - assertEquals(-1, mis.read()); - - return; - } - - /** - * Test of parseBeFloat method, of class MmdInputStream. - */ - @Test - public void testParseBeFloat() throws Exception { - System.out.println("parseBeFloat"); - - MmdInputStream mis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream( - new byte[]{0x01, 0x02, 0x03, 0x04, 0x05}); - mis = new MmdInputStream(bis); - - float result; - - result = mis.parseBeFloat(); - assertEquals(Float.intBitsToFloat(0x01020304), result, 0.0f); - - try{ - mis.parseBeFloat(); - fail(); - }catch(MmdEofException e){ - // GOOD - } - - assertEquals(-1, mis.read()); - - return; - } - - /** - * Test of parseLeFloat method, of class MmdInputStream. - */ - @Test - public void testParseLeFloat() throws Exception { - System.out.println("parseLeFloat"); - - MmdInputStream mis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream( - new byte[]{0x01, 0x02, 0x03, 0x04, 0x05}); - mis = new MmdInputStream(bis); - - float result; - - result = mis.parseLeFloat(); - assertEquals(Float.intBitsToFloat(0x04030201), result, 0.0f); - - try{ - mis.parseLeFloat(); - fail(); - }catch(MmdEofException e){ - // GOOD - } - - assertEquals(-1, mis.read()); - - return; - } - - /** - * Test of skipRepeat method, of class MmdInputStream. - */ - @Test - public void testSkipRepeat() throws Exception { - System.out.println("skipRepeat"); - - MmdInputStream mis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream(new byte[]{0x11, 0x12, 0x13}); - mis = new MmdInputStream(bis); - - int result; - long skipped; - - result = mis.read(); - assertEquals(0x11, result); - - skipped = mis.skip(1L); - assertEquals(1L, skipped); - - result = mis.read(); - assertEquals(0x13, result); - - skipped = mis.skip(1L); - assertEquals(0L, skipped); - - // TODO: BufferedInputStreamと組み合わせた時の不思議なskip動作 - - return; - } - -} diff --git a/src/test/java/jp/sourceforge/mikutoga/parser/SpottedInputStreamTest.java b/src/test/java/jp/sourceforge/mikutoga/parser/SpottedInputStreamTest.java deleted file mode 100644 index 1c551b6..0000000 --- a/src/test/java/jp/sourceforge/mikutoga/parser/SpottedInputStreamTest.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - */ -package jp.sourceforge.mikutoga.parser; - -import java.io.ByteArrayInputStream; -import java.io.FilterInputStream; -import java.io.IOException; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; - -/** - * - */ -public class SpottedInputStreamTest { - - public SpottedInputStreamTest() { - } - - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() { - } - - @After - public void tearDown() { - } - - /** - * Test of read method, of class SpottedInputStream. - */ - @Test - public void testRead_0args() throws Exception { - System.out.println("read"); - - SpottedInputStream sis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream(new byte[]{0x01, 0x02}); - sis = new SpottedInputStream(bis); - - int result; - - result = sis.read(); - assertEquals(0x01, result); - - result = sis.read(); - assertEquals(0x02, result); - - result = sis.read(); - assertEquals(-1, result); - - return; - } - - /** - * Test of read method, of class SpottedInputStream. - */ - @Test - public void testRead_byteArr() throws Exception { - System.out.println("read"); - - SpottedInputStream sis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream(new byte[]{0x01, 0x02, 0x03}); - sis = new SpottedInputStream(bis); - - byte[] buf = new byte[2]; - int result; - - result = sis.read(buf); - assertEquals(2, result); - assertEquals((byte)0x01, buf[0]); - assertEquals((byte)0x02, buf[1]); - - result = sis.read(buf); - assertEquals(1, result); - assertEquals((byte)0x03, buf[0]); - - result = sis.read(buf); - assertEquals(-1, result); - - return; - } - - /** - * Test of read method, of class SpottedInputStream. - */ - @Test - public void testRead_3args() throws Exception { - System.out.println("read"); - - - SpottedInputStream sis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream(new byte[]{0x01, 0x02, 0x03}); - sis = new SpottedInputStream(bis); - - byte[] buf = new byte[3]; - buf[0] = (byte)0xf1; - buf[1] = (byte)0xf2; - buf[2] = (byte)0xf3; - int result; - - result = sis.read(buf, 1, 2); - assertEquals(2, result); - assertEquals((byte)0xf1, buf[0]); - assertEquals((byte)0x01, buf[1]); - assertEquals((byte)0x02, buf[2]); - - result = sis.read(buf, 0, 1); - assertEquals(1, result); - assertEquals((byte)0x03, buf[0]); - assertEquals((byte)0x01, buf[1]); - assertEquals((byte)0x02, buf[2]); - - result = sis.read(buf, 0, 1); - assertEquals(-1, result); - - return; - } - - /** - * Test of skip method, of class SpottedInputStream. - */ - @Test - public void testSkip() throws Exception { - System.out.println("skip"); - - SpottedInputStream sis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream(new byte[]{0x11, 0x12, 0x13}); - sis = new SpottedInputStream(bis); - - int result; - long skipped; - - result = sis.read(); - assertEquals(0x11, result); - - skipped = sis.skip(1L); - assertEquals(1L, skipped); - - result = sis.read(); - assertEquals(0x13, result); - - skipped = sis.skip(1L); - assertEquals(0L, skipped); - - return; - } - - /** - * Test of close method, of class SpottedInputStream. - */ - @Test - public void testClose() throws Exception { - System.out.println("close"); - - SpottedInputStream sis; - TestInputStream tis; - - tis = new TestInputStream(); - sis = new SpottedInputStream(tis); - - assertFalse(tis.closed); - sis.close(); - assertTrue(tis.closed); - - return; - } - - /** - * Test of getPosition method, of class SpottedInputStream. - */ - @Test - public void testGetPosition() throws Exception{ - System.out.println("getPosition"); - - SpottedInputStream sis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream(new byte[]{0x01,0x02}); - sis = new SpottedInputStream(bis); - - assertEquals(0L, sis.getPosition()); - - sis.read(); - assertEquals(1L, sis.getPosition()); - - sis.read(); - assertEquals(2L, sis.getPosition()); - - sis.read(); - assertEquals(2L, sis.getPosition()); - - return; - } - - /** - * Test of hasMore method, of class SpottedInputStream. - */ - @Test - public void testHasMore() throws Exception { - System.out.println("hasMore"); - - SpottedInputStream sis; - ByteArrayInputStream bis; - - bis = new ByteArrayInputStream(new byte[]{0x01,0x02}); - sis = new SpottedInputStream(bis); - - assertTrue(sis.hasMore()); - sis.read(); - assertTrue(sis.hasMore()); - sis.read(); - assertFalse(sis.hasMore()); - - return; - } - - private static class TestInputStream extends FilterInputStream{ - public boolean closed = false; - public boolean flushed = false; - - TestInputStream(){ - super(new ByteArrayInputStream(new byte[]{})); - return; - } - - @Override - public void close() throws IOException { - super.close(); - this.closed = true; - return; - } - - } - -} diff --git a/src/test/java/jp/sourceforge/mikutoga/parser/TextDecoderTest.java b/src/test/java/jp/sourceforge/mikutoga/parser/TextDecoderTest.java index d09b5fd..6a22e15 100644 --- a/src/test/java/jp/sourceforge/mikutoga/parser/TextDecoderTest.java +++ b/src/test/java/jp/sourceforge/mikutoga/parser/TextDecoderTest.java @@ -3,9 +3,6 @@ package jp.sourceforge.mikutoga.parser; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @@ -104,6 +101,7 @@ public class TextDecoderTest { /** * Test of setChopMode, getChopMode method, of class TextDecoder. + * @throws Exception */ @Test public void testChopMode() throws Exception { @@ -125,16 +123,13 @@ public class TextDecoderTest { /** * Test of parseString method, of class TextDecoder. + * @throws Exception */ @Test public void testParseStringChop() throws Exception { System.out.println("parseString(Chop)"); TextDecoder decoder; - byte[] bdata; - InputStream istream; - MmdInputStream source; - CharBuffer cb; decoder = new TextDecoder(CS_WIN31J); decoder.setZeroChopMode(true); @@ -144,19 +139,15 @@ public class TextDecoderTest { assertDecoded("00:41:42", "", decoder); assertDecoded("41:00:88", "A", decoder); - bdata = byteArray("41:00:42:43"); - istream = new ByteArrayInputStream(bdata); - source = new MmdInputStream(istream); - cb =decoder.parseString(source, 3); - assertEquals("A", cb.toString()); - cb =decoder.parseString(source, 1); - assertEquals("C", cb.toString()); + decoder.setZeroChopMode(false); + assertDecoded("41:00:42", "A\u0000B", decoder); return; } /** * Test of parseString method, of class TextDecoder. + * @throws Exception */ @Test public void testParseStringWin31J() throws Exception { @@ -175,27 +166,12 @@ public class TextDecoderTest { assertFormatError("88:9F:88:A0", decoder, 3); - - byte[] bdata; - InputStream istream; - MmdInputStream source; - CharBuffer cb; - - bdata = byteArray("88:9F:88:A0"); - istream = new ByteArrayInputStream(bdata); - source = new MmdInputStream(istream); - try{ - cb =decoder.parseString(source, 5); - fail(); - }catch(MmdEofException e){ - // OK - } - return; } /** * Test of parseString method, of class TextDecoder. + * @throws Exception */ @Test public void testParseStringUTF8() throws Exception { @@ -215,27 +191,12 @@ public class TextDecoderTest { assertFormatError("E4:BA:9C:E5:94:96", decoder, 5); - - byte[] bdata; - InputStream istream; - MmdInputStream source; - CharBuffer cb; - - bdata = byteArray("E4:BA:9C:E5:94:96"); - istream = new ByteArrayInputStream(bdata); - source = new MmdInputStream(istream); - try{ - cb =decoder.parseString(source, 7); - fail(); - }catch(MmdEofException e){ - // OK - } - return; } /** * Test of parseString method, of class TextDecoder. + * @throws Exception */ @Test public void testParseStringUTF16LE() throws Exception { @@ -260,26 +221,12 @@ public class TextDecoderTest { assertFormatError("9C:4E:16:55", decoder, 3); - - byte[] bdata; - InputStream istream; - MmdInputStream source; - CharBuffer cb; - bdata = byteArray("9C:4E:16:55"); - istream = new ByteArrayInputStream(bdata); - source = new MmdInputStream(istream); - try{ - cb =decoder.parseString(source, 5); - fail(); - }catch(MmdEofException e){ - // OK - } - return; } /** * Test of Yen(U+00A5) & Backslash(U+005C) encoding, of class TextDecoder. + * @throws Exception */ @Test public void testYenAndBackslash() throws Exception { @@ -303,6 +250,7 @@ public class TextDecoderTest { /** * Test of unmapped char, of class TextDecoder. + * @throws Exception */ @Test public void testUnmapChar() throws Exception { @@ -338,25 +286,14 @@ public class TextDecoderTest { TextDecoder decoder, int len) throws Exception{ byte[] bdata; - InputStream istream; - MmdInputStream source; - CharBuffer cb; bdata = byteArray(bin); - istream = new ByteArrayInputStream(bdata); - source = new MmdInputStream(istream); - assertDecoded(source, desired, decoder, len); + String result; + result = decoder.decode(0, bdata, 0, len); - return; - } + assertEquals(desired, result); - public void assertDecoded(MmdInputStream source, String desired, - TextDecoder decoder, int len) - throws Exception{ - CharBuffer cb; - cb =decoder.parseString(source, len); - assertEquals(desired, cb.toString()); return; } @@ -364,15 +301,11 @@ public class TextDecoderTest { TextDecoder decoder, int len) throws Exception{ byte[] bdata; - InputStream istream; - MmdInputStream source; bdata = byteArray(bin); - istream = new ByteArrayInputStream(bdata); - source = new MmdInputStream(istream); try{ - decoder.parseString(source, len); + decoder.decode(0, bdata, 0, len); fail(); }catch(MmdFormatException e){ // OK diff --git a/src/test/java/sample/pmd/DummyMain.java b/src/test/java/sample/pmd/DummyMain.java index b0f0d1f..f5cb697 100644 --- a/src/test/java/sample/pmd/DummyMain.java +++ b/src/test/java/sample/pmd/DummyMain.java @@ -14,7 +14,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import jp.sourceforge.mikutoga.parser.MmdFormatException; -import jp.sourceforge.mikutoga.parser.MmdInputStream; import jp.sourceforge.mikutoga.pmd.parser.PmdParser; /** @@ -57,7 +56,7 @@ public class DummyMain { * @param fname ファイル名 * @return 入力ソース */ - private static MmdInputStream buildSource(String fname){ + private static InputStream buildSource(String fname){ File file = new File(fname); InputStream is; @@ -68,11 +67,10 @@ public class DummyMain { exit(1); return null; } - is = new BufferedInputStream(is, BUF_SZ); - MmdInputStream source = new MmdInputStream(is); + is = new BufferedInputStream(is, BUF_SZ); - return source; + return is; } /** @@ -102,7 +100,7 @@ public class DummyMain { if(args.length == 1) fname = args[0]; else fname = PMDFILE; - MmdInputStream source = buildSource(fname); + InputStream source = buildSource(fname); PmdParser parser = new PmdParser(source); diff --git a/src/test/java/sample/vmd/DummyMain.java b/src/test/java/sample/vmd/DummyMain.java index 28015cc..ce3beed 100644 --- a/src/test/java/sample/vmd/DummyMain.java +++ b/src/test/java/sample/vmd/DummyMain.java @@ -14,7 +14,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import jp.sourceforge.mikutoga.parser.MmdFormatException; -import jp.sourceforge.mikutoga.parser.MmdInputStream; import jp.sourceforge.mikutoga.vmd.parser.VmdParser; /** @@ -32,7 +31,7 @@ public class DummyMain { VMDFILE = "D:\\Test\\camera.vmd"; } - private static MmdInputStream buildSource(String fname){ + private static InputStream buildSource(String fname){ File file = new File(fname); InputStream is; @@ -43,11 +42,10 @@ public class DummyMain { System.exit(1); return null; } - is = new BufferedInputStream(is, BUF_SZ); - MmdInputStream source = new MmdInputStream(is); + is = new BufferedInputStream(is, BUF_SZ); - return source; + return is; } private static void setupHandler(VmdParser parser){ @@ -63,7 +61,7 @@ public class DummyMain { if(args.length == 1) fname = args[0]; else fname = VMDFILE; - MmdInputStream source = buildSource(fname); + InputStream source = buildSource(fname); VmdParser parser = new VmdParser(source);