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