4 * License : The MIT License
5 * Copyright(c) 2011 MikuToga Partners
8 package jp.sfjp.mikutoga.bin.export;
10 import java.io.ByteArrayOutputStream;
11 import java.io.Closeable;
12 import java.io.Flushable;
13 import java.io.IOException;
14 import java.io.OutputStream;
15 import java.nio.charset.CharacterCodingException;
16 import java.nio.charset.Charset;
17 import java.text.MessageFormat;
20 * バイナリデータの出力を行う汎用エクスポーター。
22 * <p>基本的にリトルエンディアン形式で出力される。
24 public class BinaryExporter implements Closeable, Flushable{
26 private static final Charset CS_UTF16LE = Charset.forName("UTF-16LE");
27 private static final Charset CS_WIN31J = Charset.forName("windows-31j");
29 private static final String ERRMSG_ILLENC = "illegal encoding";
30 private static final String ERRMSG_TOOLONGTX =
32 + "text \"{0}\" needs {1}bytes encoded but limit={2}bytes";
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;
40 private static final int BUFSZ_PRIM = BYTES_DOUBLE;
42 private static final int IDX0 = 0;
43 private static final int IDX1 = 1;
44 private static final int IDX2 = 2;
45 private static final int IDX3 = 3;
46 private static final int IDX4 = 4;
47 private static final int IDX5 = 5;
48 private static final int IDX6 = 6;
49 private static final int IDX7 = 7;
51 private static final int SH00 = 0;
52 private static final int SH08 = 8;
53 private static final int SH16 = 16;
54 private static final int SH24 = 24;
55 private static final int SH32 = 32;
56 private static final int SH40 = 40;
57 private static final int SH48 = 48;
58 private static final int SH56 = 56;
61 assert BYTES_DOUBLE <= BUFSZ_PRIM;
62 assert BYTES_FLOAT <= BUFSZ_PRIM;
63 assert BYTES_LONG <= BUFSZ_PRIM;
64 assert BYTES_INT <= BUFSZ_PRIM;
65 assert BYTES_SHORT <= BUFSZ_PRIM;
69 private final OutputStream ostream;
71 private final byte[] barray;
73 private final TextExporter texporter_w31j;
74 private final TextExporter texporter_u16le;
75 private final ByteArrayOutputStream xos;
80 * @param ostream 出力ストリーム
81 * @throws NullPointerException 引数がnull
83 public BinaryExporter(OutputStream ostream) throws NullPointerException{
86 if(ostream == null) throw new NullPointerException();
87 this.ostream = ostream;
89 this.barray = new byte[BUFSZ_PRIM];
91 this.texporter_w31j = new TextExporter(CS_WIN31J);
92 this.texporter_u16le = new TextExporter(CS_UTF16LE);
93 this.xos = new ByteArrayOutputStream();
101 * @throws IOException 出力エラー
104 public void close() throws IOException{
105 this.ostream.close();
111 * I/O効率とデバッグ効率のバランスを考え、ご利用は計画的に。
112 * @throws IOException 出力エラー
115 public void flush() throws IOException{
116 this.ostream.flush();
124 * @throws IOException 出力エラー
126 public BinaryExporter dumpByte(byte bVal) throws IOException{
127 this.ostream.write(bVal);
133 * @param iVal int値。上位24bitは捨てられる。
135 * @throws IOException 出力エラー
137 public BinaryExporter dumpByte(int iVal) throws IOException{
138 this.ostream.write(iVal);
146 * @throws IOException 出力エラー
148 public BinaryExporter dumpByteArray(byte[] array)
150 this.ostream.write(array);
157 * @param offset 出力開始位置
158 * @param length 出力バイト数
160 * @throws IOException 出力エラー
162 public BinaryExporter dumpByteArray(byte[] array, int offset, int length)
164 this.ostream.write(array, offset, length);
169 * short値をリトルエンディアンで出力する。
172 * @throws IOException 出力エラー
174 @SuppressWarnings("PMD.AvoidUsingShortType")
175 public BinaryExporter dumpLeShort(short sVal) throws IOException{
176 this.barray[IDX0] = (byte)(sVal >>> SH00);
177 this.barray[IDX1] = (byte)(sVal >>> SH08);
179 this.ostream.write(this.barray, 0, BYTES_SHORT);
185 * short値をリトルエンディアンで出力する。
186 * @param iVal int値。上位16bitは捨てられる。
188 * @throws IOException 出力エラー
190 @SuppressWarnings("PMD.AvoidUsingShortType")
191 public BinaryExporter dumpLeShort(int iVal) throws IOException{
192 short sVal = (short)iVal;
198 * int値をリトルエンディアンで出力する。
201 * @throws IOException 出力エラー
203 public BinaryExporter dumpLeInt(int iVal) throws IOException{
204 this.barray[IDX0] = (byte)(iVal >>> SH00);
205 this.barray[IDX1] = (byte)(iVal >>> SH08);
206 this.barray[IDX2] = (byte)(iVal >>> SH16);
207 this.barray[IDX3] = (byte)(iVal >>> SH24);
209 this.ostream.write(this.barray, 0, BYTES_INT);
215 * long値をリトルエンディアンで出力する。
218 * @throws IOException 出力エラー
220 public BinaryExporter dumpLeLong(long lVal) throws IOException{
221 this.barray[IDX0] = (byte)(lVal >>> SH00);
222 this.barray[IDX1] = (byte)(lVal >>> SH08);
223 this.barray[IDX2] = (byte)(lVal >>> SH16);
224 this.barray[IDX3] = (byte)(lVal >>> SH24);
225 this.barray[IDX4] = (byte)(lVal >>> SH32);
226 this.barray[IDX5] = (byte)(lVal >>> SH40);
227 this.barray[IDX6] = (byte)(lVal >>> SH48);
228 this.barray[IDX7] = (byte)(lVal >>> SH56);
230 this.ostream.write(this.barray, 0, BYTES_LONG);
236 * float値をリトルエンディアンで出力する。
239 * @throws IOException 出力エラー
241 public BinaryExporter dumpLeFloat(float fVal) throws IOException{
242 int rawiVal = Float.floatToRawIntBits(fVal);
248 * double値をリトルエンディアンで出力する。
249 * @param dVal double値
251 * @throws IOException 出力エラー
253 public BinaryExporter dumpLeDouble(double dVal) throws IOException{
254 long rawlVal = Double.doubleToRawLongBits(dVal);
262 * @param filler byte型配列によるパディングデータの並び。
264 * <p>指定パディング長より長い部分は出力されない。
266 * 最後の要素が繰り返し出力される。</p>
268 * <p>配列長が0の場合は何も出力されない。</p>
270 * @param fillerLength パディング長。
272 * <p>パディング長が0以下の場合は何も出力されない。</p>
275 * @throws IOException 出力エラー
277 public BinaryExporter dumpFiller(byte[] filler, int fillerLength)
279 if(filler.length <= 0 || fillerLength <= 0){
283 byte lastData = filler[filler.length - 1];
286 for(int remain = fillerLength; remain > 0; remain--){
288 if(fillerIdx < filler.length) bVal = filler[fillerIdx++];
289 else bVal = lastData;
297 * Windows31J文字列をを固定バイト長で出力する。
298 * 固定バイト長に満たない箇所はパディングデータが詰められる。
300 * @param fixedLength 固定バイト長。0以下の場合は無制限。
301 * @param filler 詰め物パディングデータ
303 * @throws IOException 出力エラー
304 * @throws IllegalTextExportException テキスト出力エラー。
306 * もしくは不正なエンコードが行われたかのいずれか。
308 public BinaryExporter dumpFixedW31j(CharSequence text,
311 throws IOException, IllegalTextExportException{
317 this.texporter_w31j.encodeToByteStream(text, this.xos);
318 }catch(CharacterCodingException e){
319 throw new IllegalTextExportException(ERRMSG_ILLENC, e);
322 if( 0 < fixedLength && fixedLength < encodedSize ){
324 MessageFormat.format(ERRMSG_TOOLONGTX,
325 text, encodedSize, fixedLength);
326 throw new IllegalTextExportException(message);
329 this.xos.writeTo(this.ostream);
330 int xferred = this.xos.size();
332 int remain = fixedLength - xferred;
334 dumpFiller(filler, remain);
341 * UTF16-LE文字列をホレリス形式で出力する。
342 * UTF16-LEエンコード結果のバイト長を
343 * 4byte整数としてリトルエンディアンで出力した後に、
344 * エンコード結果のバイト列が出力される。
347 * @throws IOException 出力エラー
348 * @throws IllegalTextExportException テキスト出力エラー。
350 * もしくは不正なエンコードが行われたかのいずれか。
352 public int dumpHollerithUtf16LE(CharSequence text)
353 throws IOException, IllegalTextExportException{
359 this.texporter_u16le.encodeToByteStream(text, this.xos);
360 }catch(CharacterCodingException e){
361 assert false; // これはない
362 throw new IllegalTextExportException(ERRMSG_ILLENC, e);
365 dumpLeInt(encodedSize);
367 this.xos.writeTo(this.ostream);
368 int xferred = this.xos.size();
370 assert xferred == encodedSize;