OSDN Git Service

bbbc4733cccb6f093ade1e130129cb5a8de07d77
[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      */
187     protected byte parseByte()
188             throws IOException, MmdEofException{
189         int bData = this.is.read();
190         if(bData < 0){
191             throw new MmdEofException(this.position);
192         }
193
194         byte result = (byte) bData;
195         this.position++;
196
197         return result;
198     }
199
200     /**
201      * 符号無し値としてbyte値を読み込み、int型に変換して返す。
202      * 符号は拡張されない。(0xffは0x000000ffとなる)
203      * @return 読み込まれた値のint値
204      * @throws IOException IOエラー
205      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
206      */
207     protected int parseUByteAsInt()
208             throws IOException, MmdEofException{
209         return ((int) parseByte()) & MASK_8BIT;
210     }
211
212     /**
213      * byte値を読み込み、boolean型に変換して返す。
214      * 0x00は偽、それ以外は真と解釈される。
215      * @return 読み込まれた値のboolean値
216      * @throws IOException IOエラー
217      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
218      */
219     protected boolean parseBoolean()
220             throws IOException, MmdEofException{
221         byte result = parseByte();
222         if(result == 0x00) return false;
223         return true;
224     }
225
226     /**
227      * short値を読み込む。
228      * short値はリトルエンディアンで格納されていると仮定される。
229      * @return 読み込んだshort値
230      * @throws IOException IOエラー
231      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
232      */
233     protected short parseLeShort()
234             throws IOException, MmdEofException{
235         fillBuffer(BYTES_SHORT);
236         short result = this.leBuf.getShort(0);
237         return result;
238     }
239
240     /**
241      * 符号無し値としてshort値を読み込み、int型に変換して返す。
242      * 符号は拡張されない。(0xffffは0x0000ffffとなる)
243      * short値はリトルエンディアンで格納されていると仮定される。
244      * @return 読み込まれた値のint値
245      * @throws IOException IOエラー
246      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
247      */
248     protected int parseLeUShortAsInt()
249             throws IOException, MmdEofException{
250         return ((int) parseLeShort()) & MASK_16BIT;
251     }
252
253     /**
254      * int値を読み込む。
255      * int値はリトルエンディアンで格納されていると仮定される。
256      * @return 読み込んだint値
257      * @throws IOException IOエラー
258      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
259      */
260     protected int parseLeInt()
261             throws IOException, MmdEofException{
262         fillBuffer(BYTES_INT);
263         int result = this.leBuf.getInt(0);
264         return result;
265     }
266
267     /**
268      * float値を読み込む。
269      * float値はリトルエンディアンで格納されていると仮定される。
270      * @return 読み込んだfloat値
271      * @throws IOException IOエラー
272      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
273      */
274     protected float parseLeFloat()
275             throws IOException, MmdEofException{
276         fillBuffer(BYTES_FLOAT);
277         float result = this.leBuf.getFloat(0);
278         return result;
279     }
280
281     /**
282      * 固定バイト長の文字列を読み込む。
283      * @param decoder 文字デコーダ
284      * @param byteLen 読み込む固定バイト長
285      * @return 文字列
286      * @throws IOException 入力エラー
287      * @throws MmdEofException 固定長バイト列を読む前に末端に達した。
288      * @throws MmdFormatException 文字エンコーディングに関するエラー
289      */
290     protected String parseString(TextDecoder decoder, int byteLen)
291             throws IOException, MmdEofException, MmdFormatException {
292         byte[] buf = decoder.prepareBuffer(byteLen);
293         parseByteArray(buf, 0, byteLen);
294         long basePos = getPosition();
295         String result= decoder.decode(basePos, byteLen);
296         return result;
297     }
298
299 }