2 * abstract xml exporter
4 * License : The MIT License
5 * Copyright(c) 2013 MikuToga Partners
8 package jp.sfjp.mikutoga.xml;
10 import java.io.IOException;
13 * Appendable実装に依存したXMLエクスポータの半実装。
16 abstract class AbstractXmlExporter implements XmlExporter{
19 private static final String DEF_NL = "\n"; // 0x0a(LF)
21 private static final String DEF_INDENT_UNIT = "\u0020\u0020"; // ␣␣
23 private static final char CH_SP = '\u0020'; // ␣
24 private static final char CH_YEN = '\u00a5'; // ¥
25 private static final char CH_BSLASH = (char)0x005c; // \
26 private static final char CH_DQ = '\u0022'; // "
27 private static final char CH_SQ = (char)0x0027; // '
28 private static final char CH_EQ = '='; // =
29 private static final char CH_LT = '<';
30 private static final char CH_GT = '>';
32 private static final String COMM_START = "<!--";
33 private static final String COMM_END = "-->";
35 private static final String REF_HEX = "&#x";
36 private static final int HEX_EXP = 4; // 2 ** 4 == 16
37 private static final int MASK_1HEX = (1 << HEX_EXP) - 1; // 0b00001111
38 private static final int MAX_OCTET = (1 << Byte.SIZE) - 1; // 0xff
39 private static final char[] HEXCHAR_TABLE = {
40 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
41 'A', 'B', 'C', 'D', 'E', 'F',
45 assert HEX_EXP * 2 == Byte.SIZE;
46 assert HEXCHAR_TABLE.length == (1 << HEX_EXP);
50 private boolean basicLatinOnlyOut = true;
51 private String newline = DEF_NL;
52 private String indentUnit = DEF_INDENT_UNIT;
53 private int indentNest = 0;
59 protected AbstractXmlExporter(){
66 * ASCIIコード相当(UCS:Basic-Latin)の文字か否か判定する。
68 * <p>※ Basic-Latinには各種制御文字も含まれる。
71 * @return Basic-Latin文字ならtrue
72 * <a href="http://www.unicode.org/charts/PDF/U0000.pdf">
73 * Unicode 6.2 Controls and Basic Latin
76 protected static boolean isBasicLatin(char ch){
77 if('\u0000' <= ch && ch <= '\u007f'){
86 * @param ch {@inheritDoc}
87 * @return {@inheritDoc}
88 * @throws IOException {@inheritDoc}
91 public abstract Appendable append(char ch) throws IOException;
95 * @param seq {@inheritDoc}
96 * @return {@inheritDoc}
97 * @throws IOException {@inheritDoc}
100 public abstract Appendable append(CharSequence seq) throws IOException;
104 * @param seq {@inheritDoc}
105 * @param start {@inheritDoc}
106 * @param end {@inheritDoc}
107 * @return {@inheritDoc}
108 * @throws IOException {@inheritDoc}
111 public abstract Appendable append(CharSequence seq, int start, int end)
116 * @throws IOException {@inheritDoc}
119 public abstract void flush() throws IOException;
123 * @throws IOException {@inheritDoc}
126 public abstract void close() throws IOException;
131 * @param ch {@inheritDoc}
132 * @return {@inheritDoc}
133 * @throws IOException {@inheritDoc}
136 public XmlExporter putRawCh(char ch) throws IOException{
143 * @param seq {@inheritDoc}
144 * @return {@inheritDoc}
145 * @throws IOException {@inheritDoc}
148 public XmlExporter putRawText(CharSequence seq)
156 * @return {@inheritDoc}
157 * @throws IOException {@inheritDoc}
160 public XmlExporter sp() throws IOException{
167 * @param count {@inheritDoc}
168 * @return {@inheritDoc}
169 * @throws IOException {@inheritDoc}
172 public XmlExporter sp(int count) throws IOException{
173 for(int ct = 1; ct <= count; ct++){
181 * @return {@inheritDoc}
184 public String getNewLine(){
190 * @param newLine {@inheritDoc}
191 * @throws NullPointerException {@inheritDoc}
194 public void setNewLine(String newLine) throws NullPointerException{
195 if(newLine == null) throw new NullPointerException();
196 this.newline = newLine;
202 * @return {@inheritDoc}
203 * @throws IOException {@inheritDoc}
206 public XmlExporter ln() throws IOException{
207 putRawText(getNewLine());
213 * @param count {@inheritDoc}
214 * @return {@inheritDoc}
215 * @throws IOException {@inheritDoc}
218 public XmlExporter ln(int count) throws IOException{
219 for(int ct = 1; ct <= count; ct++){
227 * @return {@inheritDoc}
230 public String getIndentUnit(){
231 return this.indentUnit;
236 * @param indUnit {@inheritDoc}
237 * @throws NullPointerException {@inheritDoc}
240 public void setIndentUnit(String indUnit) throws NullPointerException{
241 if(indUnit == null) throw new NullPointerException();
242 this.indentUnit = indUnit;
250 public void pushNest(){
259 public void popNest(){
261 if(this.indentNest < 0) this.indentNest = 0;
267 * @return {@inheritDoc}
270 public int getIndentLevel(){
271 return this.indentNest;
276 * @return {@inheritDoc}
277 * @throws IOException {@inheritDoc}
280 public XmlExporter ind() throws IOException{
281 int level = getIndentLevel();
282 for(int ct = 1; ct <= level; ct++){
283 putRawText(getIndentUnit());
290 * @return {@inheritDoc}
293 public boolean isBasicLatinOnlyOut(){
294 return this.basicLatinOnlyOut;
299 * @param bool {@inheritDoc}
302 public void setBasicLatinOnlyOut(boolean bool){
303 this.basicLatinOnlyOut = bool;
309 * @param ch {@inheritDoc}
310 * @return {@inheritDoc}
311 * @throws IOException {@inheritDoc}
314 public XmlExporter putCharRef2Hex(char ch) throws IOException{
315 if(ch > MAX_OCTET) return putCharRef4Hex(ch);
317 int ibits = ch; // 常に正なので符号拡張なし
319 int idx4 = ibits & MASK_1HEX;
321 int idx3 = ibits & MASK_1HEX;
323 char hex3 = HEXCHAR_TABLE[idx3];
324 char hex4 = HEXCHAR_TABLE[idx4];
326 putRawText(REF_HEX).putRawCh(hex3).putRawCh(hex4)
334 * @param ch {@inheritDoc}
335 * @return {@inheritDoc}
336 * @throws IOException {@inheritDoc}
339 public XmlExporter putCharRef4Hex(char ch) throws IOException{
340 int ibits = ch; // 常に正なので符号拡張なし
342 int idx4 = ibits & MASK_1HEX;
344 int idx3 = ibits & MASK_1HEX;
346 int idx2 = ibits & MASK_1HEX;
348 int idx1 = ibits & MASK_1HEX;
350 char hex1 = HEXCHAR_TABLE[idx1];
351 char hex2 = HEXCHAR_TABLE[idx2];
352 char hex3 = HEXCHAR_TABLE[idx3];
353 char hex4 = HEXCHAR_TABLE[idx4];
355 putRawText(REF_HEX).putRawCh(hex1).putRawCh(hex2)
356 .putRawCh(hex3).putRawCh(hex4)
364 * @param ch {@inheritDoc}
365 * @return {@inheritDoc}
366 * @throws IOException {@inheritDoc}
369 public XmlExporter putCh(char ch) throws IOException{
370 if(Character.isISOControl(ch)){
377 case '&': escTxt = "&"; break;
378 case CH_LT: escTxt = "<"; break;
379 case CH_GT: escTxt = ">"; break;
380 case CH_DQ: escTxt = """; break;
381 case CH_SQ: escTxt = "'"; break;
382 default: escTxt = null; break;
396 * @param content {@inheritDoc}
397 * @return {@inheritDoc}
398 * @throws IOException {@inheritDoc}
401 public XmlExporter putContent(CharSequence content)
403 int length = content.length();
406 for(int pos = 0; pos < length; pos++){
407 char ch = content.charAt(pos);
409 if( isBasicLatinOnlyOut() && ! isBasicLatin(ch) ){
411 }else if(ch == CH_YEN){
413 }else if(Character.isSpaceChar(ch)){
414 if(ch == CH_SP && prev != CH_SP){
431 * @param comment {@inheritDoc}
432 * @return {@inheritDoc}
433 * @throws IOException {@inheritDoc}
436 public XmlExporter putCommentContent(CharSequence comment)
438 int length = comment.length();
441 for(int pos = 0; pos < length; pos++){
442 char ch = comment.charAt(pos);
446 }else if('\u0000' <= ch && ch <= '\u001f'){
447 putRawCh((char)('\u2400' + ch));
448 }else if(ch == '\u007f'){
450 }else if(prev == '-' && ch == '-'){
464 * @param comment {@inheritDoc}
465 * @return {@inheritDoc}
466 * @throws IOException {@inheritDoc}
469 public XmlExporter putLineComment(CharSequence comment)
471 putRawText(COMM_START).sp();
472 putCommentContent(comment);
473 sp().putRawText(COMM_END);
479 * @param comment {@inheritDoc}
480 * @return {@inheritDoc}
481 * @throws IOException {@inheritDoc}
484 public XmlExporter putBlockComment(CharSequence comment)
486 putRawText(COMM_START).ln();
488 putCommentContent(comment);
490 int commentLength = comment.length();
491 if(commentLength > 0){
492 char lastCh = comment.charAt(commentLength - 1);
498 putRawText(COMM_END).ln();
505 * @param tagName {@inheritDoc}
506 * @return {@inheritDoc}
507 * @throws IOException {@inheritDoc}
510 public XmlExporter putOpenSTag(CharSequence tagName)
519 * @return {@inheritDoc}
520 * @throws IOException {@inheritDoc}
523 public XmlExporter putCloseSTag()
531 * @param tagName {@inheritDoc}
532 * @return {@inheritDoc}
533 * @throws IOException {@inheritDoc}
536 public XmlExporter putSimpleSTag(CharSequence tagName)
546 * @param tagName {@inheritDoc}
547 * @return {@inheritDoc}
548 * @throws IOException {@inheritDoc}
551 public XmlExporter putETag(CharSequence tagName)
561 * @param tagName {@inheritDoc}
562 * @return {@inheritDoc}
563 * @throws IOException {@inheritDoc}
566 public XmlExporter putSimpleEmpty(CharSequence tagName)
569 putRawText(tagName).sp();
576 * @return {@inheritDoc}
577 * @throws IOException {@inheritDoc}
580 public XmlExporter putCloseEmpty()
588 * @param iVal {@inheritDoc}
589 * @return {@inheritDoc}
590 * @throws IOException {@inheritDoc}
593 public XmlExporter putXsdInt(int iVal) throws IOException{
594 String value = DatatypeIo.printInt(iVal);
601 * @param fVal {@inheritDoc}
602 * @return {@inheritDoc}
603 * @throws IOException {@inheritDoc}
606 public XmlExporter putXsdFloat(float fVal) throws IOException{
607 String value = DatatypeIo.printFloat(fVal);
614 * @param attrName {@inheritDoc}
615 * @param iVal {@inheritDoc}
616 * @return {@inheritDoc}
617 * @throws IOException {@inheritDoc}
620 public XmlExporter putIntAttr(CharSequence attrName,
623 putRawText(attrName).putRawCh(CH_EQ);
634 * @param attrName {@inheritDoc}
635 * @param fVal {@inheritDoc}
636 * @return {@inheritDoc}
637 * @throws IOException {@inheritDoc}
640 public XmlExporter putFloatAttr(CharSequence attrName,
643 putRawText(attrName).putRawCh(CH_EQ);
654 * @param attrName {@inheritDoc}
655 * @param content {@inheritDoc}
656 * @return {@inheritDoc}
657 * @throws IOException {@inheritDoc}
660 public XmlExporter putAttr(CharSequence attrName,
661 CharSequence content)
663 putRawText(attrName).putRawCh(CH_EQ);