1 /*
2  * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  *
25  * THIS FILE WAS MODIFIED BY SUN MICROSYSTEMS, INC.
26  */
27 
28 package com.sun.xml.internal.fastinfoset.stax;
29 
30 import com.sun.xml.internal.fastinfoset.Encoder;
31 import com.sun.xml.internal.fastinfoset.EncodingConstants;
32 import com.sun.xml.internal.fastinfoset.util.NamespaceContextImplementation;
33 import java.io.IOException;
34 import java.io.OutputStream;
35 import java.util.EmptyStackException;
36 import javax.xml.namespace.NamespaceContext;
37 import javax.xml.stream.XMLStreamException;
38 import javax.xml.stream.XMLStreamWriter;
39 import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithmIndexes;
40 import com.sun.xml.internal.fastinfoset.CommonResourceBundle;
41 import com.sun.xml.internal.fastinfoset.QualifiedName;
42 import com.sun.xml.internal.fastinfoset.util.LocalNameQualifiedNamesMap;
43 import com.sun.xml.internal.org.jvnet.fastinfoset.stax.LowLevelFastInfosetStreamWriter;
44 
45 /**
46  * The Fast Infoset StAX serializer.
47  * <p>
48  * Instantiate this serializer to serialize a fast infoset document in accordance
49  * with the StAX API.
50  *
51  * <p>
52  * More than one fast infoset document may be encoded to the
53  * {@link java.io.OutputStream}.
54  */
55 public class StAXDocumentSerializer extends Encoder
56         implements XMLStreamWriter, LowLevelFastInfosetStreamWriter {
57     protected StAXManager _manager;
58 
59     protected String _encoding;
60     /**
61      * Local name of current element.
62      */
63     protected String _currentLocalName;
64 
65     /**
66      * Namespace of current element.
67      */
68     protected String _currentUri;
69 
70     /**
71      * Prefix of current element.
72      */
73     protected String _currentPrefix;
74 
75    /**
76      * This flag indicates when there is a pending start element event.
77      */
78     protected boolean _inStartElement = false;
79 
80     /**
81      * This flag indicates if the current element is empty.
82      */
83     protected boolean _isEmptyElement = false;
84 
85     /**
86      * List of attributes qnames and values defined in the current element.
87      */
88     protected String[] _attributesArray = new String[4 * 16];
89     protected int _attributesArrayIndex = 0;
90 
91     protected boolean[] _nsSupportContextStack = new boolean[32];
92     protected int _stackCount = -1;
93 
94     /**
95      * Mapping between uris and prefixes.
96      */
97     protected NamespaceContextImplementation _nsContext =
98             new NamespaceContextImplementation();
99 
100     /**
101      * List of namespaces defined in the current element.
102      */
103     protected String[] _namespacesArray = new String[2 * 8];
104     protected int _namespacesArrayIndex = 0;
105 
StAXDocumentSerializer()106     public StAXDocumentSerializer() {
107         super(true);
108         _manager = new StAXManager(StAXManager.CONTEXT_WRITER);
109     }
110 
StAXDocumentSerializer(OutputStream outputStream)111     public StAXDocumentSerializer(OutputStream outputStream) {
112         super(true);
113         setOutputStream(outputStream);
114         _manager = new StAXManager(StAXManager.CONTEXT_WRITER);
115     }
116 
StAXDocumentSerializer(OutputStream outputStream, StAXManager manager)117     public StAXDocumentSerializer(OutputStream outputStream, StAXManager manager) {
118         super(true);
119         setOutputStream(outputStream);
120         _manager = manager;
121     }
122 
reset()123     public void reset() {
124         super.reset();
125 
126         _attributesArrayIndex = 0;
127         _namespacesArrayIndex = 0;
128 
129         _nsContext.reset();
130         _stackCount = -1;
131 
132         _currentUri = _currentPrefix = null;
133         _currentLocalName = null;
134 
135         _inStartElement = _isEmptyElement = false;
136     }
137 
138     // -- XMLStreamWriter Interface -------------------------------------------
139 
writeStartDocument()140     public void writeStartDocument() throws XMLStreamException {
141         writeStartDocument("finf", "1.0");
142     }
143 
writeStartDocument(String version)144     public void writeStartDocument(String version) throws XMLStreamException {
145         writeStartDocument("finf", version);
146     }
147 
writeStartDocument(String encoding, String version)148     public void writeStartDocument(String encoding, String version)
149         throws XMLStreamException
150     {
151         reset();
152 
153         try {
154             encodeHeader(false);
155             encodeInitialVocabulary();
156         } catch (IOException e) {
157             throw new XMLStreamException(e);
158         }
159     }
160 
writeEndDocument()161     public void writeEndDocument() throws XMLStreamException {
162         try {
163 
164             // terminate all elements not terminated
165             // by writeEndElement
166             for(;_stackCount >= 0; _stackCount--) {
167                 writeEndElement();
168             }
169 
170             encodeDocumentTermination();
171         }
172         catch (IOException e) {
173             throw new XMLStreamException(e);
174         }
175     }
176 
close()177     public void close() throws XMLStreamException {
178         reset();
179     }
180 
flush()181     public void flush() throws XMLStreamException {
182         try {
183             _s.flush();
184         }
185         catch (IOException e) {
186             throw new XMLStreamException(e);
187         }
188     }
189 
writeStartElement(String localName)190     public void writeStartElement(String localName)
191         throws XMLStreamException
192     {
193         // TODO is it necessary for FI to obtain the default namespace in scope?
194         writeStartElement("", localName, "");
195     }
196 
writeStartElement(String namespaceURI, String localName)197     public void writeStartElement(String namespaceURI, String localName)
198         throws XMLStreamException
199     {
200         writeStartElement("", localName, namespaceURI);
201     }
202 
writeStartElement(String prefix, String localName, String namespaceURI)203     public void writeStartElement(String prefix, String localName,
204         String namespaceURI) throws XMLStreamException
205     {
206         encodeTerminationAndCurrentElement(false);
207 
208         _inStartElement = true;
209         _isEmptyElement = false;
210 
211         _currentLocalName = localName;
212         _currentPrefix = prefix;
213         _currentUri = namespaceURI;
214 
215         _stackCount++;
216         if (_stackCount == _nsSupportContextStack.length) {
217             boolean[] nsSupportContextStack = new boolean[_stackCount * 2];
218             System.arraycopy(_nsSupportContextStack, 0, nsSupportContextStack, 0, _nsSupportContextStack.length);
219             _nsSupportContextStack = nsSupportContextStack;
220         }
221 
222         _nsSupportContextStack[_stackCount] = false;
223     }
224 
writeEmptyElement(String localName)225     public void writeEmptyElement(String localName)
226         throws XMLStreamException
227     {
228         writeEmptyElement("", localName, "");
229     }
230 
writeEmptyElement(String namespaceURI, String localName)231     public void writeEmptyElement(String namespaceURI, String localName)
232         throws XMLStreamException
233     {
234         writeEmptyElement("", localName, namespaceURI);
235     }
236 
writeEmptyElement(String prefix, String localName, String namespaceURI)237     public void writeEmptyElement(String prefix, String localName,
238         String namespaceURI) throws XMLStreamException
239     {
240         encodeTerminationAndCurrentElement(false);
241 
242         _isEmptyElement = _inStartElement = true;
243 
244         _currentLocalName = localName;
245         _currentPrefix = prefix;
246         _currentUri = namespaceURI;
247 
248         _stackCount++;
249         if (_stackCount == _nsSupportContextStack.length) {
250             boolean[] nsSupportContextStack = new boolean[_stackCount * 2];
251             System.arraycopy(_nsSupportContextStack, 0, nsSupportContextStack, 0, _nsSupportContextStack.length);
252             _nsSupportContextStack = nsSupportContextStack;
253         }
254 
255         _nsSupportContextStack[_stackCount] = false;
256     }
257 
writeEndElement()258     public void writeEndElement() throws XMLStreamException {
259         if (_inStartElement) {
260             encodeTerminationAndCurrentElement(false);
261         }
262 
263         try {
264             encodeElementTermination();
265             if (_nsSupportContextStack[_stackCount--] == true) {
266                 _nsContext.popContext();
267             }
268         }
269         catch (IOException e) {
270             throw new XMLStreamException(e);
271         }
272         catch (EmptyStackException e) {
273             throw new XMLStreamException(e);
274         }
275     }
276 
277 
writeAttribute(String localName, String value)278     public void writeAttribute(String localName, String value)
279         throws XMLStreamException
280     {
281         writeAttribute("", "", localName, value);
282     }
283 
writeAttribute(String namespaceURI, String localName, String value)284     public void writeAttribute(String namespaceURI, String localName,
285         String value) throws XMLStreamException
286     {
287         String prefix = "";
288 
289         // Find prefix for attribute, ignoring default namespace
290         if (namespaceURI.length() > 0) {
291             prefix = _nsContext.getNonDefaultPrefix(namespaceURI);
292 
293             // Undeclared prefix or ignorable default ns?
294             if (prefix == null || prefix.length() == 0) {
295                 // Workaround for BUG in SAX NamespaceSupport helper
296                 // which incorrectly defines namespace declaration URI
297                 if (namespaceURI == EncodingConstants.XMLNS_NAMESPACE_NAME ||
298                         namespaceURI.equals(EncodingConstants.XMLNS_NAMESPACE_NAME)) {
299                     // TODO
300                     // Need to check carefully the rule for the writing of
301                     // namespaces in StAX. Is it safe to ignore such
302                     // attributes, as declarations will be made using the
303                     // writeNamespace method
304                     return;
305                 }
306                 throw new XMLStreamException(CommonResourceBundle.getInstance().getString("message.URIUnbound", new Object[]{namespaceURI}));
307             }
308         }
309         writeAttribute(prefix, namespaceURI, localName, value);
310     }
311 
writeAttribute(String prefix, String namespaceURI, String localName, String value)312     public void writeAttribute(String prefix, String namespaceURI,
313         String localName, String value) throws XMLStreamException
314     {
315         if (!_inStartElement) {
316             throw new IllegalStateException(CommonResourceBundle.getInstance().getString("message.attributeWritingNotAllowed"));
317         }
318 
319         // TODO
320         // Need to check carefully the rule for the writing of
321         // namespaces in StAX. Is it safe to ignore such
322         // attributes, as declarations will be made using the
323         // writeNamespace method
324         if (namespaceURI == EncodingConstants.XMLNS_NAMESPACE_NAME ||
325                 namespaceURI.equals(EncodingConstants.XMLNS_NAMESPACE_NAME)) {
326             return;
327         }
328 
329         if (_attributesArrayIndex == _attributesArray.length) {
330             final String[] attributesArray = new String[_attributesArrayIndex * 2];
331             System.arraycopy(_attributesArray, 0, attributesArray, 0, _attributesArrayIndex);
332             _attributesArray = attributesArray;
333         }
334 
335         _attributesArray[_attributesArrayIndex++] = namespaceURI;
336         _attributesArray[_attributesArrayIndex++] = prefix;
337         _attributesArray[_attributesArrayIndex++] = localName;
338         _attributesArray[_attributesArrayIndex++] = value;
339     }
340 
writeNamespace(String prefix, String namespaceURI)341     public void writeNamespace(String prefix, String namespaceURI)
342         throws XMLStreamException
343     {
344         if (prefix == null || prefix.length() == 0 || prefix.equals(EncodingConstants.XMLNS_NAMESPACE_PREFIX)) {
345             writeDefaultNamespace(namespaceURI);
346         }
347         else {
348             if (!_inStartElement) {
349                 throw new IllegalStateException(CommonResourceBundle.getInstance().getString("message.attributeWritingNotAllowed"));
350             }
351 
352             if (_namespacesArrayIndex == _namespacesArray.length) {
353                 final String[] namespacesArray = new String[_namespacesArrayIndex * 2];
354                 System.arraycopy(_namespacesArray, 0, namespacesArray, 0, _namespacesArrayIndex);
355                 _namespacesArray = namespacesArray;
356             }
357 
358             _namespacesArray[_namespacesArrayIndex++] = prefix;
359             _namespacesArray[_namespacesArrayIndex++] = namespaceURI;
360             setPrefix(prefix, namespaceURI);
361         }
362     }
363 
writeDefaultNamespace(String namespaceURI)364     public void writeDefaultNamespace(String namespaceURI)
365         throws XMLStreamException
366     {
367         if (!_inStartElement) {
368             throw new IllegalStateException(CommonResourceBundle.getInstance().getString("message.attributeWritingNotAllowed"));
369         }
370 
371         if (_namespacesArrayIndex == _namespacesArray.length) {
372             final String[] namespacesArray = new String[_namespacesArrayIndex * 2];
373             System.arraycopy(_namespacesArray, 0, namespacesArray, 0, _namespacesArrayIndex);
374             _namespacesArray = namespacesArray;
375         }
376 
377         _namespacesArray[_namespacesArrayIndex++] = "";
378         _namespacesArray[_namespacesArrayIndex++] = namespaceURI;
379         setPrefix("", namespaceURI);
380     }
381 
writeComment(String data)382     public void writeComment(String data) throws XMLStreamException {
383         try {
384             if (getIgnoreComments()) return;
385 
386             encodeTerminationAndCurrentElement(true);
387 
388             // TODO: avoid array copy here
389             encodeComment(data.toCharArray(), 0, data.length());
390         }
391         catch (IOException e) {
392             throw new XMLStreamException(e);
393         }
394     }
395 
writeProcessingInstruction(String target)396     public void writeProcessingInstruction(String target)
397         throws XMLStreamException
398     {
399         writeProcessingInstruction(target, "");
400     }
401 
writeProcessingInstruction(String target, String data)402     public void writeProcessingInstruction(String target, String data)
403         throws XMLStreamException
404     {
405         try {
406             if (getIgnoreProcesingInstructions()) return;
407 
408             encodeTerminationAndCurrentElement(true);
409 
410             encodeProcessingInstruction(target, data);
411         }
412         catch (IOException e) {
413             throw new XMLStreamException(e);
414         }
415     }
416 
writeCData(String text)417     public void writeCData(String text) throws XMLStreamException {
418          try {
419             final int length = text.length();
420             if (length == 0) {
421                 return;
422             } else if (length < _charBuffer.length) {
423                 if (getIgnoreWhiteSpaceTextContent() &&
424                         isWhiteSpace(text)) return;
425 
426                 // Warning: this method must be called before any state
427                 // is modified, such as the _charBuffer contents,
428                 // so the characters of text cannot be copied to _charBuffer
429                 // before this call
430                 encodeTerminationAndCurrentElement(true);
431 
432                 text.getChars(0, length, _charBuffer, 0);
433                 encodeCIIBuiltInAlgorithmDataAsCDATA(_charBuffer, 0, length);
434             } else {
435                 final char ch[] = text.toCharArray();
436                 if (getIgnoreWhiteSpaceTextContent() &&
437                         isWhiteSpace(ch, 0, length)) return;
438 
439                 encodeTerminationAndCurrentElement(true);
440 
441                 encodeCIIBuiltInAlgorithmDataAsCDATA(ch, 0, length);
442             }
443         } catch (Exception e) {
444             throw new XMLStreamException(e);
445         }
446     }
447 
writeDTD(String dtd)448     public void writeDTD(String dtd) throws XMLStreamException {
449         throw new UnsupportedOperationException(CommonResourceBundle.getInstance().getString("message.notImplemented"));
450     }
451 
writeEntityRef(String name)452     public void writeEntityRef(String name) throws XMLStreamException {
453         throw new UnsupportedOperationException(CommonResourceBundle.getInstance().getString("message.notImplemented"));
454     }
455 
writeCharacters(String text)456     public void writeCharacters(String text) throws XMLStreamException {
457          try {
458             final int length = text.length();
459             if (length == 0) {
460                 return;
461             } else if (length < _charBuffer.length) {
462                 if (getIgnoreWhiteSpaceTextContent() &&
463                         isWhiteSpace(text)) return;
464 
465                 // Warning: this method must be called before any state
466                 // is modified, such as the _charBuffer contents,
467                 // so the characters of text cannot be copied to _charBuffer
468                 // before this call
469                 encodeTerminationAndCurrentElement(true);
470 
471                 text.getChars(0, length, _charBuffer, 0);
472                 encodeCharacters(_charBuffer, 0, length);
473             } else {
474                 final char ch[] = text.toCharArray();
475                 if (getIgnoreWhiteSpaceTextContent() &&
476                         isWhiteSpace(ch, 0, length)) return;
477 
478                 encodeTerminationAndCurrentElement(true);
479 
480                 encodeCharactersNoClone(ch, 0, length);
481             }
482         }
483         catch (IOException e) {
484             throw new XMLStreamException(e);
485         }
486     }
487 
writeCharacters(char[] text, int start, int len)488     public void writeCharacters(char[] text, int start, int len)
489         throws XMLStreamException
490     {
491          try {
492             if (len <= 0) {
493                 return;
494             }
495 
496             if (getIgnoreWhiteSpaceTextContent() &&
497                     isWhiteSpace(text, start, len)) return;
498 
499             encodeTerminationAndCurrentElement(true);
500 
501             encodeCharacters(text, start, len);
502         }
503         catch (IOException e) {
504             throw new XMLStreamException(e);
505         }
506     }
507 
getPrefix(String uri)508     public String getPrefix(String uri) throws XMLStreamException {
509         return _nsContext.getPrefix(uri);
510     }
511 
setPrefix(String prefix, String uri)512     public void setPrefix(String prefix, String uri)
513         throws XMLStreamException
514     {
515         if (_stackCount > -1 && _nsSupportContextStack[_stackCount] == false) {
516             _nsSupportContextStack[_stackCount] = true;
517             _nsContext.pushContext();
518         }
519 
520         _nsContext.declarePrefix(prefix, uri);
521     }
522 
setDefaultNamespace(String uri)523     public void setDefaultNamespace(String uri) throws XMLStreamException {
524         setPrefix("", uri);
525     }
526 
527     /**
528      * Sets the current namespace context for prefix and uri bindings.
529      * This context becomes the root namespace context for writing and
530      * will replace the current root namespace context.  Subsequent calls
531      * to setPrefix and setDefaultNamespace will bind namespaces using
532      * the context passed to the method as the root context for resolving
533      * namespaces.  This method may only be called once at the start of
534      * the document.  It does not cause the namespaces to be declared.
535      * If a namespace URI to prefix mapping is found in the namespace
536      * context it is treated as declared and the prefix may be used
537      * by the StreamWriter.
538      * @param context the namespace context to use for this writer, may not be null
539      * @throws XMLStreamException
540      */
setNamespaceContext(NamespaceContext context)541     public void setNamespaceContext(NamespaceContext context)
542         throws XMLStreamException
543     {
544         throw new UnsupportedOperationException("setNamespaceContext");
545     }
546 
getNamespaceContext()547     public NamespaceContext getNamespaceContext() {
548         return _nsContext;
549     }
550 
getProperty(java.lang.String name)551     public Object getProperty(java.lang.String name)
552         throws IllegalArgumentException
553     {
554         if (_manager != null) {
555             return _manager.getProperty(name);
556         }
557         return null;
558     }
559 
setManager(StAXManager manager)560     public void setManager(StAXManager manager) {
561         _manager = manager;
562     }
563 
setEncoding(String encoding)564     public void setEncoding(String encoding) {
565         _encoding = encoding;
566     }
567 
568 
writeOctets(byte[] b, int start, int len)569     public void writeOctets(byte[] b, int start, int len)
570         throws XMLStreamException
571     {
572          try {
573             if (len == 0) {
574                 return;
575             }
576 
577             encodeTerminationAndCurrentElement(true);
578 
579             encodeCIIOctetAlgorithmData(EncodingAlgorithmIndexes.BASE64, b, start, len);
580         }
581         catch (IOException e) {
582             throw new XMLStreamException(e);
583         }
584     }
585 
encodeTerminationAndCurrentElement(boolean terminateAfter)586     protected void encodeTerminationAndCurrentElement(boolean terminateAfter) throws XMLStreamException {
587         try {
588             encodeTermination();
589 
590             if (_inStartElement) {
591 
592                 _b = EncodingConstants.ELEMENT;
593                 if (_attributesArrayIndex > 0) {
594                     _b |= EncodingConstants.ELEMENT_ATTRIBUTE_FLAG;
595                 }
596 
597                 // Encode namespace decls associated with this element
598                 if (_namespacesArrayIndex > 0) {
599                     write(_b | EncodingConstants.ELEMENT_NAMESPACES_FLAG);
600                     for (int i = 0; i < _namespacesArrayIndex;) {
601                         encodeNamespaceAttribute(_namespacesArray[i++], _namespacesArray[i++]);
602                     }
603                     _namespacesArrayIndex = 0;
604 
605                     write(EncodingConstants.TERMINATOR);
606 
607                     _b = 0;
608                 }
609 
610                 // If element's prefix is empty - apply default scope namespace
611                 if (_currentPrefix.length() == 0) {
612                     if (_currentUri.length() == 0) {
613                         _currentUri = _nsContext.getNamespaceURI("");
614                     } else {
615                         String tmpPrefix = getPrefix(_currentUri);
616                         if (tmpPrefix != null) {
617                             _currentPrefix = tmpPrefix;
618                         }
619                     }
620                 }
621 
622                 encodeElementQualifiedNameOnThirdBit(_currentUri, _currentPrefix, _currentLocalName);
623 
624                 for (int i = 0; i < _attributesArrayIndex;) {
625                     encodeAttributeQualifiedNameOnSecondBit(
626                             _attributesArray[i++], _attributesArray[i++], _attributesArray[i++]);
627 
628                     final String value = _attributesArray[i];
629                     _attributesArray[i++] = null;
630                     final boolean addToTable = isAttributeValueLengthMatchesLimit(value.length());
631                     encodeNonIdentifyingStringOnFirstBit(value, _v.attributeValue, addToTable, false);
632 
633                     _b = EncodingConstants.TERMINATOR;
634                     _terminate = true;
635                 }
636                 _attributesArrayIndex = 0;
637                 _inStartElement = false;
638 
639                 if (_isEmptyElement) {
640                     encodeElementTermination();
641                     if (_nsSupportContextStack[_stackCount--] == true) {
642                         _nsContext.popContext();
643                     }
644 
645                     _isEmptyElement = false;
646                 }
647 
648                 if (terminateAfter) {
649                     encodeTermination();
650                 }
651             }
652         } catch (IOException e) {
653             throw new XMLStreamException(e);
654         }
655     }
656 
657 
658     // LowLevelFastInfosetSerializer
659 
initiateLowLevelWriting()660     public final void initiateLowLevelWriting() throws XMLStreamException {
661         encodeTerminationAndCurrentElement(false);
662     }
663 
getNextElementIndex()664     public final int getNextElementIndex() {
665         return _v.elementName.getNextIndex();
666     }
667 
getNextAttributeIndex()668     public final int getNextAttributeIndex() {
669         return _v.attributeName.getNextIndex();
670     }
671 
getLocalNameIndex()672     public final int getLocalNameIndex() {
673         return _v.localName.getIndex();
674     }
675 
getNextLocalNameIndex()676     public final int getNextLocalNameIndex() {
677         return _v.localName.getNextIndex();
678     }
679 
writeLowLevelTerminationAndMark()680     public final void writeLowLevelTerminationAndMark() throws IOException {
681         encodeTermination();
682         mark();
683     }
684 
writeLowLevelStartElementIndexed(int type, int index)685     public final void writeLowLevelStartElementIndexed(int type, int index) throws IOException {
686         _b = type;
687         encodeNonZeroIntegerOnThirdBit(index);
688     }
689 
writeLowLevelStartElement(int type, String prefix, String localName, String namespaceURI)690     public final boolean writeLowLevelStartElement(int type, String prefix, String localName,
691             String namespaceURI) throws IOException {
692         final boolean isIndexed = encodeElement(type, namespaceURI, prefix, localName);
693 
694         if (!isIndexed)
695             encodeLiteral(type | EncodingConstants.ELEMENT_LITERAL_QNAME_FLAG,
696                     namespaceURI, prefix, localName);
697 
698         return isIndexed;
699     }
700 
writeLowLevelStartNamespaces()701     public final void writeLowLevelStartNamespaces() throws IOException {
702         write(EncodingConstants.ELEMENT | EncodingConstants.ELEMENT_NAMESPACES_FLAG);
703     }
704 
writeLowLevelNamespace(String prefix, String namespaceName)705     public final void writeLowLevelNamespace(String prefix, String namespaceName)
706         throws IOException {
707         encodeNamespaceAttribute(prefix, namespaceName);
708     }
709 
writeLowLevelEndNamespaces()710     public final void writeLowLevelEndNamespaces() throws IOException {
711         write(EncodingConstants.TERMINATOR);
712     }
713 
writeLowLevelStartAttributes()714     public final void writeLowLevelStartAttributes() throws IOException {
715         if (hasMark()) {
716             _octetBuffer[_markIndex] |= EncodingConstants.ELEMENT_ATTRIBUTE_FLAG;
717             resetMark();
718         }
719     }
720 
writeLowLevelAttributeIndexed(int index)721     public final void writeLowLevelAttributeIndexed(int index) throws IOException {
722         encodeNonZeroIntegerOnSecondBitFirstBitZero(index);
723     }
724 
writeLowLevelAttribute(String prefix, String namespaceURI, String localName)725     public final boolean writeLowLevelAttribute(String prefix, String namespaceURI, String localName) throws IOException {
726         final boolean isIndexed = encodeAttribute(namespaceURI, prefix, localName);
727 
728         if (!isIndexed)
729             encodeLiteral(EncodingConstants.ATTRIBUTE_LITERAL_QNAME_FLAG,
730                     namespaceURI, prefix, localName);
731 
732         return isIndexed;
733     }
734 
writeLowLevelAttributeValue(String value)735     public final void writeLowLevelAttributeValue(String value) throws IOException
736     {
737         final boolean addToTable = isAttributeValueLengthMatchesLimit(value.length());
738         encodeNonIdentifyingStringOnFirstBit(value, _v.attributeValue, addToTable, false);
739     }
740 
writeLowLevelStartNameLiteral(int type, String prefix, byte[] utf8LocalName, String namespaceURI)741     public final void writeLowLevelStartNameLiteral(int type, String prefix, byte[] utf8LocalName,
742             String namespaceURI) throws IOException {
743         encodeLiteralHeader(type, namespaceURI, prefix);
744         encodeNonZeroOctetStringLengthOnSecondBit(utf8LocalName.length);
745         write(utf8LocalName, 0, utf8LocalName.length);
746     }
747 
writeLowLevelStartNameLiteral(int type, String prefix, int localNameIndex, String namespaceURI)748     public final void writeLowLevelStartNameLiteral(int type, String prefix, int localNameIndex,
749             String namespaceURI) throws IOException {
750         encodeLiteralHeader(type, namespaceURI, prefix);
751         encodeNonZeroIntegerOnSecondBitFirstBitOne(localNameIndex);
752     }
753 
writeLowLevelEndStartElement()754     public final void writeLowLevelEndStartElement() throws IOException {
755         if (hasMark()) {
756             resetMark();
757         } else {
758             // Terminate the attributes
759             _b = EncodingConstants.TERMINATOR;
760             _terminate = true;
761         }
762     }
763 
writeLowLevelEndElement()764     public final void writeLowLevelEndElement() throws IOException {
765         encodeElementTermination();
766     }
767 
writeLowLevelText(char[] text, int length)768     public final void writeLowLevelText(char[] text, int length) throws IOException {
769         if (length == 0)
770             return;
771 
772         encodeTermination();
773 
774         encodeCharacters(text, 0, length);
775     }
776 
writeLowLevelText(String text)777     public final void writeLowLevelText(String text) throws IOException {
778         final int length = text.length();
779         if (length == 0)
780             return;
781 
782         encodeTermination();
783 
784         if (length < _charBuffer.length) {
785             text.getChars(0, length, _charBuffer, 0);
786             encodeCharacters(_charBuffer, 0, length);
787         } else {
788             final char ch[] = text.toCharArray();
789             encodeCharactersNoClone(ch, 0, length);
790         }
791     }
792 
writeLowLevelOctets(byte[] octets, int length)793     public final void writeLowLevelOctets(byte[] octets, int length) throws IOException {
794         if (length == 0)
795             return;
796 
797         encodeTermination();
798 
799         encodeCIIOctetAlgorithmData(EncodingAlgorithmIndexes.BASE64, octets, 0, length);
800     }
801 
encodeElement(int type, String namespaceURI, String prefix, String localName)802     private boolean encodeElement(int type, String namespaceURI, String prefix, String localName) throws IOException {
803         final LocalNameQualifiedNamesMap.Entry entry = _v.elementName.obtainEntry(localName);
804         for (int i = 0; i < entry._valueIndex; i++) {
805             final QualifiedName name = entry._value[i];
806             if ((prefix == name.prefix || prefix.equals(name.prefix))
807                     && (namespaceURI == name.namespaceName || namespaceURI.equals(name.namespaceName))) {
808                 _b = type;
809                 encodeNonZeroIntegerOnThirdBit(name.index);
810                 return true;
811             }
812         }
813 
814         entry.addQualifiedName(new QualifiedName(prefix, namespaceURI, localName, "", _v.elementName.getNextIndex()));
815         return false;
816     }
817 
encodeAttribute(String namespaceURI, String prefix, String localName)818     private boolean encodeAttribute(String namespaceURI, String prefix, String localName) throws IOException {
819         final LocalNameQualifiedNamesMap.Entry entry = _v.attributeName.obtainEntry(localName);
820         for (int i = 0; i < entry._valueIndex; i++) {
821             final QualifiedName name = entry._value[i];
822             if ((prefix == name.prefix || prefix.equals(name.prefix))
823                     && (namespaceURI == name.namespaceName || namespaceURI.equals(name.namespaceName))) {
824                 encodeNonZeroIntegerOnSecondBitFirstBitZero(name.index);
825                 return true;
826             }
827         }
828 
829         entry.addQualifiedName(new QualifiedName(prefix, namespaceURI, localName, "", _v.attributeName.getNextIndex()));
830         return false;
831     }
832 
encodeLiteralHeader(int type, String namespaceURI, String prefix)833     private void encodeLiteralHeader(int type, String namespaceURI, String prefix) throws IOException {
834         if (namespaceURI != "") {
835             type |= EncodingConstants.LITERAL_QNAME_NAMESPACE_NAME_FLAG;
836             if (prefix != "")
837                 type |= EncodingConstants.LITERAL_QNAME_PREFIX_FLAG;
838 
839             write(type);
840             if (prefix != "")
841                 encodeNonZeroIntegerOnSecondBitFirstBitOne(_v.prefix.get(prefix));
842             encodeNonZeroIntegerOnSecondBitFirstBitOne(_v.namespaceName.get(namespaceURI));
843         } else
844             write(type);
845     }
846 
encodeLiteral(int type, String namespaceURI, String prefix, String localName)847     private void encodeLiteral(int type, String namespaceURI, String prefix, String localName) throws IOException {
848         encodeLiteralHeader(type, namespaceURI, prefix);
849 
850         final int localNameIndex = _v.localName.obtainIndex(localName);
851         if (localNameIndex == -1) {
852             encodeNonEmptyOctetStringOnSecondBit(localName);
853         } else
854             encodeNonZeroIntegerOnSecondBitFirstBitOne(localNameIndex);
855     }
856 }
857