001: /*
002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
003: * Copyright (C) 2006 - Javolution (http://javolution.org/)
004: * All rights reserved.
005: *
006: * Permission to use, copy, modify, and distribute this software is
007: * freely granted, provided that this notice is preserved.
008: */
009: package javolution.xml.stream;
010:
011: import java.io.IOException;
012: import java.io.OutputStream;
013: import java.io.OutputStreamWriter;
014: import java.io.UnsupportedEncodingException;
015: import java.io.Writer;
016:
017: import javolution.context.ObjectFactory;
018: import javolution.io.UTF8StreamWriter;
019: import javolution.lang.Reusable;
020: import javolution.text.CharArray;
021: import javolution.text.TextBuilder;
022: import j2me.lang.CharSequence;
023: import j2me.lang.IllegalStateException;
024: import j2mex.realtime.MemoryArea;
025:
026: /**
027: * <p> This class represents a {@link javolution.lang.Reusable reusable}
028: * implementation of {@link XMLStreamWriter}.</p>
029: *
030: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
031: * @version 4.0, September 4, 2006
032: */
033: public final class XMLStreamWriterImpl implements XMLStreamWriter,
034: Reusable {
035:
036: /**
037: * Holds the length of intermediate buffer.
038: */
039: private static final int BUFFER_LENGTH = 2048;
040:
041: /**
042: * Holds the current nesting level.
043: */
044: private int _nesting = 0;
045:
046: /**
047: * Holds the element qualified name (indexed per nesting level)
048: */
049: private TextBuilder[] _qNames = new TextBuilder[16];
050:
051: /**
052: * Indicates if the current element is open.
053: */
054: private boolean _isElementOpen;
055:
056: /**
057: * Indicates if the current element is an empty element.
058: */
059: private boolean _isEmptyElement;
060:
061: /**
062: * Holds intermediate buffer.
063: */
064: private final char[] _buffer = new char[BUFFER_LENGTH];
065:
066: /**
067: * Holds the namespace stack.
068: */
069: private final NamespacesImpl _namespaces = new NamespacesImpl();
070:
071: /**
072: * Holds the buffer current index.
073: */
074: private int _index;
075:
076: /**
077: * Holds repairing namespace property.
078: */
079: private boolean _isRepairingNamespaces;
080:
081: /**
082: * Holds repairing prefix property.
083: */
084: private String _repairingPrefix = "ns";
085:
086: /**
087: * Holds indentation property.
088: */
089: private String _indentation;
090:
091: /**
092: * Holds current indentation level.
093: */
094: private int _indentationLevel;
095:
096: /**
097: * Holds automatic empty elements property.
098: */
099: private boolean _automaticEmptyElements;
100:
101: /**
102: * Holds counter for automatic namespace generation.
103: */
104: private int _autoNSCount;
105:
106: /**
107: * Holds recycling factory if any.
108: */
109: ObjectFactory _objectFactory;
110:
111: ////////////////////////
112: // Temporary Settings //
113: ////////////////////////
114:
115: /**
116: * Holds the writer destination (<code>null</code> when unused).
117: */
118: private Writer _writer;
119:
120: /**
121: * Holds the encoding (<code>null</code> if N/A).
122: */
123: private String _encoding;
124:
125: /**
126: * Holds the defautl writer for output streams.
127: */
128: private final UTF8StreamWriter _utf8StreamWriter = new UTF8StreamWriter();
129:
130: /**
131: * Default constructor.
132: */
133: public XMLStreamWriterImpl() {
134: for (int i = 0; i < _qNames.length;) {
135: _qNames[i++] = new TextBuilder();
136: }
137: }
138:
139: /**
140: * Sets the output stream destination for this XML stream writer
141: * (UTF-8 encoding).
142: *
143: * @param out the output source with utf-8 encoding.
144: */
145: public void setOutput(OutputStream out) throws XMLStreamException {
146: _utf8StreamWriter.setOutput(out);
147: _encoding = "UTF-8";
148: setOutput(_utf8StreamWriter);
149: }
150:
151: /**
152: * Sets the output stream destination and encoding for this XML stream
153: * writer.
154: *
155: * @param out the output source.
156: * @param encoding the associated encoding.
157: * @throws XMLStreamException if the specified encoding is not supported.
158: */
159: public void setOutput(OutputStream out, String encoding)
160: throws XMLStreamException {
161: if (encoding.equals("UTF-8") || encoding.equals("utf-8")
162: || encoding.equals("ASCII")) {
163: setOutput(out); // Default encoding.
164: } else {
165: try {
166: _encoding = encoding;
167: setOutput(new OutputStreamWriter(out, encoding));
168: } catch (UnsupportedEncodingException e) {
169: throw new XMLStreamException(e);
170: }
171: }
172: }
173:
174: /**
175: * Sets the writer output destination for this XML stream writer.
176: *
177: * @param writer the output destination writer.
178: * @see javolution.io.UTF8StreamWriter
179: * @see javolution.io.UTF8ByteBufferWriter
180: * @see javolution.io.AppendableWriter
181: */
182: public void setOutput(Writer writer) throws XMLStreamException {
183: if (_writer != null)
184: throw new IllegalStateException(
185: "Writer not closed or reset");
186: _writer = writer;
187: }
188:
189: /**
190: * Requires this writer to create a new prefix when a namespace has none
191: * (default <code>false</code>).
192: *
193: * @param isRepairingNamespaces <code>true</code> if namespaces
194: * are repaired; <code>false</code> otherwise.
195: */
196: public void setRepairingNamespaces(boolean isRepairingNamespaces) {
197: _isRepairingNamespaces = isRepairingNamespaces;
198: }
199:
200: /**
201: * Specifies the prefix to be append by a trailing part
202: * (a sequence number) in order to make it unique to be usable as
203: * a temporary non-colliding prefix when repairing namespaces
204: * (default <code>"ns"</code>).
205: *
206: * @param repairingPrefix the prefix root.
207: */
208: public void setRepairingPrefix(String repairingPrefix) {
209: _repairingPrefix = repairingPrefix;
210: }
211:
212: /**
213: * Specifies the indentation string; non-null indentation
214: * forces the writer to write elements into separate lines
215: * (default <code>null</code>).
216: *
217: * @param indentation the indentation string.
218: */
219: public void setIndentation(String indentation) {
220: _indentation = indentation;
221: }
222:
223: /**
224: * Requires this writer to automatically output empty elements when a
225: * start element is immediately followed by matching end element
226: * (default <code>false</code>).
227: *
228: * @param automaticEmptyElements <code>true</code> if start element
229: * immediately followed by end element results in an empty element
230: * beign written; <code>false</code> otherwise.
231: */
232: public void setAutomaticEmptyElements(boolean automaticEmptyElements) {
233: _automaticEmptyElements = automaticEmptyElements;
234: }
235:
236: // Implements reusable.
237: public void reset() {
238: _automaticEmptyElements = false;
239: _autoNSCount = 0;
240: _encoding = null;
241: _indentation = null;
242: _indentationLevel = 0;
243: _index = 0;
244: _isElementOpen = false;
245: _isEmptyElement = false;
246: _isRepairingNamespaces = false;
247: _namespaces.reset();
248: _nesting = 0;
249: _objectFactory = null;
250: _repairingPrefix = "ns";
251: _utf8StreamWriter.reset();
252: _writer = null;
253: }
254:
255: // Implements XMLStreamWriter interface.
256: public void writeStartElement(CharSequence localName)
257: throws XMLStreamException {
258: if (localName == null)
259: throw new XMLStreamException("Local name cannot be null");
260: writeNewElement(null, localName, null);
261: }
262:
263: // Implements XMLStreamWriter interface.
264: public void writeStartElement(CharSequence namespaceURI,
265: CharSequence localName) throws XMLStreamException {
266: if (localName == null)
267: throw new XMLStreamException("Local name cannot be null");
268: if (namespaceURI == null)
269: throw new XMLStreamException("Namespace URI cannot be null");
270: writeNewElement(null, localName, namespaceURI);
271: }
272:
273: // Implements XMLStreamWriter interface.
274: public void writeStartElement(CharSequence prefix,
275: CharSequence localName, CharSequence namespaceURI)
276: throws XMLStreamException {
277: if (localName == null)
278: throw new XMLStreamException("Local name cannot be null");
279: if (namespaceURI == null)
280: throw new XMLStreamException("Namespace URI cannot be null");
281: if (prefix == null)
282: throw new XMLStreamException("Prefix cannot be null");
283: writeNewElement(prefix, localName, namespaceURI);
284: }
285:
286: // Implements XMLStreamWriter interface.
287: public void writeEmptyElement(CharSequence localName)
288: throws XMLStreamException {
289: writeStartElement(localName);
290: _isEmptyElement = true;
291: }
292:
293: // Implements XMLStreamWriter interface.
294: public void writeEmptyElement(CharSequence namespaceURI,
295: CharSequence localName) throws XMLStreamException {
296: writeStartElement(namespaceURI, localName);
297: _isEmptyElement = true;
298: }
299:
300: // Implements XMLStreamWriter interface.
301: public void writeEmptyElement(CharSequence prefix,
302: CharSequence localName, CharSequence namespaceURI)
303: throws XMLStreamException {
304: writeStartElement(prefix, localName, namespaceURI);
305: _isEmptyElement = true;
306: }
307:
308: // Implements XMLStreamWriter interface.
309: public void writeEndElement() throws XMLStreamException {
310: if (_isElementOpen) { // Empty element.
311: if (_isEmptyElement) { // Closes the empty element tag.
312: closeOpenTag();
313: } else { // Start element open.
314: if (_automaticEmptyElements) { // Do as if empty element written.
315: _isEmptyElement = true;
316: closeOpenTag();
317: return;
318: } else { // Closes the start element tag.
319: closeOpenTag();
320: }
321: }
322: }
323: if ((_indentation != null)
324: && (_indentationLevel != _nesting - 1)) {
325: // Do not indent if no change in indentation level
326: // to avoid interfering with text only elements.
327: write('\n');
328: for (int i = 1; i < _nesting; i++) {
329: writeStr(_indentation);
330: }
331: }
332:
333: write('<');
334: write('/');
335: writeTB(_qNames[_nesting--]);
336: write('>');
337: _namespaces.pop();
338: }
339:
340: // Implements XMLStreamWriter interface.
341: public void writeEndDocument() throws XMLStreamException {
342: if (_isElementOpen)
343: closeOpenTag();
344: while (_nesting > 0) { // Implicits closing of all elements.
345: writeEndElement();
346: }
347: }
348:
349: // Implements XMLStreamWriter interface.
350: public void close() throws XMLStreamException {
351: if (_writer != null) {
352: if (_nesting != 0) { // Closes all elements.
353: writeEndDocument();
354: }
355: flush();
356: }
357: if (_objectFactory != null) {
358: _objectFactory.recycle(this ); // Implicit reset.
359: } else {
360: reset(); // Explicit reset.
361: }
362: }
363:
364: // Implements XMLStreamWriter interface.
365: public void flush() throws XMLStreamException {
366: flushBuffer();
367: try {
368: _writer.flush();
369: } catch (IOException e) {
370: throw new XMLStreamException(e);
371: }
372: }
373:
374: // Implements XMLStreamWriter interface.
375: public void writeAttribute(CharSequence localName,
376: CharSequence value) throws XMLStreamException {
377: if (localName == null)
378: throw new XMLStreamException("Local name cannot be null");
379: if (value == null)
380: throw new XMLStreamException("Value cannot be null");
381: writeAttributeOrNamespace(null, null, localName, value);
382: }
383:
384: // Implements XMLStreamWriter interface.
385: public void writeAttribute(CharSequence namespaceURI,
386: CharSequence localName, CharSequence value)
387: throws XMLStreamException {
388: if (localName == null)
389: throw new XMLStreamException("Local name cannot be null");
390: if (value == null)
391: throw new XMLStreamException("Value cannot be null");
392: if (namespaceURI == null)
393: throw new XMLStreamException("Namespace URI cannot be null");
394: writeAttributeOrNamespace(null, namespaceURI, localName, value);
395: }
396:
397: // Implements XMLStreamWriter interface.
398: public void writeAttribute(CharSequence prefix,
399: CharSequence namespaceURI, CharSequence localName,
400: CharSequence value) throws XMLStreamException {
401: if (localName == null)
402: throw new XMLStreamException("Local name cannot be null");
403: if (value == null)
404: throw new XMLStreamException("Value cannot be null");
405: if (namespaceURI == null)
406: throw new XMLStreamException("Namespace URI cannot be null");
407: if (prefix == null)
408: throw new XMLStreamException("Prefix cannot be null");
409: writeAttributeOrNamespace(prefix, namespaceURI, localName,
410: value);
411: }
412:
413: // Implements XMLStreamWriter interface.
414: public void writeNamespace(CharSequence prefix,
415: CharSequence namespaceURI) throws XMLStreamException {
416: if ((prefix == null) || (prefix.length() == 0)
417: || _namespaces._xmlns.equals(prefix)) {
418: prefix = _namespaces._defaultNsPrefix;
419: }
420: if (!_isElementOpen) // Check now as the actual writting is queued.
421: throw new IllegalStateException("No open start element");
422: _namespaces.setPrefix(prefix,
423: (namespaceURI == null) ? _namespaces._nullNsURI
424: : namespaceURI, true);
425: }
426:
427: // Implements XMLStreamWriter interface.
428: public void writeDefaultNamespace(CharSequence namespaceURI)
429: throws XMLStreamException {
430: writeNamespace(_namespaces._defaultNsPrefix, namespaceURI);
431: }
432:
433: // Implements XMLStreamWriter interface.
434: public void writeComment(CharSequence data)
435: throws XMLStreamException {
436: if (_isElementOpen)
437: closeOpenTag();
438: writeStr("<!--");
439: if (data != null) { // null values allowed.
440: write(data);
441: }
442: writeStr("-->");
443: }
444:
445: // Implements XMLStreamWriter interface.
446: public void writeProcessingInstruction(CharSequence target)
447: throws XMLStreamException {
448: writeProcessingInstruction(target, _noChar);
449: }
450:
451: private final CharArray _noChar = new CharArray("");
452:
453: // Implements XMLStreamWriter interface.
454: public void writeProcessingInstruction(CharSequence target,
455: CharSequence data) throws XMLStreamException {
456: if (target == null)
457: throw new XMLStreamException("Target cannot be null");
458: if (data == null)
459: throw new XMLStreamException("Data cannot be null");
460: if (_isElementOpen)
461: closeOpenTag();
462: writeStr("<?");
463: write(target);
464: write(' ');
465: write(data);
466: write(" ?>");
467: }
468:
469: // Implements XMLStreamWriter interface.
470: public void writeCData(CharSequence data) throws XMLStreamException {
471: if (data == null)
472: throw new XMLStreamException("Data cannot be null");
473: if (_isElementOpen)
474: closeOpenTag();
475: writeStr("<![CDATA[");
476: write(data);
477: writeStr("]]>");
478: }
479:
480: // Implements XMLStreamWriter interface.
481: public void writeDTD(CharSequence dtd) throws XMLStreamException {
482: if (dtd == null)
483: throw new XMLStreamException("DTD cannot be null");
484: if (_nesting > 0)
485: throw new XMLStreamException(
486: "DOCTYPE declaration (DTD) when not in document root (prolog)");
487: write(dtd);
488: }
489:
490: // Implements XMLStreamWriter interface.
491: public void writeEntityRef(CharSequence name)
492: throws XMLStreamException {
493: write('&');
494: write(name);
495: write(';');
496: }
497:
498: // Implements XMLStreamWriter interface.
499: public void writeStartDocument() throws XMLStreamException {
500: writeStartDocument(null, null);
501: }
502:
503: // Implements XMLStreamWriter interface.
504: public void writeStartDocument(CharSequence version)
505: throws XMLStreamException {
506: writeStartDocument(null, version);
507: }
508:
509: // Implements XMLStreamWriter interface.
510: public void writeStartDocument(CharSequence encoding,
511: CharSequence version) throws XMLStreamException {
512: if (_nesting > 0)
513: throw new XMLStreamException("Not in document root");
514: writeStr("<?xml version=\"");
515: if (version != null) {
516: write(version);
517: write('"');
518: } else { // Default to 1.0
519: writeStr("1.0\"");
520: }
521: if (encoding != null) {
522: writeStr(" encoding=\"");
523: write(encoding);
524: write('"');
525: } else if (_encoding != null) { // Use init encoding (if any).
526: writeStr(" encoding=\"");
527: writeStr(_encoding);
528: write('"');
529: }
530: writeStr(" ?>");
531: }
532:
533: // Implements XMLStreamWriter interface.
534: public void writeCharacters(CharSequence text)
535: throws XMLStreamException {
536: if (_isElementOpen)
537: closeOpenTag();
538: if (text == null)
539: return;
540: writeEsc(text);
541: }
542:
543: // Implements XMLStreamWriter interface.
544: public void writeCharacters(char[] text, int start, int length)
545: throws XMLStreamException {
546: if (_isElementOpen)
547: closeOpenTag();
548: for (int i = start, end = start + length; i < end;) {
549: writeEsc(text[i++]);
550: }
551: }
552:
553: // Implements XMLStreamWriter interface.
554: public CharSequence getPrefix(CharSequence uri)
555: throws XMLStreamException {
556: return _namespaces.getPrefix(uri);
557: }
558:
559: // Implements XMLStreamWriter interface.
560: public void setPrefix(CharSequence prefix, CharSequence uri)
561: throws XMLStreamException {
562: _namespaces.setPrefix(prefix,
563: (uri == null) ? _namespaces._nullNsURI : uri, false);
564: }
565:
566: // Implements XMLStreamWriter interface.
567: public void setDefaultNamespace(CharSequence uri)
568: throws XMLStreamException {
569: setPrefix(_namespaces._defaultNsPrefix, uri);
570: }
571:
572: // Implements XMLStreamWriter interface.
573: public Object getProperty(String name)
574: throws IllegalArgumentException {
575: if (name.equals(XMLOutputFactory.IS_REPAIRING_NAMESPACES)) {
576: return new Boolean(_isRepairingNamespaces);
577: } else if (name.equals(XMLOutputFactory.REPAIRING_PREFIX)) {
578: return _repairingPrefix;
579: } else if (name
580: .equals(XMLOutputFactory.AUTOMATIC_EMPTY_ELEMENTS)) {
581: return new Boolean(_automaticEmptyElements);
582: } else if (name.equals(XMLOutputFactory.INDENTATION)) {
583: return _indentation;
584: } else {
585: throw new IllegalArgumentException("Property: " + name
586: + " not supported");
587: }
588: }
589:
590: // Writes a new start or empty element.
591: private void writeNewElement(CharSequence prefix,
592: CharSequence localName, CharSequence namespaceURI)
593: throws XMLStreamException {
594:
595: // Close any open element and gets ready to write a new one.
596: if (_isElementOpen)
597: closeOpenTag();
598: if (_indentation != null) {
599: write('\n');
600: _indentationLevel = _nesting;
601: for (int i = 0; i < _indentationLevel; i++) {
602: writeStr(_indentation);
603: }
604: }
605: write('<');
606: _isElementOpen = true;
607:
608: // Enters a new local scope.
609: if (++_nesting >= _qNames.length)
610: resizeElemStack();
611: _namespaces.push();
612:
613: // Constructs qName.
614: TextBuilder qName = _qNames[_nesting].clear();
615:
616: // Writes prefix if any.
617: if ((namespaceURI != null)
618: && (!_namespaces._defaultNamespace.equals(namespaceURI))) {
619: if (_isRepairingNamespaces) { // Repairs prefix.
620: prefix = getRepairedPrefix(prefix, namespaceURI);
621: } else if (prefix == null) { // Retrieves prefix.
622: prefix = getPrefix(namespaceURI);
623: if (prefix == null)
624: throw new XMLStreamException(
625: "URI: "
626: + namespaceURI
627: + " not bound and repairing namespaces disabled");
628: }
629: if (prefix.length() > 0) {
630: qName.append(prefix);
631: qName.append(':');
632: }
633: }
634: qName.append(localName);
635: writeTB(qName);
636: }
637:
638: // Writes a new attribute.
639: private void writeAttributeOrNamespace(CharSequence prefix,
640: CharSequence namespaceURI, CharSequence localName,
641: CharSequence value) throws XMLStreamException {
642: if (!_isElementOpen)
643: throw new IllegalStateException("No open start element");
644: write(' ');
645:
646: // Writes prefix if any.
647: if ((namespaceURI != null)
648: && (!_namespaces._defaultNamespace.equals(namespaceURI))) {
649: if (_isRepairingNamespaces) { // Repairs prefix if current prefix is not correct.
650: prefix = getRepairedPrefix(prefix, namespaceURI);
651: } else if (prefix == null) {
652: prefix = getPrefix(namespaceURI);
653: if (prefix == null)
654: throw new XMLStreamException(
655: "URI: "
656: + namespaceURI
657: + " not bound and repairing namespaces disabled");
658: }
659: if (prefix.length() > 0) {
660: write(prefix);
661: write(':');
662: }
663: }
664:
665: write(localName);
666: write('=');
667: write('"');
668: writeEsc(value);
669: write('"');
670: }
671:
672: // Closes the current element (scope if empty element).
673: private void closeOpenTag() throws XMLStreamException {
674:
675: // Writes namespaces now.
676: writeNamespaces();
677:
678: // Closes the tag.
679: _isElementOpen = false;
680: if (_isEmptyElement) {
681: write('/');
682: write('>');
683: _nesting--;
684: _namespaces.pop();
685: _isEmptyElement = false;
686: } else {
687: write('>');
688: }
689: }
690:
691: // Writes all namespaces, these include namespaces set but
692: // not written in outer scope.
693: private void writeNamespaces() throws XMLStreamException {
694: int i0 = (_nesting > 1) ? _namespaces._namespacesCount[_nesting - 2]
695: : NamespacesImpl.NBR_PREDEFINED_NAMESPACES;
696: int i1 = _namespaces._namespacesCount[_nesting - 1];
697: int i2 = _namespaces._namespacesCount[_nesting];
698: for (int i = i0; i < i2; i++) {
699: if (((_isRepairingNamespaces && (i < i1) && !_namespaces._prefixesWritten[i]))
700: || ((i >= i1) && _namespaces._prefixesWritten[i])) { // Write namespace.
701:
702: // In repairing mode, removes redondancy.
703: if (_isRepairingNamespaces) {
704: CharArray prefix = _namespaces.getPrefix(
705: _namespaces._namespaces[i], i);
706: if (_namespaces._prefixes[i].equals(prefix))
707: continue; // Not necessary.
708: } // Direct mode, just write them as requested (no check).
709:
710: // Writes namespace.
711: if (_namespaces._prefixes[i].length() == 0) { // Default namespace.
712: writeAttributeOrNamespace(null, null,
713: _namespaces._xmlns,
714: _namespaces._namespaces[i]);
715: } else {
716: writeAttributeOrNamespace(_namespaces._xmlns,
717: _namespaces._xmlnsURI,
718: _namespaces._prefixes[i],
719: _namespaces._namespaces[i]);
720: }
721: }
722: }
723: }
724:
725: // Returns the prefix for the specified namespace.
726: private CharSequence getRepairedPrefix(CharSequence prefix,
727: CharSequence namespaceURI) throws XMLStreamException {
728: CharArray prefixForURI = _namespaces.getPrefix(namespaceURI);
729: if ((prefixForURI != null)
730: && ((prefix == null) || prefixForURI.equals(prefix)))
731: return prefixForURI; // No repair needed.
732: if ((prefix == null) || (prefix.length() == 0)) { // Creates new prefix.
733: prefix = _autoPrefix.clear().append(_repairingPrefix)
734: .append(_autoNSCount++);
735: }
736: _namespaces.setPrefix(prefix, namespaceURI, true); // Map to namespace URI.
737: return prefix;
738: }
739:
740: private final TextBuilder _autoPrefix = new TextBuilder();
741:
742: // Resizes element stack (same memory area as the writer).
743: private void resizeElemStack() {
744: MemoryArea.getMemoryArea(this ).executeInArea(new Runnable() {
745: public void run() {
746: final int oldLength = _qNames.length;
747: final int newLength = oldLength * 2;
748:
749: // Resizes elements qNames stack.
750: TextBuilder[] tmp = new TextBuilder[newLength];
751: System.arraycopy(_qNames, 0, tmp, 0, oldLength);
752: _qNames = tmp;
753: for (int i = oldLength; i < newLength; i++) {
754: _qNames[i] = new TextBuilder();
755: }
756: }
757: });
758: }
759:
760: // Writes methods.
761: //
762:
763: private final void write(char c) throws XMLStreamException {
764: _buffer[_index++] = c;
765: if (_index == BUFFER_LENGTH) {
766: flushBuffer();
767: }
768: }
769:
770: private final void writeEsc(char c) throws XMLStreamException {
771: if ((c >= '?')
772: || ((c >= ' ') && (c != '<') && (c != '>')
773: && (c != '"') && (c != '&'))) { // Most common case.
774: write(c);
775: } else {
776: writeEsc2(c);
777: }
778: }
779:
780: private final void writeEsc2(char c) throws XMLStreamException {
781: switch (c) {
782: case '<':
783: writeStr("<");
784: break;
785: case '>':
786: writeStr(">");
787: break;
788: // case '\'': // Unnecessary due to exclusive use of "
789: // writeStr("'");
790: // break;
791: case '"':
792: writeStr(""");
793: break;
794: case '&':
795: writeStr("&");
796: break;
797: default:
798: if (c >= ' ') {
799: write(c);
800: } else {
801: writeStr("&#");
802: write((char) ('0' + c / 10));
803: write((char) ('0' + c % 10));
804: write(';');
805: }
806: }
807: }
808:
809: private void write(Object obj) throws XMLStreamException {
810: if (obj instanceof String) {
811: writeStr((String) obj);
812: } else {
813: writeCsq((CharSequence) obj);
814: }
815: }
816:
817: private void writeStr(String str) throws XMLStreamException {
818: int n = str.length();
819: if (_index + n < BUFFER_LENGTH) {
820: str.getChars(0, n, _buffer, _index);
821: _index += n;
822: } else {
823: writeStrImmediate(str);
824: }
825: }
826:
827: private void writeStrImmediate(String str)
828: throws XMLStreamException {
829: flushBuffer();
830: try {
831: _writer.write(str);
832: } catch (IOException e) {
833: throw new XMLStreamException(e);
834: }
835: }
836:
837: private void writeCsq(CharSequence csq) throws XMLStreamException {
838: for (int i = 0, n = csq.length(); i < n;) {
839: // Inline write(char)
840: _buffer[_index++] = csq.charAt(i++);
841: if (_index == BUFFER_LENGTH) {
842: flushBuffer();
843: }
844: }
845: }
846:
847: private void writeTB(TextBuilder tb) throws XMLStreamException {
848: for (int i = 0, n = tb.length(); i < n;) {
849: // Inline write(char)
850: _buffer[_index++] = tb.charAt(i++);
851: if (_index == BUFFER_LENGTH) {
852: flushBuffer();
853: }
854: }
855: }
856:
857: private void writeEsc(Object obj) throws XMLStreamException {
858: if (obj instanceof String) {
859: writeEscStr((String) obj);
860: } else {
861: writeEscCsq((CharSequence) obj);
862: }
863: }
864:
865: private void writeEscStr(String str) throws XMLStreamException {
866: for (int i = 0, n = str.length(); i < n;) {
867: // Inline writeEsc(char)
868: char c = str.charAt(i++);
869: if ((c >= '?')
870: || ((c >= ' ') && (c != '<') && (c != '>')
871: && (c != '"') && (c != '&'))) { // Most common case.
872: // Inline write(char)
873: _buffer[_index++] = c;
874: if (_index == BUFFER_LENGTH) {
875: flushBuffer();
876: }
877: } else {
878: writeEsc2(c);
879: }
880: }
881: }
882:
883: private void writeEscCsq(CharSequence csq)
884: throws XMLStreamException {
885: for (int i = 0, n = csq.length(); i < n;) {
886: // Inline writeEsc(char)
887: char c = csq.charAt(i++);
888: if ((c >= '?')
889: || ((c >= ' ') && (c != '<') && (c != '>')
890: && (c != '"') && (c != '&'))) { // Most common case.
891: // Inline write(char)
892: _buffer[_index++] = c;
893: if (_index == BUFFER_LENGTH) {
894: flushBuffer();
895: }
896: } else {
897: writeEsc2(c);
898: }
899: }
900: }
901:
902: private void flushBuffer() throws XMLStreamException {
903: try {
904: _writer.write(_buffer, 0, _index);
905: } catch (IOException e) {
906: throw new XMLStreamException(e);
907: } finally {
908: _index = 0;
909: }
910: }
911:
912: }
|