001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: *
025: * THIS FILE WAS MODIFIED BY SUN MICROSYSTEMS, INC.
026: */
027:
028: /*
029: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
030: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
031: *
032: * This code is free software; you can redistribute it and/or modify it
033: * under the terms of the GNU General Public License version 2 only, as
034: * published by the Free Software Foundation. Sun designates this
035: * particular file as subject to the "Classpath" exception as provided
036: * by Sun in the LICENSE file that accompanied this code.
037: *
038: * This code is distributed in the hope that it will be useful, but WITHOUT
039: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
040: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
041: * version 2 for more details (a copy is included in the LICENSE file that
042: * accompanied this code).
043: *
044: * You should have received a copy of the GNU General Public License version
045: * 2 along with this work; if not, write to the Free Software Foundation,
046: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
047: *
048: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
049: * CA 95054 USA or visit www.sun.com if you need additional information or
050: * have any questions.
051: *
052: * THIS FILE WAS MODIFIED BY SUN MICROSYSTEMS, INC.
053: *
054: */
055:
056: package com.sun.xml.internal.fastinfoset.stax;
057:
058: import com.sun.xml.internal.fastinfoset.Encoder;
059: import com.sun.xml.internal.fastinfoset.EncodingConstants;
060: import java.io.IOException;
061: import java.io.OutputStream;
062: import java.util.EmptyStackException;
063: import java.util.Enumeration;
064: import java.util.Iterator;
065: import javax.xml.namespace.NamespaceContext;
066: import javax.xml.stream.XMLStreamException;
067: import javax.xml.stream.XMLStreamWriter;
068: import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithmIndexes;
069: import org.xml.sax.helpers.NamespaceSupport;
070: import com.sun.xml.internal.fastinfoset.CommonResourceBundle;
071:
072: /**
073: * The Fast Infoset StAX serializer.
074: * <p>
075: * Instantiate this serializer to serialize a fast infoset document in accordance
076: * with the StAX API.
077: *
078: * <p>
079: * More than one fast infoset document may be encoded to the
080: * {@link java.io.OutputStream}.
081: */
082: public class StAXDocumentSerializer extends Encoder implements
083: XMLStreamWriter {
084: protected StAXManager _manager;
085:
086: protected String _encoding;
087: /**
088: * Local name of current element.
089: */
090: protected String _currentLocalName;
091:
092: /**
093: * Namespace of current element.
094: */
095: protected String _currentUri;
096:
097: /**
098: * Prefix of current element.
099: */
100: protected String _currentPrefix;
101:
102: /**
103: * This flag indicates when there is a pending start element event.
104: */
105: protected boolean _inStartElement = false;
106:
107: /**
108: * This flag indicates if the current element is empty.
109: */
110: protected boolean _isEmptyElement = false;
111:
112: /**
113: * List of attributes qnames and values defined in the current element.
114: */
115: protected String[] _attributesArray = new String[4 * 16];
116: protected int _attributesArrayIndex = 0;
117:
118: /**
119: * Mapping between uris and prefixes.
120: */
121: protected NamespaceSupport _nsSupport = new NamespaceSupport();
122:
123: protected boolean[] _nsSupportContextStack = new boolean[32];
124: protected int _stackCount = -1;
125:
126: protected NamespaceContext _nsContext = new NamespaceContextImpl();
127:
128: /**
129: * List of namespaces defined in the current element.
130: */
131: protected String[] _namespacesArray = new String[2 * 8];
132: protected int _namespacesArrayIndex = 0;
133:
134: public StAXDocumentSerializer() {
135: super (true);
136: }
137:
138: public StAXDocumentSerializer(OutputStream outputStream) {
139: super (true);
140: setOutputStream(outputStream);
141: }
142:
143: public StAXDocumentSerializer(OutputStream outputStream,
144: StAXManager manager) {
145: super (true);
146: setOutputStream(outputStream);
147: _manager = manager;
148: }
149:
150: public void reset() {
151: super .reset();
152:
153: _attributesArrayIndex = 0;
154: _namespacesArrayIndex = 0;
155: _nsSupport.reset();
156: _stackCount = -1;
157:
158: _currentUri = _currentPrefix = null;
159: _currentLocalName = null;
160:
161: _inStartElement = _isEmptyElement = false;
162: }
163:
164: // -- XMLStreamWriter Interface -------------------------------------------
165:
166: public void writeStartDocument() throws XMLStreamException {
167: writeStartDocument("finf", "1.0");
168: }
169:
170: public void writeStartDocument(String version)
171: throws XMLStreamException {
172: writeStartDocument("finf", version);
173: }
174:
175: public void writeStartDocument(String encoding, String version)
176: throws XMLStreamException {
177: reset();
178:
179: try {
180: encodeHeader(false);
181: encodeInitialVocabulary();
182: } catch (IOException e) {
183: throw new XMLStreamException(e);
184: }
185: }
186:
187: public void writeEndDocument() throws XMLStreamException {
188: // Need to flush a pending empty element?
189: if (_inStartElement) {
190: // encodeTerminationAndCurrentElement();
191: }
192:
193: try {
194: // TODO
195: // Use nsSupport to terminate all elements not terminated
196: // by writeEndElement
197:
198: encodeDocumentTermination();
199: } catch (IOException e) {
200: throw new XMLStreamException(e);
201: }
202: }
203:
204: public void close() throws XMLStreamException {
205: reset();
206: }
207:
208: public void flush() throws XMLStreamException {
209: try {
210: _s.flush();
211: } catch (IOException e) {
212: throw new XMLStreamException(e);
213: }
214: }
215:
216: public void writeStartElement(String localName)
217: throws XMLStreamException {
218: // TODO is it necessary for FI to obtain the default namespace in scope?
219: writeStartElement("", localName, "");
220: }
221:
222: public void writeStartElement(String namespaceURI, String localName)
223: throws XMLStreamException {
224: writeStartElement(getPrefix(namespaceURI), localName,
225: namespaceURI);
226: }
227:
228: public void writeStartElement(String prefix, String localName,
229: String namespaceURI) throws XMLStreamException {
230: encodeTerminationAndCurrentElement(false);
231:
232: _inStartElement = true;
233: _isEmptyElement = false;
234:
235: _currentLocalName = localName;
236: _currentPrefix = prefix;
237: _currentUri = namespaceURI;
238:
239: _stackCount++;
240: if (_stackCount == _nsSupportContextStack.length) {
241: boolean[] nsSupportContextStack = new boolean[_stackCount * 2];
242: System.arraycopy(_nsSupportContextStack, 0,
243: nsSupportContextStack, 0,
244: _nsSupportContextStack.length);
245: _nsSupportContextStack = nsSupportContextStack;
246: }
247:
248: _nsSupportContextStack[_stackCount] = false;
249: // _nsSupport.pushContext();
250: }
251:
252: public void writeEmptyElement(String localName)
253: throws XMLStreamException {
254: writeEmptyElement("", localName, "");
255: }
256:
257: public void writeEmptyElement(String namespaceURI, String localName)
258: throws XMLStreamException {
259: writeEmptyElement(getPrefix(namespaceURI), localName,
260: namespaceURI);
261: }
262:
263: public void writeEmptyElement(String prefix, String localName,
264: String namespaceURI) throws XMLStreamException {
265: encodeTerminationAndCurrentElement(false);
266:
267: _isEmptyElement = _inStartElement = true;
268:
269: _currentLocalName = localName;
270: _currentPrefix = prefix;
271: _currentUri = namespaceURI;
272:
273: _stackCount++;
274: if (_stackCount == _nsSupportContextStack.length) {
275: boolean[] nsSupportContextStack = new boolean[_stackCount * 2];
276: System.arraycopy(_nsSupportContextStack, 0,
277: nsSupportContextStack, 0,
278: _nsSupportContextStack.length);
279: _nsSupportContextStack = nsSupportContextStack;
280: }
281:
282: _nsSupportContextStack[_stackCount] = false;
283: //_nsSupport.pushContext();
284: }
285:
286: public void writeEndElement() throws XMLStreamException {
287: if (_inStartElement) {
288: encodeTerminationAndCurrentElement(false);
289: }
290:
291: try {
292: encodeElementTermination();
293: if (_nsSupportContextStack[_stackCount--] == true) {
294: _nsSupport.popContext();
295: }
296: } catch (IOException e) {
297: throw new XMLStreamException(e);
298: } catch (EmptyStackException e) {
299: throw new XMLStreamException(e);
300: }
301: }
302:
303: public void writeAttribute(String localName, String value)
304: throws XMLStreamException {
305: writeAttribute("", "", localName, value);
306: }
307:
308: public void writeAttribute(String namespaceURI, String localName,
309: String value) throws XMLStreamException {
310: String prefix = "";
311:
312: // Find prefix for attribute, ignoring default namespace
313: if (namespaceURI.length() > 0) {
314: prefix = _nsSupport.getPrefix(namespaceURI);
315:
316: // Undeclared prefix or ignorable default ns?
317: if (prefix == null || prefix.length() == 0) {
318: // Workaround for BUG in SAX NamespaceSupport helper
319: // which incorrectly defines namespace declaration URI
320: if (namespaceURI == EncodingConstants.XMLNS_NAMESPACE_NAME
321: || namespaceURI
322: .equals(EncodingConstants.XMLNS_NAMESPACE_NAME)) {
323: // TODO
324: // Need to check carefully the rule for the writing of
325: // namespaces in StAX. Is it safe to ignore such
326: // attributes, as declarations will be made using the
327: // writeNamespace method
328: return;
329: }
330: throw new XMLStreamException(CommonResourceBundle
331: .getInstance().getString("message.URIUnbound",
332: new Object[] { namespaceURI }));
333: }
334: }
335: writeAttribute(prefix, namespaceURI, localName, value);
336: }
337:
338: public void writeAttribute(String prefix, String namespaceURI,
339: String localName, String value) throws XMLStreamException {
340: if (!_inStartElement) {
341: throw new IllegalStateException(CommonResourceBundle
342: .getInstance().getString(
343: "message.attributeWritingNotAllowed"));
344: }
345:
346: // TODO
347: // Need to check carefully the rule for the writing of
348: // namespaces in StAX. Is it safe to ignore such
349: // attributes, as declarations will be made using the
350: // writeNamespace method
351: if (namespaceURI == EncodingConstants.XMLNS_NAMESPACE_NAME
352: || namespaceURI
353: .equals(EncodingConstants.XMLNS_NAMESPACE_NAME)) {
354: return;
355: }
356:
357: if (_attributesArrayIndex == _attributesArray.length) {
358: final String[] attributesArray = new String[_attributesArrayIndex * 2];
359: System.arraycopy(_attributesArray, 0, attributesArray, 0,
360: _attributesArrayIndex);
361: _attributesArray = attributesArray;
362: }
363:
364: _attributesArray[_attributesArrayIndex++] = namespaceURI;
365: _attributesArray[_attributesArrayIndex++] = prefix;
366: _attributesArray[_attributesArrayIndex++] = localName;
367: _attributesArray[_attributesArrayIndex++] = value;
368: }
369:
370: public void writeNamespace(String prefix, String namespaceURI)
371: throws XMLStreamException {
372: if (prefix == null
373: || prefix.length() == 0
374: || prefix
375: .equals(EncodingConstants.XMLNS_NAMESPACE_PREFIX)) {
376: writeDefaultNamespace(namespaceURI);
377: } else {
378: if (!_inStartElement) {
379: throw new IllegalStateException(CommonResourceBundle
380: .getInstance().getString(
381: "message.attributeWritingNotAllowed"));
382: }
383:
384: if (_namespacesArrayIndex == _namespacesArray.length) {
385: final String[] namespacesArray = new String[_namespacesArrayIndex * 2];
386: System.arraycopy(_namespacesArray, 0, namespacesArray,
387: 0, _namespacesArrayIndex);
388: _namespacesArray = namespacesArray;
389: }
390:
391: _namespacesArray[_namespacesArrayIndex++] = prefix;
392: _namespacesArray[_namespacesArrayIndex++] = namespaceURI;
393: }
394: }
395:
396: public void writeDefaultNamespace(String namespaceURI)
397: throws XMLStreamException {
398: if (!_inStartElement) {
399: throw new IllegalStateException(CommonResourceBundle
400: .getInstance().getString(
401: "message.attributeWritingNotAllowed"));
402: }
403:
404: if (_namespacesArrayIndex == _namespacesArray.length) {
405: final String[] namespacesArray = new String[_namespacesArrayIndex * 2];
406: System.arraycopy(_namespacesArray, 0, namespacesArray, 0,
407: _namespacesArrayIndex);
408: _namespacesArray = namespacesArray;
409: }
410:
411: _namespacesArray[_namespacesArrayIndex++] = "";
412: _namespacesArray[_namespacesArrayIndex++] = namespaceURI;
413: }
414:
415: public void writeComment(String data) throws XMLStreamException {
416: try {
417: if (getIgnoreComments())
418: return;
419:
420: encodeTerminationAndCurrentElement(true);
421:
422: // TODO: avoid array copy here
423: encodeComment(data.toCharArray(), 0, data.length());
424: } catch (IOException e) {
425: throw new XMLStreamException(e);
426: }
427: }
428:
429: public void writeProcessingInstruction(String target)
430: throws XMLStreamException {
431: writeProcessingInstruction(target, "");
432: }
433:
434: public void writeProcessingInstruction(String target, String data)
435: throws XMLStreamException {
436: try {
437: if (getIgnoreProcesingInstructions())
438: return;
439:
440: encodeTerminationAndCurrentElement(true);
441:
442: encodeProcessingInstruction(target, data);
443: } catch (IOException e) {
444: throw new XMLStreamException(e);
445: }
446: }
447:
448: public void writeCData(String data) throws XMLStreamException {
449: throw new UnsupportedOperationException(CommonResourceBundle
450: .getInstance().getString("message.notImplemented"));
451: }
452:
453: public void writeDTD(String dtd) throws XMLStreamException {
454: throw new UnsupportedOperationException(CommonResourceBundle
455: .getInstance().getString("message.notImplemented"));
456: }
457:
458: public void writeEntityRef(String name) throws XMLStreamException {
459: throw new UnsupportedOperationException(CommonResourceBundle
460: .getInstance().getString("message.notImplemented"));
461: }
462:
463: public void writeCharacters(String text) throws XMLStreamException {
464: try {
465: final int length = text.length();
466: if (length == 0) {
467: return;
468: } else if (length < _charBuffer.length) {
469: if (getIgnoreWhiteSpaceTextContent()
470: && isWhiteSpace(text))
471: return;
472:
473: // Warning: this method must be called before any state
474: // is modified, such as the _charBuffer contents,
475: // so the characters of text cannot be copied to _charBuffer
476: // before this call
477: encodeTerminationAndCurrentElement(true);
478:
479: text.getChars(0, length, _charBuffer, 0);
480: encodeCharacters(_charBuffer, 0, length);
481: } else {
482: final char ch[] = text.toCharArray();
483: if (getIgnoreWhiteSpaceTextContent()
484: && isWhiteSpace(ch, 0, length))
485: return;
486:
487: encodeTerminationAndCurrentElement(true);
488:
489: encodeCharactersNoClone(ch, 0, length);
490: }
491: } catch (IOException e) {
492: throw new XMLStreamException(e);
493: }
494: }
495:
496: public void writeCharacters(char[] text, int start, int len)
497: throws XMLStreamException {
498: try {
499: if (len <= 0) {
500: return;
501: }
502:
503: if (getIgnoreWhiteSpaceTextContent()
504: && isWhiteSpace(text, start, len))
505: return;
506:
507: encodeTerminationAndCurrentElement(true);
508:
509: encodeCharacters(text, start, len);
510: } catch (IOException e) {
511: throw new XMLStreamException(e);
512: }
513: }
514:
515: public String getPrefix(String uri) throws XMLStreamException {
516: return _nsSupport.getPrefix(uri);
517: }
518:
519: public void setPrefix(String prefix, String uri)
520: throws XMLStreamException {
521: if (_stackCount > -1
522: && _nsSupportContextStack[_stackCount] == false) {
523: _nsSupportContextStack[_stackCount] = true;
524: _nsSupport.pushContext();
525: }
526:
527: _nsSupport.declarePrefix(prefix, uri);
528: }
529:
530: public void setDefaultNamespace(String uri)
531: throws XMLStreamException {
532: setPrefix("", uri);
533: }
534:
535: /**
536: * Sets the current namespace context for prefix and uri bindings.
537: * This context becomes the root namespace context for writing and
538: * will replace the current root namespace context. Subsequent calls
539: * to setPrefix and setDefaultNamespace will bind namespaces using
540: * the context passed to the method as the root context for resolving
541: * namespaces. This method may only be called once at the start of
542: * the document. It does not cause the namespaces to be declared.
543: * If a namespace URI to prefix mapping is found in the namespace
544: * context it is treated as declared and the prefix may be used
545: * by the StreamWriter.
546: * @param context the namespace context to use for this writer, may not be null
547: * @throws XMLStreamException
548: */
549: public void setNamespaceContext(NamespaceContext context)
550: throws XMLStreamException {
551: throw new UnsupportedOperationException("setNamespaceContext");
552: }
553:
554: public NamespaceContext getNamespaceContext() {
555: return _nsContext;
556: }
557:
558: public Object getProperty(java.lang.String name)
559: throws IllegalArgumentException {
560: if (_manager != null) {
561: return _manager.getProperty(name);
562: }
563: return null;
564: }
565:
566: public void setManager(StAXManager manager) {
567: _manager = manager;
568: }
569:
570: public void setEncoding(String encoding) {
571: _encoding = encoding;
572: }
573:
574: protected class NamespaceContextImpl implements NamespaceContext {
575: public final String getNamespaceURI(String prefix) {
576: return _nsSupport.getURI(prefix);
577: }
578:
579: public final String getPrefix(String namespaceURI) {
580: return _nsSupport.getPrefix(namespaceURI);
581: }
582:
583: public final Iterator getPrefixes(String namespaceURI) {
584: final Enumeration e = _nsSupport.getPrefixes(namespaceURI);
585:
586: return new Iterator() {
587: public boolean hasNext() {
588: return e.hasMoreElements();
589: }
590:
591: public Object next() {
592: return e.nextElement();
593: }
594:
595: public void remove() {
596: throw new UnsupportedOperationException();
597: }
598: };
599: }
600: }
601:
602: public void writeOctets(byte[] b, int start, int len)
603: throws XMLStreamException {
604: try {
605: if (len == 0) {
606: return;
607: }
608:
609: encodeTerminationAndCurrentElement(true);
610:
611: encodeCIIOctetAlgorithmData(
612: EncodingAlgorithmIndexes.BASE64, b, start, len);
613: } catch (IOException e) {
614: throw new XMLStreamException(e);
615: }
616: }
617:
618: protected void encodeTerminationAndCurrentElement(
619: boolean terminateAfter) throws XMLStreamException {
620: try {
621: encodeTermination();
622:
623: if (_inStartElement) {
624:
625: _b = EncodingConstants.ELEMENT;
626: if (_attributesArrayIndex > 0) {
627: _b |= EncodingConstants.ELEMENT_ATTRIBUTE_FLAG;
628: }
629:
630: // Encode namespace decls associated with this element
631: if (_namespacesArrayIndex > 0) {
632: write(_b
633: | EncodingConstants.ELEMENT_NAMESPACES_FLAG);
634: for (int i = 0; i < _namespacesArrayIndex;) {
635: encodeNamespaceAttribute(_namespacesArray[i++],
636: _namespacesArray[i++]);
637: }
638: _namespacesArrayIndex = 0;
639:
640: write(EncodingConstants.TERMINATOR);
641:
642: _b = 0;
643: }
644:
645: // Encode element and its attributes
646: encodeElementQualifiedNameOnThirdBit(_currentUri,
647: _currentPrefix, _currentLocalName);
648:
649: for (int i = 0; i < _attributesArrayIndex;) {
650: encodeAttributeQualifiedNameOnSecondBit(
651: _attributesArray[i++],
652: _attributesArray[i++],
653: _attributesArray[i++]);
654:
655: final String value = _attributesArray[i];
656: _attributesArray[i++] = null;
657: final boolean addToTable = (value.length() < attributeValueSizeConstraint) ? true
658: : false;
659: encodeNonIdentifyingStringOnFirstBit(value,
660: _v.attributeValue, addToTable);
661:
662: _b = EncodingConstants.TERMINATOR;
663: _terminate = true;
664: }
665: _attributesArrayIndex = 0;
666: _inStartElement = false;
667:
668: if (_isEmptyElement) {
669: encodeElementTermination();
670: if (_nsSupportContextStack[_stackCount--] == true) {
671: _nsSupport.popContext();
672: }
673:
674: _isEmptyElement = false;
675: }
676:
677: if (terminateAfter) {
678: encodeTermination();
679: }
680: }
681: } catch (IOException e) {
682: throw new XMLStreamException(e);
683: }
684: }
685: }
|