OSDN Git Service

8ee1a7c8d18c1023fea950931db634c07f68ea01
[mikutoga/TogaGem.git] / src / main / java / jp / sourceforge / mikutoga / parser / CommonParser.java
1 /*
2  * common MMD parser
3  *
4  * License : The MIT License
5  * Copyright(c) 2010 MikuToga Partners
6  */
7
8 package jp.sourceforge.mikutoga.parser;
9
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
17 /**
18  * 各種パーサの共通実装。
19  */
20 public class CommonParser {
21
22     private static final int BYTES_SHORT = Short  .SIZE / Byte.SIZE;
23     private static final int BYTES_INT   = Integer.SIZE / Byte.SIZE;
24     private static final int BYTES_FLOAT = Float  .SIZE / Byte.SIZE;
25     private static final int BYTES_PRIM = 4;
26
27     private static final int MASK_8BIT  =   0xff;
28     private static final int MASK_16BIT = 0xffff;
29
30     static{
31         assert BYTES_PRIM >= BYTES_FLOAT;
32         assert BYTES_PRIM >= BYTES_INT;
33         assert BYTES_PRIM >= BYTES_SHORT;
34     }
35
36
37     private final PushbackInputStream is;
38
39     private final byte[] readBuffer;
40     private final ByteBuffer beBuf;
41     private final ByteBuffer leBuf;
42
43     private long position = 0L;
44
45
46     /**
47      * コンストラクタ。
48      * @param source 入力ソース
49      */
50     public CommonParser(InputStream source){
51         super();
52
53         this.is = new PushbackInputStream(source, 1);
54
55         this.readBuffer = new byte[BYTES_PRIM];
56
57         this.beBuf = ByteBuffer.wrap(this.readBuffer);
58         this.leBuf = ByteBuffer.wrap(this.readBuffer);
59
60         this.beBuf.order(ByteOrder.BIG_ENDIAN);
61         this.leBuf.order(ByteOrder.LITTLE_ENDIAN);
62
63         return;
64     }
65
66
67     /**
68      * 入力ソースの読み込み位置を返す。
69      * @return 入力ソースの読み込み位置。単位はbyte。
70      */
71     protected long getPosition(){
72         long result = this.position;
73         return result;
74     }
75
76     /**
77      * 入力ソースにまだデータが残っているか判定する。
78      * @return まだ読み込んでいないデータが残っていればtrue
79      * @throws IOException IOエラー
80      */
81     public boolean hasMore() throws IOException{
82         int bVal;
83
84         try{
85             bVal = this.is.read();
86         }catch(EOFException e){ // ありえない?
87             return false;
88         }
89
90         if(bVal < 0){
91             return false;
92         }
93
94         this.is.unread(bVal);
95
96         return true;
97     }
98
99     /**
100      * 入力ソースを読み飛ばす。
101      * @param skipLength 読み飛ばすバイト数。
102      * @throws IOException IOエラー
103      * @throws MmdEofException 読み飛ばす途中でストリーム終端に達した。
104      * @see InputStream#skip(long)
105      */
106     protected void skip(long skipLength)
107             throws IOException, MmdEofException {
108         long remain = skipLength;
109
110         while(remain > 0L){
111             long txSize = this.is.skip(remain);
112             if(txSize <= 0L){
113                 throw new MmdEofException(this.position);
114             }
115             remain -= txSize;
116             this.position += txSize;
117         }
118
119         return;
120     }
121
122     /**
123      * byte配列を読み込む。
124      * @param dst 格納先配列
125      * @param off 読み込み開始オフセット
126      * @param length 読み込みバイト数
127      * @throws IOException IOエラー
128      * @throws NullPointerException 配列がnull
129      * @throws IndexOutOfBoundsException 引数が配列属性と矛盾
130      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
131      * @see InputStream#read(byte[], int, int)
132      */
133     protected void parseByteArray(byte[] dst, int off, int length)
134             throws IOException,
135                    NullPointerException,
136                    IndexOutOfBoundsException,
137                    MmdEofException {
138         int remain = length;
139         int offset = off;
140
141         while(remain > 0){
142             int txSize = this.is.read(dst, offset, remain);
143             if(txSize <= 0){
144                 throw new MmdEofException(this.position);
145             }
146             remain -= txSize;
147             offset += txSize;
148             this.position += txSize;
149         }
150
151         return;
152     }
153
154     /**
155      * byte配列を読み込む。
156      * 配列要素全ての読み込みが試みられる。
157      * @param dst 格納先配列
158      * @throws IOException IOエラー
159      * @throws NullPointerException 配列がnull
160      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
161      * @see InputStream#read(byte[])
162      */
163     protected void parseByteArray(byte[] dst)
164             throws IOException, NullPointerException, MmdEofException{
165         parseByteArray(dst, 0, dst.length);
166         return;
167     }
168
169     /**
170      * 内部バッファへ指定バイト数だけ読み込む。
171      * @param fillSize
172      * @throws IOException
173      * @throws MmdEofException
174      */
175     private void fillBuffer(int fillSize)
176             throws IOException, MmdEofException {
177         parseByteArray(this.readBuffer, 0, fillSize);
178         return;
179     }
180
181     /**
182      * byte値を読み込む。
183      * @return 読み込んだbyte値
184      * @throws IOException IOエラー
185      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
186      * @see MmdInputStream#parseByte()
187      */
188     protected byte parseByte()
189             throws IOException, MmdEofException{
190         int bData = this.is.read();
191         if(bData < 0){
192             throw new MmdEofException(this.position);
193         }
194
195         byte result = (byte) bData;
196         this.position++;
197
198         return result;
199     }
200
201     /**
202      * 符号無し値としてbyte値を読み込み、int型に変換して返す。
203      * 符号は拡張されない。(0xffは0x000000ffとなる)
204      * @return 読み込まれた値のint値
205      * @throws IOException IOエラー
206      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
207      * @see MmdInputStream#parseUByteAsInt()
208      */
209     protected int parseUByteAsInt()
210             throws IOException, MmdEofException{
211         return ((int) parseByte()) & MASK_8BIT;
212     }
213
214     /**
215      * byte値を読み込み、boolean型に変換して返す。
216      * 0x00は偽、それ以外は真と解釈される。
217      * @return 読み込まれた値のboolean値
218      * @throws IOException IOエラー
219      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
220      * @see MmdInputStream#parseBoolean()
221      */
222     protected boolean parseBoolean()
223             throws IOException, MmdEofException{
224         byte result = parseByte();
225         if(result == 0x00) return false;
226         return true;
227     }
228
229     /**
230      * short値を読み込む。
231      * short値はリトルエンディアンで格納されていると仮定される。
232      * @return 読み込んだshort値
233      * @throws IOException IOエラー
234      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
235      * @see MmdInputStream#parseShort()
236      */
237     protected short parseLeShort()
238             throws IOException, MmdEofException{
239         fillBuffer(BYTES_SHORT);
240         short result = this.leBuf.getShort(0);
241         return result;
242     }
243
244     /**
245      * 符号無し値としてshort値を読み込み、int型に変換して返す。
246      * 符号は拡張されない。(0xffffは0x0000ffffとなる)
247      * short値はリトルエンディアンで格納されていると仮定される。
248      * @return 読み込まれた値のint値
249      * @throws IOException IOエラー
250      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
251      * @see MmdInputStream#parseUShortAsInteger()
252      */
253     protected int parseLeUShortAsInt()
254             throws IOException, MmdEofException{
255         return ((int) parseLeShort()) & MASK_16BIT;
256     }
257
258     /**
259      * int値を読み込む。
260      * int値はリトルエンディアンで格納されていると仮定される。
261      * @return 読み込んだint値
262      * @throws IOException IOエラー
263      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
264      * @see MmdInputStream#parseInteger()
265      */
266     protected int parseLeInt()
267             throws IOException, MmdEofException{
268         fillBuffer(BYTES_INT);
269         int result = this.leBuf.getInt(0);
270         return result;
271     }
272
273     /**
274      * float値を読み込む。
275      * float値はリトルエンディアンで格納されていると仮定される。
276      * @return 読み込んだfloat値
277      * @throws IOException IOエラー
278      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
279      * @see MmdInputStream#parseFloat()
280      */
281     protected float parseLeFloat()
282             throws IOException, MmdEofException{
283         fillBuffer(BYTES_FLOAT);
284         float result = this.leBuf.getFloat(0);
285         return result;
286     }
287
288     /**
289      * 固定バイト長の文字列を読み込む。
290      * @param decoder 文字デコーダ
291      * @param byteLen 読み込む固定バイト長
292      * @return 文字列
293      * @throws IOException 入力エラー
294      * @throws MmdEofException 固定長バイト列を読む前に末端に達した。
295      * @throws MmdFormatException 文字エンコーディングに関するエラー
296      */
297     protected String parseString(TextDecoder decoder, int byteLen)
298             throws IOException, MmdEofException, MmdFormatException {
299         byte[] buf = decoder.prepareBuffer(byteLen);
300         parseByteArray(buf, 0, byteLen);
301         long basePos = getPosition();
302         String result= decoder.decode(basePos, byteLen);
303         return result;
304     }
305
306 }