OSDN Git Service

XML空白出力処理の共通化
[mikutoga/TogaGem.git] / src / main / java / jp / sourceforge / mikutoga / pmd / model / binio / AbstractExporter.java
1 /*
2  * abstract exporter
3  *
4  * License : The MIT License
5  * Copyright(c) 2010 MikuToga Partners
6  */
7
8 package jp.sourceforge.mikutoga.pmd.model.binio;
9
10 import java.io.IOException;
11 import java.io.OutputStream;
12 import java.nio.BufferOverflowException;
13 import java.nio.ByteBuffer;
14 import java.nio.ByteOrder;
15 import java.nio.CharBuffer;
16 import java.nio.charset.Charset;
17 import java.nio.charset.CharsetEncoder;
18 import java.nio.charset.CoderResult;
19 import java.nio.charset.CodingErrorAction;
20
21 /**
22  * 抽象化されたエクスポーター共通部。
23  */
24 public abstract class AbstractExporter {
25
26     private static final Charset CS_WIN31J = Charset.forName("windows-31j");
27
28     private static final String ERRMSG_TOOLONGTEXT = "too long text";
29     private static final String ERRMSG_INVUCSSEQ = "invalid unicode sequence";
30     private static final String ERRMSG_NONWIN31J = "no character in win31j";
31
32     private static final int BYTES_SHORT = Short  .SIZE / Byte.SIZE;
33     private static final int BYTES_INT   = Integer.SIZE / Byte.SIZE;
34     private static final int BYTES_FLOAT = Float  .SIZE / Byte.SIZE;
35
36     private static final int BUFSZ_BYTE = 512;
37     private static final int BUFSZ_CHAR = 512;
38
39     private final OutputStream ostream;
40
41     private final byte[] barray;
42     private final ByteBuffer bbuf;
43
44     private final CharBuffer cbuf;
45     private final CharsetEncoder encoder;
46
47
48     /**
49      * コンストラクタ。
50      * @param stream 出力ストリーム
51      * @throws NullPointerException 引数がnull
52      */
53     protected AbstractExporter(OutputStream stream)
54             throws NullPointerException{
55         super();
56
57         if(stream == null) throw new NullPointerException();
58         this.ostream = stream;
59
60         this.barray = new byte[BUFSZ_BYTE];
61         this.bbuf = ByteBuffer.wrap(this.barray);
62         this.bbuf.order(ByteOrder.LITTLE_ENDIAN);
63
64         this.cbuf = CharBuffer.allocate(BUFSZ_CHAR);
65         this.encoder = CS_WIN31J.newEncoder();
66         this.encoder.onMalformedInput(CodingErrorAction.REPORT);
67         this.encoder.onUnmappableCharacter(CodingErrorAction.REPORT);
68         this.encoder.reset();
69
70         return;
71     }
72
73     /**
74      * 出力をフラッシュする。
75      * I/O効率とデバッグ効率のバランスを考え、ご利用は計画的に。
76      * @throws IOException 出力エラー
77      */
78     protected void flush() throws IOException{
79         this.ostream.flush();
80         return;
81     }
82
83     /**
84      * byte値を出力する。
85      * @param bVal byte値
86      * @throws IOException 出力エラー
87      */
88     protected void dumpByte(byte bVal) throws IOException{
89         this.ostream.write((int)bVal);
90         return;
91     }
92
93     /**
94      * int値をbyte値で出力する。
95      * byte値に収まらない上位ビットはそのまま捨てられる。
96      * @param iVal int値
97      * @throws IOException 出力エラー
98      */
99     protected void dumpByte(int iVal) throws IOException{
100         dumpByte((byte)iVal);
101         return;
102     }
103
104     /**
105      * short値を出力する。
106      * @param sVal short値
107      * @throws IOException 出力エラー
108      */
109     protected void dumpShort(short sVal) throws IOException{
110         this.bbuf.clear();
111         this.bbuf.putShort(sVal);
112         this.ostream.write(this.barray, 0, BYTES_SHORT);
113         return;
114     }
115
116     /**
117      * int値をshort値で出力する。
118      * short値に収まらない上位ビットはそのまま捨てられる。
119      * @param iVal int値
120      * @throws IOException 出力エラー
121      */
122     protected void dumpShort(int iVal) throws IOException{
123         dumpShort((short)iVal);
124         return;
125     }
126
127     /**
128      * int値を出力する。
129      * @param iVal int値
130      * @throws IOException 出力エラー
131      */
132     protected void dumpInt(int iVal) throws IOException{
133         this.bbuf.clear();
134         this.bbuf.putInt(iVal);
135         this.ostream.write(this.barray, 0, BYTES_INT);
136         return;
137     }
138
139     /**
140      * float値を出力する。
141      * @param fVal float値
142      * @throws IOException 出力エラー
143      */
144     protected void dumpFloat(float fVal) throws IOException{
145         this.bbuf.clear();
146         this.bbuf.putFloat(fVal);
147         this.ostream.write(this.barray, 0, BYTES_FLOAT);
148         return;
149     }
150
151     /**
152      * 文字列をwindows-31jエンコーディングで出力する。
153      * @param seq 文字列
154      * @return 出力されたbyte総数。
155      * @throws IOException 出力エラー
156      * @throws IllegalPmdTextException 文字エンコーディングに関するエラー
157      */
158     protected int dumpCharSequence(CharSequence seq)
159             throws IOException, IllegalPmdTextException{
160         encodeToByteBuffer(seq);
161         this.bbuf.flip();
162         int length = dumpByteBuffer();
163         return length;
164     }
165
166     /**
167      * windows-31jにエンコーディングした文字列を内部バッファに蓄積する。
168      * @param seq 文字列
169      * @throws IllegalPmdTextException 文字エンコーディングに関するエラー。
170      * 考えられる状況としては、
171      * <ul>
172      * <li>入力文字列がUnicodeとしてすでにおかしい。
173      * (サロゲートペアがペアになってないなど)
174      * <li>windows-31jにない文字をエンコーディングしようとした
175      * <li>エンコーディング結果が内部バッファに収まらなかった。
176      * </ul>
177      * など。
178      */
179     private void encodeToByteBuffer(CharSequence seq)
180             throws IllegalPmdTextException{
181         this.cbuf.clear();
182         try{
183             this.cbuf.append(seq);
184         }catch(BufferOverflowException e){
185             throw new IllegalPmdTextException(ERRMSG_TOOLONGTEXT);
186         }
187         this.cbuf.flip();
188
189         this.bbuf.clear();
190
191         CoderResult result = this.encoder.encode(this.cbuf, this.bbuf, true);
192         if( ! result.isUnderflow()){
193             if(result.isOverflow()){
194                 throw new IllegalPmdTextException(ERRMSG_TOOLONGTEXT);
195             }else if(result.isMalformed()){
196                 throw new IllegalPmdTextException(ERRMSG_INVUCSSEQ);
197             }else if(result.isUnmappable()){
198                 throw new IllegalPmdTextException(ERRMSG_NONWIN31J);
199             }else{
200                 assert false;
201                 throw new AssertionError();
202             }
203         }
204
205         return;
206     }
207
208     /**
209      * 内部バッファに蓄積されたbyte値並びを出力する。
210      * @return 出力されたbyte数
211      * @throws IOException 出力エラー
212      */
213     private int dumpByteBuffer() throws IOException{
214         int offset = this.bbuf.position();
215         int limit = this.bbuf.limit();
216         int length = limit - offset;
217         this.ostream.write(this.barray, offset, length);
218
219         this.bbuf.position(limit);
220
221         return length;
222     }
223
224 }