4 * License : The MIT License
5 * Copyright(c) 2010 MikuToga Partners
8 package jp.sfjp.mikutoga.bin.parser;
10 import java.io.EOFException;
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;
16 import java.nio.charset.CharacterCodingException;
17 import java.nio.charset.MalformedInputException;
18 import java.nio.charset.UnmappableCharacterException;
22 * <p>バイト列、各種プリミティブ型値およびエンコードされた文字列を読み込む。
23 * <p>long,double、およびビッグエンディアン形式のデータは未サポート。
25 public class CommonParser {
27 private static final String ERRMSG_ILLENC =
28 "illegal character encoding";
29 private static final String ERRMSG_UNMAP =
32 private static final int BYTES_SHORT = Short .SIZE / Byte.SIZE;
33 private static final int BYTES_INT = Integer.SIZE / Byte.SIZE;
34 private static final int BYTES_FLOAT = Float .SIZE / Byte.SIZE;
35 private static final int BYTES_PRIM = 4;
37 private static final int MASK_8BIT = 0xff;
38 private static final int MASK_16BIT = 0xffff;
41 assert BYTES_PRIM >= BYTES_FLOAT;
42 assert BYTES_PRIM >= BYTES_INT;
43 assert BYTES_PRIM >= BYTES_SHORT;
47 private final PushbackInputStream is;
49 private final byte[] readBuffer;
50 // private final ByteBuffer beBuf;
51 private final ByteBuffer leBuf;
53 private long position = 0L;
55 private ByteBuffer btextBuf;
62 public CommonParser(InputStream source){
65 this.is = new PushbackInputStream(source, 1);
67 this.readBuffer = new byte[BYTES_PRIM];
69 // this.beBuf = ByteBuffer.wrap(this.readBuffer);
70 this.leBuf = ByteBuffer.wrap(this.readBuffer);
72 // this.beBuf.order(ByteOrder.BIG_ENDIAN);
73 this.leBuf.order(ByteOrder.LITTLE_ENDIAN);
81 * @return 入力ソースの読み込み位置。単位はbyte。
83 protected long getPosition(){
84 long result = this.position;
89 * 入力ソースにまだデータが残っているか判定する。
90 * @return まだ読み込んでいないデータが残っていればtrue
91 * @throws IOException IOエラー
93 public boolean hasMore() throws IOException{
97 bVal = this.is.read();
98 }catch(EOFException e){ // ありえない?
106 this.is.unread(bVal);
113 * @param skipLength 読み飛ばすバイト数。
114 * @throws IOException IOエラー
115 * @throws MmdEofException 読み飛ばす途中でストリーム終端に達した。
116 * @see InputStream#skip(long)
118 protected void skip(long skipLength)
119 throws IOException, MmdEofException {
120 long remain = skipLength;
123 long txSize = this.is.skip(remain);
125 throw new MmdEofException(this.position);
128 this.position += txSize;
137 * @param off 読み込み開始オフセット
138 * @param length 読み込みバイト数
139 * @throws IOException IOエラー
140 * @throws NullPointerException 配列がnull
141 * @throws IndexOutOfBoundsException 引数が配列属性と矛盾
142 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
143 * @see InputStream#read(byte[], int, int)
145 protected void parseByteArray(byte[] dst, int off, int length)
147 NullPointerException,
148 IndexOutOfBoundsException,
154 int txSize = this.is.read(dst, offset, remain);
156 throw new MmdEofException(this.position);
160 this.position += txSize;
168 * <p>配列要素全ての読み込みが試みられる。
170 * @throws IOException IOエラー
171 * @throws NullPointerException 配列がnull
172 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
173 * @see InputStream#read(byte[])
175 protected void parseByteArray(byte[] dst)
176 throws IOException, NullPointerException, MmdEofException{
177 parseByteArray(dst, 0, dst.length);
182 * 内部バッファへ指定バイト数だけ読み込む。
183 * @param fillSize バイト長
184 * @throws IOException IOエラー
185 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
187 private void fillBuffer(int fillSize)
188 throws IOException, MmdEofException {
189 parseByteArray(this.readBuffer, 0, fillSize);
196 * @throws IOException IOエラー
197 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
199 protected byte parseByte()
200 throws IOException, MmdEofException{
201 int bData = this.is.read();
203 throw new MmdEofException(this.position);
206 byte result = (byte) bData;
213 * 符号無し値としてbyte値を読み込み、int型に変換して返す。
214 * <p>符号は拡張されない。(0xffは0x000000ffとなる)
215 * @return 読み込まれた値のint値
216 * @throws IOException IOエラー
217 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
219 protected int parseUByteAsInt()
220 throws IOException, MmdEofException{
221 return parseByte() & MASK_8BIT;
225 * byte値を読み込み、boolean型に変換して返す。
226 * <p>0x00は偽、それ以外は真と解釈される。
227 * @return 読み込まれた値のboolean値
228 * @throws IOException IOエラー
229 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
231 protected boolean parseBoolean()
232 throws IOException, MmdEofException{
233 byte result = parseByte();
234 if(result == 0x00) return false;
240 * <p>short値はリトルエンディアンで格納されていると仮定される。
241 * @return 読み込んだshort値
242 * @throws IOException IOエラー
243 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
245 protected short parseLeShort()
246 throws IOException, MmdEofException{
247 fillBuffer(BYTES_SHORT);
248 short result = this.leBuf.getShort(0);
253 * 符号無し値としてshort値を読み込み、int型に変換して返す。
254 * <p>符号は拡張されない。(0xffffは0x0000ffffとなる)
255 * <p>short値はリトルエンディアンで格納されていると仮定される。
256 * @return 読み込まれた値のint値
257 * @throws IOException IOエラー
258 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
260 protected int parseLeUShortAsInt()
261 throws IOException, MmdEofException{
262 return parseLeShort() & MASK_16BIT;
267 * <p>int値はリトルエンディアンで格納されていると仮定される。
269 * @throws IOException IOエラー
270 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
272 protected int parseLeInt()
273 throws IOException, MmdEofException{
274 fillBuffer(BYTES_INT);
275 int result = this.leBuf.getInt(0);
281 * <p>float値はリトルエンディアンで格納されていると仮定される。
282 * @return 読み込んだfloat値
283 * @throws IOException IOエラー
284 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
286 protected float parseLeFloat()
287 throws IOException, MmdEofException{
288 fillBuffer(BYTES_FLOAT);
289 float result = this.leBuf.getFloat(0);
295 * @param decoder 文字デコーダ
296 * @param byteLen 読み込む固定バイト長
298 * @throws IOException 入力エラー
299 * @throws MmdEofException 固定長バイト列を読む前に末端に達した。
300 * @throws MmdFormatException 文字エンコーディングに関するエラー
302 protected String parseString(TextDecoder decoder, int byteLen)
303 throws IOException, MmdEofException, MmdFormatException {
304 if(this.btextBuf == null || this.btextBuf.capacity() < byteLen){
305 this.btextBuf = ByteBuffer.allocate(byteLen);
308 byte[] buf = this.btextBuf.array();
309 this.btextBuf.clear();
310 parseByteArray(buf, 0, byteLen);
311 this.btextBuf.limit(byteLen);
316 result = decoder.decode(this.btextBuf);
317 }catch(UnmappableCharacterException e){
318 String errmsg = ERRMSG_UNMAP;
319 long errpos = getPosition() - byteLen + e.getInputLength();
320 MmdFormatException ex = new MmdFormatException(errmsg, errpos);
323 }catch(MalformedInputException e){
324 String errmsg = ERRMSG_ILLENC;
325 long errpos = getPosition() - byteLen + e.getInputLength();
326 MmdFormatException ex = new MmdFormatException(errmsg, errpos);
329 }catch(CharacterCodingException e){ // 状況不明
330 String errmsg = ERRMSG_ILLENC;
331 long errpos = getPosition();
332 MmdFormatException ex = new MmdFormatException(errmsg, errpos);