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 * バイナリデータの出力を行う汎用エクスポーター。
21 * <p>基本的にリトルエンディアン形式で出力される。
23 public class BinaryExporter implements Closeable, Flushable{
25 private static final Charset CS_UTF16LE = Charset.forName("UTF-16LE");
26 private static final Charset CS_WIN31J = Charset.forName("windows-31j");
28 private static final String ERRMSG_ILLENC = "illegal encoding";
29 private static final String ERRMSG_TOOLONGTX =
31 + "text \"{0}\" needs {1}bytes encoded but limit={2}bytes";
33 private static final int BYTES_SHORT = Short .SIZE / Byte.SIZE;
34 private static final int BYTES_INT = Integer .SIZE / Byte.SIZE;
35 private static final int BYTES_LONG = Long .SIZE / Byte.SIZE;
36 private static final int BYTES_FLOAT = Float .SIZE / Byte.SIZE;
37 private static final int BYTES_DOUBLE = Double .SIZE / Byte.SIZE;
39 private static final int BUFSZ_PRIM = BYTES_DOUBLE;
41 private static final int IDX0 = 0;
42 private static final int IDX1 = 1;
43 private static final int IDX2 = 2;
44 private static final int IDX3 = 3;
45 private static final int IDX4 = 4;
46 private static final int IDX5 = 5;
47 private static final int IDX6 = 6;
48 private static final int IDX7 = 7;
50 private static final int SH00 = 0;
51 private static final int SH08 = 8;
52 private static final int SH16 = 16;
53 private static final int SH24 = 24;
54 private static final int SH32 = 32;
55 private static final int SH40 = 40;
56 private static final int SH48 = 48;
57 private static final int SH56 = 56;
60 assert BYTES_DOUBLE <= BUFSZ_PRIM;
61 assert BYTES_FLOAT <= BUFSZ_PRIM;
62 assert BYTES_LONG <= BUFSZ_PRIM;
63 assert BYTES_INT <= BUFSZ_PRIM;
64 assert BYTES_SHORT <= BUFSZ_PRIM;
68 private final OutputStream ostream;
70 private final byte[] barray;
72 private final TextExporter texporter_w31j;
73 private final TextExporter texporter_u16le;
74 private final ByteArrayOutputStream xos;
79 * @param ostream 出力ストリーム
80 * @throws NullPointerException 引数がnull
82 public BinaryExporter(OutputStream ostream) throws NullPointerException{
85 if(ostream == null) throw new NullPointerException();
86 this.ostream = ostream;
88 this.barray = new byte[BUFSZ_PRIM];
90 this.texporter_w31j = new TextExporter(CS_WIN31J);
91 this.texporter_u16le = new TextExporter(CS_UTF16LE);
92 this.xos = new ByteArrayOutputStream();
100 * @throws IOException 出力エラー
103 public void close() throws IOException{
104 this.ostream.close();
110 * I/O効率とデバッグ効率のバランスを考え、ご利用は計画的に。
111 * @throws IOException 出力エラー
114 public void flush() throws IOException{
115 this.ostream.flush();
123 * @throws IOException 出力エラー
125 public BinaryExporter dumpByte(byte bVal) throws IOException{
126 this.ostream.write(bVal);
132 * @param iVal int値。上位24bitは捨てられる。
134 * @throws IOException 出力エラー
136 public BinaryExporter dumpByte(int iVal) throws IOException{
137 this.ostream.write(iVal);
145 * @throws IOException 出力エラー
147 public BinaryExporter dumpByteArray(byte[] array)
149 this.ostream.write(array);
156 * @param offset 出力開始位置
157 * @param length 出力バイト数
159 * @throws IOException 出力エラー
161 public BinaryExporter dumpByteArray(byte[] array, int offset, int length)
163 this.ostream.write(array, offset, length);
168 * short値をリトルエンディアンで出力する。
171 * @throws IOException 出力エラー
173 @SuppressWarnings("PMD.AvoidUsingShortType")
174 public BinaryExporter dumpLeShort(short sVal) throws IOException{
175 this.barray[IDX0] = (byte)(sVal >>> SH00);
176 this.barray[IDX1] = (byte)(sVal >>> SH08);
178 this.ostream.write(this.barray, 0, BYTES_SHORT);
184 * short値をリトルエンディアンで出力する。
185 * @param iVal int値。上位16bitは捨てられる。
187 * @throws IOException 出力エラー
189 @SuppressWarnings("PMD.AvoidUsingShortType")
190 public BinaryExporter dumpLeShort(int iVal) throws IOException{
191 short sVal = (short)iVal;
197 * int値をリトルエンディアンで出力する。
200 * @throws IOException 出力エラー
202 public BinaryExporter dumpLeInt(int iVal) throws IOException{
203 this.barray[IDX0] = (byte)(iVal >>> SH00);
204 this.barray[IDX1] = (byte)(iVal >>> SH08);
205 this.barray[IDX2] = (byte)(iVal >>> SH16);
206 this.barray[IDX3] = (byte)(iVal >>> SH24);
208 this.ostream.write(this.barray, 0, BYTES_INT);
214 * long値をリトルエンディアンで出力する。
217 * @throws IOException 出力エラー
219 public BinaryExporter dumpLeLong(long lVal) throws IOException{
220 this.barray[IDX0] = (byte)(lVal >>> SH00);
221 this.barray[IDX1] = (byte)(lVal >>> SH08);
222 this.barray[IDX2] = (byte)(lVal >>> SH16);
223 this.barray[IDX3] = (byte)(lVal >>> SH24);
224 this.barray[IDX4] = (byte)(lVal >>> SH32);
225 this.barray[IDX5] = (byte)(lVal >>> SH40);
226 this.barray[IDX6] = (byte)(lVal >>> SH48);
227 this.barray[IDX7] = (byte)(lVal >>> SH56);
229 this.ostream.write(this.barray, 0, BYTES_LONG);
235 * float値をリトルエンディアンで出力する。
238 * @throws IOException 出力エラー
240 public BinaryExporter dumpLeFloat(float fVal) throws IOException{
241 int rawiVal = Float.floatToRawIntBits(fVal);
247 * double値をリトルエンディアンで出力する。
248 * @param dVal double値
250 * @throws IOException 出力エラー
252 public BinaryExporter dumpLeDouble(double dVal) throws IOException{
253 long rawlVal = Double.doubleToRawLongBits(dVal);
260 * @param filler byte型配列によるパディングデータの並び。
261 * <p>指定パディング長より長い部分は出力されない。
262 * 指定パディング長に満たない場合は最後の要素が繰り返し出力される。
263 * <p>配列長が0の場合は何も出力されない。
264 * @param fillerLength パディング長。
265 * <p>パディング長が0以下の場合は何も出力されない。
267 * @throws IOException 出力エラー
269 public BinaryExporter dumpFiller(byte[] filler, int fillerLength)
271 if(filler.length <= 0 || fillerLength <= 0){
275 byte lastData = filler[filler.length - 1];
278 for(int remain = fillerLength; remain > 0; remain--){
280 if(fillerIdx < filler.length) bVal = filler[fillerIdx++];
281 else bVal = lastData;
289 * Windows31J文字列をを固定バイト長で出力する。
290 * 固定バイト長に満たない箇所はパディングデータが詰められる。
292 * @param fixedLength 固定バイト長。0以下の場合は無制限。
293 * @param filler 詰め物パディングデータ
295 * @throws IOException 出力エラー
296 * @throws IllegalTextExportException テキスト出力エラー。
298 * もしくは不正なエンコードが行われたかのいずれか。
300 public BinaryExporter dumpFixedW31j(CharSequence text,
303 throws IOException, IllegalTextExportException{
309 this.texporter_w31j.encodeToByteStream(text, this.xos);
310 }catch(CharacterCodingException e){
311 throw new IllegalTextExportException(ERRMSG_ILLENC, e);
314 if( 0 < fixedLength && fixedLength < encodedSize ){
316 MessageFormat.format(ERRMSG_TOOLONGTX,
317 text, encodedSize, fixedLength);
318 throw new IllegalTextExportException(message);
321 this.xos.writeTo(this.ostream);
322 int xferred = this.xos.size();
324 int remain = fixedLength - xferred;
326 dumpFiller(filler, remain);
333 * UTF16-LE文字列をホレリス形式で出力する。
334 * UTF16-LEエンコード結果のバイト長を
335 * 4byte整数としてリトルエンディアンで出力した後に、
336 * エンコード結果のバイト列が出力される。
339 * @throws IOException 出力エラー
340 * @throws IllegalTextExportException テキスト出力エラー。
342 * もしくは不正なエンコードが行われたかのいずれか。
344 public int dumpHollerithUtf16LE(CharSequence text)
345 throws IOException, IllegalTextExportException{
351 this.texporter_u16le.encodeToByteStream(text, this.xos);
352 }catch(CharacterCodingException e){
353 assert false; // これはない
354 throw new IllegalTextExportException(ERRMSG_ILLENC, e);
357 dumpLeInt(encodedSize);
359 this.xos.writeTo(this.ostream);
360 int xferred = this.xos.size();
362 assert xferred == encodedSize;