4 * License : The MIT License
\r
5 * Copyright(c) 2010 MikuToga Partners
\r
8 package jp.sourceforge.mikutoga.xml;
\r
10 import java.io.BufferedWriter;
\r
11 import java.io.Closeable;
\r
12 import java.io.Flushable;
\r
13 import java.io.IOException;
\r
14 import java.io.OutputStream;
\r
15 import java.io.OutputStreamWriter;
\r
16 import java.nio.charset.Charset;
\r
17 import javax.xml.bind.DatatypeConverter;
\r
23 public class BasicXmlExporter {
\r
25 /** デフォルトエンコーディング。 */
\r
26 private static final Charset CS_UTF8 = Charset.forName("UTF-8");
\r
29 private static final String LF = "\n"; // 0x0a
\r
30 /** デフォルトのインデント単位。 */
\r
31 private static final String DEFAULT_INDENT_UNIT = "\u0020\u0020";
\r
33 private static final char[] HEXCHAR_TABLE = {
\r
34 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
\r
35 'A', 'B', 'C', 'D', 'E', 'F',
\r
39 assert HEXCHAR_TABLE.length == 16;
\r
43 private final Appendable appendable;
\r
45 private String newline = LF;
\r
46 private String indentUnit = DEFAULT_INDENT_UNIT;
\r
48 private int indentNest = 0;
\r
49 private boolean basicLatinOnlyOut = true;
\r
54 * 文字エンコーディングはUTF-8が用いられる。
\r
55 * @param stream 出力ストリーム
\r
57 public BasicXmlExporter(OutputStream stream){
\r
58 this(stream, CS_UTF8);
\r
64 * @param stream 出力ストリーム
\r
65 * @param charSet 文字エンコーディング指定
\r
67 public BasicXmlExporter(OutputStream stream, Charset charSet){
\r
70 new OutputStreamWriter(stream, charSet)
\r
78 * @param appendable 文字列出力
\r
80 public BasicXmlExporter(Appendable appendable){
\r
82 this.appendable = appendable;
\r
87 * ASCIIコード相当(UCS:Basic-Latin)の文字か否か判定する。
\r
89 * @return Basic-Latin文字ならtrue
\r
91 public static boolean isBasicLatin(char ch){
\r
92 if(ch <= 0x7f) return true;
\r
98 * @param newLine 改行文字列
\r
99 * @throws NullPointerException 引数がnull
\r
101 public void setNewLine(String newLine) throws NullPointerException{
\r
102 if(newLine == null) throw new NullPointerException();
\r
103 this.newline = newLine;
\r
108 * BasicLatin文字だけで出力するか設定する。
\r
109 * BasicLatin以外の文字(≒日本語)をそのまま出力するか
\r
110 * 文字参照で出力するかの設定が可能。
\r
112 * @param bool BasicLatin文字だけで出力するならtrue
\r
114 public void setBasicLatinOnlyOut(boolean bool){
\r
115 this.basicLatinOnlyOut = bool;
\r
120 * BasicLatin文字だけを出力する状態か判定する。
\r
122 * @return BasicLatin文字だけで出力するならtrue
\r
124 public boolean isBasicLatinOnlyOut(){
\r
125 return this.basicLatinOnlyOut;
\r
130 * デフォルトではLF(0x0a)\nが用いられる。
\r
131 * @param seq 改行文字列。nullは空文字列""と解釈される。
\r
133 public void setNewLine(CharSequence seq){
\r
134 if(seq == null) this.newline = "";
\r
135 else this.newline = seq.toString();
\r
142 * @param seq インデント単位文字列。nullは空文字列""と解釈される。
\r
144 public void setIndentUnit(CharSequence seq){
\r
145 if(seq == null) this.indentUnit = "";
\r
146 else this.indentUnit = seq.toString();
\r
150 * 可能であれば出力をフラッシュする。
\r
151 * @throws IOException 出力エラー
\r
153 public void flush() throws IOException{
\r
154 if(this.appendable instanceof Flushable){
\r
155 ((Flushable)this.appendable).flush();
\r
162 * @throws IOException 出力エラー
\r
164 public void close() throws IOException{
\r
165 if(this.appendable instanceof Closeable){
\r
166 ((Closeable)this.appendable).close();
\r
175 * @throws IOException 出力エラー
\r
177 public BasicXmlExporter put(char ch) throws IOException{
\r
178 this.appendable.append(ch);
\r
186 * @throws IOException 出力エラー
\r
188 public BasicXmlExporter put(CharSequence seq) throws IOException{
\r
189 this.appendable.append(seq);
\r
197 * @throws IOException 出力エラー
\r
198 * @see java.lang.Integer#toString(int)
\r
200 public BasicXmlExporter put(int iVal) throws IOException{
\r
201 String value = DatatypeConverter.printInt(iVal);
\r
202 this.appendable.append(value);
\r
208 * @param fVal float値
\r
210 * @throws IOException 出力エラー
\r
211 * @see java.lang.Float#toString(float)
\r
213 public BasicXmlExporter put(float fVal) throws IOException{
\r
214 String value = DatatypeConverter.printFloat(fVal);
\r
215 this.appendable.append(value);
\r
222 * @throws IOException 出力エラー
\r
224 public BasicXmlExporter ln() throws IOException{
\r
225 this.appendable.append(this.newline);
\r
231 * @param count 改行回数。0以下の場合は何も出力しない。
\r
233 * @throws IOException 出力エラー
\r
235 public BasicXmlExporter ln(int count) throws IOException{
\r
236 for(int ct = 1; ct <= count; ct++){
\r
237 this.appendable.append(this.newline);
\r
244 * インデント単位文字列をネストレベル回数分出力する。
\r
246 * @throws IOException 出力エラー
\r
248 public BasicXmlExporter ind() throws IOException{
\r
249 for(int ct = 1; ct <= this.indentNest; ct++){
\r
250 put(this.indentUnit);
\r
258 public void pushNest(){
\r
265 * インデントレベル0の状態をさらに上げようとした場合、何も起こらない。
\r
267 public void popNest(){
\r
269 if(this.indentNest < 0) this.indentNest = 0;
\r
274 * 指定された文字を16進2桁の文字参照形式で出力する。
\r
275 * 2桁で出力できない場合は4桁で出力する。
\r
278 * @throws IOException 出力エラー
\r
280 public BasicXmlExporter putCharRef2Hex(char ch) throws IOException{
\r
281 if(ch > 0xff) return putCharRef4Hex(ch);
\r
283 char hex3 = HEXCHAR_TABLE[(ch >> 4) & 0x000f];
\r
284 char hex4 = HEXCHAR_TABLE[(ch ) & 0x000f];
\r
286 this.appendable.append("&#x");
\r
287 this.appendable.append(hex3);
\r
288 this.appendable.append(hex4);
\r
289 this.appendable.append(';');
\r
295 * 指定された文字を16進4桁の文字参照形式で出力する。
\r
296 * UCS4に伴うサロゲートペアは未サポート
\r
299 * @throws IOException 出力エラー
\r
301 public BasicXmlExporter putCharRef4Hex(char ch) throws IOException{
\r
302 char hex1 = HEXCHAR_TABLE[(ch >> 12) & 0x000f];
\r
303 char hex2 = HEXCHAR_TABLE[(ch >> 8) & 0x000f];
\r
304 char hex3 = HEXCHAR_TABLE[(ch >> 4) & 0x000f];
\r
305 char hex4 = HEXCHAR_TABLE[(ch ) & 0x000f];
\r
307 this.appendable.append("&#x");
\r
308 this.appendable.append(hex1);
\r
309 this.appendable.append(hex2);
\r
310 this.appendable.append(hex3);
\r
311 this.appendable.append(hex4);
\r
312 this.appendable.append(';');
\r
318 * 要素の中身および属性値中身を出力する。
\r
319 * 必要に応じてXML定義済み実体文字が割り振られた文字、
\r
320 * コントロールコード、および非BasicLatin文字がエスケープされる。
\r
321 * @param content 内容
\r
323 * @throws IOException 出力エラー
\r
325 public BasicXmlExporter putContent(CharSequence content)
\r
326 throws IOException{
\r
327 int length = content.length();
\r
329 for(int pos = 0; pos < length; pos++){
\r
330 char ch = content.charAt(pos);
\r
331 if(Character.isISOControl(ch)){
\r
332 putCharRef2Hex(ch);
\r
333 }else if( ! isBasicLatin(ch) && isBasicLatinOnlyOut()){
\r
334 putCharRef4Hex(ch);
\r
337 case '&': this.appendable.append("&"); break;
\r
338 case '<': this.appendable.append("<"); break;
\r
339 case '>': this.appendable.append(">"); break;
\r
340 case '"': this.appendable.append("""); break;
\r
341 case '\'': this.appendable.append("'"); break;
\r
342 case '\u00a5': this.appendable.append('\u005c\u005c'); break;
\r
343 default: this.appendable.append(ch); break;
\r
353 * @param attrName 属性名
\r
354 * @param content 属性内容
\r
356 * @throws IOException 出力エラー
\r
358 public BasicXmlExporter putAttr(CharSequence attrName,
\r
359 CharSequence content)
\r
360 throws IOException{
\r
361 put(attrName).put('=').put('"').putContent(content).put('"');
\r
367 * @param attrName 属性名
\r
370 * @throws IOException 出力エラー
\r
372 public BasicXmlExporter putIntAttr(CharSequence attrName,
\r
374 throws IOException{
\r
375 put(attrName).put('=').put('"').put(iVal).put('"');
\r
381 * @param attrName 属性名
\r
382 * @param fVal float値
\r
384 * @throws IOException 出力エラー
\r
386 public BasicXmlExporter putFloatAttr(CharSequence attrName,
\r
388 throws IOException{
\r
389 put(attrName).put('=').put('"').put(fVal).put('"');
\r
396 * あらかじめ指定された改行文字が出力される。
\r
397 * \n以外のコントロールコード各種、
\r
398 * 及び非BasicLatin文字はそのまま出力される。
\r
399 * 連続するハイフン(-)記号間には強制的にスペースが挿入される。
\r
400 * @param comment コメント内容
\r
402 * @throws IOException 出力エラー
\r
404 public BasicXmlExporter putCommentContent(CharSequence comment)
\r
405 throws IOException{
\r
406 int length = comment.length();
\r
409 for(int pos = 0; pos < length; pos++){
\r
410 char ch = comment.charAt(pos);
\r
416 if(prev == '-' && ch == '-') put(' ');
\r
427 * あらかじめ指定された改行文字が出力される。
\r
428 * \n以外のコントロールコード各種、
\r
429 * 及び非BasicLatin文字はそのまま出力される。
\r
430 * 連続するハイフン(-)記号間には強制的にスペースが挿入される。
\r
431 * @param comment コメント内容
\r
433 * @throws IOException 出力エラー
\r
435 public BasicXmlExporter putLineComment(CharSequence comment)
\r
436 throws IOException{
\r
437 put("<!--").put(' ');
\r
438 putCommentContent(comment);
\r
439 put(' ').put("-->");
\r
446 * あらかじめ指定された改行文字が出力される。
\r
447 * \n以外のコントロールコード各種、
\r
448 * 及び非BasicLatin文字はそのまま出力される。
\r
449 * 連続するハイフン(-)記号間には強制的にスペースが挿入される。
\r
450 * @param comment コメント内容
\r
452 * @throws IOException 出力エラー
\r
454 public BasicXmlExporter putBlockComment(CharSequence comment)
\r
455 throws IOException{
\r
458 putCommentContent(comment);
\r
460 int commentLength = comment.length();
\r
461 if(commentLength > 0){
\r
462 char lastCh = comment.charAt(commentLength - 1);
\r
463 if(lastCh != '\n') ln();
\r