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)の文字か否か判定する。
67 * <p>※ Basic-Latinには各種制御文字も含まれる。
69 * @return Basic-Latin文字ならtrue
70 * <a href="http://www.unicode.org/charts/PDF/U0000.pdf">
71 * Unicode 6.2 Controls and Basic Latin
74 protected static boolean isBasicLatin(char ch){
75 if('\u0000' <= ch && ch <= '\u007f'){
84 * @param ch {@inheritDoc}
85 * @return {@inheritDoc}
86 * @throws IOException {@inheritDoc}
89 public abstract Appendable append(char ch) throws IOException;
93 * @param seq {@inheritDoc}
94 * @return {@inheritDoc}
95 * @throws IOException {@inheritDoc}
98 public abstract Appendable append(CharSequence seq) throws IOException;
102 * @param seq {@inheritDoc}
103 * @param start {@inheritDoc}
104 * @param end {@inheritDoc}
105 * @return {@inheritDoc}
106 * @throws IOException {@inheritDoc}
109 public abstract Appendable append(CharSequence seq, int start, int end)
114 * @throws IOException {@inheritDoc}
117 public abstract void flush() throws IOException;
121 * @throws IOException {@inheritDoc}
124 public abstract void close() throws IOException;
129 * @param ch {@inheritDoc}
130 * @return {@inheritDoc}
131 * @throws IOException {@inheritDoc}
134 public XmlExporter putRawCh(char ch) throws IOException{
141 * @param seq {@inheritDoc}
142 * @return {@inheritDoc}
143 * @throws IOException {@inheritDoc}
146 public XmlExporter putRawText(CharSequence seq)
154 * @return {@inheritDoc}
155 * @throws IOException {@inheritDoc}
158 public XmlExporter sp() throws IOException{
165 * @param count {@inheritDoc}
166 * @return {@inheritDoc}
167 * @throws IOException {@inheritDoc}
170 public XmlExporter sp(int count) throws IOException{
171 for(int ct = 1; ct <= count; ct++){
179 * @return {@inheritDoc}
182 public String getNewLine(){
188 * @param newLine {@inheritDoc}
189 * @throws NullPointerException {@inheritDoc}
192 public void setNewLine(String newLine) throws NullPointerException{
193 if(newLine == null) throw new NullPointerException();
194 this.newline = newLine;
200 * @return {@inheritDoc}
201 * @throws IOException {@inheritDoc}
204 public XmlExporter ln() throws IOException{
205 putRawText(getNewLine());
211 * @param count {@inheritDoc}
212 * @return {@inheritDoc}
213 * @throws IOException {@inheritDoc}
216 public XmlExporter ln(int count) throws IOException{
217 for(int ct = 1; ct <= count; ct++){
225 * @return {@inheritDoc}
228 public String getIndentUnit(){
229 return this.indentUnit;
234 * @param indUnit {@inheritDoc}
235 * @throws NullPointerException {@inheritDoc}
238 public void setIndentUnit(String indUnit) throws NullPointerException{
239 if(indUnit == null) throw new NullPointerException();
240 this.indentUnit = indUnit;
248 public void pushNest(){
257 public void popNest(){
259 if(this.indentNest < 0) this.indentNest = 0;
265 * @return {@inheritDoc}
268 public int getIndentLevel(){
269 return this.indentNest;
274 * @return {@inheritDoc}
275 * @throws IOException {@inheritDoc}
278 public XmlExporter ind() throws IOException{
279 int level = getIndentLevel();
280 for(int ct = 1; ct <= level; ct++){
281 putRawText(getIndentUnit());
288 * @return {@inheritDoc}
291 public boolean isBasicLatinOnlyOut(){
292 return this.basicLatinOnlyOut;
297 * @param bool {@inheritDoc}
300 public void setBasicLatinOnlyOut(boolean bool){
301 this.basicLatinOnlyOut = bool;
307 * @param ch {@inheritDoc}
308 * @return {@inheritDoc}
309 * @throws IOException {@inheritDoc}
312 public XmlExporter putCharRef2Hex(char ch) throws IOException{
313 if(ch > MAX_OCTET) return putCharRef4Hex(ch);
315 int ibits = ch; // 常に正なので符号拡張なし
317 int idx4 = ibits & MASK_1HEX;
319 int idx3 = ibits & MASK_1HEX;
321 char hex3 = HEXCHAR_TABLE[idx3];
322 char hex4 = HEXCHAR_TABLE[idx4];
324 putRawText(REF_HEX).putRawCh(hex3).putRawCh(hex4)
332 * @param ch {@inheritDoc}
333 * @return {@inheritDoc}
334 * @throws IOException {@inheritDoc}
337 public XmlExporter putCharRef4Hex(char ch) throws IOException{
338 int ibits = ch; // 常に正なので符号拡張なし
340 int idx4 = ibits & MASK_1HEX;
342 int idx3 = ibits & MASK_1HEX;
344 int idx2 = ibits & MASK_1HEX;
346 int idx1 = ibits & MASK_1HEX;
348 char hex1 = HEXCHAR_TABLE[idx1];
349 char hex2 = HEXCHAR_TABLE[idx2];
350 char hex3 = HEXCHAR_TABLE[idx3];
351 char hex4 = HEXCHAR_TABLE[idx4];
353 putRawText(REF_HEX).putRawCh(hex1).putRawCh(hex2)
354 .putRawCh(hex3).putRawCh(hex4)
362 * @param ch {@inheritDoc}
363 * @return {@inheritDoc}
364 * @throws IOException {@inheritDoc}
367 public XmlExporter putCh(char ch) throws IOException{
368 if(Character.isISOControl(ch)){
375 case '&': escTxt = "&"; break;
376 case CH_LT: escTxt = "<"; break;
377 case CH_GT: escTxt = ">"; break;
378 case CH_DQ: escTxt = """; break;
379 case CH_SQ: escTxt = "'"; break;
380 default: escTxt = null; break;
394 * @param content {@inheritDoc}
395 * @return {@inheritDoc}
396 * @throws IOException {@inheritDoc}
399 public XmlExporter putContent(CharSequence content)
401 int length = content.length();
404 for(int pos = 0; pos < length; pos++){
405 char ch = content.charAt(pos);
407 if( isBasicLatinOnlyOut() && ! isBasicLatin(ch) ){
409 }else if(ch == CH_YEN){
411 }else if(Character.isSpaceChar(ch)){
412 if(ch == CH_SP && prev != CH_SP){
429 * @param comment {@inheritDoc}
430 * @return {@inheritDoc}
431 * @throws IOException {@inheritDoc}
434 public XmlExporter putCommentContent(CharSequence comment)
436 int length = comment.length();
439 for(int pos = 0; pos < length; pos++){
440 char ch = comment.charAt(pos);
444 }else if('\u0000' <= ch && ch <= '\u001f'){
445 putRawCh((char)('\u2400' + ch));
446 }else if(ch == '\u007f'){
448 }else if(prev == '-' && ch == '-'){
462 * @param comment {@inheritDoc}
463 * @return {@inheritDoc}
464 * @throws IOException {@inheritDoc}
467 public XmlExporter putLineComment(CharSequence comment)
469 putRawText(COMM_START).sp();
470 putCommentContent(comment);
471 sp().putRawText(COMM_END);
477 * @param comment {@inheritDoc}
478 * @return {@inheritDoc}
479 * @throws IOException {@inheritDoc}
482 public XmlExporter putBlockComment(CharSequence comment)
484 putRawText(COMM_START).ln();
486 putCommentContent(comment);
488 int commentLength = comment.length();
489 if(commentLength > 0){
490 char lastCh = comment.charAt(commentLength - 1);
496 putRawText(COMM_END).ln();
503 * @param tagName {@inheritDoc}
504 * @return {@inheritDoc}
505 * @throws IOException {@inheritDoc}
508 public XmlExporter putOpenSTag(CharSequence tagName)
517 * @return {@inheritDoc}
518 * @throws IOException {@inheritDoc}
521 public XmlExporter putCloseSTag()
529 * @param tagName {@inheritDoc}
530 * @return {@inheritDoc}
531 * @throws IOException {@inheritDoc}
534 public XmlExporter putSimpleSTag(CharSequence tagName)
544 * @param tagName {@inheritDoc}
545 * @return {@inheritDoc}
546 * @throws IOException {@inheritDoc}
549 public XmlExporter putETag(CharSequence tagName)
559 * @param tagName {@inheritDoc}
560 * @return {@inheritDoc}
561 * @throws IOException {@inheritDoc}
564 public XmlExporter putSimpleEmpty(CharSequence tagName)
567 putRawText(tagName).sp();
574 * @return {@inheritDoc}
575 * @throws IOException {@inheritDoc}
578 public XmlExporter putCloseEmpty()
586 * @param iVal {@inheritDoc}
587 * @return {@inheritDoc}
588 * @throws IOException {@inheritDoc}
591 public XmlExporter putXsdInt(int iVal) throws IOException{
592 String value = DatatypeIo.printInt(iVal);
599 * @param fVal {@inheritDoc}
600 * @return {@inheritDoc}
601 * @throws IOException {@inheritDoc}
604 public XmlExporter putXsdFloat(float fVal) throws IOException{
605 String value = DatatypeIo.printFloat(fVal);
612 * @param attrName {@inheritDoc}
613 * @param iVal {@inheritDoc}
614 * @return {@inheritDoc}
615 * @throws IOException {@inheritDoc}
618 public XmlExporter putIntAttr(CharSequence attrName,
621 putRawText(attrName).putRawCh(CH_EQ);
632 * @param attrName {@inheritDoc}
633 * @param fVal {@inheritDoc}
634 * @return {@inheritDoc}
635 * @throws IOException {@inheritDoc}
638 public XmlExporter putFloatAttr(CharSequence attrName,
641 putRawText(attrName).putRawCh(CH_EQ);
652 * @param attrName {@inheritDoc}
653 * @param content {@inheritDoc}
654 * @return {@inheritDoc}
655 * @throws IOException {@inheritDoc}
658 public XmlExporter putAttr(CharSequence attrName,
659 CharSequence content)
661 putRawText(attrName).putRawCh(CH_EQ);