/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2005-2012 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * http://glassfish.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.xml.stream.buffer.sax;

import com.sun.xml.stream.buffer.AbstractProcessor;
import com.sun.xml.stream.buffer.AttributesHolder;
import com.sun.xml.stream.buffer.XMLStreamBuffer;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.LocatorImpl;

import javax.xml.XMLConstants;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * A processor of a {@link XMLStreamBuffer} that that reads the XML infoset as
 * {@link XMLReader}.
 */
public class SAXBufferProcessor extends AbstractProcessor implements XMLReader {
    /**
     * Reference to entity resolver.
     */
    protected EntityResolver _entityResolver = DEFAULT_LEXICAL_HANDLER;
    
    /**
     * Reference to dtd handler.
     */
    protected DTDHandler _dtdHandler = DEFAULT_LEXICAL_HANDLER;
    
    /**
     * Reference to content handler.
     */
    protected ContentHandler _contentHandler = DEFAULT_LEXICAL_HANDLER;
    
    /**
     * Reference to error handler.
     */
    protected ErrorHandler _errorHandler = DEFAULT_LEXICAL_HANDLER;
    
    /**
     * Reference to lexical handler.
     */
    protected LexicalHandler _lexicalHandler = DEFAULT_LEXICAL_HANDLER;

    /**
     * SAX Namespace attributes features
     */
    protected boolean _namespacePrefixesFeature = false;
    
    protected AttributesHolder _attributes = new AttributesHolder();
    
    protected String[] _namespacePrefixes = new String[16];
    protected int _namespacePrefixesIndex;

    protected int[] _namespaceAttributesStartingStack = new int[16];
    protected int[] _namespaceAttributesStack = new int[16];
    protected int _namespaceAttributesStackIndex;

    public SAXBufferProcessor() {
    }

    /**
     * @deprecated
     *      Use {@link #SAXBufferProcessor(XMLStreamBuffer, boolean)}
     */
    public SAXBufferProcessor(XMLStreamBuffer buffer) {
        setXMLStreamBuffer(buffer);
    }

    /**
     * @param produceFragmentEvent
     *      True to generate fragment SAX events without start/endDocument.
     *      False to generate a full document SAX events.
     */
    public SAXBufferProcessor(XMLStreamBuffer buffer, boolean produceFragmentEvent) {
        setXMLStreamBuffer(buffer,produceFragmentEvent);
    }

    public boolean getFeature(String name)
            throws SAXNotRecognizedException, SAXNotSupportedException {
        if (name.equals(Features.NAMESPACES_FEATURE)) {
            return true;
        } else if (name.equals(Features.NAMESPACE_PREFIXES_FEATURE)) {
            return _namespacePrefixesFeature;
        } else if (name.equals(Features.EXTERNAL_GENERAL_ENTITIES)) {
            return true;
        } else if (name.equals(Features.EXTERNAL_PARAMETER_ENTITIES)) {
            return true;
        } else if (name.equals(Features.STRING_INTERNING_FEATURE)) {
            return _stringInterningFeature;
        } else {
            throw new SAXNotRecognizedException(
                    "Feature not supported: " + name);
        }
    }
    
    public void setFeature(String name, boolean value)
            throws SAXNotRecognizedException, SAXNotSupportedException {
        if (name.equals(Features.NAMESPACES_FEATURE)) {
            if (!value) {
                throw new SAXNotSupportedException(name + ":" + value);
            }
        } else if (name.equals(Features.NAMESPACE_PREFIXES_FEATURE)) {
            _namespacePrefixesFeature = value;
        } else if (name.equals(Features.EXTERNAL_GENERAL_ENTITIES)) {
            // ignore
        } else if (name.equals(Features.EXTERNAL_PARAMETER_ENTITIES)) {
            // ignore
        } else if (name.equals(Features.STRING_INTERNING_FEATURE)) {
            if (value != _stringInterningFeature) {
                throw new SAXNotSupportedException(name + ":" + value);
            }
        } else {
            throw new SAXNotRecognizedException(
                    "Feature not supported: " + name);
        }        
    }
    
