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.IOException;
12 import java.io.OutputStream;
13 import java.nio.ByteBuffer;
14 import java.nio.CharBuffer;
15 import java.nio.charset.CharacterCodingException;
16 import java.nio.charset.Charset;
17 import java.nio.charset.CharsetEncoder;
18 import java.nio.charset.CoderResult;
19 import java.nio.charset.CodingErrorAction;
22 * 任意のエンコーダによるテキストデータのバイナリ出力を行う。
24 public class TextExporter {
26 /** デフォルトの入力バッファサイズ(単位:char)。 */
27 public static final int DEFBUFSZ_CHAR = 128;
28 /** デフォルトの出力バッファサイズ(単位:byte)。 */
29 public static final int DEFBUFSZ_BYTE = 128;
31 private static final String DUMMYTXT = "";
34 private final CharsetEncoder encoder;
35 private CharBuffer cbuf = CharBuffer.allocate(DEFBUFSZ_CHAR);
36 private byte[] barray = new byte[DEFBUFSZ_BYTE];
37 private ByteBuffer bbuf = ByteBuffer.wrap(this.barray);
39 private CharSequence textData = DUMMYTXT;
40 private int textLength;
46 * @param encoder エンコーダ
47 * @throws NullPointerException 引数がnull
49 public TextExporter(CharsetEncoder encoder) throws NullPointerException{
52 if(encoder == null) throw new NullPointerException();
53 this.encoder = encoder;
55 this.encoder.onMalformedInput(CodingErrorAction.REPORT);
56 this.encoder.onUnmappableCharacter(CodingErrorAction.REPORT);
68 public TextExporter(Charset cs){
69 this(cs.newEncoder());
77 public CharsetEncoder getEncoder(){
83 * @param newSize バッファサイズ。(単位:char)
84 * @throws IllegalArgumentException サイズ指定が正で無かった。
86 public void setCharBufSize(int newSize)
87 throws IllegalArgumentException {
88 if(newSize <= 0) throw new IllegalArgumentException();
90 this.cbuf = CharBuffer.allocate(newSize);
98 * 最低限必要な出力バッファサイズはエンコード設定により異なる。
99 * @param newSize バッファサイズ。(単位:byte)
100 * @throws IllegalArgumentException サイズ指定が小さすぎる。
102 public void setByteBufSize(int newSize)
103 throws IllegalArgumentException {
104 float ratio = this.encoder.maxBytesPerChar();
105 int minSz = (int)( StrictMath.floor(ratio) );
106 if(newSize < minSz) throw new IllegalArgumentException();
108 this.barray = new byte[newSize];
109 this.bbuf = ByteBuffer.wrap(this.barray);
116 * 与えられた文字列をエンコードしてストリームに出力する。
120 * @throws IOException 出力エラー
121 * @throws CharacterCodingException エンコードエラー
123 public int dumpText(CharSequence text, OutputStream os)
124 throws IOException, CharacterCodingException {
125 this.textData = text;
129 total = dumpTextImpl(os);
131 this.textData = DUMMYTXT;
138 * 文字列をエンコードしてストリームに出力する。
141 * @throws IOException 出力エラー
142 * @throws CharacterCodingException エンコードエラー
144 private int dumpTextImpl(OutputStream os)
145 throws IOException, CharacterCodingException {
152 CoderResult result = encode();
153 if(result.isUnderflow()){
155 if( ! hasMoreInput() ){
156 total += sweepByteBuffer(os);
159 }else if(result.isOverflow()){
160 total += sweepByteBuffer(os);
162 }else if(result.isError()){
163 result.throwException();
175 private void reset(){
179 this.encoder.reset();
181 this.textLength = this.textData.length();
188 * 入力バッファにまだ入力していない文字があるか判定する。
189 * @return 入力バッファにまだ入力していない文字があればtrue
191 private boolean hasMoreInput(){
192 if(this.inPos < this.textLength) return true;
200 * 入力文字列がなくなるまでバッファが埋められる。
202 private void loadCharBuffer(){
203 while(this.cbuf.hasRemaining() && hasMoreInput()){
204 char ch = this.textData.charAt(this.inPos++);
215 private CoderResult encode(){
217 if(hasMoreInput()) endOfInput = false;
218 else endOfInput = true;
221 result = this.encoder.encode(this.cbuf, this.bbuf, endOfInput);
229 * @throws IOException 出力エラー
231 private int sweepByteBuffer(OutputStream os) throws IOException{
234 int total = this.bbuf.remaining();
235 os.write(this.barray, 0, total);
246 * @throws IOException 出力エラー
247 * @throws CharacterCodingException エンコーディングエラー
249 private int flush(OutputStream os)
250 throws IOException, CharacterCodingException {
255 result = this.encoder.flush(this.bbuf);
256 if(result.isError()) result.throwException();
258 total += sweepByteBuffer(os);
260 }while( ! result.isUnderflow() );
266 * 与えられた文字列のエンコード結果を格納先バイトストリームへ格納する。
268 * <p>エンコード結果は格納先ストリームに追記される。
271 * @param bos 格納先ストリーム
272 * @return エンコードしたバイト数。
273 * @throws CharacterCodingException エンコードエラー
275 @SuppressWarnings("PMD.AvoidRethrowingException")
276 public int encodeToByteStream(CharSequence text,
277 ByteArrayOutputStream bos )
278 throws CharacterCodingException {
281 result = dumpText(text, bos);
282 }catch(CharacterCodingException e){
284 }catch(IOException e){
287 throw new AssertionError(e);