1/*
2 * Copyright (c) 2005, 2013, 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
26package com.sun.xml.internal.stream.buffer.sax;
27
28import com.sun.xml.internal.stream.buffer.AbstractProcessor;
29import com.sun.xml.internal.stream.buffer.AttributesHolder;
30import com.sun.xml.internal.stream.buffer.XMLStreamBuffer;
31import org.xml.sax.ContentHandler;
32import org.xml.sax.DTDHandler;
33import org.xml.sax.EntityResolver;
34import org.xml.sax.ErrorHandler;
35import org.xml.sax.InputSource;
36import org.xml.sax.SAXException;
37import org.xml.sax.SAXNotRecognizedException;
38import org.xml.sax.SAXNotSupportedException;
39import org.xml.sax.SAXParseException;
40import org.xml.sax.XMLReader;
41import org.xml.sax.ext.LexicalHandler;
42import org.xml.sax.helpers.LocatorImpl;
43
44import javax.xml.XMLConstants;
45import java.io.IOException;
46import java.util.Collections;
47import java.util.HashSet;
48import java.util.Map;
49import java.util.Set;
50
51/**
52 * A processor of a {@link XMLStreamBuffer} that that reads the XML infoset as
53 * {@link XMLReader}.
54 */
55public class SAXBufferProcessor extends AbstractProcessor implements XMLReader {
56    /**
57     * Reference to entity resolver.
58     */
59    protected EntityResolver _entityResolver = DEFAULT_LEXICAL_HANDLER;
60
61    /**
62     * Reference to dtd handler.
63     */
64    protected DTDHandler _dtdHandler = DEFAULT_LEXICAL_HANDLER;
65
66    /**
67     * Reference to content handler.
68     */
69    protected ContentHandler _contentHandler = DEFAULT_LEXICAL_HANDLER;
70
71    /**
72     * Reference to error handler.
73     */
74    protected ErrorHandler _errorHandler = DEFAULT_LEXICAL_HANDLER;
75
76    /**
77     * Reference to lexical handler.
78     */
79    protected LexicalHandler _lexicalHandler = DEFAULT_LEXICAL_HANDLER;
80
81    /**
82     * SAX Namespace attributes features
83     */
84    protected boolean _namespacePrefixesFeature = false;
85
86    protected AttributesHolder _attributes = new AttributesHolder();
87
88    protected String[] _namespacePrefixes = new String[16];
89    protected int _namespacePrefixesIndex;
90
91    protected int[] _namespaceAttributesStartingStack = new int[16];
92    protected int[] _namespaceAttributesStack = new int[16];
93    protected int _namespaceAttributesStackIndex;
94
95    public SAXBufferProcessor() {
96    }
97
98    /**
99     * @deprecated
100     *      Use {@link #SAXBufferProcessor(XMLStreamBuffer, boolean)}
101     */
102    public SAXBufferProcessor(XMLStreamBuffer buffer) {
103        setXMLStreamBuffer(buffer);
104    }
105
106    /**
107     * @param produceFragmentEvent
108     *      True to generate fragment SAX events without start/endDocument.
109     *      False to generate a full document SAX events.
110     */
111    public SAXBufferProcessor(XMLStreamBuffer buffer, boolean produceFragmentEvent) {
112        setXMLStreamBuffer(buffer,produceFragmentEvent);
113    }
114
115    public boolean getFeature(String name)
116            throws SAXNotRecognizedException, SAXNotSupportedException {
117        if (name.equals(Features.NAMESPACES_FEATURE)) {
118            return true;
119        } else if (name.equals(Features.NAMESPACE_PREFIXES_FEATURE)) {
120            return _namespacePrefixesFeature;
121        } else if (name.equals(Features.EXTERNAL_GENERAL_ENTITIES)) {
122            return true;
123        } else if (name.equals(Features.EXTERNAL_PARAMETER_ENTITIES)) {
124            return true;
125        } else if (name.equals(Features.STRING_INTERNING_FEATURE)) {
126            return _stringInterningFeature;
127        } else {
128            throw new SAXNotRecognizedException(
129                    "Feature not supported: " + name);
130        }
131    }
132
133    public void setFeature(String name, boolean value)
134            throws SAXNotRecognizedException, SAXNotSupportedException {
135        if (name.equals(Features.NAMESPACES_FEATURE)) {
136            if (!value) {
137                throw new SAXNotSupportedException(name + ":" + value);
138            }
139        } else if (name.equals(Features.NAMESPACE_PREFIXES_FEATURE)) {
140            _namespacePrefixesFeature = value;
141        } else if (name.equals(Features.EXTERNAL_GENERAL_ENTITIES)) {
142            // ignore
143        } else if (name.equals(Features.EXTERNAL_PARAMETER_ENTITIES)) {
144            // ignore
145        } else if (name.equals(Features.STRING_INTERNING_FEATURE)) {
146            if (value != _stringInterningFeature) {
147                throw new SAXNotSupportedException(name + ":" + value);
148            }
149        } else {
150            throw new SAXNotRecognizedException(
151                    "Feature not supported: " + name);
152        }
153    }
154
155    public Object getProperty(String name)
156            throws SAXNotRecognizedException, SAXNotSupportedException {
157        if (name.equals(Properties.LEXICAL_HANDLER_PROPERTY)) {
158            return getLexicalHandler();
159        } else {
160            throw new SAXNotRecognizedException("Property not recognized: " + name);
161        }
162    }
163
164    public void setProperty(String name, Object value)
165            throws SAXNotRecognizedException, SAXNotSupportedException {
166        if (name.equals(Properties.LEXICAL_HANDLER_PROPERTY)) {
167            if (value instanceof LexicalHandler) {
168                setLexicalHandler((LexicalHandler)value);
169            } else {
170                throw new SAXNotSupportedException(Properties.LEXICAL_HANDLER_PROPERTY);
171            }
172        } else {
173            throw new SAXNotRecognizedException("Property not recognized: " + name);
174        }
175    }
176
177    public void setEntityResolver(EntityResolver resolver) {
178        _entityResolver = resolver;
179    }
180
181    public EntityResolver getEntityResolver() {
182        return _entityResolver;
183    }
184
185    public void setDTDHandler(DTDHandler handler) {
186        _dtdHandler = handler;
187    }
188
189    public DTDHandler getDTDHandler() {
190        return _dtdHandler;
191    }
192
193    public void setContentHandler(ContentHandler handler) {
194        _contentHandler = handler;
195    }
196
197    public ContentHandler getContentHandler() {
198        return _contentHandler;
199    }
200
201    public void setErrorHandler(ErrorHandler handler) {
202        _errorHandler = handler;
203    }
204
205    public ErrorHandler getErrorHandler() {
206        return _errorHandler;
207    }
208
209    public void setLexicalHandler(LexicalHandler handler) {
210        _lexicalHandler = handler;
211    }
212
213    public LexicalHandler getLexicalHandler() {
214        return _lexicalHandler;
215    }
216
217    public void parse(InputSource input) throws IOException, SAXException {
218        // InputSource is ignored
219        process();
220    }
221
222    public void parse(String systemId) throws IOException, SAXException {
223        // systemId is ignored
224        process();
225    }
226
227    /**
228     * Short-hand for {@link #setXMLStreamBuffer(XMLStreamBuffer)} then {@link #process()}.
229     *
230     * @deprecated
231     *      Use {@link #process(XMLStreamBuffer, boolean)}
232     */
233    public final void process(XMLStreamBuffer buffer) throws SAXException {
234        setXMLStreamBuffer(buffer);
235        process();
236    }
237
238    /**
239     * Short-hand for {@link #setXMLStreamBuffer(XMLStreamBuffer,boolean)} then {@link #process()}.
240     *
241     * @param produceFragmentEvent
242     *      True to generate fragment SAX events without start/endDocument.
243     *      False to generate a full document SAX events.
244     */
245    public final void process(XMLStreamBuffer buffer, boolean produceFragmentEvent) throws SAXException {
246        setXMLStreamBuffer(buffer);
247        process();
248    }
249
250    /**
251     * Resets the parser to read from the beginning of the given {@link XMLStreamBuffer}.
252     *
253     * @deprecated
254     *      Use {@link #setXMLStreamBuffer(XMLStreamBuffer, boolean)}.
255     */
256    public void setXMLStreamBuffer(XMLStreamBuffer buffer) {
257        setBuffer(buffer);
258    }
259
260    /**
261     * Resets the parser to read from the beginning of the given {@link XMLStreamBuffer}.
262     *
263     * @param produceFragmentEvent
264     *      True to generate fragment SAX events without start/endDocument.
265     *      False to generate a full document SAX events.
266     */
267    public void setXMLStreamBuffer(XMLStreamBuffer buffer, boolean produceFragmentEvent) {
268        if(!produceFragmentEvent && _treeCount>1)
269            throw new IllegalStateException("Can't write a forest to a full XML infoset");
270        setBuffer(buffer,produceFragmentEvent);
271    }
272
273    /**
274     * Parse the sub-tree (or a whole document) that {@link XMLStreamBuffer}
275     * points to, and sends events to handlers.
276     *
277     * <p>
278     * TODO:
279     * We probably need two modes for a sub-tree event generation. One for
280     * firing a sub-tree as if it's a whole document (in which case start/endDocument
281     * and appropriate additional namespace bindings are necessary), and the other
282     * mode for firing a subtree as a subtree, like it does today.
283     * A stream buffer SAX feature could be used to specify this.
284     *
285     * @throws SAXException
286     *      Follow the same semantics as {@link XMLReader#parse(InputSource)}.
287     */
288    public final void process() throws SAXException {
289        if(!_fragmentMode) {
290            LocatorImpl nullLocator = new LocatorImpl();
291            nullLocator.setSystemId(_buffer.getSystemId());
292            nullLocator.setLineNumber(-1);
293            nullLocator.setColumnNumber(-1);
294            _contentHandler.setDocumentLocator(nullLocator);
295
296            _contentHandler.startDocument();
297            // TODO: if we are writing a fragment stream buffer as a full XML document,
298            // we need to declare in-scope namespaces as if they are on the root element.
299        }
300
301        while (_treeCount>0) {
302            final int item = readEiiState();
303            switch(item) {
304                case STATE_DOCUMENT:
305                    processDocument();
306                    _treeCount--;
307                    break;
308                case STATE_END:
309                    // Empty buffer
310                    return;
311                case STATE_ELEMENT_U_LN_QN:
312                    processElement(readStructureString(), readStructureString(), readStructureString(), isInscope());
313                    _treeCount--;
314                    break;
315                case STATE_ELEMENT_P_U_LN:
316                {
317                    final String prefix = readStructureString();
318                    final String uri = readStructureString();
319                    final String localName = readStructureString();
320                    processElement(uri, localName, getQName(prefix, localName),isInscope());
321                    _treeCount--;
322                    break;
323                }
324                case STATE_ELEMENT_U_LN: {
325                    final String uri = readStructureString();
326                    final String localName = readStructureString();
327                    processElement(uri, localName, localName,isInscope());
328                    _treeCount--;
329                    break;
330                }
331                case STATE_ELEMENT_LN:
332                {
333                    final String localName = readStructureString();
334                    processElement("", localName, localName,isInscope());
335                    _treeCount--;
336                    break;
337                }
338                case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
339                    processCommentAsCharArraySmall();
340                    break;
341                case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
342                    processCommentAsCharArrayMedium();
343                    break;
344                case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
345                    processCommentAsCharArrayCopy();
346                    break;
347                case STATE_COMMENT_AS_STRING:
348                    processComment(readContentString());
349                    break;
350                case STATE_PROCESSING_INSTRUCTION:
351                    processProcessingInstruction(readStructureString(), readStructureString());
352                    break;
353                default:
354                    throw reportFatalError("Illegal state for DIIs: "+item);
355            }
356        }
357
358        if(!_fragmentMode)
359            _contentHandler.endDocument();
360    }
361
362    private void processCommentAsCharArraySmall() throws SAXException {
363        final int length = readStructure();
364        final int start = readContentCharactersBuffer(length);
365        processComment(_contentCharactersBuffer, start, length);
366    }
367
368    /**
369     * Report a fatal error and abort.
370     *
371     * This is necessary to follow the SAX semantics of error handling.
372     */
373    private SAXParseException reportFatalError(String msg) throws SAXException {
374        SAXParseException spe = new SAXParseException(msg, null);
375        if(_errorHandler!=null)
376            _errorHandler.fatalError(spe);
377        return spe;
378    }
379
380    private boolean isInscope() {
381        return _buffer.getInscopeNamespaces().size() > 0;
382    }
383
384    private void processDocument() throws SAXException {
385        while(true) {
386            int item = readEiiState();
387            switch(item) {
388                case STATE_ELEMENT_U_LN_QN:
389                    processElement(readStructureString(), readStructureString(), readStructureString(),isInscope());
390                    break;
391                case STATE_ELEMENT_P_U_LN:
392                {
393                    final String prefix = readStructureString();
394                    final String uri = readStructureString();
395                    final String localName = readStructureString();
396                    processElement(uri, localName, getQName(prefix, localName),isInscope());
397                    break;
398                }
399                case STATE_ELEMENT_U_LN: {
400                    final String uri = readStructureString();
401                    final String localName = readStructureString();
402                    processElement(uri, localName, localName,isInscope());
403                    break;
404                }
405                case STATE_ELEMENT_LN:
406                {
407                    final String localName = readStructureString();
408                    processElement("", localName, localName,isInscope());
409                    break;
410                }
411                case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
412                    processCommentAsCharArraySmall();
413                    break;
414                case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
415                    processCommentAsCharArrayMedium();
416                    break;
417                case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
418                    processCommentAsCharArrayCopy();
419                    break;
420                case STATE_COMMENT_AS_STRING:
421                    processComment(readContentString());
422                    break;
423                case STATE_PROCESSING_INSTRUCTION:
424                    processProcessingInstruction(readStructureString(), readStructureString());
425                    break;
426                case STATE_END:
427                    return;
428                default:
429                    throw reportFatalError("Illegal state for child of DII: "+item);
430            }
431        }
432    }
433
434    protected void processElement(String uri, String localName, String qName, boolean inscope) throws SAXException {
435        boolean hasAttributes = false;
436        boolean hasNamespaceAttributes = false;
437        int item = peekStructure();
438        Set<String> prefixSet = inscope ? new HashSet<String>() : Collections.<String>emptySet();
439        if ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE) {
440            cacheNamespacePrefixStartingIndex();
441            hasNamespaceAttributes = true;
442            item = processNamespaceAttributes(item, inscope, prefixSet);
443        }
444        if (inscope) {
445            readInscopeNamespaces(prefixSet);
446        }
447
448        if ((item & TYPE_MASK) == T_ATTRIBUTE) {
449            hasAttributes = true;
450            processAttributes(item);
451        }
452
453        _contentHandler.startElement(uri, localName, qName, _attributes);
454
455        if (hasAttributes) {
456            _attributes.clear();
457        }
458
459        do {
460            item = readEiiState();
461            switch(item) {
462                case STATE_ELEMENT_U_LN_QN:
463                    processElement(readStructureString(), readStructureString(), readStructureString(), false);
464                    break;
465                case STATE_ELEMENT_P_U_LN:
466                {
467                    final String p = readStructureString();
468                    final String u = readStructureString();
469                    final String ln = readStructureString();
470                    processElement(u, ln, getQName(p, ln),false);
471                    break;
472                }
473                case STATE_ELEMENT_U_LN: {
474                    final String u = readStructureString();
475                    final String ln = readStructureString();
476                    processElement(u, ln, ln,false);
477                    break;
478                }
479                case STATE_ELEMENT_LN: {
480                    final String ln = readStructureString();
481                    processElement("", ln, ln,false);
482                    break;
483                }
484                case STATE_TEXT_AS_CHAR_ARRAY_SMALL:
485                {
486                    final int length = readStructure();
487                    int start = readContentCharactersBuffer(length);
488                    _contentHandler.characters(_contentCharactersBuffer, start, length);
489                    break;
490                }
491                case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM:
492                {
493                    final int length = readStructure16();
494                    int start = readContentCharactersBuffer(length);
495                    _contentHandler.characters(_contentCharactersBuffer, start, length);
496                    break;
497                }
498                case STATE_TEXT_AS_CHAR_ARRAY_COPY:
499                {
500                    final char[] ch = readContentCharactersCopy();
501
502                    _contentHandler.characters(ch, 0, ch.length);
503                    break;
504                }
505                case STATE_TEXT_AS_STRING:
506                {
507                    final String s = readContentString();
508                    _contentHandler.characters(s.toCharArray(), 0, s.length());
509                    break;
510                }
511                case STATE_TEXT_AS_OBJECT:
512                {
513                    final CharSequence c = (CharSequence)readContentObject();
514                    final String s = c.toString();
515                    _contentHandler.characters(s.toCharArray(), 0, s.length());
516                    break;
517                }
518                case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
519                    processCommentAsCharArraySmall();
520                    break;
521                case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
522                    processCommentAsCharArrayMedium();
523                    break;
524                case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
525                    processCommentAsCharArrayCopy();
526                    break;
527                case T_COMMENT_AS_STRING:
528                    processComment(readContentString());
529                    break;
530                case STATE_PROCESSING_INSTRUCTION:
531                    processProcessingInstruction(readStructureString(), readStructureString());
532                    break;
533                case STATE_END:
534                    break;
535                default:
536                    throw reportFatalError("Illegal state for child of EII: "+item);
537            }
538        } while(item != STATE_END);
539
540        _contentHandler.endElement(uri, localName, qName);
541
542        if (hasNamespaceAttributes) {
543            processEndPrefixMapping();
544        }
545    }
546
547    private void readInscopeNamespaces(Set<String> prefixSet) throws SAXException {
548        for (Map.Entry<String, String> e : _buffer.getInscopeNamespaces().entrySet()) {
549            String key = fixNull(e.getKey());
550            // If the prefix is already written, do not write the prefix
551            if (!prefixSet.contains(key)) {
552                processNamespaceAttribute(key,e.getValue());
553            }
554        }
555
556    }
557
558     private static String fixNull(String s) {
559        if (s == null) return "";
560        else return s;
561    }
562    private void processCommentAsCharArrayCopy() throws SAXException {
563        final char[] ch = readContentCharactersCopy();
564        processComment(ch, 0, ch.length);
565    }
566
567    private void processCommentAsCharArrayMedium() throws SAXException {
568        final int length = readStructure16();
569        final int start = readContentCharactersBuffer(length);
570        processComment(_contentCharactersBuffer, start, length);
571    }
572
573    private void processEndPrefixMapping() throws SAXException {
574        final int end = _namespaceAttributesStack[--_namespaceAttributesStackIndex];
575//      final int start = (_namespaceAttributesStackIndex > 0) ? _namespaceAttributesStack[_namespaceAttributesStackIndex] : 0;
576        final int start = (_namespaceAttributesStackIndex >= 0) ? _namespaceAttributesStartingStack[_namespaceAttributesStackIndex] : 0;
577
578        for (int i = end - 1; i >= start; i--) {
579            _contentHandler.endPrefixMapping(_namespacePrefixes[i]);
580        }
581        _namespacePrefixesIndex = start;
582    }
583
584    private int processNamespaceAttributes(int item,boolean collectPrefixes, Set<String> prefixSet) throws SAXException {
585        do {
586            String prefix;
587            switch(getNIIState(item)) {
588                case STATE_NAMESPACE_ATTRIBUTE:
589                    // Undeclaration of default namespace
590                    processNamespaceAttribute("", "");
591                    if(collectPrefixes) {
592                        prefixSet.add("");
593                    }
594                    break;
595                case STATE_NAMESPACE_ATTRIBUTE_P:
596                    // Undeclaration of namespace
597                    prefix = readStructureString();
598                    processNamespaceAttribute(prefix, "");
599                    if(collectPrefixes) {
600                        prefixSet.add(prefix);
601                    }
602                    break;
603                case STATE_NAMESPACE_ATTRIBUTE_P_U:
604                    // Declaration with prefix
605                   prefix = readStructureString();
606                    processNamespaceAttribute(prefix, readStructureString());
607                    if(collectPrefixes) {
608                        prefixSet.add(prefix);
609                    }
610                    break;
611                case STATE_NAMESPACE_ATTRIBUTE_U:
612                    // Default declaration
613                    processNamespaceAttribute("", readStructureString());
614                    if(collectPrefixes) {
615                        prefixSet.add("");
616                    }
617                    break;
618                default:
619                    throw reportFatalError("Illegal state: "+item);
620            }
621            readStructure();
622
623            item = peekStructure();
624        } while((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE);
625
626
627        cacheNamespacePrefixIndex();
628
629        return item;
630    }
631
632    private void processAttributes(int item) throws SAXException {
633        do {
634            switch(getAIIState(item)) {
635                case STATE_ATTRIBUTE_U_LN_QN:
636                    _attributes.addAttributeWithQName(readStructureString(), readStructureString(), readStructureString(), readStructureString(), readContentString());
637                    break;
638                case STATE_ATTRIBUTE_P_U_LN:
639                {
640                    final String p = readStructureString();
641                    final String u = readStructureString();
642                    final String ln = readStructureString();
643                    _attributes.addAttributeWithQName(u, ln, getQName(p, ln), readStructureString(), readContentString());
644                    break;
645                }
646                case STATE_ATTRIBUTE_U_LN: {
647                    final String u = readStructureString();
648                    final String ln = readStructureString();
649                    _attributes.addAttributeWithQName(u, ln, ln, readStructureString(), readContentString());
650                    break;
651                }
652                case STATE_ATTRIBUTE_LN: {
653                    final String ln = readStructureString();
654                    _attributes.addAttributeWithQName("", ln, ln, readStructureString(), readContentString());
655                    break;
656                }
657                default:
658                    throw reportFatalError("Illegal state: "+item);
659            }
660            readStructure();
661
662            item = peekStructure();
663        } while((item & TYPE_MASK) == T_ATTRIBUTE);
664    }
665
666    private void processNamespaceAttribute(String prefix, String uri) throws SAXException {
667        _contentHandler.startPrefixMapping(prefix, uri);
668
669        if (_namespacePrefixesFeature) {
670            // Add the namespace delcaration as an attribute
671            if (prefix != "") {
672                _attributes.addAttributeWithQName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, prefix,
673                        getQName(XMLConstants.XMLNS_ATTRIBUTE, prefix),
674                        "CDATA", uri);
675            } else {
676                _attributes.addAttributeWithQName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, XMLConstants.XMLNS_ATTRIBUTE,
677                        XMLConstants.XMLNS_ATTRIBUTE,
678                        "CDATA", uri);
679            }
680        }
681
682        cacheNamespacePrefix(prefix);
683    }
684
685    private void cacheNamespacePrefix(String prefix) {
686        if (_namespacePrefixesIndex == _namespacePrefixes.length) {
687            final String[] namespaceAttributes = new String[_namespacePrefixesIndex * 3 / 2 + 1];
688            System.arraycopy(_namespacePrefixes, 0, namespaceAttributes, 0, _namespacePrefixesIndex);
689            _namespacePrefixes = namespaceAttributes;
690        }
691
692        _namespacePrefixes[_namespacePrefixesIndex++] = prefix;
693    }
694
695    private void cacheNamespacePrefixIndex() {
696        if (_namespaceAttributesStackIndex == _namespaceAttributesStack.length) {
697            final int[] namespaceAttributesStack = new int[_namespaceAttributesStackIndex * 3 /2 + 1];
698            System.arraycopy(_namespaceAttributesStack, 0, namespaceAttributesStack, 0, _namespaceAttributesStackIndex);
699            _namespaceAttributesStack = namespaceAttributesStack;
700        }
701
702        _namespaceAttributesStack[_namespaceAttributesStackIndex++] = _namespacePrefixesIndex;
703    }
704
705    private void cacheNamespacePrefixStartingIndex() {
706        if (_namespaceAttributesStackIndex == _namespaceAttributesStartingStack.length) {
707            final int[] namespaceAttributesStart = new int[_namespaceAttributesStackIndex * 3 /2 + 1];
708            System.arraycopy(_namespaceAttributesStartingStack, 0, namespaceAttributesStart, 0, _namespaceAttributesStackIndex);
709            _namespaceAttributesStartingStack = namespaceAttributesStart;
710        }
711        _namespaceAttributesStartingStack[_namespaceAttributesStackIndex] = _namespacePrefixesIndex;
712    }
713
714    private void processComment(String s)  throws SAXException {
715        processComment(s.toCharArray(), 0, s.length());
716    }
717
718    private void processComment(char[] ch, int start, int length) throws SAXException {
719        _lexicalHandler.comment(ch, start, length);
720    }
721
722    private void processProcessingInstruction(String target, String data) throws SAXException {
723        _contentHandler.processingInstruction(target, data);
724    }
725
726    private static final DefaultWithLexicalHandler DEFAULT_LEXICAL_HANDLER = new DefaultWithLexicalHandler();
727}
728