    public Object getProperty(String name)
            throws SAXNotRecognizedException, SAXNotSupportedException {
        if (name.equals(Properties.LEXICAL_HANDLER_PROPERTY)) {
            return getLexicalHandler();
        } else {
            throw new SAXNotRecognizedException("Property not recognized: " + name);
        }
    }
    
    public void setProperty(String name, Object value)
            throws SAXNotRecognizedException, SAXNotSupportedException {
        if (name.equals(Properties.LEXICAL_HANDLER_PROPERTY)) {
            if (value instanceof LexicalHandler) {
                setLexicalHandler((LexicalHandler)value);
            } else {
                throw new SAXNotSupportedException(Properties.LEXICAL_HANDLER_PROPERTY);
            }
        } else {
            throw new SAXNotRecognizedException("Property not recognized: " + name);
        }
    }
    
    public void setEntityResolver(EntityResolver resolver) {
        _entityResolver = resolver;
    }
    
    public EntityResolver getEntityResolver() {
        return _entityResolver;
    }
    
    public void setDTDHandler(DTDHandler handler) {
        _dtdHandler = handler;
    }
    
    public DTDHandler getDTDHandler() {
        return _dtdHandler;
    }
    
    public void setContentHandler(ContentHandler handler) {
        _contentHandler = handler;
    }
    
    public ContentHandler getContentHandler() {
        return _contentHandler;
    }
    
    public void setErrorHandler(ErrorHandler handler) {
        _errorHandler = handler;
    }
    
    public ErrorHandler getErrorHandler() {
        return _errorHandler;
    }
    
    public void setLexicalHandler(LexicalHandler handler) {
        _lexicalHandler = handler;
    }
    
    public LexicalHandler getLexicalHandler() {
        return _lexicalHandler;
    }
    
    public void parse(InputSource input) throws IOException, SAXException {
        // InputSource is ignored
        process();
    }
    
    public void parse(String systemId) throws IOException, SAXException {
        // systemId is ignored
        process();
    }

    /**
     * Short-hand for {@link #setXMLStreamBuffer(XMLStreamBuffer)} then {@link #process()}.
     *
     * @deprecated
     *      Use {@link #process(XMLStreamBuffer, boolean)}
     */
    public final void process(XMLStreamBuffer buffer) throws SAXException {
        setXMLStreamBuffer(buffer);
        process();
    }

    /**
     * Short-hand for {@link #setXMLStreamBuffer(XMLStreamBuffer,boolean)} then {@link #process()}.
     *
     * @param produceFragmentEvent
     *      True to generate fragment SAX events without start/endDocument.
     *      False to generate a full document SAX events.
     */
    public final void process(XMLStreamBuffer buffer, boolean produceFragmentEvent) throws SAXException {
        setXMLStreamBuffer(buffer);
        process();
    }

    /**
     * Resets the parser to read from the beginning of the given {@link XMLStreamBuffer}.
     *
     * @deprecated
     *      Use {@link #setXMLStreamBuffer(XMLStreamBuffer, boolean)}.
     */
    public void setXMLStreamBuffer(XMLStreamBuffer buffer) {
        setBuffer(buffer);
    }

    /**
     * Resets the parser to read from the beginning of the given {@link XMLStreamBuffer}.
     *
     * @param produceFragmentEvent
     *      True to generate fragment SAX events without start/endDocument.
     *      False to generate a full document SAX events.
     */
    public void setXMLStreamBuffer(XMLStreamBuffer buffer, boolean produceFragmentEvent) {
        if(!produceFragmentEvent && _treeCount>1)
            throw new IllegalStateException("Can't write a forest to a full XML infoset");
        setBuffer(buffer,produceFragmentEvent);
    }

