1/*
2 * Copyright (c) 2004, 2012, 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
28package com.sun.xml.internal.fastinfoset.stax;
29
30import com.sun.xml.internal.fastinfoset.Encoder;
31import com.sun.xml.internal.fastinfoset.EncodingConstants;
32import com.sun.xml.internal.fastinfoset.util.NamespaceContextImplementation;
33import java.io.IOException;
34import java.io.OutputStream;
35import java.util.EmptyStackException;
36import javax.xml.namespace.NamespaceContext;
37import javax.xml.stream.XMLStreamException;
38import javax.xml.stream.XMLStreamWriter;
39import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithmIndexes;
40import com.sun.xml.internal.fastinfoset.CommonResourceBundle;
41import com.sun.xml.internal.fastinfoset.QualifiedName;
42import com.sun.xml.internal.fastinfoset.util.LocalNameQualifiedNamesMap;
43import 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 */
55public 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
106    public StAXDocumentSerializer() {
107        super(true);
108        _manager = new StAXManager(StAXManager.CONTEXT_WRITER);
109    }
110
111    public StAXDocumentSerializer(OutputStream outputStream) {
112        super(true);
113        setOutputStream(outputStream);
114        _manager = new StAXManager(StAXManager.CONTEXT_WRITER);
115    }
116
117    public StAXDocumentSerializer(OutputStream outputStream, StAXManager manager) {
118        super(true);
119        setOutputStream(outputStream);
120        _manager = manager;
121    }
122
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
140    public void writeStartDocument() throws XMLStreamException {
141        writeStartDocument("finf", "1.0");
142    }
143
144    public void writeStartDocument(String version) throws XMLStreamException {
145        writeStartDocument("finf", version);
146    }
147
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
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
177    public void close() throws XMLStreamException {
178        reset();
179    }
180
181    public void flush() throws XMLStreamException {
182        try {
183            _s.flush();
184        }
185        catch (IOException e) {
186            throw new XMLStreamException(e);
187        }
188    }
189
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
197    public void writeStartElement(String namespaceURI, String localName)
198        throws XMLStreamException
199    {
200        writeStartElement("", localName, namespaceURI);
201    }
202
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
225    public void writeEmptyElement(String localName)
226        throws XMLStreamException
227    {
228        writeEmptyElement("", localName, "");
229    }
230
231    public void writeEmptyElement(String namespaceURI, String localName)
232        throws XMLStreamException
233    {
234        writeEmptyElement("", localName, namespaceURI);
235    }
236
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
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
278    public void writeAttribute(String localName, String value)
279        throws XMLStreamException
280    {
281        writeAttribute("", "", localName, value);
282    }
283
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
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
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
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
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
396    public void writeProcessingInstruction(String target)
397        throws XMLStreamException
398    {
399        writeProcessingInstruction(target, "");
400    }
401
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
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
448    public void writeDTD(String dtd) throws XMLStreamException {
449        throw new UnsupportedOperationException(CommonResourceBundle.getInstance().getString("message.notImplemented"));
450    }
451
452    public void writeEntityRef(String name) throws XMLStreamException {
453        throw new UnsupportedOperationException(CommonResourceBundle.getInstance().getString("message.notImplemented"));
454    }
455
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
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
508    public String getPrefix(String uri) throws XMLStreamException {
509        return _nsContext.getPrefix(uri);
510    }
511
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
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     */
541    public void setNamespaceContext(NamespaceContext context)
542        throws XMLStreamException
543    {
544        throw new UnsupportedOperationException("setNamespaceContext");
545    }
546
547    public NamespaceContext getNamespaceContext() {
548        return _nsContext;
549    }
550
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
560    public void setManager(StAXManager manager) {
561        _manager = manager;
562    }
563
564    public void setEncoding(String encoding) {
565        _encoding = encoding;
566    }
567
568
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
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
660    public final void initiateLowLevelWriting() throws XMLStreamException {
661        encodeTerminationAndCurrentElement(false);
662    }
663
664    public final int getNextElementIndex() {
665        return _v.elementName.getNextIndex();
666    }
667
668    public final int getNextAttributeIndex() {
669        return _v.attributeName.getNextIndex();
670    }
671
672    public final int getLocalNameIndex() {
673        return _v.localName.getIndex();
674    }
675
676    public final int getNextLocalNameIndex() {
677        return _v.localName.getNextIndex();
678    }
679
680    public final void writeLowLevelTerminationAndMark() throws IOException {
681        encodeTermination();
682        mark();
683    }
684
685    public final void writeLowLevelStartElementIndexed(int type, int index) throws IOException {
686        _b = type;
687        encodeNonZeroIntegerOnThirdBit(index);
688    }
689
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
701    public final void writeLowLevelStartNamespaces() throws IOException {
702        write(EncodingConstants.ELEMENT | EncodingConstants.ELEMENT_NAMESPACES_FLAG);
703    }
704
705    public final void writeLowLevelNamespace(String prefix, String namespaceName)
706        throws IOException {
707        encodeNamespaceAttribute(prefix, namespaceName);
708    }
709
710    public final void writeLowLevelEndNamespaces() throws IOException {
711        write(EncodingConstants.TERMINATOR);
712    }
713
714    public final void writeLowLevelStartAttributes() throws IOException {
715        if (hasMark()) {
716            _octetBuffer[_markIndex] |= EncodingConstants.ELEMENT_ATTRIBUTE_FLAG;
717            resetMark();
718        }
719    }
720
721    public final void writeLowLevelAttributeIndexed(int index) throws IOException {
722        encodeNonZeroIntegerOnSecondBitFirstBitZero(index);
723    }
724
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
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
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
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
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
764    public final void writeLowLevelEndElement() throws IOException {
765        encodeElementTermination();
766    }
767
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
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
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
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
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
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
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