2 * MMD file input source
4 * License : The MIT License
5 * Copyright(c) 2010 MikuToga Partners
8 package jp.sourceforge.mikutoga.parser;
10 import java.io.Closeable;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.io.PushbackInputStream;
14 import java.nio.ByteBuffer;
15 import java.nio.ByteOrder;
20 * パースエラー発生位置(バイト単位)の取得が可能。
21 * リトルエンディアン形式で格納された各種プリミティブ型値の解決を行う。
23 public class MmdSource implements Closeable {
25 private static final int BYTES_SHORT = Short .SIZE / Byte.SIZE;
26 private static final int BYTES_INT = Integer.SIZE / Byte.SIZE;
27 private static final int BYTES_FLOAT = Float .SIZE / Byte.SIZE;
28 private static final int BUF_SZ = 4;
30 private static final int MASK_8BIT = 0xff;
31 private static final int MASK_16BIT = 0xffff;
34 assert BUF_SZ >= BYTES_SHORT;
35 assert BUF_SZ >= BYTES_INT;
36 assert BUF_SZ >= BYTES_FLOAT;
39 private final PushbackInputStream istream;
40 private final byte[] readArray; // 読み込みバッファ
41 private final ByteBuffer readBuffer; // 読み込みバッファの別ビュー
42 private long position; // 読み込み位置
47 * I/O効率が考慮されたバッファリングを行うストリームを渡すのが望ましい。
48 * @throws NullPointerException ストリーム引数がnull。
50 public MmdSource(InputStream is)
51 throws NullPointerException {
54 if(is == null) throw new NullPointerException();
57 this.istream = new PushbackInputStream(is);
59 this.readArray = new byte[BUF_SZ];
60 this.readBuffer = ByteBuffer.wrap(this.readArray);
61 this.readBuffer.order(ByteOrder.LITTLE_ENDIAN);
62 this.readBuffer.clear();
70 * 今までに読み込みに成功したバイト数を返す。
71 * @return 読み込みに成功したバイト数。
73 public long getPosition(){
79 * 入力ソースがディスクファイルに由来する場合、
81 * @param skipLength 読み飛ばすバイト数。
82 * @return 実際に読み飛ばしたバイト数。
83 * @throws IOException IOエラー
84 * @see java.io.InputStream#skip(long)
86 public long skip(long skipLength)
88 if(skipLength <= 0L) return 0L;
90 long remain = skipLength;
91 while(remain > 0L){ // BufferedInputStream対策
92 long result = this.istream.skip(remain);
93 if(result <= 0L) break;
94 this.position += result;
98 return skipLength - remain;
102 * 入力ソースにまだデータが残っているか判定する。
103 * @return まだ読み込んでいないデータが残っていればtrue
104 * @throws IOException IOエラー
106 public boolean hasMore() throws IOException{
107 int bData = this.istream.read();
112 this.istream.unread(bData);
119 * 読み込み済みバイト数の情報は保持される。
120 * @throws IOException IOエラー
121 * @see java.io.InputStream#close()
124 public void close() throws IOException{
125 this.istream.close();
126 this.readBuffer.clear();
131 * 指定したバイト数だけ内部バッファに読み込む。
132 * @param fillSize 読み込むバイト数
133 * @throws IOException IOエラー
134 * @throws IndexOutOfBoundsException 引数がバッファサイズと矛盾。
135 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
137 protected void fillBuffer(int fillSize)
138 throws IOException, IndexOutOfBoundsException, MmdEofException{
139 int result = this.istream.read(this.readArray, 0, fillSize);
141 this.position += result;
144 if(result != fillSize){
145 throw new MmdEofException(this.position);
148 this.readBuffer.rewind();
156 * @throws IOException IOエラー
157 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
159 public byte parseByte() throws IOException, MmdEofException{
160 int bData = this.istream.read();
162 throw new MmdEofException(this.position);
167 byte result = (byte) bData;
172 * 符号無し値としてbyte値を読み込み、int型に変換して返す。
173 * 符号は拡張されない。(0xffは0x000000ffとなる)
174 * @return 読み込まれた値のint値
175 * @throws IOException IOエラー
176 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
178 public int parseUByteAsInteger()
179 throws IOException, MmdEofException{
180 return ((int) parseByte()) & MASK_8BIT;
184 * byte値を読み込み、boolean型に変換して返す。
185 * 0x00は偽、それ以外は真と解釈される。
186 * @return 読み込まれた値のboolean値
187 * @throws IOException IOエラー
188 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
190 public boolean parseBoolean() throws IOException, MmdEofException{
191 byte result = parseByte();
192 if(result == 0x00) return false;
198 * short値はリトルエンディアンで格納されていると仮定される。
199 * @return 読み込んだshort値
200 * @throws IOException IOエラー
201 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
203 public short parseShort() throws IOException, MmdEofException{
204 fillBuffer(BYTES_SHORT);
205 short result = this.readBuffer.getShort();
210 * 符号無し値としてshort値を読み込み、int型に変換して返す。
211 * 符号は拡張されない。(0xffffは0x0000ffffとなる)
212 * short値はリトルエンディアンで格納されていると仮定される。
213 * @return 読み込まれた値のint値
214 * @throws IOException IOエラー
215 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
217 public int parseUShortAsInteger()
218 throws IOException, MmdEofException{
219 return ((int) parseShort()) & MASK_16BIT;
224 * int値はリトルエンディアンで格納されていると仮定される。
226 * @throws IOException IOエラー
227 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
229 public int parseInteger() throws IOException, MmdEofException{
230 fillBuffer(BYTES_INT);
231 int result = this.readBuffer.getInt();
237 * float値はリトルエンディアンで格納されていると仮定される。
238 * @return 読み込んだfloat値
239 * @throws IOException IOエラー
240 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
242 public float parseFloat() throws IOException, MmdEofException{
243 fillBuffer(BYTES_FLOAT);
244 float result = this.readBuffer.getFloat();
251 * @param offset 読み込み開始オフセット
252 * @param length 読み込みバイト数
253 * @throws IOException IOエラー
254 * @throws NullPointerException 配列がnull
255 * @throws IndexOutOfBoundsException 引数が配列属性と矛盾
256 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
257 * @see java.io.InputStream#read(byte[], int, int)
259 public void parseByteArray(byte[] dst, int offset, int length)
261 NullPointerException,
262 IndexOutOfBoundsException,
264 int result = this.istream.read(dst, offset, length);
266 this.position += result;
269 if(result != length){
270 throw new MmdEofException(this.position);
279 * @throws IOException IOエラー
280 * @throws NullPointerException 配列がnull
281 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
283 public void parseByteArray(byte[] dst)
284 throws IOException, NullPointerException, MmdEofException{
285 parseByteArray(dst, 0, dst.length);
292 * @param offset 読み込み開始オフセット
293 * @param length 読み込みfloat要素数
294 * @throws IOException IOエラー
295 * @throws NullPointerException 配列がnull
296 * @throws IndexOutOfBoundsException 引数が配列属性と矛盾
297 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
299 public void parseFloatArray(float[] dst, int offset, int length)
301 NullPointerException,
302 IndexOutOfBoundsException,
304 if(offset < 0 || length < 0 || dst.length - offset < length){
305 throw new IndexOutOfBoundsException();
308 for(int idx = 0; idx < length; idx++){
309 dst[offset+idx] = parseFloat();
318 * @throws IOException IOエラー
319 * @throws NullPointerException 配列がnull
320 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
322 public void parseFloatArray(float[] dst)
323 throws IOException, NullPointerException, MmdEofException{
324 parseFloatArray(dst, 0, dst.length);
328 // TODO ビッグエンディアン対応が今後必要になる状況はありうるか?