    /**
     * Parse the sub-tree (or a whole document) that {@link XMLStreamBuffer}
     * points to, and sends events to handlers.
     * 
     * <p>
     * TODO:
     * We probably need two modes for a sub-tree event generation. One for
     * firing a sub-tree as if it's a whole document (in which case start/endDocument
     * and appropriate additional namespace bindings are necessary), and the other
     * mode for firing a subtree as a subtree, like it does today.
     * A stream buffer SAX feature could be used to specify this. 
     * 
     * @throws SAXException
     *      Follow the same semantics as {@link XMLReader#parse(InputSource)}.
     */
    public final void process() throws SAXException {
        if(!_fragmentMode) {
            LocatorImpl nullLocator = new LocatorImpl();
            nullLocator.setSystemId(_buffer.getSystemId());
            nullLocator.setLineNumber(-1);
            nullLocator.setColumnNumber(-1);
            _contentHandler.setDocumentLocator(nullLocator);
            
            _contentHandler.startDocument();
            // TODO: if we are writing a fragment stream buffer as a full XML document,
            // we need to declare in-scope namespaces as if they are on the root element.
        }

        while (_treeCount>0) {
            final int item = readEiiState();
            switch(item) {
                case STATE_DOCUMENT:
                    processDocument();
                    _treeCount--;
                    break;
                case STATE_END:
                    // Empty buffer
                    return;
                case STATE_ELEMENT_U_LN_QN:
                    processElement(readStructureString(), readStructureString(), readStructureString(), isInscope());
                    _treeCount--;
                    break;
                case STATE_ELEMENT_P_U_LN:
                {
                    final String prefix = readStructureString();
                    final String uri = readStructureString();
                    final String localName = readStructureString();
                    processElement(uri, localName, getQName(prefix, localName),isInscope());
                    _treeCount--;
                    break;
                }
                case STATE_ELEMENT_U_LN: {
                    final String uri = readStructureString();
                    final String localName = readStructureString();
                    processElement(uri, localName, localName,isInscope());
                    _treeCount--;
                    break;
                }
                case STATE_ELEMENT_LN:
                {
                    final String localName = readStructureString();
                    processElement("", localName, localName,isInscope());
                    _treeCount--;
                    break;
                }
                case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
                    processCommentAsCharArraySmall();
                    break;
                case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
                    processCommentAsCharArrayMedium();
                    break;
                case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
                    processCommentAsCharArrayCopy();
                    break;
                case STATE_COMMENT_AS_STRING:
                    processComment(readContentString());
                    break;
                case STATE_PROCESSING_INSTRUCTION:
                    processProcessingInstruction(readStructureString(), readStructureString());
                    break;
                default:
                    throw reportFatalError("Illegal state for DIIs: "+item);
            }
        }

        if(!_fragmentMode)
            _contentHandler.endDocument();
    }

    private void processCommentAsCharArraySmall() throws SAXException {
        final int length = readStructure();
        final int start = readContentCharactersBuffer(length);
        processComment(_contentCharactersBuffer, start, length);
    }

    /**
     * Report a fatal error and abort.
     *
     * This is necessary to follow the SAX semantics of error handling.
     */
    private SAXParseException reportFatalError(String msg) throws SAXException {
        SAXParseException spe = new SAXParseException(msg, null);
        if(_errorHandler!=null)
            _errorHandler.fatalError(spe);
        return spe;
    }

    private boolean isInscope() {
        return _buffer.getInscopeNamespaces().size() > 0;
    }

