1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 public class MXSerializer implements XmlSerializer {
29 protected static final String XML_URI = "http://www.w3.org/XML/1998/namespace";
30
31 protected static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
32
33 private static final boolean TRACE_SIZING = false;
34
35 protected final String FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE =
36 "http://xmlpull.org/v1/doc/features.html#serializer-attvalue-use-apostrophe";
37
38 protected final String FEATURE_NAMES_INTERNED = "http://xmlpull.org/v1/doc/features.html#names-interned";
39
40 protected final String PROPERTY_SERIALIZER_INDENTATION =
41 "http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
42
43 protected final String PROPERTY_SERIALIZER_LINE_SEPARATOR =
44 "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
45
46 protected static final String PROPERTY_LOCATION = "http://xmlpull.org/v1/doc/properties.html#location";
47
48
49 protected boolean namesInterned;
50
51 protected boolean attributeUseApostrophe;
52
53 protected String indentationString = null;
54
55 protected String lineSeparator = "\n";
56
57 protected String location;
58
59 protected Writer out;
60
61 protected int autoDeclaredPrefixes;
62
63 protected int depth = 0;
64
65
66 protected String elNamespace[] = new String[2];
67
68 protected String elName[] = new String[elNamespace.length];
69
70 protected int elNamespaceCount[] = new int[elNamespace.length];
71
72
73 protected int namespaceEnd = 0;
74
75 protected String namespacePrefix[] = new String[8];
76
77 protected String namespaceUri[] = new String[namespacePrefix.length];
78
79 protected boolean finished;
80
81 protected boolean pastRoot;
82
83 protected boolean setPrefixCalled;
84
85 protected boolean startTagIncomplete;
86
87 protected boolean doIndent;
88
89 protected boolean seenTag;
90
91 protected boolean seenBracket;
92
93 protected boolean seenBracketBracket;
94
95
96 private static final int BUF_LEN = Runtime.getRuntime().freeMemory() > 1000000L ? 8 * 1024 : 256;
97
98 protected char buf[] = new char[BUF_LEN];
99
100 protected static final String precomputedPrefixes[];
101
102 static {
103 precomputedPrefixes = new String[32];
104 for (int i = 0; i < precomputedPrefixes.length; i++) {
105 precomputedPrefixes[i] = ("n" + i).intern();
106 }
107 }
108
109 private boolean checkNamesInterned = false;
110
111 private void checkInterning(String name) {
112 if (namesInterned && name != name.intern()) {
113 throw new IllegalArgumentException(
114 "all names passed as arguments must be interned" + "when NAMES INTERNED feature is enabled");
115 }
116 }
117
118 protected void reset() {
119 location = null;
120 out = null;
121 autoDeclaredPrefixes = 0;
122 depth = 0;
123
124
125 for (int i = 0; i < elNamespaceCount.length; i++) {
126 elName[i] = null;
127 elNamespace[i] = null;
128 elNamespaceCount[i] = 2;
129 }
130
131 namespaceEnd = 0;
132
133
134
135
136
137
138
139
140
141 namespacePrefix[namespaceEnd] = "xmlns";
142 namespaceUri[namespaceEnd] = XMLNS_URI;
143 ++namespaceEnd;
144
145 namespacePrefix[namespaceEnd] = "xml";
146 namespaceUri[namespaceEnd] = XML_URI;
147 ++namespaceEnd;
148
149 finished = false;
150 pastRoot = false;
151 setPrefixCalled = false;
152 startTagIncomplete = false;
153
154 seenTag = false;
155
156 seenBracket = false;
157 seenBracketBracket = false;
158 }
159
160 protected void ensureElementsCapacity() {
161 final int elStackSize = elName.length;
162
163
164 final int newSize = (depth >= 7 ? 2 * depth : 8) + 2;
165 if (TRACE_SIZING) {
166 System.err.println(getClass().getName() + " elStackSize " + elStackSize + " ==> " + newSize);
167 }
168 final boolean needsCopying = elStackSize > 0;
169 String[] arr = null;
170
171 arr = new String[newSize];
172 if (needsCopying) System.arraycopy(elName, 0, arr, 0, elStackSize);
173 elName = arr;
174 arr = new String[newSize];
175 if (needsCopying) System.arraycopy(elNamespace, 0, arr, 0, elStackSize);
176 elNamespace = arr;
177
178 final int[] iarr = new int[newSize];
179 if (needsCopying) {
180 System.arraycopy(elNamespaceCount, 0, iarr, 0, elStackSize);
181 } else {
182
183 iarr[0] = 0;
184 }
185 elNamespaceCount = iarr;
186 }
187
188 protected void ensureNamespacesCapacity() {
189
190
191
192
193
194 final int newSize = namespaceEnd > 7 ? 2 * namespaceEnd : 8;
195 if (TRACE_SIZING) {
196 System.err.println(getClass().getName() + " namespaceSize " + namespacePrefix.length + " ==> " + newSize);
197 }
198 final String[] newNamespacePrefix = new String[newSize];
199 final String[] newNamespaceUri = new String[newSize];
200 if (namespacePrefix != null) {
201 System.arraycopy(namespacePrefix, 0, newNamespacePrefix, 0, namespaceEnd);
202 System.arraycopy(namespaceUri, 0, newNamespaceUri, 0, namespaceEnd);
203 }
204 namespacePrefix = newNamespacePrefix;
205 namespaceUri = newNamespaceUri;
206
207
208
209
210
211
212
213
214
215
216
217
218
219 }
220
221 @Override
222 public void setFeature(String name, boolean state) throws IllegalArgumentException, IllegalStateException {
223 if (name == null) {
224 throw new IllegalArgumentException("feature name can not be null");
225 }
226 if (FEATURE_NAMES_INTERNED.equals(name)) {
227 namesInterned = state;
228 } else if (FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) {
229 attributeUseApostrophe = state;
230 } else {
231 throw new IllegalStateException("unsupported feature " + name);
232 }
233 }
234
235 @Override
236 public boolean getFeature(String name) throws IllegalArgumentException {
237 if (name == null) {
238 throw new IllegalArgumentException("feature name can not be null");
239 }
240 if (FEATURE_NAMES_INTERNED.equals(name)) {
241 return namesInterned;
242 } else if (FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) {
243 return attributeUseApostrophe;
244 } else {
245 return false;
246 }
247 }
248
249
250 protected int offsetNewLine;
251
252 protected int indentationJump;
253
254 protected char[] indentationBuf;
255
256 protected int maxIndentLevel;
257
258 protected boolean writeLineSeparator;
259
260 protected boolean writeIndentation;
261
262
263
264
265
266 protected void rebuildIndentationBuf() {
267 if (doIndent == false) return;
268 final int maxIndent = 65;
269 int bufSize = 0;
270 offsetNewLine = 0;
271 if (writeLineSeparator) {
272 offsetNewLine = lineSeparator.length();
273 bufSize += offsetNewLine;
274 }
275 maxIndentLevel = 0;
276 if (writeIndentation) {
277 indentationJump = indentationString.length();
278 maxIndentLevel = maxIndent / indentationJump;
279 bufSize += maxIndentLevel * indentationJump;
280 }
281 if (indentationBuf == null || indentationBuf.length < bufSize) {
282 indentationBuf = new char[bufSize + 8];
283 }
284 int bufPos = 0;
285 if (writeLineSeparator) {
286 for (int i = 0; i < lineSeparator.length(); i++) {
287 indentationBuf[bufPos++] = lineSeparator.charAt(i);
288 }
289 }
290 if (writeIndentation) {
291 for (int i = 0; i < maxIndentLevel; i++) {
292 for (int j = 0; j < indentationString.length(); j++) {
293 indentationBuf[bufPos++] = indentationString.charAt(j);
294 }
295 }
296 }
297 }
298
299
300 protected void writeIndent() throws IOException {
301 final int start = writeLineSeparator ? 0 : offsetNewLine;
302 final int level = (depth > maxIndentLevel) ? maxIndentLevel : depth;
303 out.write(indentationBuf, start, (level * indentationJump) + offsetNewLine);
304 }
305
306 @Override
307 public void setProperty(String name, Object value) throws IllegalArgumentException, IllegalStateException {
308 if (name == null) {
309 throw new IllegalArgumentException("property name can not be null");
310 }
311 if (PROPERTY_SERIALIZER_INDENTATION.equals(name)) {
312 indentationString = (String) value;
313 } else if (PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) {
314 lineSeparator = (String) value;
315 } else if (PROPERTY_LOCATION.equals(name)) {
316 location = (String) value;
317 } else {
318 throw new IllegalStateException("unsupported property " + name);
319 }
320 writeLineSeparator = lineSeparator != null && lineSeparator.length() > 0;
321 writeIndentation = indentationString != null && indentationString.length() > 0;
322
323 doIndent = indentationString != null && (writeLineSeparator || writeIndentation);
324
325
326 rebuildIndentationBuf();
327 seenTag = false;
328 }
329
330 @Override
331 public Object getProperty(String name) throws IllegalArgumentException {
332 if (name == null) {
333 throw new IllegalArgumentException("property name can not be null");
334 }
335 if (PROPERTY_SERIALIZER_INDENTATION.equals(name)) {
336 return indentationString;
337 } else if (PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) {
338 return lineSeparator;
339 } else if (PROPERTY_LOCATION.equals(name)) {
340 return location;
341 } else {
342 return null;
343 }
344 }
345
346 private String getLocation() {
347 return location != null ? " @" + location : "";
348 }
349
350
351 public Writer getWriter() {
352 return out;
353 }
354
355 @Override
356 public void setOutput(Writer writer) {
357 reset();
358 out = writer;
359 }
360
361 @Override
362 public void setOutput(OutputStream os, String encoding) throws IOException {
363 if (os == null) throw new IllegalArgumentException("output stream can not be null");
364 reset();
365 if (encoding != null) {
366 out = new OutputStreamWriter(os, encoding);
367 } else {
368 out = new OutputStreamWriter(os);
369 }
370 }
371
372 @Override
373 public void startDocument(String encoding, Boolean standalone) throws IOException {
374 char apos = attributeUseApostrophe ? '\'' : '"';
375 if (attributeUseApostrophe) {
376 out.write("<?xml version='1.0'");
377 } else {
378 out.write("<?xml version=\"1.0\"");
379 }
380 if (encoding != null) {
381 out.write(" encoding=");
382 out.write(apos);
383 out.write(encoding);
384 out.write(apos);
385
386 }
387 if (standalone != null) {
388 out.write(" standalone=");
389 out.write(apos);
390 if (standalone) {
391 out.write("yes");
392 } else {
393 out.write("no");
394 }
395 out.write(apos);
396
397
398
399
400
401 }
402 out.write("?>");
403 if (writeLineSeparator) {
404 out.write(lineSeparator);
405 }
406 }
407
408 @Override
409 public void endDocument() throws IOException {
410
411 while (depth > 0) {
412 endTag(elNamespace[depth], elName[depth]);
413 }
414 if (writeLineSeparator) {
415 out.write(lineSeparator);
416 }
417
418
419 finished = pastRoot = startTagIncomplete = true;
420 out.flush();
421 }
422
423 @Override
424 public void setPrefix(String prefix, String namespace) throws IOException {
425 if (startTagIncomplete) closeStartTag();
426
427
428 if (prefix == null) {
429 prefix = "";
430 }
431 if (!namesInterned) {
432 prefix = prefix.intern();
433 } else if (checkNamesInterned) {
434 checkInterning(prefix);
435 } else if (prefix == null) {
436 throw new IllegalArgumentException("prefix must be not null" + getLocation());
437 }
438
439
440 for (int i = elNamespaceCount[depth]; i < namespaceEnd; i++) {
441 if (prefix == namespacePrefix[i]) {
442 throw new IllegalStateException("duplicated prefix " + printable(prefix) + getLocation());
443 }
444 }
445
446 if (!namesInterned) {
447 namespace = namespace.intern();
448 } else if (checkNamesInterned) {
449 checkInterning(namespace);
450 } else if (namespace == null) {
451 throw new IllegalArgumentException("namespace must be not null" + getLocation());
452 }
453
454 if (namespaceEnd >= namespacePrefix.length) {
455 ensureNamespacesCapacity();
456 }
457 namespacePrefix[namespaceEnd] = prefix;
458 namespaceUri[namespaceEnd] = namespace;
459 ++namespaceEnd;
460 setPrefixCalled = true;
461 }
462
463 protected String lookupOrDeclarePrefix(String namespace) {
464 return getPrefix(namespace, true);
465 }
466
467 @Override
468 public String getPrefix(String namespace, boolean generatePrefix) {
469
470 if (!namesInterned) {
471
472 namespace = namespace.intern();
473 } else if (checkNamesInterned) {
474 checkInterning(namespace);
475
476 }
477 if (namespace == null) {
478 throw new IllegalArgumentException("namespace must be not null" + getLocation());
479 } else if (namespace.length() == 0) {
480 throw new IllegalArgumentException("default namespace cannot have prefix" + getLocation());
481 }
482
483
484 for (int i = namespaceEnd - 1; i >= 0; --i) {
485 if (namespace == namespaceUri[i]) {
486 final String prefix = namespacePrefix[i];
487
488 for (int p = namespaceEnd - 1; p > i; --p) {
489 if (prefix == namespacePrefix[p])
490 continue;
491 }
492 return prefix;
493 }
494 }
495
496
497 if (!generatePrefix) {
498 return null;
499 }
500 return generatePrefix(namespace);
501 }
502
503 private String generatePrefix(String namespace) {
504
505 while (true) {
506 ++autoDeclaredPrefixes;
507
508 final String prefix = autoDeclaredPrefixes < precomputedPrefixes.length
509 ? precomputedPrefixes[autoDeclaredPrefixes]
510 : ("n" + autoDeclaredPrefixes).intern();
511
512 for (int i = namespaceEnd - 1; i >= 0; --i) {
513 if (prefix == namespacePrefix[i]) {
514 continue;
515 }
516 }
517
518
519 if (namespaceEnd >= namespacePrefix.length) {
520 ensureNamespacesCapacity();
521 }
522 namespacePrefix[namespaceEnd] = prefix;
523 namespaceUri[namespaceEnd] = namespace;
524 ++namespaceEnd;
525
526 return prefix;
527 }
528 }
529
530 @Override
531 public int getDepth() {
532 return depth;
533 }
534
535 @Override
536 public String getNamespace() {
537 return elNamespace[depth];
538 }
539
540 @Override
541 public String getName() {
542 return elName[depth];
543 }
544
545 @Override
546 public XmlSerializer startTag(String namespace, String name) throws IOException {
547
548 if (startTagIncomplete) {
549 closeStartTag();
550 }
551 seenBracket = seenBracketBracket = false;
552 if (doIndent && depth > 0 && seenTag) {
553 writeIndent();
554 }
555 seenTag = true;
556 setPrefixCalled = false;
557 startTagIncomplete = true;
558 ++depth;
559 if ((depth + 1) >= elName.length) {
560 ensureElementsCapacity();
561 }
562
563
564 if (checkNamesInterned && namesInterned) checkInterning(namespace);
565 elNamespace[depth] = (namesInterned || namespace == null) ? namespace : namespace.intern();
566
567
568 if (checkNamesInterned && namesInterned) checkInterning(name);
569 elName[depth] = (namesInterned || name == null) ? name : name.intern();
570 if (out == null) {
571 throw new IllegalStateException("setOutput() must called set before serialization can start");
572 }
573 out.write('<');
574 if (namespace != null) {
575
576 if (namespace.length() > 0) {
577
578 String prefix = null;
579 if (depth > 0 && (namespaceEnd - elNamespaceCount[depth - 1]) == 1) {
580
581
582 String uri = namespaceUri[namespaceEnd - 1];
583 if (uri == namespace || uri.equals(namespace)) {
584 String elPfx = namespacePrefix[namespaceEnd - 1];
585
586 for (int pos = elNamespaceCount[depth - 1] - 1; pos >= 2; --pos) {
587 String pf = namespacePrefix[pos];
588 if (pf == elPfx || pf.equals(elPfx)) {
589 String n = namespaceUri[pos];
590 if (n == uri || n.equals(uri)) {
591 --namespaceEnd;
592 prefix = elPfx;
593 }
594 break;
595 }
596 }
597 }
598 }
599 if (prefix == null) {
600 prefix = lookupOrDeclarePrefix(namespace);
601 }
602
603
604 if (prefix.length() > 0) {
605 out.write(prefix);
606 out.write(':');
607 }
608 } else {
609
610 for (int i = namespaceEnd - 1; i >= 0; --i) {
611 if (namespacePrefix[i] == "") {
612 final String uri = namespaceUri[i];
613 if (uri == null) {
614
615 setPrefix("", "");
616 } else if (uri.length() > 0) {
617 throw new IllegalStateException("start tag can not be written in empty default namespace "
618 + "as default namespace is currently bound to '" + uri + "'" + getLocation());
619 }
620 break;
621 }
622 }
623 }
624 }
625 out.write(name);
626 return this;
627 }
628
629 @Override
630 public XmlSerializer attribute(String namespace, String name, String value) throws IOException {
631 if (!startTagIncomplete) {
632 throw new IllegalArgumentException("startTag() must be called before attribute()" + getLocation());
633 }
634
635 out.write(' ');
636
637 if (namespace != null && namespace.length() > 0) {
638
639 if (!namesInterned) {
640 namespace = namespace.intern();
641 } else if (checkNamesInterned) {
642 checkInterning(namespace);
643 }
644 String prefix = lookupOrDeclarePrefix(namespace);
645
646 if (prefix.length() == 0) {
647
648
649 prefix = generatePrefix(namespace);
650 }
651 out.write(prefix);
652 out.write(':');
653
654
655
656
657 }
658
659 out.write(name);
660 out.write('=');
661
662 out.write(attributeUseApostrophe ? '\'' : '"');
663 writeAttributeValue(value, out);
664 out.write(attributeUseApostrophe ? '\'' : '"');
665 return this;
666 }
667
668 protected void closeStartTag() throws IOException {
669 if (finished) {
670 throw new IllegalArgumentException("trying to write past already finished output" + getLocation());
671 }
672 if (seenBracket) {
673 seenBracket = seenBracketBracket = false;
674 }
675 if (startTagIncomplete || setPrefixCalled) {
676 if (setPrefixCalled) {
677 throw new IllegalArgumentException(
678 "startTag() must be called immediately after setPrefix()" + getLocation());
679 }
680 if (!startTagIncomplete) {
681 throw new IllegalArgumentException("trying to close start tag that is not opened" + getLocation());
682 }
683
684
685 writeNamespaceDeclarations();
686 out.write('>');
687 elNamespaceCount[depth] = namespaceEnd;
688 startTagIncomplete = false;
689 }
690 }
691
692 private void writeNamespaceDeclarations() throws IOException {
693
694 for (int i = elNamespaceCount[depth - 1]; i < namespaceEnd; i++) {
695 if (doIndent && namespaceUri[i].length() > 40) {
696 writeIndent();
697 out.write(" ");
698 }
699 if (namespacePrefix[i] != "") {
700 out.write(" xmlns:");
701 out.write(namespacePrefix[i]);
702 out.write('=');
703 } else {
704 out.write(" xmlns=");
705 }
706 out.write(attributeUseApostrophe ? '\'' : '"');
707
708
709 writeAttributeValue(namespaceUri[i], out);
710
711 out.write(attributeUseApostrophe ? '\'' : '"');
712 }
713 }
714
715 @Override
716 public XmlSerializer endTag(String namespace, String name) throws IOException {
717
718
719
720
721
722 seenBracket = seenBracketBracket = false;
723 if (namespace != null) {
724 if (!namesInterned) {
725 namespace = namespace.intern();
726 } else if (checkNamesInterned) {
727 checkInterning(namespace);
728 }
729 }
730
731 if (namespace != elNamespace[depth]) {
732 throw new IllegalArgumentException("expected namespace " + printable(elNamespace[depth]) + " and not "
733 + printable(namespace) + getLocation());
734 }
735 if (name == null) {
736 throw new IllegalArgumentException("end tag name can not be null" + getLocation());
737 }
738 if (checkNamesInterned && namesInterned) {
739 checkInterning(name);
740 }
741
742 if ((!namesInterned && !name.equals(elName[depth])) || (namesInterned && name != elName[depth])) {
743 throw new IllegalArgumentException("expected element name " + printable(elName[depth]) + " and not "
744 + printable(name) + getLocation());
745 }
746 if (startTagIncomplete) {
747 writeNamespaceDeclarations();
748 out.write(" />");
749 --depth;
750 } else {
751 --depth;
752
753 if (doIndent && seenTag) {
754 writeIndent();
755 }
756 out.write("</");
757 if (namespace != null && namespace.length() > 0) {
758
759 final String prefix = lookupOrDeclarePrefix(namespace);
760
761 if (prefix.length() > 0) {
762 out.write(prefix);
763 out.write(':');
764 }
765 }
766 out.write(name);
767 out.write('>');
768 }
769 namespaceEnd = elNamespaceCount[depth];
770 startTagIncomplete = false;
771 seenTag = true;
772 return this;
773 }
774
775 @Override
776 public XmlSerializer text(String text) throws IOException {
777
778 if (startTagIncomplete || setPrefixCalled) closeStartTag();
779 if (doIndent && seenTag) seenTag = false;
780 writeElementContent(text, out);
781 return this;
782 }
783
784 @Override
785 public XmlSerializer text(char[] buf, int start, int len) throws IOException {
786 if (startTagIncomplete || setPrefixCalled) closeStartTag();
787 if (doIndent && seenTag) seenTag = false;
788 writeElementContent(buf, start, len, out);
789 return this;
790 }
791
792 @Override
793 public void cdsect(String text) throws IOException {
794 if (startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
795 if (doIndent && seenTag) seenTag = false;
796 out.write("<![CDATA[");
797 out.write(text);
798 out.write("]]>");
799 }
800
801 @Override
802 public void entityRef(String text) throws IOException {
803 if (startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
804 if (doIndent && seenTag) seenTag = false;
805 out.write('&');
806 out.write(text);
807 out.write(';');
808 }
809
810 @Override
811 public void processingInstruction(String text) throws IOException {
812 if (startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
813 if (doIndent && seenTag) seenTag = false;
814 out.write("<?");
815 out.write(text);
816 out.write("?>");
817 }
818
819 @Override
820 public void comment(String text) throws IOException {
821 if (startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
822 if (doIndent && seenTag) seenTag = false;
823 out.write("<!--");
824 out.write(text);
825 out.write("-->");
826 }
827
828 @Override
829 public void docdecl(String text) throws IOException {
830 if (startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
831 if (doIndent && seenTag) seenTag = false;
832 out.write("<!DOCTYPE ");
833 out.write(text);
834 out.write(">");
835 }
836
837 @Override
838 public void ignorableWhitespace(String text) throws IOException {
839 if (startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag();
840 if (doIndent && seenTag) seenTag = false;
841 if (text.length() == 0) {
842 throw new IllegalArgumentException("empty string is not allowed for ignorable whitespace" + getLocation());
843 }
844 out.write(text);
845 }
846
847 @Override
848 public void flush() throws IOException {
849 if (!finished && startTagIncomplete) closeStartTag();
850 out.flush();
851 }
852
853
854
855 protected void writeAttributeValue(String value, Writer out) throws IOException {
856
857 final char quot = attributeUseApostrophe ? '\'' : '"';
858 final String quotEntity = attributeUseApostrophe ? "'" : """;
859
860 int pos = 0;
861 for (int i = 0; i < value.length(); i++) {
862 char ch = value.charAt(i);
863 if (ch == '&') {
864 if (i > pos) out.write(value.substring(pos, i));
865 out.write("&");
866 pos = i + 1;
867 }
868 if (ch == '<') {
869 if (i > pos) out.write(value.substring(pos, i));
870 out.write("<");
871 pos = i + 1;
872 } else if (ch == quot) {
873 if (i > pos) out.write(value.substring(pos, i));
874 out.write(quotEntity);
875 pos = i + 1;
876 } else if (ch < 32) {
877
878
879 if (ch == 13 || ch == 10 || ch == 9) {
880 if (i > pos) out.write(value.substring(pos, i));
881 out.write("&#");
882 out.write(Integer.toString(ch));
883 out.write(';');
884 pos = i + 1;
885 } else {
886 throw new IllegalStateException(
887 "character " + Integer.toString(ch) + " is not allowed in output" + getLocation());
888
889
890
891
892
893
894
895
896
897
898
899 }
900 }
901 }
902 if (pos > 0) {
903 out.write(value.substring(pos));
904 } else {
905 out.write(value);
906 }
907 }
908
909 protected void writeElementContent(String text, Writer out) throws IOException {
910
911 int pos = 0;
912 for (int i = 0; i < text.length(); i++) {
913
914 char ch = text.charAt(i);
915 if (ch == ']') {
916 if (seenBracket) {
917 seenBracketBracket = true;
918 } else {
919 seenBracket = true;
920 }
921 } else {
922 if (ch == '&') {
923 if (i > pos) out.write(text.substring(pos, i));
924 out.write("&");
925 pos = i + 1;
926 } else if (ch == '<') {
927 if (i > pos) out.write(text.substring(pos, i));
928 out.write("<");
929 pos = i + 1;
930 } else if (seenBracketBracket && ch == '>') {
931 if (i > pos) out.write(text.substring(pos, i));
932 out.write(">");
933 pos = i + 1;
934 } else if (ch < 32) {
935
936 if (ch == 9 || ch == 10 || ch == 13) {
937
938
939
940
941
942
943
944
945 } else {
946 throw new IllegalStateException(
947 "character " + Integer.toString(ch) + " is not allowed in output" + getLocation());
948
949
950
951
952
953
954
955
956
957
958
959 }
960 }
961 if (seenBracket) {
962 seenBracketBracket = seenBracket = false;
963 }
964 }
965 }
966 if (pos > 0) {
967 out.write(text.substring(pos));
968 } else {
969 out.write(text);
970 }
971 }
972
973 protected void writeElementContent(char[] buf, int off, int len, Writer out) throws IOException {
974
975 final int end = off + len;
976 int pos = off;
977 for (int i = off; i < end; i++) {
978 final char ch = buf[i];
979 if (ch == ']') {
980 if (seenBracket) {
981 seenBracketBracket = true;
982 } else {
983 seenBracket = true;
984 }
985 } else {
986 if (ch == '&') {
987 if (i > pos) {
988 out.write(buf, pos, i - pos);
989 }
990 out.write("&");
991 pos = i + 1;
992 } else if (ch == '<') {
993 if (i > pos) {
994 out.write(buf, pos, i - pos);
995 }
996 out.write("<");
997 pos = i + 1;
998
999 } else if (seenBracketBracket && ch == '>') {
1000 if (i > pos) {
1001 out.write(buf, pos, i - pos);
1002 }
1003 out.write(">");
1004 pos = i + 1;
1005 } else if (ch < 32) {
1006
1007 if (ch == 9 || ch == 10 || ch == 13) {
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018 } else {
1019 throw new IllegalStateException(
1020 "character " + Integer.toString(ch) + " is not allowed in output" + getLocation());
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032 }
1033 }
1034 if (seenBracket) {
1035 seenBracketBracket = seenBracket = false;
1036 }
1037
1038 }
1039 }
1040 if (end > pos) {
1041 out.write(buf, pos, end - pos);
1042 }
1043 }
1044
1045
1046 protected static final String printable(String s) {
1047 if (s == null) return "null";
1048 StringBuilder retval = new StringBuilder(s.length() + 16);
1049 retval.append("'");
1050 char ch;
1051 for (int i = 0; i < s.length(); i++) {
1052 addPrintable(retval, s.charAt(i));
1053 }
1054 retval.append("'");
1055 return retval.toString();
1056 }
1057
1058 protected static final String printable(char ch) {
1059 StringBuilder retval = new StringBuilder();
1060 addPrintable(retval, ch);
1061 return retval.toString();
1062 }
1063
1064 private static void addPrintable(StringBuilder retval, char ch) {
1065 switch (ch) {
1066 case '\b':
1067 retval.append("\\b");
1068 break;
1069 case '\t':
1070 retval.append("\\t");
1071 break;
1072 case '\n':
1073 retval.append("\\n");
1074 break;
1075 case '\f':
1076 retval.append("\\f");
1077 break;
1078 case '\r':
1079 retval.append("\\r");
1080 break;
1081 case '\"':
1082 retval.append("\\\"");
1083 break;
1084 case '\'':
1085 retval.append("\\\'");
1086 break;
1087 case '\\':
1088 retval.append("\\\\");
1089 break;
1090 default:
1091 if (ch < 0x20 || ch > 0x7e) {
1092 final String ss = "0000" + Integer.toString(ch, 16);
1093 retval.append("\\u").append(ss, ss.length() - 4, ss.length());
1094 } else {
1095 retval.append(ch);
1096 }
1097 }
1098 }
1099 }