OSDN Git Service

e3226b9eda898915ae7095eb2259e51f0d6261dd
[mikutoga/TogaGem.git] / src / main / java / jp / sourceforge / mikutoga / binio / BinaryExporter.java
1 /*
2  * binary data exporter
3  *
4  * License : The MIT License
5  * Copyright(c) 2011 MikuToga Partners
6  */
7
8 package jp.sourceforge.mikutoga.binio;
9
10 import java.io.IOException;
11 import java.io.OutputStream;
12 import java.nio.ByteBuffer;
13 import java.nio.ByteOrder;
14 import java.nio.charset.CharacterCodingException;
15 import java.nio.charset.Charset;
16 import java.text.MessageFormat;
17
18 /**
19  * バイナリデータの出力を行う汎用エクスポーター。
20  * <p>デフォルトではリトルエンディアン形式で出力される。
21  */
22 public class BinaryExporter {
23
24     private static final Charset CS_UTF16LE = Charset.forName("UTF-16LE");
25     private static final Charset CS_WIN31J  = Charset.forName("windows-31j");
26
27     private static final String ERRMSG_ILLENC    = "illegal encoding";
28     private static final String ERRMSG_TOOLONGTX =
29               "too long text: "
30             + "text \"{0}\" needs {1}bytes encoded but limit={2}bytes";
31
32     private static final int MASK_16 = 0xffff;
33
34     private static final int BYTES_SHORT  = Short   .SIZE / Byte.SIZE;
35     private static final int BYTES_INT    = Integer .SIZE / Byte.SIZE;
36     private static final int BYTES_LONG   = Long    .SIZE / Byte.SIZE;
37     private static final int BYTES_FLOAT  = Float   .SIZE / Byte.SIZE;
38     private static final int BYTES_DOUBLE = Double  .SIZE / Byte.SIZE;
39
40     private static final int BUFSZ_PRIM = BYTES_DOUBLE;
41
42
43     private final OutputStream ostream;
44
45     private final byte[] barray;
46     private final ByteBuffer primbuf;
47
48     private final TextExporter texporter_w31j;
49     private final TextExporter texporter_u16le;
50     private final FeedableOutputStream xos;
51
52
53     /**
54      * コンストラクタ。
55      * @param ostream 出力ストリーム
56      * @throws NullPointerException 引数がnull
57      */
58     public BinaryExporter(OutputStream ostream) throws NullPointerException{
59         super();
60
61         if(ostream == null) throw new NullPointerException();
62         this.ostream = ostream;
63
64         this.barray = new byte[BUFSZ_PRIM];
65         this.primbuf = ByteBuffer.wrap(this.barray);
66         this.primbuf.order(ByteOrder.LITTLE_ENDIAN);
67
68         this.primbuf.clear();
69
70         this.texporter_w31j  = new TextExporter(CS_WIN31J);
71         this.texporter_u16le = new TextExporter(CS_UTF16LE);
72         this.xos = new FeedableOutputStream();
73
74         return;
75     }
76
77
78     /**
79      * バイトオーダーを設定する。
80      * @param order バイトオーダー
81      */
82     public void setOrder(ByteOrder order){
83         this.primbuf.order(order);
84         return;
85     }
86
87     /**
88      * 設定されたバイトオーダーを返す。
89      * @return 設定されたバイトオーダー
90      */
91     public ByteOrder getOrder(){
92         return this.primbuf.order();
93     }
94
95     /**
96      * 出力ストリームを閉じる。
97      * @throws IOException 出力エラー
98      */
99     public void close() throws IOException{
100         this.ostream.close();
101         return;
102     }
103
104     /**
105      * 出力をフラッシュする。
106      * I/O効率とデバッグ効率のバランスを考え、ご利用は計画的に。
107      * @return this
108      * @throws IOException 出力エラー
109      */
110     public BinaryExporter flush() throws IOException{
111         this.ostream.flush();
112         return this;
113     }
114
115     /**
116      * byte値を出力する。
117      * @param bVal byte値
118      * @return this自身
119      * @throws IOException 出力エラー
120      */
121     public BinaryExporter dumpByte(byte bVal) throws IOException{
122         this.ostream.write((int)bVal);
123         return this;
124     }
125
126     /**
127      * byte値を出力する。
128      * @param iVal int値。上位24bitは捨てられる。
129      * @return this自身
130      * @throws IOException 出力エラー
131      */
132     public BinaryExporter dumpByte(int iVal) throws IOException{
133         this.ostream.write(iVal);
134         return this;
135     }
136
137     /**
138      * byte型配列を出力する。
139      * @param array 配列
140      * @return this自身
141      * @throws IOException 出力エラー
142      */
143     public BinaryExporter dumpByteArray(byte[] array)
144             throws IOException{
145         dumpByteArray(array, 0, array.length);
146         return this;
147     }
148
149     /**
150      * byte型配列の部分列を出力する。
151      * @param array 配列
152      * @param offset 出力開始位置
153      * @param length 出力バイト数
154      * @return this自身
155      * @throws IOException 出力エラー
156      */
157     public BinaryExporter dumpByteArray(byte[] array, int offset, int length)
158             throws IOException{
159         this.ostream.write(array, offset, length);
160         return this;
161     }
162
163     /**
164      * 内部バッファの先頭を出力する。
165      * @param length 出力バイト数
166      * @throws IOException 出力エラー
167      */
168     private void dumpBuffer(int length) throws IOException{
169         this.ostream.write(this.barray, 0, length);
170         return;
171     }
172
173     /**
174      * short値を出力する。
175      * @param sVal short値
176      * @return this自身
177      * @throws IOException 出力エラー
178      */
179     @SuppressWarnings("PMD.AvoidUsingShortType")
180     public BinaryExporter dumpShort(short sVal) throws IOException{
181         this.primbuf.putShort(0, sVal);
182         dumpBuffer(BYTES_SHORT);
183         return this;
184     }
185
186     /**
187      * short値を出力する。
188      * @param iVal int値。上位16bitは捨てられる。
189      * @return this自身
190      * @throws IOException 出力エラー
191      */
192     @SuppressWarnings("PMD.AvoidUsingShortType")
193     public BinaryExporter dumpShort(int iVal) throws IOException{
194         short sVal = (short)(iVal & MASK_16);
195         dumpShort(sVal);
196         return this;
197     }
198
199     /**
200      * int値を出力する。
201      * @param iVal int値
202      * @return this自身
203      * @throws IOException 出力エラー
204      */
205     public BinaryExporter dumpInt(int iVal) throws IOException{
206         this.primbuf.putInt(0, iVal);
207         dumpBuffer(BYTES_INT);
208         return this;
209     }
210
211     /**
212      * long値を出力する。
213      * @param lVal long値
214      * @return this自身
215      * @throws IOException 出力エラー
216      */
217     public BinaryExporter dumpLong(long lVal) throws IOException{
218         this.primbuf.putLong(0, lVal);
219         dumpBuffer(BYTES_LONG);
220         return this;
221     }
222
223     /**
224      * float値を出力する。
225      * @param fVal float値
226      * @return this自身
227      * @throws IOException 出力エラー
228      */
229     public BinaryExporter dumpFloat(float fVal) throws IOException{
230         this.primbuf.putFloat(0, fVal);
231         dumpBuffer(BYTES_FLOAT);
232         return this;
233     }
234
235     /**
236      * double値を出力する。
237      * @param dVal double値
238      * @return this自身
239      * @throws IOException 出力エラー
240      */
241     public BinaryExporter dumpDouble(double dVal) throws IOException{
242         this.primbuf.putDouble(0, dVal);
243         dumpBuffer(BYTES_DOUBLE);
244         return this;
245     }
246
247     /**
248      * 詰め物パディングを出力する。
249      * @param filler byte型配列によるパディングデータの並び。
250      * <p>指定パディング長より長い部分は出力されない。
251      * 指定パディング長に満たない場合は最後の要素が繰り返し出力される。
252      * <p>配列長が0の場合は何も出力されない。
253      * @param fillerLength パディング長。
254      * <p>パディング長が0以下の場合は何も出力されない。
255      * @return this
256      * @throws IOException 出力エラー
257      */
258     public BinaryExporter dumpFiller(byte[] filler, int fillerLength)
259             throws IOException {
260         if(filler.length <= 0 || fillerLength <= 0){
261             return this;
262         }
263
264         byte lastData = filler[filler.length - 1];
265
266         int fillerIdx = 0;
267         for(int remain = fillerLength; remain > 0; remain--){
268             byte bVal;
269             if(fillerIdx < filler.length) bVal = filler[fillerIdx++];
270             else                          bVal = lastData;
271             dumpByte(bVal);
272         }
273
274         return this;
275     }
276
277     /**
278      * Windows31J文字列をを固定バイト長で出力する。
279      * 固定バイト長に満たない箇所はパディングデータが詰められる。
280      * @param text テキスト
281      * @param fixedLength 固定バイト長。0以下の場合は無制限。
282      * @param filler 詰め物パディングデータ
283      * @return this
284      * @throws IOException 出力エラー
285      * @throws IllegalTextExportException テキスト出力エラー。
286      * 出力が固定長を超えようとした、
287      * もしくは不正なエンコードが行われたかのいずれか。
288      */
289     public BinaryExporter dumpFixedW31j(CharSequence text,
290                                           int fixedLength,
291                                           byte[] filler )
292             throws IOException, IllegalTextExportException{
293         this.xos.reset();
294
295         int encodedSize;
296         try{
297             encodedSize =
298                     this.texporter_w31j.encodeToByteStream(text, this.xos);
299         }catch(CharacterCodingException e){
300             throw new IllegalTextExportException(ERRMSG_ILLENC, e);
301         }
302
303         if( 0 < fixedLength && fixedLength < encodedSize ){
304             String message =
305                     MessageFormat.format(ERRMSG_TOOLONGTX,
306                                          text, encodedSize, fixedLength);
307             throw new IllegalTextExportException(message);
308         }
309
310         int xferred = this.xos.feedStored(this.ostream);
311
312         int remain = fixedLength - xferred;
313         if(remain > 0){
314             dumpFiller(filler, remain);
315         }
316
317         return this;
318     }
319
320     /**
321      * UTF16-LE文字列をホレリス形式で出力する。
322      * UTF16-LEエンコード結果のバイト長を
323      * 4byte整数としてリトルエンディアンで出力した後に、
324      * エンコード結果のバイト列が出力される。
325      * @param text 文字列
326      * @return エンコードバイト列長
327      * @throws IOException 出力エラー
328      * @throws IllegalTextExportException テキスト出力エラー。
329      * 出力が固定長を超えようとした、
330      * もしくは不正なエンコードが行われたかのいずれか。
331      */
332     public int dumpHollerithUtf16LE(CharSequence text)
333             throws IOException, IllegalTextExportException{
334         this.xos.reset();
335
336         int encodedSize;
337         try{
338             encodedSize =
339                     this.texporter_u16le.encodeToByteStream(text, this.xos);
340         }catch(CharacterCodingException e){
341             throw new IllegalTextExportException(ERRMSG_ILLENC, e);
342         }
343
344         dumpInt(encodedSize);
345
346         int xferred = this.xos.feedStored(this.ostream);
347         assert xferred == encodedSize;
348
349         return xferred;
350     }
351
352 }