    private void processDocument() throws SAXException {
        while(true) {
            int item = readEiiState();
            switch(item) {
                case STATE_ELEMENT_U_LN_QN:
                    processElement(readStructureString(), readStructureString(), readStructureString(),isInscope());
                    break;
                case STATE_ELEMENT_P_U_LN:
                {
                    final String prefix = readStructureString();
                    final String uri = readStructureString();
                    final String localName = readStructureString();
                    processElement(uri, localName, getQName(prefix, localName),isInscope());
                    break;
                }
                case STATE_ELEMENT_U_LN: {
                    final String uri = readStructureString();
                    final String localName = readStructureString();
                    processElement(uri, localName, localName,isInscope());
                    break;
                }
                case STATE_ELEMENT_LN:
                {
                    final String localName = readStructureString();
                    processElement("", localName, localName,isInscope());
                    break;
                }
                case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
                    processCommentAsCharArraySmall();
                    break;
                case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
                    processCommentAsCharArrayMedium();
                    break;
                case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
                    processCommentAsCharArrayCopy();
                    break;
                case STATE_COMMENT_AS_STRING:
                    processComment(readContentString());
                    break;
                case STATE_PROCESSING_INSTRUCTION:
                    processProcessingInstruction(readStructureString(), readStructureString());
                    break;
                case STATE_END:
                    return;
                default:
                    throw reportFatalError("Illegal state for child of DII: "+item);
            }
        }
    }

    protected void processElement(String uri, String localName, String qName, boolean inscope) throws SAXException {
        boolean hasAttributes = false;
        boolean hasNamespaceAttributes = false;
        int item = peekStructure();
        Set<String> prefixSet = inscope ? new HashSet<String>() : Collections.<String>emptySet();
        if ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE) {
            cacheNamespacePrefixStartingIndex();
            hasNamespaceAttributes = true;
            item = processNamespaceAttributes(item, inscope, prefixSet);
        }        
        if (inscope) {
            readInscopeNamespaces(prefixSet);
        }

        if ((item & TYPE_MASK) == T_ATTRIBUTE) {
            hasAttributes = true;
            processAttributes(item);
        }

        _contentHandler.startElement(uri, localName, qName, _attributes);

        if (hasAttributes) {
            _attributes.clear();
        }

        do {
            item = readEiiState();
            switch(item) {
                case STATE_ELEMENT_U_LN_QN:
                    processElement(readStructureString(), readStructureString(), readStructureString(), false);
                    break;
                case STATE_ELEMENT_P_U_LN:
                {
                    final String p = readStructureString();
                    final String u = readStructureString();
                    final String ln = readStructureString();
                    processElement(u, ln, getQName(p, ln),false);
                    break;
                }
                case STATE_ELEMENT_U_LN: {
                    final String u = readStructureString();
                    final String ln = readStructureString();
                    processElement(u, ln, ln,false);
                    break;
                }
                case STATE_ELEMENT_LN: {
                    final String ln = readStructureString();
                    processElement("", ln, ln,false);
                    break;
                }
                case STATE_TEXT_AS_CHAR_ARRAY_SMALL:
                {
                    final int length = readStructure();
                    int start = readContentCharactersBuffer(length);
                    _contentHandler.characters(_contentCharactersBuffer, start, length);
                    break;
                }
                case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM:
                {
                    final int length = readStructure16();
                    int start = readContentCharactersBuffer(length);
                    _contentHandler.characters(_contentCharactersBuffer, start, length);
                    break;
                }
                case STATE_TEXT_AS_CHAR_ARRAY_COPY:
                {
                    final char[] ch = readContentCharactersCopy();

                    _contentHandler.characters(ch, 0, ch.length);
                    break;
                }
                case STATE_TEXT_AS_STRING:
                {
                    final String s = readContentString();
                    _contentHandler.characters(s.toCharArray(), 0, s.length());
                    break;
                }
                case STATE_TEXT_AS_OBJECT:
                {
                    final CharSequence c = (CharSequence)readContentObject();
                    final String s = c.toString();
                    _contentHandler.characters(s.toCharArray(), 0, s.length());
                    break;
                }
                case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
                    processCommentAsCharArraySmall();
                    break;
                case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
                    processCommentAsCharArrayMedium();
                    break;
                case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
                    processCommentAsCharArrayCopy();
                    break;
                case T_COMMENT_AS_STRING:
                    processComment(readContentString());
                    break;
                case STATE_PROCESSING_INSTRUCTION:
                    processProcessingInstruction(readStructureString(), readStructureString());
                    break;
                case STATE_END:
                    break;
                default:
                    throw reportFatalError("Illegal state for child of EII: "+item);
            }
        } while(item != STATE_END);
        
