OSDN Git Service

0c9dcce921455be309043e21964efb409867e976
[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.IOException;
11 import java.nio.CharBuffer;
12 import java.nio.charset.Charset;
13
14 /**
15  * 各種パーサの共通実装。
16  */
17 public class CommonParser {
18
19     /**
20      * PMDで用いられる文字エンコーディング(windows-31j)。
21      * ほぼShift_JISのスーパーセットと思ってよい。
22      * デコード結果はUCS-2集合に収まるはず。
23      */
24     public static final Charset CS_WIN31J = Charset.forName("windows-31j");
25
26     /** PMXで用いられる文字エンコーディング(UTF-8)。 */
27     public static final Charset CS_UTF8 = Charset.forName("UTF-8");
28
29     /** PMXで用いられる文字エンコーディング(UTF-16のリトルエンディアン)。 */
30     public static final Charset CS_UTF16LE = Charset.forName("UTF-16LE");
31
32     private static final int MASK_8BIT  =   0xff;
33     private static final int MASK_16BIT = 0xffff;
34
35
36     private final MmdInputStream is;
37
38     private final TextDecoder decoderWin31j  = new TextDecoder(CS_WIN31J);
39     private final TextDecoder decoderUTF8    = new TextDecoder(CS_UTF8);
40     private final TextDecoder decoderUTF16LE = new TextDecoder(CS_UTF16LE);
41
42     /**
43      * コンストラクタ。
44      * @param source 入力ソース
45      */
46     public CommonParser(MmdInputStream source){
47         super();
48
49         this.is = source;
50
51         this.decoderWin31j .setZeroChopMode(true);
52         this.decoderUTF8   .setZeroChopMode(false);
53         this.decoderUTF16LE.setZeroChopMode(false);
54
55         return;
56     }
57
58     /**
59      * 入力ソースを返す。
60      * @return 入力ソース
61      */
62     protected MmdInputStream getSource(){
63         return this.is;
64     }
65
66     /**
67      * 入力ソースの読み込み位置を返す。
68      * @return 入力ソースの読み込み位置。単位はbyte。
69      */
70     protected long getPosition(){
71         long result = this.is.getPosition();
72         return result;
73     }
74
75     /**
76      * 入力ソースにまだデータが残っているか判定する。
77      * @return まだ読み込んでいないデータが残っていればtrue
78      * @throws IOException IOエラー
79      * @see MmdInputStream#hasMore()
80      */
81     protected boolean hasMore() throws IOException{
82         boolean result = this.is.hasMore();
83         return result;
84     }
85
86     /**
87      * 入力ソースを読み飛ばす。
88      * @param skipLength 読み飛ばすバイト数。
89      * @throws IOException IOエラー
90      * @throws MmdEofException 読み飛ばす途中でストリーム終端に達した。
91      * @see MmdInputStream#skip(long)
92      */
93     protected void skip(long skipLength)
94             throws IOException, MmdEofException {
95         long result = this.is.skipRepeat(skipLength);
96         if(result != skipLength){
97             throw new MmdEofException(this.is.getPosition());
98         }
99
100         return;
101     }
102
103     /**
104      * 入力ソースを読み飛ばす。
105      * @param skipLength 読み飛ばすバイト数。
106      * @throws IOException IOエラー
107      * @throws MmdEofException 読み飛ばす途中でストリーム終端に達した。
108      * @see MmdInputStream#skip(long)
109      */
110     protected void skip(int skipLength)
111             throws IOException, MmdEofException {
112         skip((long) skipLength);
113     }
114
115     /**
116      * byte値を読み込む。
117      * @return 読み込んだbyte値
118      * @throws IOException IOエラー
119      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
120      * @see MmdInputStream#parseByte()
121      */
122     protected byte parseByte()
123             throws IOException, MmdEofException{
124         return this.is.parseByte();
125     }
126
127     /**
128      * 符号無し値としてbyte値を読み込み、int型に変換して返す。
129      * 符号は拡張されない。(0xffは0x000000ffとなる)
130      * @return 読み込まれた値のint値
131      * @throws IOException IOエラー
132      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
133      * @see MmdInputStream#parseUByteAsInt()
134      */
135     protected int parseUByteAsInt()
136             throws IOException, MmdEofException{
137         return ((int) parseByte()) & MASK_8BIT;
138     }
139
140     /**
141      * byte値を読み込み、boolean型に変換して返す。
142      * 0x00は偽、それ以外は真と解釈される。
143      * @return 読み込まれた値のboolean値
144      * @throws IOException IOエラー
145      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
146      * @see MmdInputStream#parseBoolean()
147      */
148     protected boolean parseBoolean()
149             throws IOException, MmdEofException{
150         return this.is.parseBoolean();
151     }
152
153     /**
154      * short値を読み込む。
155      * short値はリトルエンディアンで格納されていると仮定される。
156      * @return 読み込んだshort値
157      * @throws IOException IOエラー
158      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
159      * @see MmdInputStream#parseShort()
160      */
161     protected short parseLeShort()
162             throws IOException, MmdEofException{
163         return this.is.parseLeShort();
164     }
165
166     /**
167      * 符号無し値としてshort値を読み込み、int型に変換して返す。
168      * 符号は拡張されない。(0xffffは0x0000ffffとなる)
169      * short値はリトルエンディアンで格納されていると仮定される。
170      * @return 読み込まれた値のint値
171      * @throws IOException IOエラー
172      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
173      * @see MmdInputStream#parseUShortAsInteger()
174      */
175     protected int parseLeUShortAsInt()
176             throws IOException, MmdEofException{
177         return ((int) parseLeShort()) & MASK_16BIT;
178     }
179
180     /**
181      * int値を読み込む。
182      * int値はリトルエンディアンで格納されていると仮定される。
183      * @return 読み込んだint値
184      * @throws IOException IOエラー
185      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
186      * @see MmdInputStream#parseInteger()
187      */
188     protected int parseLeInt()
189             throws IOException, MmdEofException{
190         return this.is.parseLeInt();
191     }
192
193     /**
194      * float値を読み込む。
195      * float値はリトルエンディアンで格納されていると仮定される。
196      * @return 読み込んだfloat値
197      * @throws IOException IOエラー
198      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
199      * @see MmdInputStream#parseFloat()
200      */
201     protected float parseLeFloat()
202             throws IOException, MmdEofException{
203         return this.is.parseLeFloat();
204     }
205
206     /**
207      * byte配列を読み込む。
208      * @param dst 格納先配列
209      * @param offset 読み込み開始オフセット
210      * @param length 読み込みバイト数
211      * @throws IOException IOエラー
212      * @throws NullPointerException 配列がnull
213      * @throws IndexOutOfBoundsException 引数が配列属性と矛盾
214      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
215      * @see MmdInputStream#parseByteArray(byte[], int, int)
216      */
217     protected void parseByteArray(byte[] dst, int offset, int length)
218             throws IOException,
219                    NullPointerException,
220                    IndexOutOfBoundsException,
221                    MmdEofException {
222         int readSize = this.is.read(dst, offset, length);
223         if(readSize != length){
224             throw new MmdEofException(this.is.getPosition());
225         }
226
227         return;
228     }
229
230     /**
231      * byte配列を読み込む。
232      * 配列要素全ての読み込みが試みられる。
233      * @param dst 格納先配列
234      * @throws IOException IOエラー
235      * @throws NullPointerException 配列がnull
236      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
237      * @see MmdInputStream#parseByteArray(byte[])
238      */
239     protected void parseByteArray(byte[] dst)
240             throws IOException, NullPointerException, MmdEofException{
241         parseByteArray(dst, 0, dst.length);
242         return;
243     }
244
245     /**
246      * 指定された最大バイト長に収まるゼロ終端(0x00)文字列を読み込む。
247      * 入力バイト列はwindows-31jエンコーディングとして解釈される。
248      * ゼロ終端以降のデータは無視されるが、
249      * IO入力は指定バイト数だけ読み進められる。
250      * ゼロ終端が見つからないまま指定バイト数が読み込み終わった場合、
251      * そこまでのデータから文字列を構成する。
252      * @param maxlen 読み込みバイト数
253      * @return デコードされた文字列
254      * @throws IOException IOエラー
255      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
256      * @throws MmdFormatException 不正な文字エンコーディングが検出された。
257      */
258     protected String parseZeroTermWin31J(int maxlen)
259             throws IOException,
260                    MmdEofException,
261                    MmdFormatException {
262         CharBuffer encoded =
263                 this.decoderWin31j.parseString(this.is, maxlen);
264
265         String result = encoded.toString();
266
267         return result;
268     }
269
270     /**
271      * 4byte整数によるバイト列長とそれに続くUTF8バイト列を
272      * 文字にデコードする。
273      * @return デコードされた文字列。
274      * @throws IOException IOエラー
275      * @throws MmdEofException 予期せぬ入力終端
276      * @throws MmdFormatException 不正な文字エンコーディングが検出された。
277      */
278     protected String parseHollerithUtf8()
279             throws IOException,
280                    MmdEofException,
281                    MmdFormatException {
282         int byteLen = this.is.parseLeInt();
283
284         CharBuffer encoded =
285                 this.decoderUTF8.parseString(this.is, byteLen);
286
287         String result = encoded.toString();
288
289         return result;
290     }
291
292     /**
293      * 4byte整数によるバイト列長とそれに続くUTF16-LEバイト列を
294      * 文字にデコードする。
295      * @return デコードされた文字列。
296      * @throws IOException IOエラー
297      * @throws MmdEofException 予期せぬ入力終端
298      * @throws MmdFormatException 不正な文字エンコーディングが検出された。
299      */
300     protected String parseHollerithUtf16LE()
301             throws IOException,
302                    MmdEofException,
303                    MmdFormatException {
304         int byteLen = this.is.parseLeInt();
305
306         CharBuffer encoded =
307                 this.decoderUTF16LE.parseString(this.is, byteLen);
308
309         String result = encoded.toString();
310
311         return result;
312     }
313
314 }