        _contentHandler.endElement(uri, localName, qName);

        if (hasNamespaceAttributes) {
            processEndPrefixMapping();
        }
    }

    private void readInscopeNamespaces(Set<String> prefixSet) throws SAXException {
        for (Map.Entry<String, String> e : _buffer.getInscopeNamespaces().entrySet()) {
            String key = fixNull(e.getKey());
            // If the prefix is already written, do not write the prefix
            if (!prefixSet.contains(key)) {
                processNamespaceAttribute(key,e.getValue());
            }
        }

    }

     private static String fixNull(String s) {
        if (s == null) return "";
        else return s;
    }
    private void processCommentAsCharArrayCopy() throws SAXException {
        final char[] ch = readContentCharactersCopy();
        processComment(ch, 0, ch.length);
    }

    private void processCommentAsCharArrayMedium() throws SAXException {
        final int length = readStructure16();
        final int start = readContentCharactersBuffer(length);
        processComment(_contentCharactersBuffer, start, length);
    }

    private void processEndPrefixMapping() throws SAXException {
        final int end = _namespaceAttributesStack[--_namespaceAttributesStackIndex];
//      final int start = (_namespaceAttributesStackIndex > 0) ? _namespaceAttributesStack[_namespaceAttributesStackIndex] : 0;
        final int start = (_namespaceAttributesStackIndex >= 0) ? _namespaceAttributesStartingStack[_namespaceAttributesStackIndex] : 0;
        
        for (int i = end - 1; i >= start; i--) {
            _contentHandler.endPrefixMapping(_namespacePrefixes[i]);
        }
        _namespacePrefixesIndex = start;
    }
    
    private int processNamespaceAttributes(int item,boolean collectPrefixes, Set<String> prefixSet) throws SAXException {
        do {
            String prefix;
            switch(getNIIState(item)) {
                case STATE_NAMESPACE_ATTRIBUTE:
                    // Undeclaration of default namespace
                    processNamespaceAttribute("", "");
                    if(collectPrefixes) {
                        prefixSet.add("");
                    }
                    break;
                case STATE_NAMESPACE_ATTRIBUTE_P:
                    // Undeclaration of namespace
                    prefix = readStructureString();
                    processNamespaceAttribute(prefix, "");
                    if(collectPrefixes) {
                        prefixSet.add(prefix);
                    }
                    break;
                case STATE_NAMESPACE_ATTRIBUTE_P_U:
                    // Declaration with prefix
                   prefix = readStructureString();
                    processNamespaceAttribute(prefix, readStructureString());
                    if(collectPrefixes) {
                        prefixSet.add(prefix);
                    }
                    break;
                case STATE_NAMESPACE_ATTRIBUTE_U:
                    // Default declaration
                    processNamespaceAttribute("", readStructureString());
                    if(collectPrefixes) {
                        prefixSet.add("");
                    }
                    break;
                default:
                    throw reportFatalError("Illegal state: "+item);
            }
            readStructure();
            
            item = peekStructure();
        } while((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE);
        
        
        cacheNamespacePrefixIndex();
        
        return item;
    }
    
    private void processAttributes(int item) throws SAXException {
        do {
            switch(getAIIState(item)) {
                case STATE_ATTRIBUTE_U_LN_QN:
                    _attributes.addAttributeWithQName(readStructureString(), readStructureString(), readStructureString(), readStructureString(), readContentString());
                    break;
                case STATE_ATTRIBUTE_P_U_LN:
                {
                    final String p = readStructureString();
                    final String u = readStructureString();
                    final String ln = readStructureString();
                    _attributes.addAttributeWithQName(u, ln, getQName(p, ln), readStructureString(), readContentString());
                    break;
                }
                case STATE_ATTRIBUTE_U_LN: {
                    final String u = readStructureString();
                    final String ln = readStructureString();
                    _attributes.addAttributeWithQName(u, ln, ln, readStructureString(), readContentString()); 
                    break;
                }
                case STATE_ATTRIBUTE_LN: {
                    final String ln = readStructureString();
                    _attributes.addAttributeWithQName("", ln, ln, readStructureString(), readContentString()); 
                    break;
                }
                default:
                    throw reportFatalError("Illegal state: "+item);
            }
            readStructure();
            
            item = peekStructure();
        } while((item & TYPE_MASK) == T_ATTRIBUTE);
    }

    private void processNamespaceAttribute(String prefix, String uri) throws SAXException {
        _contentHandler.startPrefixMapping(prefix, uri);

        if (_namespacePrefixesFeature) {
            // Add the namespace delcaration as an attribute
            if (prefix != "") {
                _attributes.addAttributeWithQName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, prefix,
                        getQName(XMLConstants.XMLNS_ATTRIBUTE, prefix),
                        "CDATA", uri);
            } else {
                _attributes.addAttributeWithQName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, XMLConstants.XMLNS_ATTRIBUTE, 
                        XMLConstants.XMLNS_ATTRIBUTE,
                        "CDATA", uri);
            }
        }
        
        cacheNamespacePrefix(prefix);
    }
    
    private void cacheNamespacePrefix(String prefix) {
        if (_namespacePrefixesIndex == _namespacePrefixes.length) {
            final String[] namespaceAttributes = new String[_namespacePrefixesIndex * 3 / 2 + 1];
            System.arraycopy(_namespacePrefixes, 0, namespaceAttributes, 0, _namespacePrefixesIndex);
            _namespacePrefixes = namespaceAttributes;
        }
        
        _namespacePrefixes[_namespacePrefixesIndex++] = prefix;
    }
    
    private void cacheNamespacePrefixIndex() {
        if (_namespaceAttributesStackIndex == _namespaceAttributesStack.length) {
            final int[] namespaceAttributesStack = new int[_namespaceAttributesStackIndex * 3 /2 + 1];
            System.arraycopy(_namespaceAttributesStack, 0, namespaceAttributesStack, 0, _namespaceAttributesStackIndex);
            _namespaceAttributesStack = namespaceAttributesStack;
        }

        _namespaceAttributesStack[_namespaceAttributesStackIndex++] = _namespacePrefixesIndex;
    }
    
    private void cacheNamespacePrefixStartingIndex() {
        if (_namespaceAttributesStackIndex == _namespaceAttributesStartingStack.length) {
            final int[] namespaceAttributesStart = new int[_namespaceAttributesStackIndex * 3 /2 + 1];
            System.arraycopy(_namespaceAttributesStartingStack, 0, namespaceAttributesStart, 0, _namespaceAttributesStackIndex);
            _namespaceAttributesStartingStack = namespaceAttributesStart;
        }
        _namespaceAttributesStartingStack[_namespaceAttributesStackIndex] = _namespacePrefixesIndex;
    }
    
    private void processComment(String s)  throws SAXException {
        processComment(s.toCharArray(), 0, s.length());
    }
    
    private void processComment(char[] ch, int start, int length) throws SAXException {
        _lexicalHandler.comment(ch, start, length);
    }

    private void processProcessingInstruction(String target, String data) throws SAXException {
        _contentHandler.processingInstruction(target, data);
    }

    private static final DefaultWithLexicalHandler DEFAULT_LEXICAL_HANDLER = new DefaultWithLexicalHandler();
}
