/*
 * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * Copyright 2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.sun.org.apache.xerces.internal.impl;

import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
import com.sun.org.apache.xerces.internal.util.AugmentationsImpl;
import com.sun.org.apache.xerces.internal.util.XMLAttributesIteratorImpl;
import com.sun.org.apache.xerces.internal.util.XMLChar;
import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;
import com.sun.org.apache.xerces.internal.util.XMLSymbols;
import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager.Limit;
import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager;
import com.sun.org.apache.xerces.internal.xni.Augmentations;
import com.sun.org.apache.xerces.internal.xni.QName;
import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
import com.sun.org.apache.xerces.internal.xni.XMLString;
import com.sun.org.apache.xerces.internal.xni.XNIException;
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentScanner;
import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
import com.sun.xml.internal.stream.XMLBufferListener;
import com.sun.xml.internal.stream.XMLEntityStorage;
import com.sun.xml.internal.stream.dtd.DTDGrammarUtil;
import java.io.EOFException;
import java.io.IOException;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.events.XMLEvent;

/**
 *
 * This class is responsible for scanning the structure and content
 * of document fragments.
 *
 * This class has been modified as per the new design which is more suited to
 * efficiently build pull parser. Lot of improvements have been done and
 * the code has been added to support stax functionality/features.
 *
 * @author Neeraj Bajaj SUN Microsystems
 * @author K.Venugopal SUN Microsystems
 * @author Glenn Marcy, IBM
 * @author Andy Clark, IBM
 * @author Arnaud  Le Hors, IBM
 * @author Eric Ye, IBM
 * @author Sunitha Reddy, SUN Microsystems
 * @version $Id: XMLDocumentFragmentScannerImpl.java,v 1.19 2010-11-02 19:54:55 joehw Exp $
 *
 */
public class XMLDocumentFragmentScannerImpl
        extends XMLScanner
        implements XMLDocumentScanner, XMLComponent, XMLEntityHandler, XMLBufferListener {

    //
    // Constants
    //

    protected int fElementAttributeLimit, fXMLNameLimit;

    /** External subset resolver. **/
    protected ExternalSubsetResolver fExternalSubsetResolver;

    // scanner states

    //XXX this should be divided into more states.
    /** Scanner state: start of markup. */
    protected static final int SCANNER_STATE_START_OF_MARKUP = 21;

    /** Scanner state: content. */
    protected static final int SCANNER_STATE_CONTENT = 22;

    /** Scanner state: processing instruction. */
    protected static final int SCANNER_STATE_PI = 23;

    /** Scanner state: DOCTYPE. */
    protected static final int SCANNER_STATE_DOCTYPE = 24;

    /** Scanner state: XML Declaration */
    protected static final int SCANNER_STATE_XML_DECL = 25;

    /** Scanner state: root element. */
    protected static final int SCANNER_STATE_ROOT_ELEMENT = 26;

    /** Scanner state: comment. */
    protected static final int SCANNER_STATE_COMMENT = 27;

    /** Scanner state: reference. */
    protected static final int SCANNER_STATE_REFERENCE = 28;

    // <book type="hard"> reading attribute name 'type'
    protected static final int SCANNER_STATE_ATTRIBUTE = 29;

    // <book type="hard"> //reading attribute value.
    protected static final int SCANNER_STATE_ATTRIBUTE_VALUE = 30;

    /** Scanner state: trailing misc. USED BY DOCUMENT_SCANNER_IMPL*/
    //protected static final int SCANNER_STATE_TRAILING_MISC = 32;

    /** Scanner state: end of input. */
    protected static final int SCANNER_STATE_END_OF_INPUT = 33;

    /** Scanner state: terminated. */
    protected static final int SCANNER_STATE_TERMINATED = 34;

    /** Scanner state: CDATA section. */
    protected static final int SCANNER_STATE_CDATA = 35;

    /** Scanner state: Text declaration. */
    protected static final int SCANNER_STATE_TEXT_DECL = 36;

    /** Scanner state: Text declaration. */
    protected static final int SCANNER_STATE_CHARACTER_DATA = 37;

    //<book type="hard">foo</book>
    protected static final int SCANNER_STATE_START_ELEMENT_TAG = 38;

    //<book type="hard">foo</book> reading </book>
    protected static final int SCANNER_STATE_END_ELEMENT_TAG = 39;

    protected static final int SCANNER_STATE_CHAR_REFERENCE = 40;
    protected static final int SCANNER_STATE_BUILT_IN_REFS = 41;

    // feature identifiers


    /** Feature identifier: notify built-in refereces. */
    protected static final String NOTIFY_BUILTIN_REFS =
            Constants.XERCES_FEATURE_PREFIX + Constants.NOTIFY_BUILTIN_REFS_FEATURE;

    /** Property identifier: entity resolver. */
    protected static final String ENTITY_RESOLVER =
            Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;

    /** Feature identifier: standard uri conformant */
    protected static final String STANDARD_URI_CONFORMANT =
            Constants.XERCES_FEATURE_PREFIX + Constants.STANDARD_URI_CONFORMANT_FEATURE;

    /** Property identifier: Security property manager. */
    private static final String XML_SECURITY_PROPERTY_MANAGER =
            Constants.XML_SECURITY_PROPERTY_MANAGER;

    /** access external dtd: file protocol
     *  For DOM/SAX, the secure feature is set to true by default
     */
    final static String EXTERNAL_ACCESS_DEFAULT = Constants.EXTERNAL_ACCESS_DEFAULT;

    // recognized features and properties

    /** Recognized features. */
    private static final String[] RECOGNIZED_FEATURES = {
                NAMESPACES,
                VALIDATION,
                NOTIFY_BUILTIN_REFS,
                NOTIFY_CHAR_REFS,
                Constants.STAX_REPORT_CDATA_EVENT
    };

    /** Feature defaults. */
    private static final Boolean[] FEATURE_DEFAULTS = {
                Boolean.TRUE,
                null,
                Boolean.FALSE,
                Boolean.FALSE,
                Boolean.TRUE
    };

    /** Recognized properties. */
    private static final String[] RECOGNIZED_PROPERTIES = {
        SYMBOL_TABLE,
                ERROR_REPORTER,
                ENTITY_MANAGER,
                XML_SECURITY_PROPERTY_MANAGER
    };

    /** Property defaults. */
    private static final Object[] PROPERTY_DEFAULTS = {
                null,
                null,
                null,
                EXTERNAL_ACCESS_DEFAULT
    };

    private static final char [] cdata = {'[','C','D','A','T','A','['};
    static final char [] xmlDecl = {'<','?','x','m','l'};
    private static final char [] endTag = {'<','/'};
    // debugging

    /** Debug scanner state. */
    private static final boolean DEBUG_SCANNER_STATE = false;

    /** Debug driver. */
    private static final boolean DEBUG_DISPATCHER = false;

    /** Debug content driver scanning. */
    protected static final boolean DEBUG_START_END_ELEMENT = false;


    /** Debug driver next */
    protected static final boolean DEBUG_NEXT = false ;

    /** Debug driver next */
    protected static final boolean DEBUG = false;
    protected static final boolean DEBUG_COALESCE = false;
    //
    // Data
    //

    // protected data

    /** Document handler. */
    protected XMLDocumentHandler fDocumentHandler;
    protected int fScannerLastState ;

    /** Entity Storage */
    protected XMLEntityStorage fEntityStore;

    /** Entity stack. */
    protected int[] fEntityStack = new int[4];

    /** Markup depth. */
    protected int fMarkupDepth;

    //is the element empty
    protected boolean fEmptyElement ;

    //track if we are reading attributes, this is usefule while
    //there is a callback
    protected boolean fReadingAttributes = false;

    /** Scanner state. */
    protected int fScannerState;

    /** SubScanner state: inside scanContent method. */
    protected boolean fInScanContent = false;
    protected boolean fLastSectionWasCData = false;
    protected boolean fLastSectionWasEntityReference = false;
    protected boolean fLastSectionWasCharacterData = false;

    /** has external dtd */
    protected boolean fHasExternalDTD;

    /** Standalone. */
    protected boolean fStandaloneSet;
    protected boolean fStandalone;
    protected String fVersion;

    // element information

    /** Current element. */
    protected QName fCurrentElement;

    /** Element stack. */
    protected ElementStack fElementStack = new ElementStack();
    protected ElementStack2 fElementStack2 = new ElementStack2();

    // other info

    /** Document system identifier.
     * REVISIT:  So what's this used for?  - NG
     * protected String fDocumentSystemId;
     ******/

    protected String fPITarget ;

    //xxx do we need to create an extra XMLString object... look for using fTempString for collecting all the data values
    protected XMLString fPIData  = new XMLString();

    // features


    /** Notify built-in references. */
    protected boolean fNotifyBuiltInRefs = false;

    //STAX related properties
    //defaultValues.
    protected boolean fSupportDTD = true;
    protected boolean fReplaceEntityReferences = true;
    protected boolean fSupportExternalEntities = false;
    protected boolean fReportCdataEvent = false ;
    protected boolean fIsCoalesce = false ;
    protected String fDeclaredEncoding =  null;
    /** Xerces Feature: Disallow doctype declaration. */
    protected boolean fDisallowDoctype = false;

    /**
     * comma-delimited list of protocols that are allowed for the purpose
     * of accessing external dtd or entity references
     */
    protected String fAccessExternalDTD = EXTERNAL_ACCESS_DEFAULT;

    /**
     * standard uri conformant (strict uri).
     * http://apache.org/xml/features/standard-uri-conformant
     */
    protected boolean fStrictURI;

    // drivers

    /** Active driver. */
    protected Driver fDriver;

    /** Content driver. */
    protected Driver fContentDriver = createContentDriver();

    // temporary variables

    /** Element QName. */
    protected QName fElementQName = new QName();

    /** Attribute QName. */
    protected QName fAttributeQName = new QName();

    /**
     * CHANGED: Using XMLAttributesIteratorImpl instead of XMLAttributesImpl. This class
     * implements Iterator interface so we can directly give Attributes in the form of
     * iterator.
     */
    protected XMLAttributesIteratorImpl fAttributes = new XMLAttributesIteratorImpl();


    /** String. */
    protected XMLString fTempString = new XMLString();

    /** String. */
    protected XMLString fTempString2 = new XMLString();

    /** Array of 3 strings. */
    private String[] fStrings = new String[3];

    /** Making the buffer accesible to derived class -- String buffer. */
    protected XMLStringBuffer fStringBuffer = new XMLStringBuffer();

    /** Making the buffer accesible to derived class -- String buffer. */
    protected XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();

    /** stores character data. */
    /** Making the buffer accesible to derived class -- stores PI data */
    protected XMLStringBuffer fContentBuffer = new XMLStringBuffer();

    /** Single character array. */
    private final char[] fSingleChar = new char[1];
    private String fCurrentEntityName = null;

    // New members
    protected boolean fScanToEnd = false;

    protected DTDGrammarUtil dtdGrammarUtil= null;

    protected boolean fAddDefaultAttr = false;

    protected boolean foundBuiltInRefs = false;


    //skip element algorithm
    static final short MAX_DEPTH_LIMIT = 5 ;
    static final short ELEMENT_ARRAY_LENGTH = 200 ;
    static final short MAX_POINTER_AT_A_DEPTH = 4 ;
    static final boolean DEBUG_SKIP_ALGORITHM = false;
    //create a elemnet array of length equal to ELEMENT_ARRAY_LENGTH
    String [] fElementArray = new String[ELEMENT_ARRAY_LENGTH] ;
    //pointer location where last element was skipped
    short fLastPointerLocation = 0 ;
    short fElementPointer = 0 ;
    //2D array to store pointer info
    short [] [] fPointerInfo = new short[MAX_DEPTH_LIMIT] [MAX_POINTER_AT_A_DEPTH] ;
    protected String fElementRawname ;
    protected boolean fShouldSkip = false;
    protected boolean fAdd = false ;
    protected boolean fSkip = false;

    /** Reusable Augmentations. */
    private Augmentations fTempAugmentations = null;
    //
    // Constructors
    //

    /** Default constructor. */
    public XMLDocumentFragmentScannerImpl() {
    } // <init>()

    //
    // XMLDocumentScanner methods
    //

    /**
     * Sets the input source.
     *
     * @param inputSource The input source.
     *
     * @throws IOException Thrown on i/o error.
     */
    public void setInputSource(XMLInputSource inputSource) throws IOException {
        fEntityManager.setEntityHandler(this);
        fEntityManager.startEntity(false, "$fragment$", inputSource, false, true);
        // fDocumentSystemId = fEntityManager.expandSystemId(inputSource.getSystemId());
    } // setInputSource(XMLInputSource)

    /**
     * Scans a document.
     *
     * @param complete True if the scanner should scan the document
     *                 completely, pushing all events to the registered
     *                 document handler. A value of false indicates that
     *                 that the scanner should only scan the next portion
     *                 of the document and return. A scanner instance is
     *                 permitted to completely scan a document if it does
     *                 not support this "pull" scanning model.
     *
     * @return True if there is more to scan, false otherwise.
     */
    public boolean scanDocument(boolean complete)
    throws IOException, XNIException {

        // keep dispatching "events"
        fEntityManager.setEntityHandler(this);
        //System.out.println(" get Document Handler in NSDocumentHandler " + fDocumentHandler );

        int event = next();
        do {
            switch (event) {
                case XMLStreamConstants.START_DOCUMENT :
                    //fDocumentHandler.startDocument(fEntityManager.getEntityScanner(),fEntityManager.getEntityScanner().getVersion(),fNamespaceContext,null);// not able to get
                    break;
                case XMLStreamConstants.START_ELEMENT :
                    //System.out.println(" in scann element");
                    //fDocumentHandler.startElement(getElementQName(),fAttributes,null);
                    break;
                case XMLStreamConstants.CHARACTERS :
                    fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
                    fDocumentHandler.characters(getCharacterData(),null);
                    break;
                case XMLStreamConstants.SPACE:
                    //check if getCharacterData() is the right function to retrieve ignorableWhitespace information.
                    //System.out.println("in the space");
                    //fDocumentHandler.ignorableWhitespace(getCharacterData(), null);
                    break;
                case XMLStreamConstants.ENTITY_REFERENCE :
                    fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
                    //entity reference callback are given in startEntity
                    break;
                case XMLStreamConstants.PROCESSING_INSTRUCTION :
                    fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
                    fDocumentHandler.processingInstruction(getPITarget(),getPIData(),null);
                    break;
                case XMLStreamConstants.COMMENT :
                    fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
                    fDocumentHandler.comment(getCharacterData(),null);
                    break;
                case XMLStreamConstants.DTD :
                    //all DTD related callbacks are handled in DTDScanner.
                    //1. Stax doesn't define DTD states as it does for XML Document.
                    //therefore we don't need to take care of anything here. So Just break;
                    break;
                case XMLStreamConstants.CDATA:
                    fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
                    fDocumentHandler.startCDATA(null);
                    //xxx: check if CDATA values comes from getCharacterData() function
                    fDocumentHandler.characters(getCharacterData(),null);
                    fDocumentHandler.endCDATA(null);
                    //System.out.println(" in CDATA of the XMLNSDocumentScannerImpl");
                    break;
                case XMLStreamConstants.NOTATION_DECLARATION :
                    break;
                case XMLStreamConstants.ENTITY_DECLARATION :
                    break;
                case XMLStreamConstants.NAMESPACE :
                    break;
                case XMLStreamConstants.ATTRIBUTE :
                    break;
                case XMLStreamConstants.END_ELEMENT :
                    //do not give callback here.
                    //this callback is given in scanEndElement function.
                    //fDocumentHandler.endElement(getElementQName(),null);
                    break;
                default :
                    throw new InternalError("processing event: " + event);

            }
            //System.out.println("here in before calling next");
            event = next();
            //System.out.println("here in after calling next");
        } while (event!=XMLStreamConstants.END_DOCUMENT && complete);

        if(event == XMLStreamConstants.END_DOCUMENT) {
            fDocumentHandler.endDocument(null);
            return false;
        }

        return true;

    } // scanDocument(boolean):boolean



    public com.sun.org.apache.xerces.internal.xni.QName getElementQName(){
        if(fScannerLastState == XMLEvent.END_ELEMENT){
            fElementQName.setValues(fElementStack.getLastPoppedElement());
        }
        return fElementQName ;
    }

    /** return the next state on the input
     * @return int
     */

    public int next() throws IOException, XNIException {
        return fDriver.next();
    }

    //
    // XMLComponent methods
    //

    /**
     * Resets the component. The component can query the component manager
     * about any features and properties that affect the operation of the
     * component.
     *
     * @param componentManager The component manager.
     *
     * @throws SAXException Thrown by component on initialization error.
     *                      For example, if a feature or property is
     *                      required for the operation of the component, the
     *                      component manager may throw a
     *                      SAXNotRecognizedException or a
     *                      SAXNotSupportedException.
     */

    public void reset(XMLComponentManager componentManager)
    throws XMLConfigurationException {

        super.reset(componentManager);

        // other settings
        // fDocumentSystemId = null;

        // sax features
        //fAttributes.setNamespaces(fNamespaces);

        // xerces features
        fReportCdataEvent = componentManager.getFeature(Constants.STAX_REPORT_CDATA_EVENT, true);
        fSecurityManager = (XMLSecurityManager)componentManager.getProperty(Constants.SECURITY_MANAGER, null);
        fNotifyBuiltInRefs = componentManager.getFeature(NOTIFY_BUILTIN_REFS, false);

        Object resolver = componentManager.getProperty(ENTITY_RESOLVER, null);
        fExternalSubsetResolver = (resolver instanceof ExternalSubsetResolver) ?
                (ExternalSubsetResolver) resolver : null;

        //attribute
        fReadingAttributes = false;
        //xxx: external entities are supported in Xerces
        // it would be good to define feature for this case
        fSupportExternalEntities = true;
        fReplaceEntityReferences = true;
        fIsCoalesce = false;

        // setup Driver
        setScannerState(SCANNER_STATE_CONTENT);
        setDriver(fContentDriver);

        // JAXP 1.5 features and properties
        XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager)
                componentManager.getProperty(XML_SECURITY_PROPERTY_MANAGER, null);
        fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD);

        fStrictURI = componentManager.getFeature(STANDARD_URI_CONFORMANT, false);

        resetCommon();
        //fEntityManager.test();
    } // reset(XMLComponentManager)


    public void reset(PropertyManager propertyManager){

        super.reset(propertyManager);

        // other settings
        // fDocumentSystemId = null;
        fNamespaces = ((Boolean)propertyManager.getProperty(XMLInputFactory.IS_NAMESPACE_AWARE)).booleanValue();
        fNotifyBuiltInRefs = false ;

        //fElementStack2.clear();
        //fReplaceEntityReferences = true;
        //fSupportExternalEntities = true;
        Boolean bo = (Boolean)propertyManager.getProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES);
        fReplaceEntityReferences = bo.booleanValue();
        bo = (Boolean)propertyManager.getProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES);
        fSupportExternalEntities = bo.booleanValue();
        Boolean cdata = (Boolean)propertyManager.getProperty(Constants.ZEPHYR_PROPERTY_PREFIX + Constants.STAX_REPORT_CDATA_EVENT) ;
        if(cdata != null)
            fReportCdataEvent = cdata.booleanValue() ;
        Boolean coalesce = (Boolean)propertyManager.getProperty(XMLInputFactory.IS_COALESCING) ;
        if(coalesce != null)
            fIsCoalesce = coalesce.booleanValue();
        fReportCdataEvent = fIsCoalesce ? false : (fReportCdataEvent && true) ;
        //if fIsCoalesce is set to true, set the value of fReplaceEntityReferences to true,
        //if fIsCoalesce is set to false, take the value of fReplaceEntityReferences as set by application
        fReplaceEntityReferences = fIsCoalesce ? true : fReplaceEntityReferences;
        // setup Driver
        //we dont need to do this -- nb.
        //setScannerState(SCANNER_STATE_CONTENT);
        //setDriver(fContentDriver);
        //fEntityManager.test();

        // JAXP 1.5 features and properties
        XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager)
                propertyManager.getProperty(XML_SECURITY_PROPERTY_MANAGER);
        fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD);

        fSecurityManager = (XMLSecurityManager)propertyManager.getProperty(Constants.SECURITY_MANAGER);
        resetCommon();
    } // reset(XMLComponentManager)

    void resetCommon() {
        // initialize vars
        fMarkupDepth = 0;
        fCurrentElement = null;
        fElementStack.clear();
        fHasExternalDTD = false;
        fStandaloneSet = false;
        fStandalone = false;
        fInScanContent = false;
        //skipping algorithm
        fShouldSkip = false;
        fAdd = false;
        fSkip = false;

        fEntityStore = fEntityManager.getEntityStore();
        dtdGrammarUtil = null;

        if (fSecurityManager != null) {
            fElementAttributeLimit = fSecurityManager.getLimit(XMLSecurityManager.Limit.ELEMENT_ATTRIBUTE_LIMIT);
            fXMLNameLimit = fSecurityManager.getLimit(XMLSecurityManager.Limit.MAX_NAME_LIMIT);
        } else {
            fElementAttributeLimit = 0;
            fXMLNameLimit = XMLSecurityManager.Limit.MAX_NAME_LIMIT.defaultValue();
        }
        fLimitAnalyzer = fEntityManager.fLimitAnalyzer;
    }

    /**
     * Returns a list of feature identifiers that are recognized by
     * this component. This method may return null if no features
     * are recognized by this component.
     */
    public String[] getRecognizedFeatures() {
        return (String[])(RECOGNIZED_FEATURES.clone());
    } // getRecognizedFeatures():String[]

    /**
     * Sets the state of a feature. This method is called by the component
     * manager any time after reset when a feature changes state.
     * <p>
     * <strong>Note:</strong> Components should silently ignore features
     * that do not affect the operation of the component.
     *
     * @param featureId The feature identifier.
     * @param state     The state of the feature.
     *
     * @throws SAXNotRecognizedException The component should not throw
     *                                   this exception.
     * @throws SAXNotSupportedException The component should not throw
     *                                  this exception.
     */
    public void setFeature(String featureId, boolean state)
    throws XMLConfigurationException {

        super.setFeature(featureId, state);

        // Xerces properties
        if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
            String feature = featureId.substring(Constants.XERCES_FEATURE_PREFIX.length());
            if (feature.equals(Constants.NOTIFY_BUILTIN_REFS_FEATURE)) {
                fNotifyBuiltInRefs = state;
            }
        }

    } // setFeature(String,boolean)

    /**
     * Returns a list of property identifiers that are recognized by
     * this component. This method may return null if no properties
     * are recognized by this component.
     */
    public String[] getRecognizedProperties() {
        return (String[])(RECOGNIZED_PROPERTIES.clone());
    } // getRecognizedProperties():String[]

    /**
     * Sets the value of a property. This method is called by the component
     * manager any time after reset when a property changes value.
     * <p>
     * <strong>Note:</strong> Components should silently ignore properties
     * that do not affect the operation of the component.
     *
     * @param propertyId The property identifier.
     * @param value      The value of the property.
     *
     * @throws SAXNotRecognizedException The component should not throw
     *                                   this exception.
     * @throws SAXNotSupportedException The component should not throw
     *                                  this exception.
     */
    public void setProperty(String propertyId, Object value)
    throws XMLConfigurationException {

        super.setProperty(propertyId, value);

        // Xerces properties
        if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
            final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
            if (suffixLength == Constants.ENTITY_MANAGER_PROPERTY.length() &&
                    propertyId.endsWith(Constants.ENTITY_MANAGER_PROPERTY)) {
                fEntityManager = (XMLEntityManager)value;
                return;
            }
            if (suffixLength == Constants.ENTITY_RESOLVER_PROPERTY.length() &&
                    propertyId.endsWith(Constants.ENTITY_RESOLVER_PROPERTY)) {
                fExternalSubsetResolver = (value instanceof ExternalSubsetResolver) ?
                    (ExternalSubsetResolver) value : null;
                return;
            }
        }


                // Xerces properties
        if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
            String property = propertyId.substring(Constants.XERCES_PROPERTY_PREFIX.length());
            if (property.equals(Constants.ENTITY_MANAGER_PROPERTY)) {
                fEntityManager = (XMLEntityManager)value;
            }
            return;
        }

        //JAXP 1.5 properties
        if (propertyId.equals(XML_SECURITY_PROPERTY_MANAGER))
            {
            XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager)value;
            fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD);
            }

    } // setProperty(String,Object)

    /**
     * Returns the default state for a feature, or null if this
     * component does not want to report a default value for this
     * feature.
     *
     * @param featureId The feature identifier.
     *
     * @since Xerces 2.2.0
     */
    public Boolean getFeatureDefault(String featureId) {
        for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
            if (RECOGNIZED_FEATURES[i].equals(featureId)) {
                return FEATURE_DEFAULTS[i];
            }
        }
        return null;
    } // getFeatureDefault(String):Boolean

    /**
     * Returns the default state for a property, or null if this
     * component does not want to report a default value for this
     * property.
     *
     * @param propertyId The property identifier.
     *
     * @since Xerces 2.2.0
     */
    public Object getPropertyDefault(String propertyId) {
        for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
            if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
                return PROPERTY_DEFAULTS[i];
            }
        }
        return null;
    } // getPropertyDefault(String):Object

    //
    // XMLDocumentSource methods
    //

    /**
     * setDocumentHandler
     *
     * @param documentHandler
     */
    public void setDocumentHandler(XMLDocumentHandler documentHandler) {
        fDocumentHandler = documentHandler;
        //System.out.println(" In Set DOCUMENT HANDLER" + fDocumentHandler + " scanner =" + this);
    } // setDocumentHandler(XMLDocumentHandler)


    /** Returns the document handler */
    public XMLDocumentHandler getDocumentHandler(){
        return fDocumentHandler;
    }

    //
    // XMLEntityHandler methods
    //

    /**
     * This method notifies of the start of an entity. The DTD has the
     * pseudo-name of "[dtd]" parameter entity names start with '%'; and
     * general entities are just specified by their name.
     *
     * @param name     The name of the entity.
     * @param identifier The resource identifier.
     * @param encoding The auto-detected IANA encoding name of the entity
     *                 stream. This value will be null in those situations
     *                 where the entity encoding is not auto-detected (e.g.
     *                 internal entities or a document entity that is
     *                 parsed from a java.io.Reader).
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void startEntity(String name,
            XMLResourceIdentifier identifier,
            String encoding, Augmentations augs) throws XNIException {

        // keep track of this entity before fEntityDepth is increased
        if (fEntityDepth == fEntityStack.length) {
            int[] entityarray = new int[fEntityStack.length * 2];
            System.arraycopy(fEntityStack, 0, entityarray, 0, fEntityStack.length);
            fEntityStack = entityarray;
        }
        fEntityStack[fEntityDepth] = fMarkupDepth;

        super.startEntity(name, identifier, encoding, augs);

        // WFC:  entity declared in external subset in standalone doc
        if(fStandalone && fEntityStore.isEntityDeclInExternalSubset(name)) {
            reportFatalError("MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE",
                    new Object[]{name});
        }

        /** we are not calling the handlers yet.. */
        // call handler
        if (fDocumentHandler != null && !fScanningAttribute) {
            if (!name.equals("[xml]")) {
                fDocumentHandler.startGeneralEntity(name, identifier, encoding, augs);
            }
        }

    } // startEntity(String,XMLResourceIdentifier,String)

    /**
     * This method notifies the end of an entity. The DTD has the pseudo-name
     * of "[dtd]" parameter entity names start with '%'; and general entities
     * are just specified by their name.
     *
     * @param name The name of the entity.
     * @param augs Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endEntity(String name, Augmentations augs) throws IOException, XNIException {

        /**
         * // flush possible pending output buffer - see scanContent
         * if (fInScanContent && fStringBuffer.length != 0
         * && fDocumentHandler != null) {
         * fDocumentHandler.characters(fStringBuffer, null);
         * fStringBuffer.length = 0; // make sure we know it's been flushed
         * }
         */
        super.endEntity(name, augs);

        // make sure markup is properly balanced
        if (fMarkupDepth != fEntityStack[fEntityDepth]) {
            reportFatalError("MarkupEntityMismatch", null);
        }

        /**/
        // call handler
        if (fDocumentHandler != null && !fScanningAttribute) {
            if (!name.equals("[xml]")) {
                fDocumentHandler.endGeneralEntity(name, augs);
            }
        }


    } // endEntity(String)

    //
    // Protected methods
    //

    // Driver factory methods

    /** Creates a content Driver. */
    protected Driver createContentDriver() {
        return new FragmentContentDriver();
    } // createContentDriver():Driver

    // scanning methods

    /**
     * Scans an XML or text declaration.
     * <p>
     * <pre>
     * [23] XMLDecl ::= '&lt;?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
     * [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ")
     * [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' |  "'" EncName "'" )
     * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
     * [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'")
     *                 | ('"' ('yes' | 'no') '"'))
     *
     * [77] TextDecl ::= '&lt;?xml' VersionInfo? EncodingDecl S? '?>'
     * </pre>
     *
     * @param scanningTextDecl True if a text declaration is to
     *                         be scanned instead of an XML
     *                         declaration.
     */
    protected void scanXMLDeclOrTextDecl(boolean scanningTextDecl)
    throws IOException, XNIException {

        // scan decl
        super.scanXMLDeclOrTextDecl(scanningTextDecl, fStrings);
        fMarkupDepth--;

        // pseudo-attribute values
        String version = fStrings[0];
        String encoding = fStrings[1];
        String standalone = fStrings[2];
        fDeclaredEncoding = encoding;
        // set standalone
        fStandaloneSet = standalone != null;
        fStandalone = fStandaloneSet && standalone.equals("yes");
        ///xxx see where its used.. this is not used anywhere. it may be useful for entity to store this information
        //but this information is only related with Document Entity.
        fEntityManager.setStandalone(fStandalone);


        // call handler
        if (fDocumentHandler != null) {
            if (scanningTextDecl) {
                fDocumentHandler.textDecl(version, encoding, null);
            } else {
                fDocumentHandler.xmlDecl(version, encoding, standalone, null);
            }
        }

        if(version != null){
            fEntityScanner.setVersion(version);
            fEntityScanner.setXMLVersion(version);
        }
        // set encoding on reader, only if encoding was not specified by the application explicitly
        if (encoding != null && !fEntityScanner.getCurrentEntity().isEncodingExternallySpecified()) {
             fEntityScanner.setEncoding(encoding);
        }

    } // scanXMLDeclOrTextDecl(boolean)

    public String getPITarget(){
        return fPITarget ;
    }

    public XMLStringBuffer getPIData(){
        return fContentBuffer ;
    }

    //XXX: why not this function behave as per the state of the parser?
    public XMLString getCharacterData(){
        if(fUsebuffer){
            return fContentBuffer ;
        }else{
            return fTempString;
        }

    }


    /**
     * Scans a processing data. This is needed to handle the situation
     * where a document starts with a processing instruction whose
     * target name <em>starts with</em> "xml". (e.g. xmlfoo)
     *
     * @param target The PI target
     * @param data The XMLStringBuffer to fill in with the data
     */
    protected void scanPIData(String target, XMLStringBuffer data)
    throws IOException, XNIException {

        super.scanPIData(target, data);

        //set the PI target and values
        fPITarget = target ;

        fMarkupDepth--;

    } // scanPIData(String)

    /**
     * Scans a comment.
     * <p>
     * <pre>
     * [15] Comment ::= '&lt!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
     * </pre>
     * <p>
     * <strong>Note:</strong> Called after scanning past '&lt;!--'
     */
    protected void scanComment() throws IOException, XNIException {
        fContentBuffer.clear();
        scanComment(fContentBuffer);
        //getTextCharacters can also be called for reading comments
        fUsebuffer = true;
        fMarkupDepth--;

    } // scanComment()

    //xxx value returned by this function may not remain valid if another event is scanned.
    public String getComment(){
        return fContentBuffer.toString();
    }

    void addElement(String rawname){
        if(fElementPointer < ELEMENT_ARRAY_LENGTH){
            //storing element raw name in a linear list of array
            fElementArray[fElementPointer] = rawname ;
            //storing elemnetPointer for particular element depth

            if(DEBUG_SKIP_ALGORITHM){
                StringBuffer sb = new StringBuffer() ;
                sb.append(" Storing element information ") ;
                sb.append(" fElementPointer = " + fElementPointer) ;
                sb.append(" fElementRawname = " + fElementQName.rawname) ;
                sb.append(" fElementStack.fDepth = " + fElementStack.fDepth);
                System.out.println(sb.toString()) ;
            }

            //store pointer information only when element depth is less MAX_DEPTH_LIMIT
            if(fElementStack.fDepth < MAX_DEPTH_LIMIT){
                short column = storePointerForADepth(fElementPointer);
                if(column > 0){
                    short pointer = getElementPointer((short)fElementStack.fDepth, (short)(column - 1) );
                    //identity comparison shouldn't take much time and we can rely on this
                    //since its guaranteed to have same object id for same string.
                    if(rawname == fElementArray[pointer]){
                        fShouldSkip = true ;
                        fLastPointerLocation = pointer ;
                        //reset the things and return.
                        resetPointer((short)fElementStack.fDepth , column) ;
                        fElementArray[fElementPointer] = null ;
                        return ;
                    }else{
                        fShouldSkip = false ;
                    }
                }
            }
            fElementPointer++ ;
        }
    }


    void resetPointer(short depth, short column){
        fPointerInfo[depth] [column] = (short)0;
    }

    //returns column information at which pointer was stored.
    short storePointerForADepth(short elementPointer){
        short depth = (short) fElementStack.fDepth ;

        //Stores element pointer locations at particular depth , only 4 pointer locations
        //are stored at particular depth for now.
        for(short i = 0 ; i < MAX_POINTER_AT_A_DEPTH ; i++){

            if(canStore(depth, i)){
                fPointerInfo[depth][i] = elementPointer ;
                if(DEBUG_SKIP_ALGORITHM){
                    StringBuffer sb = new StringBuffer() ;
                    sb.append(" Pointer information ") ;
                    sb.append(" fElementPointer = " + fElementPointer) ;
                    sb.append(" fElementStack.fDepth = " + fElementStack.fDepth);
                    sb.append(" column = " + i ) ;
                    System.out.println(sb.toString()) ;
                }
                return i;
            }
            //else
            //pointer was not stored because we reached the limit
        }
        return -1 ;
    }

    boolean canStore(short depth, short column){
        //colum = 0 , means first element at particular depth
        //column = 1, means second element at particular depth
        //        calle should make sure that it doesn't call for value outside allowed co-ordinates
        return fPointerInfo[depth][column] == 0 ? true : false ;
    }


    short getElementPointer(short depth, short column){
        //colum = 0 , means first element at particular depth
        //column = 1, means second element at particular depth
        //        calle should make sure that it doesn't call for value outside allowed co-ordinates
        return fPointerInfo[depth][column] ;
    }

    //this function assumes that string passed is not null and skips
    //the following string from the buffer this makes sure
    boolean skipFromTheBuffer(String rawname) throws IOException{
        if(fEntityScanner.skipString(rawname)){
            char c = (char)fEntityScanner.peekChar() ;
            //If the start element was completely skipped we should encounter either ' '(space),
            //or '/' (in case of empty element)  or '>'
            if( c == ' ' || c == '/' || c == '>'){
                fElementRawname = rawname ;
                return true ;
            } else{
                return false;
            }
        } else
            return false ;
    }

    boolean skipQElement(String rawname) throws IOException{

        final int c = fEntityScanner.getChar(rawname.length());
        //if this character is still valid element name -- this means string can't match
        if(XMLChar.isName(c)){
            return false;
        }else{
            return fEntityScanner.skipString(rawname);
        }
    }

    protected boolean skipElement() throws IOException {

        if(!fShouldSkip) return false ;

        if(fLastPointerLocation != 0){
            //Look at the next element stored in the array list.. we might just get a match.
            String rawname = fElementArray[fLastPointerLocation + 1] ;
            if(rawname != null && skipFromTheBuffer(rawname)){
                fLastPointerLocation++ ;
                if(DEBUG_SKIP_ALGORITHM){
                    System.out.println("Element " + fElementRawname + " was SKIPPED at pointer location = " + fLastPointerLocation);
                }
                return true ;
            } else{
                //reset it back to zero... we haven't got the correct subset yet.
                fLastPointerLocation = 0 ;

            }
        }
        //xxx: we can put some logic here as from what column it should start looking
        //for now we always start at 0
        //fallback to tolerant algorithm, it would look for differnt element stored at different
        //depth and get us the pointer location.
        return fShouldSkip && skipElement((short)0);

    }

    //start of the column at which it should try searching
    boolean skipElement(short column) throws IOException {
        short depth = (short)fElementStack.fDepth ;

        if(depth > MAX_DEPTH_LIMIT){
            return fShouldSkip = false ;
        }
        for(short i = column ; i < MAX_POINTER_AT_A_DEPTH ; i++){
            short pointer = getElementPointer(depth , i ) ;

            if(pointer == 0){
                return fShouldSkip = false ;
            }

            if(fElementArray[pointer] != null && skipFromTheBuffer(fElementArray[pointer])){
                if(DEBUG_SKIP_ALGORITHM){
                    System.out.println();
                    System.out.println("Element " + fElementRawname + " was SKIPPED at depth = " + fElementStack.fDepth + " column = " + column );
                    System.out.println();
                }
                fLastPointerLocation = pointer ;
                return fShouldSkip = true ;
            }
        }
        return fShouldSkip = false ;
    }

    /**
     * Scans a start element. This method will handle the binding of
     * namespace information and notifying the handler of the start
     * of the element.
     * <p>
     * <pre>
     * [44] EmptyElemTag ::= '&lt;' Name (S Attribute)* S? '/>'
     * [40] STag ::= '&lt;' Name (S Attribute)* S? '>'
     * </pre>
     * <p>
     * <strong>Note:</strong> This method assumes that the leading
     * '&lt;' character has been consumed.
     * <p>
     * <strong>Note:</strong> This method uses the fElementQName and
     * fAttributes variables. The contents of these variables will be
     * destroyed. The caller should copy important information out of
     * these variables before calling this method.
     * NB: Content in fAttributes is valid only till the state of the parser is XMLEvent.START_ELEMENT
     *
     * @return True if element is empty. (i.e. It matches
     *          production [44].
     */
    // fElementQName will have the details of element just read..
    // fAttributes will have the details of all the attributes.
    protected boolean scanStartElement()
    throws IOException, XNIException {

        if (DEBUG_START_END_ELEMENT) System.out.println( this.getClass().toString() + ">>> scanStartElement()");
        //when skipping is true and no more elements should be added
        if(fSkip && !fAdd){
            //get the stored element -- if everything goes right this should match the
            //token in the buffer

            QName name = fElementStack.getNext();

            if(DEBUG_SKIP_ALGORITHM){
                System.out.println("Trying to skip String = " + name.rawname);
            }

            //Be conservative -- if skipping fails -- stop.
            fSkip = fEntityScanner.skipString(name.rawname);

            if(fSkip){
                if(DEBUG_SKIP_ALGORITHM){
                    System.out.println("Element SUCESSFULLY skipped = " + name.rawname);
                }
                fElementStack.push();
                fElementQName = name;
            }else{
                //if skipping fails reposition the stack or fallback to normal way of processing
                fElementStack.reposition();
                if(DEBUG_SKIP_ALGORITHM){
                    System.out.println("Element was NOT skipped, REPOSITIONING stack" );
                }
            }
        }

        //we are still at the stage of adding elements
        //the elements were not matched or
        //fSkip is not set to true
        if(!fSkip || fAdd){
            //get the next element from the stack
            fElementQName = fElementStack.nextElement();
            // name
            if (fNamespaces) {
                fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART);
            } else {
                String name = fEntityScanner.scanName(NameType.ELEMENTSTART);
                fElementQName.setValues(null, name, name, null);
            }

            if(DEBUG)System.out.println("Element scanned in start element is " + fElementQName.toString());
            if(DEBUG_SKIP_ALGORITHM){
                if(fAdd){
                    System.out.println("Elements are being ADDED -- elemet added is = " + fElementQName.rawname + " at count = " + fElementStack.fCount);
                }
            }

        }

        //when the elements are being added , we need to check if we are set for skipping the elements
        if(fAdd){
            //this sets the value of fAdd variable
            fElementStack.matchElement(fElementQName);
        }


        //xxx: We dont need another pointer, fCurrentElement, we can use fElementQName
        fCurrentElement = fElementQName;

        String rawname = fElementQName.rawname;

        fEmptyElement = false;

        fAttributes.removeAllAttributes();

        checkDepth(rawname);
        if(!seekCloseOfStartTag()){
            fReadingAttributes = true;
            fAttributeCacheUsedCount =0;
            fStringBufferIndex =0;
            fAddDefaultAttr = true;
            do {
                scanAttribute(fAttributes);
                if (fSecurityManager != null && !fSecurityManager.isNoLimit(fElementAttributeLimit) &&
                        fAttributes.getLength() > fElementAttributeLimit){
                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
                                                 "ElementAttributeLimit",
                                                 new Object[]{rawname, fElementAttributeLimit },
                                                 XMLErrorReporter.SEVERITY_FATAL_ERROR );
                }

            } while (!seekCloseOfStartTag());
            fReadingAttributes=false;
        }

        if (fEmptyElement) {
            //decrease the markup depth..
            fMarkupDepth--;

            // check that this element was opened in the same entity
            if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
                reportFatalError("ElementEntityMismatch",
                        new Object[]{fCurrentElement.rawname});
            }
            // call handler
            if (fDocumentHandler != null) {
                fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
            }

            //We should not be popping out the context here in endELement becaause the namespace context is still
            //valid when parser is at the endElement state.
            //if (fNamespaces) {
            //  fNamespaceContext.popContext();
            //}

            //pop the element off the stack..
            fElementStack.popElement();

        } else {

            if(dtdGrammarUtil != null)
                dtdGrammarUtil.startElement(fElementQName, fAttributes);
            if(fDocumentHandler != null){
                //complete element and attributes are traversed in this function so we can send a callback
                //here.
                //<strong>we shouldn't be sending callback in scanDocument()</strong>
                fDocumentHandler.startElement(fElementQName, fAttributes, null);
            }
        }


        if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() + "<<< scanStartElement(): "+fEmptyElement);
        return fEmptyElement;

    } // scanStartElement():boolean

    /**
     * Looks for the close of start tag, i.e. if it finds '>' or '/>'
     * Characters are consumed.
     */
    protected boolean seekCloseOfStartTag() throws IOException, XNIException {
        // spaces
        boolean sawSpace = fEntityScanner.skipSpaces();

        // end tag?
        final int c = fEntityScanner.peekChar();
        if (c == '>') {
            fEntityScanner.scanChar(null);
            return true;
        } else if (c == '/') {
            fEntityScanner.scanChar(null);
            if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
                reportFatalError("ElementUnterminated",
                        new Object[]{fElementQName.rawname});
            }
            fEmptyElement = true;
            return true;
        } else if (!isValidNameStartChar(c) || !sawSpace) {
            reportFatalError("ElementUnterminated", new Object[]{fElementQName.rawname});
        }

        return false;
    }

    public boolean hasAttributes(){
        return fAttributes.getLength() > 0 ? true : false ;
    }


    /**
     * Scans an attribute.
     * <p>
     * <pre>
     * [41] Attribute ::= Name Eq AttValue
     * </pre>
     * <p>
     * <strong>Note:</strong> This method assumes that the next
     * character on the stream is the first character of the attribute
     * name.
     * <p>
     * <strong>Note:</strong> This method uses the fAttributeQName and
     * fQName variables. The contents of these variables will be
     * destroyed.
     *
     * @param attributes The attributes list for the scanned attribute.
     */

    /**
     * protected void scanAttribute(AttributeIteratorImpl attributes)
     * throws IOException, XNIException {
     * if (DEBUG_START_END_ELEMENT) System.out.println(">>> scanAttribute()");
     *
     *
     * // name
     * if (fNamespaces) {
     * fEntityScanner.scanQName(fAttributeQName);
     * }
     * else {
     * String name = fEntityScanner.scanName();
     * fAttributeQName.setValues(null, name, name, null);
     * }
     *
     * // equals
     * fEntityScanner.skipSpaces();
     * if (!fEntityScanner.skipChar('=')) {
     * reportFatalError("EqRequiredInAttribute",
     * new Object[]{fAttributeQName.rawname});
     * }
     * fEntityScanner.skipSpaces();
     *
     *
     * // content
     * int oldLen = attributes.getLength();
     */
    /**xxx there is one check of duplicate attribute that has been removed.
     * attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null);
     *
     * // WFC: Unique Att Spec
     * if (oldLen == attributes.getLength()) {
     * reportFatalError("AttributeNotUnique",
     * new Object[]{fCurrentElement.rawname,
     * fAttributeQName.rawname});
     * }
     */

    /*
        //REVISIT: one more case needs to be included: external PE and standalone is no
        boolean isVC =  fHasExternalDTD && !fStandalone;
        scanAttributeValue(fTempString, fTempString2,
                           fAttributeQName.rawname, attributes,
                           oldLen, isVC);

        //attributes.setValue(oldLen, fTempString.toString());
        //attributes.setNonNormalizedValue(oldLen, fTempString2.toString());
        //attributes.setSpecified(oldLen, true);

        AttributeImpl attribute = new AttributeImpl(fAttributeQName.prefix,fAttributeQName.localpart,fAttributeQName.uri,fTempString.toString(),fTempString2.toString(),XMLSymbols.fCDATASymbol,true);
        fAttributes.addAttribute(attribute);
        if (DEBUG_START_END_ELEMENT) System.out.println("<<< scanAttribute()");
    } // scanAttribute(XMLAttributes)

     */

    /** return the attribute iterator implementation */
    public XMLAttributesIteratorImpl getAttributeIterator(){
        if(dtdGrammarUtil != null && fAddDefaultAttr){
            dtdGrammarUtil.addDTDDefaultAttrs(fElementQName,fAttributes);
            fAddDefaultAttr = false;
        }
        return fAttributes;
    }

    /** return if standalone is set */
    public boolean standaloneSet(){
        return fStandaloneSet;
    }
    /** return if the doucment is standalone */
    public boolean isStandAlone(){
        return fStandalone ;
    }
    /**
     * Scans an attribute name value pair.
     * <p>
     * <pre>
     * [41] Attribute ::= Name Eq AttValue
     * </pre>
     * <p>
     * <strong>Note:</strong> This method assumes that the next
     * character on the stream is the first character of the attribute
     * name.
     * <p>
     * <strong>Note:</strong> This method uses the fAttributeQName and
     * fQName variables. The contents of these variables will be
     * destroyed.
     *
     * @param attributes The attributes list for the scanned attribute.
     */

    protected void scanAttribute(XMLAttributes attributes)
    throws IOException, XNIException {
        if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanAttribute()");

        // name
        if (fNamespaces) {
            fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTENAME);
        } else {
            String name = fEntityScanner.scanName(NameType.ATTRIBUTENAME);
            fAttributeQName.setValues(null, name, name, null);
        }

        // equals
        fEntityScanner.skipSpaces();
        if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) {
            reportFatalError("EqRequiredInAttribute",
                new Object[] {fCurrentElement.rawname, fAttributeQName.rawname});
        }
        fEntityScanner.skipSpaces();

        int attIndex = 0 ;
        //REVISIT: one more case needs to be included: external PE and standalone is no
        boolean isVC =  fHasExternalDTD && !fStandalone;
        //fTempString would store attribute value
        ///fTempString2 would store attribute non-normalized value

        //this function doesn't use 'attIndex'. We are adding the attribute later
        //after we have figured out that current attribute is not namespace declaration
        //since scanAttributeValue doesn't use attIndex parameter therefore we
        //can safely add the attribute later..
        XMLString tmpStr = getString();

        scanAttributeValue(tmpStr, fTempString2, fAttributeQName.rawname, attributes,
                attIndex, isVC, fCurrentElement.rawname, false);

        // content
        int oldLen = attributes.getLength();
        //if the attribute name already exists.. new value is replaced with old value
        attIndex = attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null);

        // WFC: Unique Att Spec
        //attributes count will be same if the current attribute  name already exists for this element name.
        //this means there are two duplicate attributes.
        if (oldLen == attributes.getLength()) {
            reportFatalError("AttributeNotUnique",
                    new Object[]{fCurrentElement.rawname,
                            fAttributeQName.rawname});
        }

        //tmpString contains attribute value
        //we are passing null as the attribute value
        attributes.setValue(attIndex, null, tmpStr);

        ///xxx: nonNormalizedValue is not being set as it is not required by SAX & DOM
        //attributes.setNonNormalizedValue(oldLen, fTempString2.toString());
        attributes.setSpecified(attIndex, true);

        if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +"<<< scanAttribute()");

    } // scanAttribute(XMLAttributes)

    /**
     * Scans element content.
     *
     * @return Returns the next character on the stream.
     */
    //CHANGED:
    //EARLIER: scanContent()
    //NOW: scanContent(XMLStringBuffer)
    //It makes things easy if this functions takes XMLStringBuffer as parameter..
    //this function appends the data to the buffer.
    protected int scanContent(XMLStringBuffer content) throws IOException, XNIException {
        //set the fTempString length to 0 before passing it on to scanContent
        //scanContent sets the correct co-ordinates as per the content read
        fTempString.length = 0;
        int c = fEntityScanner.scanContent(fTempString);
        content.append(fTempString);
        fTempString.length = 0;
        if (c == '\r') {
            // happens when there is the character reference &#13;
            //xxx: We know the next chracter.. we should just skip it and add ']' directlry
            fEntityScanner.scanChar(null);
            content.append((char)c);
            c = -1;
        } else if (c == ']') {
            //fStringBuffer.clear();
            //xxx: We know the next chracter.. we should just skip it and add ']' directlry
            content.append((char)fEntityScanner.scanChar(null));
            // remember where we are in case we get an endEntity before we
            // could flush the buffer out - this happens when we're parsing an
            // entity which ends with a ]
            fInScanContent = true;
            //
            // We work on a single character basis to handle cases such as:
            // ']]]>' which we might otherwise miss.
            //
            if (fEntityScanner.skipChar(']', null)) {
                content.append(']');
                while (fEntityScanner.skipChar(']', null)) {
                    content.append(']');
                }
                if (fEntityScanner.skipChar('>', null)) {
                    reportFatalError("CDEndInContent", null);
                }
            }
            fInScanContent = false;
            c = -1;
        }
        if (fDocumentHandler != null && content.length > 0) {
            //fDocumentHandler.characters(content, null);
        }
        return c;

    } // scanContent():int


    /**
     * Scans a CDATA section.
     * <p>
     * <strong>Note:</strong> This method uses the fTempString and
     * fStringBuffer variables.
     *
     * @param complete True if the CDATA section is to be scanned
     *                 completely.
     *
     * @return True if CDATA is completely scanned.
     */
    //CHANGED:
    protected boolean scanCDATASection(XMLStringBuffer contentBuffer, boolean complete)
    throws IOException, XNIException {

        // call handler
        if (fDocumentHandler != null) {
            //fDocumentHandler.startCDATA(null);
        }

        while (true) {
            //scanData will fill the contentBuffer
            if (!fEntityScanner.scanData("]]>", contentBuffer)) {
                break ;
                /** We dont need all this code if we pass ']]>' as delimeter..
                 * int brackets = 2;
                 * while (fEntityScanner.skipChar(']')) {
                 * brackets++;
                 * }
                 *
                 * //When we find more than 2 square brackets
                 * if (fDocumentHandler != null && brackets > 2) {
                 * //we dont need to clear the buffer..
                 * //contentBuffer.clear();
                 * for (int i = 2; i < brackets; i++) {
                 * contentBuffer.append(']');
                 * }
                 * fDocumentHandler.characters(contentBuffer, null);
                 * }
                 *
                 * if (fEntityScanner.skipChar('>')) {
                 * break;
                 * }
                 * if (fDocumentHandler != null) {
                 * //we dont need to clear the buffer now..
                 * //contentBuffer.clear();
                 * contentBuffer.append("]]");
                 * fDocumentHandler.characters(contentBuffer, null);
                 * }
                 **/
            } else {
                int c = fEntityScanner.peekChar();
                if (c != -1 && isInvalidLiteral(c)) {
                    if (XMLChar.isHighSurrogate(c)) {
                        //contentBuffer.clear();
                        //scan surrogates if any....
                        scanSurrogates(contentBuffer);
                    } else {
                        reportFatalError("InvalidCharInCDSect",
                                new Object[]{Integer.toString(c,16)});
                                fEntityScanner.scanChar(null);
                    }
                }
                //by this time we have also read surrogate contents if any...
                if (fDocumentHandler != null) {
                    //fDocumentHandler.characters(contentBuffer, null);
                }
            }
        }
        fMarkupDepth--;

        if (fDocumentHandler != null && contentBuffer.length > 0) {
            //fDocumentHandler.characters(contentBuffer, null);
        }

        // call handler
        if (fDocumentHandler != null) {
            //fDocumentHandler.endCDATA(null);
        }

        return true;

    } // scanCDATASection(XMLStringBuffer, boolean):boolean

    /**
     * Scans an end element.
     * <p>
     * <pre>
     * [42] ETag ::= '&lt;/' Name S? '>'
     * </pre>
     * <p>
     * <strong>Note:</strong> This method uses the fElementQName variable.
     * The contents of this variable will be destroyed. The caller should
     * copy the needed information out of this variable before calling
     * this method.
     *
     * @return The element depth.
     */
    protected int scanEndElement() throws IOException, XNIException {
        if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanEndElement()");

        // pop context
        QName endElementName = fElementStack.popElement();

        String rawname = endElementName.rawname;
        if(DEBUG)System.out.println("endElementName = " + endElementName.toString());
        // Take advantage of the fact that next string _should_ be "fElementQName.rawName",
        //In scanners most of the time is consumed on checks done for XML characters, we can
        // optimize on it and avoid the checks done for endElement,
        //we will also avoid symbol table lookup - neeraj.bajaj@sun.com

        // this should work both for namespace processing true or false...

        //REVISIT: if the string is not the same as expected.. we need to do better error handling..
        //We can skip this for now... In any case if the string doesn't match -- document is not well formed.

        if (!fEntityScanner.skipString(endElementName.rawname)) {
             reportFatalError("ETagRequired", new Object[]{rawname});
        }

        // end
        fEntityScanner.skipSpaces();
        if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
            reportFatalError("ETagUnterminated",
                    new Object[]{rawname});
        }
        fMarkupDepth--;

        //we have increased the depth for two markup "<" characters
        fMarkupDepth--;

        // check that this element was opened in the same entity
        if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
            reportFatalError("ElementEntityMismatch",
                    new Object[]{rawname});
        }

        //We should not be popping out the context here in endELement becaause the namespace context is still
        //valid when parser is at the endElement state.

        //if (fNamespaces) {
        //  fNamespaceContext.popContext();
        //}

        // call handler
        if (fDocumentHandler != null ) {
            //end element is scanned in this function so we can send a callback
            //here.
            //<strong>we shouldn't be sending callback in scanDocument()</strong>

            fDocumentHandler.endElement(endElementName, null);
        }
        if(dtdGrammarUtil != null)
            dtdGrammarUtil.endElement(endElementName);

        return fMarkupDepth;

    } // scanEndElement():int

    /**
     * Scans a character reference.
     * <p>
     * <pre>
     * [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
     * </pre>
     */
    protected void scanCharReference()
    throws IOException, XNIException {

        fStringBuffer2.clear();
        int ch = scanCharReferenceValue(fStringBuffer2, null);
        fMarkupDepth--;
        if (ch != -1) {
            // call handler

            if (fDocumentHandler != null) {
                if (fNotifyCharRefs) {
                    fDocumentHandler.startGeneralEntity(fCharRefLiteral, null, null, null);
                }
                Augmentations augs = null;
                if (fValidation && ch <= 0x20) {
                    if (fTempAugmentations != null) {
                        fTempAugmentations.removeAllItems();
                    }
                    else {
                        fTempAugmentations = new AugmentationsImpl();
                    }
                    augs = fTempAugmentations;
                    augs.putItem(Constants.CHAR_REF_PROBABLE_WS, Boolean.TRUE);
                }
                //xxx: How do we deal with this - how to return charReferenceValues
                //now this is being commented because this is taken care in scanDocument()
                //fDocumentHandler.characters(fStringBuffer2, null);
                if (fNotifyCharRefs) {
                    fDocumentHandler.endGeneralEntity(fCharRefLiteral, null);
                }
            }
        }

    } // scanCharReference()


    /**
     * Scans an entity reference.
     *
     * @return returns true if the new entity is started. If it was built-in entity
     *         'false' is returned.
     * @throws IOException  Thrown if i/o error occurs.
     * @throws XNIException Thrown if handler throws exception upon
     *                      notification.
     */
    protected void scanEntityReference(XMLStringBuffer content) throws IOException, XNIException {
        String name = fEntityScanner.scanName(NameType.REFERENCE);
        if (name == null) {
            reportFatalError("NameRequiredInReference", null);
            return;
        }
        if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
            reportFatalError("SemicolonRequiredInReference", new Object []{name});
        }
        if (fEntityStore.isUnparsedEntity(name)) {
            reportFatalError("ReferenceToUnparsedEntity", new Object[]{name});
        }
        fMarkupDepth--;
        fCurrentEntityName = name;

        // handle built-in entities
        if (name == fAmpSymbol) {
            handleCharacter('&', fAmpSymbol, content);
            fScannerState = SCANNER_STATE_BUILT_IN_REFS;
            return ;
        } else if (name == fLtSymbol) {
            handleCharacter('<', fLtSymbol, content);
            fScannerState = SCANNER_STATE_BUILT_IN_REFS;
            return ;
        } else if (name == fGtSymbol) {
            handleCharacter('>', fGtSymbol, content);
            fScannerState = SCANNER_STATE_BUILT_IN_REFS;
            return ;
        } else if (name == fQuotSymbol) {
            handleCharacter('"', fQuotSymbol, content);
            fScannerState = SCANNER_STATE_BUILT_IN_REFS;
            return ;
        } else if (name == fAposSymbol) {
            handleCharacter('\'', fAposSymbol, content);
            fScannerState = SCANNER_STATE_BUILT_IN_REFS;
            return ;
        }

        //1. if the entity is external and support to external entities is not required
        // 2. or entities should not be replaced
        //3. or if it is built in entity reference.
        boolean isEE = fEntityStore.isExternalEntity(name);
        if((isEE && !fSupportExternalEntities) || (!isEE && !fReplaceEntityReferences) || foundBuiltInRefs){
            fScannerState = SCANNER_STATE_REFERENCE;
            return ;
        }
        // start general entity
        if (!fEntityStore.isDeclaredEntity(name)) {
            //SUPPORT_DTD=false && ReplaceEntityReferences should throw exception
            if (!fSupportDTD && fReplaceEntityReferences) {
                reportFatalError("EntityNotDeclared", new Object[]{name});
                return;
            }
            //REVISIT: one more case needs to be included: external PE and standalone is no
            if ( fHasExternalDTD && !fStandalone) {
                if (fValidation)
                    fErrorReporter.reportError(fEntityScanner, XMLMessageFormatter.XML_DOMAIN,"EntityNotDeclared",
                            new Object[]{name}, XMLErrorReporter.SEVERITY_ERROR);
            } else
                reportFatalError("EntityNotDeclared", new Object[]{name});
        }
        //we are starting the entity even if the entity was not declared
        //if that was the case it its taken care in XMLEntityManager.startEntity()
        //we immediately call the endEntity. Application gets to know if there was
        //any entity that was not declared.
        fEntityManager.startEntity(true, name, false);
        //set the scaner state to content.. parser will automatically revive itself at any point of time.
        //setScannerState(SCANNER_STATE_CONTENT);
        //return true ;
    } // scanEntityReference()

    // utility methods

    /**
     * Check if the depth exceeds the maxElementDepth limit
     * @param elementName name of the current element
     */
    void checkDepth(String elementName) {
        fLimitAnalyzer.addValue(Limit.MAX_ELEMENT_DEPTH_LIMIT, elementName, fElementStack.fDepth);
        if (fSecurityManager.isOverLimit(Limit.MAX_ELEMENT_DEPTH_LIMIT,fLimitAnalyzer)) {
            fSecurityManager.debugPrint(fLimitAnalyzer);
            reportFatalError("MaxElementDepthLimit", new Object[]{elementName,
                fLimitAnalyzer.getTotalValue(Limit.MAX_ELEMENT_DEPTH_LIMIT),
                fSecurityManager.getLimit(Limit.MAX_ELEMENT_DEPTH_LIMIT),
                "maxElementDepth"});
        }
    }

    /**
     * Calls document handler with a single character resulting from
     * built-in entity resolution.
     *
     * @param c
     * @param entity built-in name
     * @param XMLStringBuffer append the character to buffer
     *
     * we really dont need to call this function -- this function is only required when
     * we integrate with rest of Xerces2. SO maintaining the current behavior and still
     * calling this function to hanlde built-in entity reference.
     *
     */
    private void handleCharacter(char c, String entity, XMLStringBuffer content) throws XNIException {
        foundBuiltInRefs = true;
        checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
        content.append(c);
        if (fDocumentHandler != null) {
            fSingleChar[0] = c;
            if (fNotifyBuiltInRefs) {
                fDocumentHandler.startGeneralEntity(entity, null, null, null);
            }
            fTempString.setValues(fSingleChar, 0, 1);
            //fDocumentHandler.characters(fTempString, null);

            if (fNotifyBuiltInRefs) {
                fDocumentHandler.endGeneralEntity(entity, null);
            }
        }
    } // handleCharacter(char)

    // helper methods

    /**
     * Sets the scanner state.
     *
     * @param state The new scanner state.
     */
    protected final void setScannerState(int state) {

        fScannerState = state;
        if (DEBUG_SCANNER_STATE) {
            System.out.print("### setScannerState: ");
            //System.out.print(fScannerState);
            System.out.print(getScannerStateName(state));
            System.out.println();
        }

    } // setScannerState(int)


    /**
     * Sets the Driver.
     *
     * @param Driver The new Driver.
     */
    protected final void setDriver(Driver driver) {
        fDriver = driver;
        if (DEBUG_DISPATCHER) {
            System.out.print("%%% setDriver: ");
            System.out.print(getDriverName(driver));
            System.out.println();
        }
    }

    //
    // Private methods
    //

    /** Returns the scanner state name. */
    protected String getScannerStateName(int state) {

        switch (state) {
            case SCANNER_STATE_DOCTYPE: return "SCANNER_STATE_DOCTYPE";
            case SCANNER_STATE_ROOT_ELEMENT: return "SCANNER_STATE_ROOT_ELEMENT";
            case SCANNER_STATE_START_OF_MARKUP: return "SCANNER_STATE_START_OF_MARKUP";
            case SCANNER_STATE_COMMENT: return "SCANNER_STATE_COMMENT";
            case SCANNER_STATE_PI: return "SCANNER_STATE_PI";
            case SCANNER_STATE_CONTENT: return "SCANNER_STATE_CONTENT";
            case SCANNER_STATE_REFERENCE: return "SCANNER_STATE_REFERENCE";
            case SCANNER_STATE_END_OF_INPUT: return "SCANNER_STATE_END_OF_INPUT";
            case SCANNER_STATE_TERMINATED: return "SCANNER_STATE_TERMINATED";
            case SCANNER_STATE_CDATA: return "SCANNER_STATE_CDATA";
            case SCANNER_STATE_TEXT_DECL: return "SCANNER_STATE_TEXT_DECL";
            case SCANNER_STATE_ATTRIBUTE: return "SCANNER_STATE_ATTRIBUTE";
            case SCANNER_STATE_ATTRIBUTE_VALUE: return "SCANNER_STATE_ATTRIBUTE_VALUE";
            case SCANNER_STATE_START_ELEMENT_TAG: return "SCANNER_STATE_START_ELEMENT_TAG";
            case SCANNER_STATE_END_ELEMENT_TAG: return "SCANNER_STATE_END_ELEMENT_TAG";
            case SCANNER_STATE_CHARACTER_DATA: return "SCANNER_STATE_CHARACTER_DATA" ;
        }

        return "??? ("+state+')';

    } // getScannerStateName(int):String
    public String getEntityName(){
        //return the cached name
        return fCurrentEntityName;
    }

    /** Returns the driver name. */
    public String getDriverName(Driver driver) {

        if (DEBUG_DISPATCHER) {
            if (driver != null) {
                String name = driver.getClass().getName();
                int index = name.lastIndexOf('.');
                if (index != -1) {
                    name = name.substring(index + 1);
                    index = name.lastIndexOf('$');
                    if (index != -1) {
                        name = name.substring(index + 1);
                    }
                }
                return name;
            }
        }
        return "null";

    } // getDriverName():String

    /**
     * Check the protocol used in the systemId against allowed protocols
     *
     * @param systemId the Id of the URI
     * @param allowedProtocols a list of allowed protocols separated by comma
     * @return the name of the protocol if rejected, null otherwise
     */
    String checkAccess(String systemId, String allowedProtocols) throws IOException {
        String baseSystemId = fEntityScanner.getBaseSystemId();
        String expandedSystemId = fEntityManager.expandSystemId(systemId, baseSystemId,fStrictURI);
        return SecuritySupport.checkAccess(expandedSystemId, allowedProtocols, Constants.ACCESS_EXTERNAL_ALL);
    }

    //
    // Classes
    //

    /**
     * @author Neeraj Bajaj, Sun Microsystems.
     */
    protected static final class Element {

        //
        // Data
        //

        /** Symbol. */
        public QName qname;

        //raw name stored as characters
        public char[] fRawname;

        /** The next Element entry. */
        public Element next;

        //
        // Constructors
        //

        /**
         * Constructs a new Element from the given QName and next Element
         * reference.
         */
        public Element(QName qname, Element next) {
            this.qname.setValues(qname);
            this.fRawname = qname.rawname.toCharArray();
            this.next = next;
        }

    } // class Element

    /**
     * Element stack.
     *
     * @author Neeraj Bajaj, Sun Microsystems.
     */
    protected class ElementStack2 {

        //
        // Data
        //

        /** The stack data. */
        protected QName [] fQName = new QName[20];

        //Element depth
        protected int fDepth;
        //total number of elements
        protected int fCount;
        //current position
        protected int fPosition;
        //Mark refers to the position
        protected int fMark;

        protected int fLastDepth ;

        //
        // Constructors
        //

        /** Default constructor. */
        public ElementStack2() {
            for (int i = 0; i < fQName.length; i++) {
                fQName[i] = new QName();
            }
            fMark = fPosition = 1;
        } // <init>()

        public void resize(){
            /**
             * int length = fElements.length;
             * Element [] temp = new Element[length * 2];
             * System.arraycopy(fElements, 0, temp, 0, length);
             * fElements = temp;
             */
            //resize QNames
            int oldLength = fQName.length;
            QName [] tmp = new QName[oldLength * 2];
            System.arraycopy(fQName, 0, tmp, 0, oldLength);
            fQName = tmp;

            for (int i = oldLength; i < fQName.length; i++) {
                fQName[i] = new QName();
            }

        }


        //
        // Public methods
        //

        /** Check if the element scanned during the start element
         *matches the stored element.
         *
         *@return true if the match suceeds.
         */
        public boolean matchElement(QName element) {
            //last depth is the depth when last elemnt was pushed
            //if last depth is greater than current depth
            if(DEBUG_SKIP_ALGORITHM){
                System.out.println("fLastDepth = " + fLastDepth);
                System.out.println("fDepth = " + fDepth);
            }
            boolean match = false;
            if(fLastDepth > fDepth && fDepth <= 2){
                if(DEBUG_SKIP_ALGORITHM){
                    System.out.println("Checking if the elements match " + element.rawname + " , " + fQName[fDepth].rawname);
                }
                if(element.rawname == fQName[fDepth].rawname){
                    fAdd = false;
                    //mark this position
                    //decrease the depth by 1 as arrays are 0 based
                    fMark = fDepth - 1;
                    //we found the match and from next element skipping will start, add 1
                    fPosition = fMark + 1 ;
                    match = true;
                    //Once we get match decrease the count -- this was increased by nextElement()
                    --fCount;
                    if(DEBUG_SKIP_ALGORITHM){
                        System.out.println("fAdd FALSE -- NOW ELEMENT SHOULD NOT BE ADDED");
                        System.out.println("fMark = " + fMark);
                        System.out.println("fPosition = " + fPosition);
                        System.out.println("fDepth = " + fDepth);
                        System.out.println("fCount = " + fCount);
                    }
                }else{
                    fAdd = true;
                    if(DEBUG_SKIP_ALGORITHM)System.out.println("fAdd is " + fAdd);
                }
            }
            //store the last depth
            fLastDepth = fDepth++;
            return match;
        } // pushElement(QName):QName

        /**
         * This function doesn't increase depth. The function in this function is
         *broken down into two functions for efficiency. <@see>matchElement</see>.
         * This function just returns the pointer to the object and its values are set.
         *
         *@return QName reference to the next element in the list
         */
        public QName nextElement() {

            //if number of elements becomes equal to the length of array -- stop the skipping
            if (fCount == fQName.length) {
                fShouldSkip = false;
                fAdd = false;
                if(DEBUG_SKIP_ALGORITHM)System.out.println("SKIPPING STOPPED, fShouldSkip = " + fShouldSkip);
                //xxx: this is not correct, we are returning the last element
                //this wont make any difference since flag has been set to 'false'
                return fQName[--fCount];
            }
            if(DEBUG_SKIP_ALGORITHM){
                System.out.println("fCount = " + fCount);
            }
            return fQName[fCount++];

        }

        /** Note that this function is considerably different than nextElement()
         * This function just returns the previously stored elements
         */
        public QName getNext(){
            //when position reaches number of elements in the list..
            //set the position back to mark,  making it a circular linked list.
            if(fPosition == fCount){
                fPosition = fMark;
            }
            return fQName[fPosition++];
        }

        /** returns the current depth
         */
        public int popElement(){
            return fDepth--;
        }


        /** Clears the stack without throwing away existing QName objects. */
        public void clear() {
            fLastDepth = 0;
            fDepth = 0;
            fCount = 0 ;
            fPosition = fMark = 1;
        } // clear()

    } // class ElementStack

    /**
     * Element stack. This stack operates without synchronization, error
     * checking, and it re-uses objects instead of throwing popped items
     * away.
     *
     * @author Andy Clark, IBM
     */
    protected class ElementStack {

        //
        // Data
        //

        /** The stack data. */
        protected QName[] fElements;
        protected int []  fInt = new int[20];


        //Element depth
        protected int fDepth;
        //total number of elements
        protected int fCount;
        //current position
        protected int fPosition;
        //Mark refers to the position
        protected int fMark;

        protected int fLastDepth ;

        //
        // Constructors
        //

        /** Default constructor. */
        public ElementStack() {
            fElements = new QName[20];
            for (int i = 0; i < fElements.length; i++) {
                fElements[i] = new QName();
            }
        } // <init>()

        //
        // Public methods
        //

        /**
         * Pushes an element on the stack.
         * <p>
         * <strong>Note:</strong> The QName values are copied into the
         * stack. In other words, the caller does <em>not</em> orphan
         * the element to the stack. Also, the QName object returned
         * is <em>not</em> orphaned to the caller. It should be
         * considered read-only.
         *
         * @param element The element to push onto the stack.
         *
         * @return Returns the actual QName object that stores the
         */
        //XXX: THIS FUNCTION IS NOT USED
        public QName pushElement(QName element) {
            if (fDepth == fElements.length) {
                QName[] array = new QName[fElements.length * 2];
                System.arraycopy(fElements, 0, array, 0, fDepth);
                fElements = array;
                for (int i = fDepth; i < fElements.length; i++) {
                    fElements[i] = new QName();
                }
            }
            fElements[fDepth].setValues(element);
            return fElements[fDepth++];
        } // pushElement(QName):QName


        /** Note that this function is considerably different than nextElement()
         * This function just returns the previously stored elements
         */
        public QName getNext(){
            //when position reaches number of elements in the list..
            //set the position back to mark,  making it a circular linked list.
            if(fPosition == fCount){
                fPosition = fMark;
            }
            //store the position of last opened tag at particular depth
            //fInt[++fDepth] = fPosition;
            if(DEBUG_SKIP_ALGORITHM){
                System.out.println("Element at fPosition = " + fPosition + " is " + fElements[fPosition].rawname);
            }
            //return fElements[fPosition++];
            return fElements[fPosition];
        }

        /** This function should be called only when element was skipped sucessfully.
         * 1. Increase the depth - because element was sucessfully skipped.
         *2. Store the position of the element token in array  "last opened tag" at depth.
         *3. increase the position counter so as to point to the next element in the array
         */
        public void push(){

            fInt[++fDepth] = fPosition++;
        }

        /** Check if the element scanned during the start element
         *matches the stored element.
         *
         *@return true if the match suceeds.
         */
        public boolean matchElement(QName element) {
            //last depth is the depth when last elemnt was pushed
            //if last depth is greater than current depth
            //if(DEBUG_SKIP_ALGORITHM){
            //   System.out.println("Check if the element " + element.rawname + " matches");
            //  System.out.println("fLastDepth = " + fLastDepth);
            // System.out.println("fDepth = " + fDepth);
            //}
            boolean match = false;
            if(fLastDepth > fDepth && fDepth <= 3){
                if(DEBUG_SKIP_ALGORITHM){
                    System.out.println("----------ENTERED THE LOOP WHERE WE CHECK FOR MATCHING OF ELMENT-----");
                    System.out.println("Depth = " + fDepth + " Checking if INCOMING element " + element.rawname + " match STORED ELEMENT " + fElements[fDepth - 1].rawname);
                }
                if(element.rawname == fElements[fDepth - 1].rawname){
                    fAdd = false;
                    //mark this position
                    //decrease the depth by 1 as arrays are 0 based
                    fMark = fDepth - 1;
                    //we found the match
                    fPosition = fMark;
                    match = true;
                    //Once we get match decrease the count -- this was increased by nextElement()
                    --fCount;
                    if(DEBUG_SKIP_ALGORITHM){
                        System.out.println("NOW ELEMENT SHOULD NOT BE ADDED, fAdd is set to false");
                        System.out.println("fMark = " + fMark);
                        System.out.println("fPosition = " + fPosition);
                        System.out.println("fDepth = " + fDepth);
                        System.out.println("fCount = " + fCount);
                        System.out.println("---------MATCH SUCEEDED-----------------");
                        System.out.println("");
                    }
                }else{
                    fAdd = true;
                    if(DEBUG_SKIP_ALGORITHM)System.out.println("fAdd is " + fAdd);
                }
            }
            //store the position for the current depth
            //when we are adding the elements, when skipping
            //starts even then this should be tracked ie. when
            //calling getNext()
            if(match){
                //from next element skipping will start, add 1
                fInt[fDepth] = fPosition++;
            } else{
                if(DEBUG_SKIP_ALGORITHM){
                    System.out.println("At depth = " + fDepth + "array position is = " + (fCount - 1));
                }
                //sicne fInt[fDepth] contains pointer to the element array which are 0 based.
                fInt[fDepth] = fCount - 1;
            }

            //if number of elements becomes equal to the length of array -- stop the skipping
            //xxx: should we do "fCount == fInt.length"
            if (fCount == fElements.length) {
                fSkip = false;
                fAdd = false;
                //reposition the stack -- it seems to be too complex document and there is no symmerty in structure
                reposition();
                if(DEBUG_SKIP_ALGORITHM){
                    System.out.println("ALL THE ELMENTS IN ARRAY HAVE BEEN FILLED");
                    System.out.println("REPOSITIONING THE STACK");
                    System.out.println("-----------SKIPPING STOPPED----------");
                    System.out.println("");
                }
                return false;
            }
            if(DEBUG_SKIP_ALGORITHM){
                if(match){
                    System.out.println("Storing fPosition = " + fInt[fDepth] + " at fDepth = " + fDepth);
                }else{
                    System.out.println("Storing fCount = " + fInt[fDepth] + " at fDepth = " + fDepth);
                }
            }
            //store the last depth
            fLastDepth = fDepth;
            return match;
        } // matchElement(QName):QName


        /**
         * Returns the next element on the stack.
         *
         * @return Returns the actual QName object. Callee should
         * use this object to store the details of next element encountered.
         */
        public QName nextElement() {
            if(fSkip){
                fDepth++;
                //boundary checks are done in matchElement()
                return fElements[fCount++];
            } else if (fDepth == fElements.length) {
                QName[] array = new QName[fElements.length * 2];
                System.arraycopy(fElements, 0, array, 0, fDepth);
                fElements = array;
                for (int i = fDepth; i < fElements.length; i++) {
                    fElements[i] = new QName();
                }
            }

            return fElements[fDepth++];

        } // pushElement(QName):QName


        /**
         * Pops an element off of the stack by setting the values of
         * the specified QName.
         * <p>
         * <strong>Note:</strong> The object returned is <em>not</em>
         * orphaned to the caller. Therefore, the caller should consider
         * the object to be read-only.
         */
        public QName popElement() {
            //return the same object that was pushed -- this would avoid
            //setting the values for every end element.
            //STRONG: this object is read only -- this object reference shouldn't be stored.
            if(fSkip || fAdd ){
                if(DEBUG_SKIP_ALGORITHM){
                    System.out.println("POPPING Element, at position " + fInt[fDepth] + " element at that count is = " + fElements[fInt[fDepth]].rawname);
                    System.out.println("");
                }
                return fElements[fInt[fDepth--]];
            } else{
                if(DEBUG_SKIP_ALGORITHM){
                    System.out.println("Retrieveing element at depth = " + fDepth + " is " + fElements[fDepth].rawname );
                }
                return fElements[--fDepth] ;
            }
            //element.setValues(fElements[--fDepth]);
        } // popElement(QName)

        /** Reposition the stack. fInt [] contains all the opened tags at particular depth.
         * Transfer all the opened tags starting from depth '2' to the current depth and reposition them
         *as per the depth.
         */
        public void reposition(){
            for( int i = 2 ; i <= fDepth ; i++){
                fElements[i-1] = fElements[fInt[i]];
            }
            if(DEBUG_SKIP_ALGORITHM){
                for( int i = 0 ; i < fDepth ; i++){
                    System.out.println("fElements[" + i + "]" + " = " + fElements[i].rawname);
                }
            }
        }

        /** Clears the stack without throwing away existing QName objects. */
        public void clear() {
            fDepth = 0;
            fLastDepth = 0;
            fCount = 0 ;
            fPosition = fMark = 1;

        } // clear()

        /**
         * This function is as a result of optimization done for endElement --
         * we dont need to set the value for every end element encouterd.
         * For Well formedness checks we can have the same QName object that was pushed.
         * the values will be set only if application need to know about the endElement
         * -- neeraj.bajaj@sun.com
         */

        public QName getLastPoppedElement(){
            return fElements[fDepth];
        }
    } // class ElementStack

    /**
     * Drives the parser to the next state/event on the input. Parser is guaranteed
     * to stop at the next state/event.
     *
     * Internally XML document is divided into several states. Each state represents
     * a sections of XML document. When this functions returns normally, it has read
     * the section of XML document and returns the state corresponding to section of
     * document which has been read. For optimizations, a particular driver
     * can read ahead of the section of document (state returned) just read and
     * can maintain a different internal state.
     *
     *
     * @author Neeraj Bajaj, Sun Microsystems
     */
    protected interface Driver {


        /**
         * Drives the parser to the next state/event on the input. Parser is guaranteed
         * to stop at the next state/event.
         *
         * Internally XML document is divided into several states. Each state represents
         * a sections of XML document. When this functions returns normally, it has read
         * the section of XML document and returns the state corresponding to section of
         * document which has been read. For optimizations, a particular driver
         * can read ahead of the section of document (state returned) just read and
         * can maintain a different internal state.
         *
         * @return state representing the section of document just read.
         *
         * @throws IOException  Thrown on i/o error.
         * @throws XNIException Thrown on parse error.
         */

        public int next() throws IOException, XNIException;

    } // interface Driver

    /**
     * Driver to handle content scanning. This driver is capable of reading
     * the fragment of XML document. When it has finished reading fragment
     * of XML documents, it can pass the job of reading to another driver.
     *
     * This class has been modified as per the new design which is more suited to
     * efficiently build pull parser. Lot of performance improvements have been done and
     * the code has been added to support stax functionality/features.
     *
     * @author Neeraj Bajaj, Sun Microsystems
     *
     *
     * @author Andy Clark, IBM
     * @author Eric Ye, IBM
     */
    protected class FragmentContentDriver
            implements Driver {

        //
        // Driver methods
        //
        private boolean fContinueDispatching = true;
        private boolean fScanningForMarkup = true;

        /**
         *  decides the appropriate state of the parser
         */
        private void startOfMarkup() throws IOException {
            fMarkupDepth++;
            final int ch = fEntityScanner.peekChar();

            switch(ch){
                case '?' :{
                    setScannerState(SCANNER_STATE_PI);
                        fEntityScanner.skipChar(ch, null);
                    break;
                }
                case '!' :{
                        fEntityScanner.skipChar(ch, null);
                        if (fEntityScanner.skipChar('-', null)) {
                            if (!fEntityScanner.skipChar('-', NameType.COMMENT)) {
                            reportFatalError("InvalidCommentStart",
                                    null);
                        }
                        setScannerState(SCANNER_STATE_COMMENT);
                    } else if (fEntityScanner.skipString(cdata)) {
                        setScannerState(SCANNER_STATE_CDATA );
                    } else if (!scanForDoctypeHook()) {
                        reportFatalError("MarkupNotRecognizedInContent",
                                null);
                    }
                    break;
                }
                case '/' :{
                    setScannerState(SCANNER_STATE_END_ELEMENT_TAG);
                        fEntityScanner.skipChar(ch, NameType.ELEMENTEND);
                    break;
                }
                default :{
                    if (isValidNameStartChar(ch)) {
                        setScannerState(SCANNER_STATE_START_ELEMENT_TAG);
                    } else {
                        reportFatalError("MarkupNotRecognizedInContent",
                                null);
                    }
                }
            }

        }//startOfMarkup

        private void startOfContent() throws IOException {
            if (fEntityScanner.skipChar('<', null)) {
                setScannerState(SCANNER_STATE_START_OF_MARKUP);
            } else if (fEntityScanner.skipChar('&', NameType.REFERENCE)) {
                setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE
            } else {
                //element content is there..
                setScannerState(SCANNER_STATE_CHARACTER_DATA);
            }
        }//startOfContent


        /**
         *
         * SCANNER_STATE_CONTENT and SCANNER_STATE_START_OF_MARKUP are two super states of the parser.
         * At any point of time when in doubt over the current state of the parser, the state should be
         * set to SCANNER_STATE_CONTENT. Parser will automatically revive itself and will set state of
         * the parser to one of its sub state.
         * sub states are defined in the parser on the basis of different XML component like
         * SCANNER_STATE_ENTITY_REFERENCE , SCANNER_STATE_START_ELEMENT, SCANNER_STATE_CDATA etc..
         * These sub states help the parser to have fine control over the parsing. These are the
         * different milepost, parser stops at each sub state (milepost). Based on this state it is
         * decided if paresr needs to stop at next milepost ??
         *
         */
        public void decideSubState() throws IOException {
            while( fScannerState == SCANNER_STATE_CONTENT || fScannerState == SCANNER_STATE_START_OF_MARKUP){

                switch (fScannerState) {

                    case SCANNER_STATE_CONTENT: {
                        startOfContent() ;
                        break;
                    }

                    case SCANNER_STATE_START_OF_MARKUP: {
                        startOfMarkup() ;
                        break;
                    }
                }
            }
        }//decideSubState

        /**
         * Drives the parser to the next state/event on the input. Parser is guaranteed
         * to stop at the next state/event. Internally XML document
         * is divided into several states. Each state represents a sections of XML
         * document. When this functions returns normally, it has read the section
         * of XML document and returns the state corresponding to section of
         * document which has been read. For optimizations, a particular driver
         * can read ahead of the section of document (state returned) just read and
         * can maintain a different internal state.
         *
         * State returned corresponds to Stax states.
         *
         * @return state representing the section of document just read.
         *
         * @throws IOException  Thrown on i/o error.
         * @throws XNIException Thrown on parse error.
         */

        public int next() throws IOException, XNIException {
            while (true) {
            try {
                if(DEBUG_NEXT){
                    System.out.println("NOW IN FragmentContentDriver");
                    System.out.println("Entering the FragmentContentDriver with = " + getScannerStateName(fScannerState));
                }

                //decide the actual sub state of the scanner.For more information refer to the javadoc of
                //decideSubState.

                switch (fScannerState) {
                    case SCANNER_STATE_CONTENT: {
                        final int ch = fEntityScanner.peekChar();
                        if (ch == '<') {
                            fEntityScanner.scanChar(null);
                            setScannerState(SCANNER_STATE_START_OF_MARKUP);
                        } else if (ch == '&') {
                            fEntityScanner.scanChar(NameType.REFERENCE);
                            setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE
                            break;
                        } else {
                            //element content is there..
                            setScannerState(SCANNER_STATE_CHARACTER_DATA);
                            break;
                        }
                    }

                    case SCANNER_STATE_START_OF_MARKUP: {
                        startOfMarkup();
                        break;
                    }//case: SCANNER_STATE_START_OF_MARKUP

                }//end of switch
                //decideSubState() ;

                //do some special handling if isCoalesce is set to true.
                if(fIsCoalesce){
                    fUsebuffer = true ;
                    //if the last section was character data
                    if(fLastSectionWasCharacterData){

                        //if we dont encounter any CDATA or ENITY REFERENCE and current state is also not SCANNER_STATE_CHARACTER_DATA
                        //return the last scanned charactrer data.
                        if((fScannerState != SCANNER_STATE_CDATA) && (fScannerState != SCANNER_STATE_REFERENCE)
                        && (fScannerState != SCANNER_STATE_CHARACTER_DATA)){
                            fLastSectionWasCharacterData = false;
                            return XMLEvent.CHARACTERS;
                        }
                    }//if last section was CDATA or ENTITY REFERENCE
                    //xxx: there might be another entity reference or CDATA after this
                    //<foo>blah blah &amp;&lt;<![CDATA[[aa]]>blah blah</foo>
                    else if((fLastSectionWasCData || fLastSectionWasEntityReference)){
                        //and current state is not SCANNER_STATE_CHARACTER_DATA
                        //or SCANNER_STATE_CDATA or SCANNER_STATE_REFERENCE
                        //this means there is nothing more to be coalesced.
                        //return the CHARACTERS event.
                        if((fScannerState != SCANNER_STATE_CDATA) && (fScannerState != SCANNER_STATE_REFERENCE)
                        && (fScannerState != SCANNER_STATE_CHARACTER_DATA)){

                            fLastSectionWasCData = false;
                            fLastSectionWasEntityReference = false;
                            return XMLEvent.CHARACTERS;
                        }
                    }
                }


                if(DEBUG_NEXT){
                    System.out.println("Actual scanner state set by decideSubState is = " + getScannerStateName(fScannerState));
                }

                switch(fScannerState){

                    case XMLEvent.START_DOCUMENT :
                        return XMLEvent.START_DOCUMENT;

                    case SCANNER_STATE_START_ELEMENT_TAG :{

                        //xxx this function returns true when element is empty.. can be linked to end element event.
                        //returns true if the element is empty
                        fEmptyElement = scanStartElement() ;
                        //if the element is empty the next event is "end element"
                        if(fEmptyElement){
                            setScannerState(SCANNER_STATE_END_ELEMENT_TAG);
                        }else{
                            //set the next possible state
                            setScannerState(SCANNER_STATE_CONTENT);
                        }
                        return XMLEvent.START_ELEMENT ;
                    }

                    case SCANNER_STATE_CHARACTER_DATA: {
                        if(DEBUG_COALESCE){
                            System.out.println("fLastSectionWasCData = " + fLastSectionWasCData);
                            System.out.println("fIsCoalesce = " + fIsCoalesce);
                        }
                        //if last section was either entity reference or cdata or character data we should be using buffer
                        fUsebuffer = fLastSectionWasEntityReference || fLastSectionWasCData || fLastSectionWasCharacterData ;

                        //When coalesce is set to true and last state was REFERENCE or CDATA or CHARACTER_DATA, buffer should not be cleared.
                        if( fIsCoalesce && (fLastSectionWasEntityReference || fLastSectionWasCData || fLastSectionWasCharacterData) ){
                            fLastSectionWasEntityReference = false;
                            fLastSectionWasCData = false;
                            fLastSectionWasCharacterData = true ;
                            fUsebuffer = true;
                        }else{
                            //clear the buffer
                            fContentBuffer.clear();
                        }

                        //set the fTempString length to 0 before passing it on to scanContent
                        //scanContent sets the correct co-ordinates as per the content read
                        fTempString.length = 0;
                        int c = fEntityScanner.scanContent(fTempString);
                        if(DEBUG){
                            System.out.println("fTempString = " + fTempString);
                        }
                        if(fEntityScanner.skipChar('<', null)){
                            //check if we have reached end of element
                            if(fEntityScanner.skipChar('/', NameType.ELEMENTEND)){
                                //increase the mark up depth
                                fMarkupDepth++;
                                fLastSectionWasCharacterData = false;
                                setScannerState(SCANNER_STATE_END_ELEMENT_TAG);
                                //check if its start of new element
                            }else if(XMLChar.isNameStart(fEntityScanner.peekChar())){
                                fMarkupDepth++;
                                fLastSectionWasCharacterData = false;
                                setScannerState(SCANNER_STATE_START_ELEMENT_TAG);
                            }else{
                                setScannerState(SCANNER_STATE_START_OF_MARKUP);
                                //there can be cdata ahead if coalesce is true we should call again
                                if(fIsCoalesce){
                                    fUsebuffer = true;
                                    fLastSectionWasCharacterData = true;
                                    fContentBuffer.append(fTempString);
                                    fTempString.length = 0;
                                    continue;
                                }
                            }
                            //in case last section was either entity reference or cdata or character data -- we should be using buffer
                            if(fUsebuffer){
                                fContentBuffer.append(fTempString);
                                fTempString.length = 0;
                            }
                            if(DEBUG){
                                System.out.println("NOT USING THE BUFFER, STRING = " + fTempString.toString());
                            }
                            if(dtdGrammarUtil!= null && dtdGrammarUtil.isIgnorableWhiteSpace(fContentBuffer)){
                                if(DEBUG)System.out.println("Return SPACE EVENT");
                                return XMLEvent.SPACE;
                            }else
                                return XMLEvent.CHARACTERS;

                        } else{
                            fUsebuffer = true ;
                            if(DEBUG){
                                System.out.println("fContentBuffer = " + fContentBuffer);
                                System.out.println("fTempString = " + fTempString);
                            }
                            fContentBuffer.append(fTempString);
                            fTempString.length = 0;
                        }
                        if (c == '\r') {
                            if(DEBUG){
                                System.out.println("'\r' character found");
                            }
                            // happens when there is the character reference &#13;
                            //xxx: We know the next chracter.. we should just skip it and add ']' directlry
                            fEntityScanner.scanChar(null);
                            fUsebuffer = true;
                            fContentBuffer.append((char)c);
                            c = -1 ;
                        } else if (c == ']') {
                            //fStringBuffer.clear();
                            //xxx: We know the next chracter.. we should just skip it and add ']' directlry
                            fUsebuffer = true;
                            fContentBuffer.append((char)fEntityScanner.scanChar(null));
                            // remember where we are in case we get an endEntity before we
                            // could flush the buffer out - this happens when we're parsing an
                            // entity which ends with a ]
                            fInScanContent = true;

                            // We work on a single character basis to handle cases such as:
                            // ']]]>' which we might otherwise miss.
                            //
                            if (fEntityScanner.skipChar(']', null)) {
                                fContentBuffer.append(']');
                                while (fEntityScanner.skipChar(']', null)) {
                                    fContentBuffer.append(']');
                                }
                                if (fEntityScanner.skipChar('>', null)) {
                                    reportFatalError("CDEndInContent", null);
                                }
                            }
                            c = -1 ;
                            fInScanContent = false;
                        }

                        do{
                            //xxx: we should be using only one buffer..
                            // we need not to grow the buffer only when isCoalesce() is not true;

                            if (c == '<') {
                                fEntityScanner.scanChar(null);
                                setScannerState(SCANNER_STATE_START_OF_MARKUP);
                                break;
                            }//xxx what should be the behavior if entity reference is present in the content ?
                            else if (c == '&') {
                                fEntityScanner.scanChar(NameType.REFERENCE);
                                setScannerState(SCANNER_STATE_REFERENCE);
                                break;
                            }///xxx since this part is also characters, it should be merged...
                            else if (c != -1 && isInvalidLiteral(c)) {
                                if (XMLChar.isHighSurrogate(c)) {
                                    // special case: surrogates
                                    scanSurrogates(fContentBuffer) ;
                                    setScannerState(SCANNER_STATE_CONTENT);
                                } else {
                                    reportFatalError("InvalidCharInContent",
                                            new Object[] {
                                        Integer.toString(c, 16)});
                                        fEntityScanner.scanChar(null);
                                }
                                break;
                            }
                            //xxx: scanContent also gives character callback.
                            c = scanContent(fContentBuffer) ;
                            //we should not be iterating again if fIsCoalesce is not set to true

                            if(!fIsCoalesce){
                                setScannerState(SCANNER_STATE_CONTENT);
                                break;
                            }

                        }while(true);

                        //if (fDocumentHandler != null) {
                        //  fDocumentHandler.characters(fContentBuffer, null);
                        //}
                        if(DEBUG)System.out.println("USING THE BUFFER, STRING START=" + fContentBuffer.toString() +"=END");
                        //if fIsCoalesce is true there might be more data so call fDriver.next()
                        if(fIsCoalesce){
                            fLastSectionWasCharacterData = true ;
                            continue;
                        }else{
                            if(dtdGrammarUtil!= null && dtdGrammarUtil.isIgnorableWhiteSpace(fContentBuffer)){
                                if(DEBUG)System.out.println("Return SPACE EVENT");
                                return XMLEvent.SPACE;
                            } else
                                return XMLEvent.CHARACTERS ;
                        }
                    }

                    case SCANNER_STATE_END_ELEMENT_TAG :{
                        if(fEmptyElement){
                            //set it back to false.
                            fEmptyElement = false;
                            setScannerState(SCANNER_STATE_CONTENT);
                            //check the case when there is comment after single element document
                            //<foo/> and some comment after this
                            return (fMarkupDepth == 0 && elementDepthIsZeroHook() ) ? XMLEvent.END_ELEMENT : XMLEvent.END_ELEMENT ;

                        } else if(scanEndElement() == 0) {
                            //It is last element of the document
                            if (elementDepthIsZeroHook()) {
                                //if element depth is zero , it indicates the end of the document
                                //the state shouldn't be set, because it is set by elementDepthIsZeroHook() function
                                //xxx understand this point once again..
                                return XMLEvent.END_ELEMENT ;
                            }

                        }
                        setScannerState(SCANNER_STATE_CONTENT);
                        return XMLEvent.END_ELEMENT ;
                    }

                    case SCANNER_STATE_COMMENT: { //SCANNER_STATE_COMMENT:
                        scanComment();
                        setScannerState(SCANNER_STATE_CONTENT);
                        return XMLEvent.COMMENT;
                        //break;
                    }
                    case SCANNER_STATE_PI:{ //SCANNER_STATE_PI: {
                        //clear the buffer first
                        fContentBuffer.clear() ;
                        //xxx: which buffer should be passed. Ideally we shouldn't have
                        //more than two buffers --
                        //xxx: where should we add the switch for buffering.
                        scanPI(fContentBuffer);
                        setScannerState(SCANNER_STATE_CONTENT);
                        return XMLEvent.PROCESSING_INSTRUCTION;
                        //break;
                    }
                    case SCANNER_STATE_CDATA :{ //SCANNER_STATE_CDATA: {
                        //xxx: What if CDATA is the first event
                        //<foo><![CDATA[hello<><>]]>append</foo>

                        //we should not clear the buffer only when the last state was either SCANNER_STATE_REFERENCE or
                        //SCANNER_STATE_CHARACTER_DATA or SCANNER_STATE_REFERENCE
                        if(fIsCoalesce && ( fLastSectionWasEntityReference || fLastSectionWasCData || fLastSectionWasCharacterData)){
                            fLastSectionWasCData = true ;
                            fLastSectionWasEntityReference = false;
                            fLastSectionWasCharacterData = false;
                        }//if we dont need to coalesce clear the buffer
                        else{
                            fContentBuffer.clear();
                        }
                        fUsebuffer = true;
                        //CDATA section is completely read in all the case.
                        scanCDATASection(fContentBuffer , true);
                        setScannerState(SCANNER_STATE_CONTENT);
                        //1. if fIsCoalesce is set to true we set the variable fLastSectionWasCData to true
                        //and just call fDispatche.next(). Since we have set the scanner state to
                        //SCANNER_STATE_CONTENT (super state) parser will automatically recover and
                        //behave appropriately. When isCoalesce is set to true we dont need to reportCDATA event
                        //2. Check if application has set for reporting CDATA event
                        //3. if the application has neither set the fIsCoalesce to true nor fReportCdataEvent
                        //return the cdata event as characters.
                        if(fIsCoalesce){
                            fLastSectionWasCData = true ;
                            //there might be more data to coalesce.
                            continue;
                        }else if(fReportCdataEvent){
                            return XMLEvent.CDATA;
                        } else{
                            return XMLEvent.CHARACTERS;
                        }
                    }

                    case SCANNER_STATE_REFERENCE :{
                        fMarkupDepth++;
                        foundBuiltInRefs = false;

                        //we should not clear the buffer only when the last state was either CDATA or
                        //SCANNER_STATE_CHARACTER_DATA or SCANNER_STATE_REFERENCE
                        if(fIsCoalesce && ( fLastSectionWasEntityReference || fLastSectionWasCData || fLastSectionWasCharacterData)){
                            //fLastSectionWasEntityReference or fLastSectionWasCData are only
                            //used when fIsCoalesce is set to true.
                            fLastSectionWasEntityReference = true ;
                            fLastSectionWasCData = false;
                            fLastSectionWasCharacterData = false;
                        }//if we dont need to coalesce clear the buffer
                        else{
                            fContentBuffer.clear();
                        }
                        fUsebuffer = true ;
                        //take care of character reference
                        if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
                            scanCharReferenceValue(fContentBuffer, null);
                            fMarkupDepth--;
                            if(!fIsCoalesce){
                                setScannerState(SCANNER_STATE_CONTENT);
                                return XMLEvent.CHARACTERS;
                            }
                        } else {
                            // this function also starts new entity
                            scanEntityReference(fContentBuffer);
                            //if there was built-in entity reference & coalesce is not true
                            //return CHARACTERS
                            if(fScannerState == SCANNER_STATE_BUILT_IN_REFS && !fIsCoalesce){
                                setScannerState(SCANNER_STATE_CONTENT);
                                return XMLEvent.CHARACTERS;
                            }

                            //if there was a text declaration, call next() it will be taken care.
                            if(fScannerState == SCANNER_STATE_TEXT_DECL){
                                fLastSectionWasEntityReference = true ;
                                continue;
                            }

                            if(fScannerState == SCANNER_STATE_REFERENCE){
                                setScannerState(SCANNER_STATE_CONTENT);
                                if (fReplaceEntityReferences && fEntityStore.isDeclaredEntity(fCurrentEntityName)) {
                                    // Skip the entity reference, we don't care
                                    continue;
                                }
                                return XMLEvent.ENTITY_REFERENCE;
                            }
                        }
                        //Wether it was character reference, entity reference or built-in entity
                        //set the next possible state to SCANNER_STATE_CONTENT
                        setScannerState(SCANNER_STATE_CONTENT);
                        fLastSectionWasEntityReference = true ;
                        continue;
                    }

                    case SCANNER_STATE_TEXT_DECL: {
                        // scan text decl
                        if (fEntityScanner.skipString("<?xml")) {
                            fMarkupDepth++;
                            // NOTE: special case where entity starts with a PI
                            //       whose name starts with "xml" (e.g. "xmlfoo")
                            if (isValidNameChar(fEntityScanner.peekChar())) {
                                fStringBuffer.clear();
                                fStringBuffer.append("xml");

                                if (fNamespaces) {
                                    while (isValidNCName(fEntityScanner.peekChar())) {
                                        fStringBuffer.append((char)fEntityScanner.scanChar(null));
                                    }
                                } else {
                                    while (isValidNameChar(fEntityScanner.peekChar())) {
                                        fStringBuffer.append((char)fEntityScanner.scanChar(null));
                                    }
                                }
                                String target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
                                fContentBuffer.clear();
                                scanPIData(target, fContentBuffer);
                            }

                            // standard text declaration
                            else {
                                //xxx: this function gives callback
                                scanXMLDeclOrTextDecl(true);
                            }
                        }
                        // now that we've straightened out the readers, we can read in chunks:
                        fEntityManager.fCurrentEntity.mayReadChunks = true;
                        setScannerState(SCANNER_STATE_CONTENT);
                        //xxx: we don't return any state, so how do we get to know about TEXT declarations.
                        //it seems we have to careful when to allow function issue a callback
                        //and when to allow adapter issue a callback.
                        continue;
                    }


                    case SCANNER_STATE_ROOT_ELEMENT: {
                        if (scanRootElementHook()) {
                            fEmptyElement = true;
                            //rest would be taken care by fTrailingMiscDriver set by scanRootElementHook
                            return XMLEvent.START_ELEMENT;
                        }
                        setScannerState(SCANNER_STATE_CONTENT);
                        return XMLEvent.START_ELEMENT ;
                    }
                    case SCANNER_STATE_CHAR_REFERENCE : {
                        fContentBuffer.clear();
                        scanCharReferenceValue(fContentBuffer, null);
                        fMarkupDepth--;
                        setScannerState(SCANNER_STATE_CONTENT);
                        return XMLEvent.CHARACTERS;
                    }
                    default:
                        throw new XNIException("Scanner State " + fScannerState + " not Recognized ");

                }//switch
            }
            // premature end of file
            catch (EOFException e) {
                endOfFileHook(e);
                return -1;
            }
            } //while loop
        }//next

        //
        // Protected methods
        //

        // hooks

        // NOTE: These hook methods are added so that the full document
        //       scanner can share the majority of code with this class.

        /**
         * Scan for DOCTYPE hook. This method is a hook for subclasses
         * to add code to handle scanning for a the "DOCTYPE" string
         * after the string "<!" has been scanned.
         *
         * @return True if the "DOCTYPE" was scanned; false if "DOCTYPE"
         *          was not scanned.
         */
        protected boolean scanForDoctypeHook()
        throws IOException, XNIException {
            return false;
        } // scanForDoctypeHook():boolean

        /**
         * Element depth iz zero. This methos is a hook for subclasses
         * to add code to handle when the element depth hits zero. When
         * scanning a document fragment, an element depth of zero is
         * normal. However, when scanning a full XML document, the
         * scanner must handle the trailing miscellanous section of
         * the document after the end of the document's root element.
         *
         * @return True if the caller should stop and return true which
         *          allows the scanner to switch to a new scanning
         *          driver. A return value of false indicates that
         *          the content driver should continue as normal.
         */
        protected boolean elementDepthIsZeroHook()
        throws IOException, XNIException {
            return false;
        } // elementDepthIsZeroHook():boolean

        /**
         * Scan for root element hook. This method is a hook for
         * subclasses to add code that handles scanning for the root
         * element. When scanning a document fragment, there is no
         * "root" element. However, when scanning a full XML document,
         * the scanner must handle the root element specially.
         *
         * @return True if the caller should stop and return true which
         *          allows the scanner to switch to a new scanning
         *          driver. A return value of false indicates that
         *          the content driver should continue as normal.
         */
        protected boolean scanRootElementHook()
        throws IOException, XNIException {
            return false;
        } // scanRootElementHook():boolean

        /**
         * End of file hook. This method is a hook for subclasses to
         * add code that handles the end of file. The end of file in
         * a document fragment is OK if the markup depth is zero.
         * However, when scanning a full XML document, an end of file
         * is always premature.
         */
        protected void endOfFileHook(EOFException e)
        throws IOException, XNIException {

            // NOTE: An end of file is only only an error if we were
            //       in the middle of scanning some markup. -Ac
            if (fMarkupDepth != 0) {
                reportFatalError("PrematureEOF", null);
            }

        } // endOfFileHook()

    } // class FragmentContentDriver

    static void pr(String str) {
        System.out.println(str) ;
    }

    protected boolean fUsebuffer ;

    /** this function gets an XMLString (which is used to store the attribute value) from the special pool
     *  maintained for attributes.
     *  fAttributeCacheUsedCount tracks the number of attributes that has been consumed from the pool.
     *  if all the attributes has been consumed, it adds a new XMLString inthe pool and returns the same
     *  XMLString.
     *
     * @return XMLString XMLString used to store an attribute value.
     */

    protected XMLString getString(){
        if(fAttributeCacheUsedCount < initialCacheCount || fAttributeCacheUsedCount < attributeValueCache.size()){
            return (XMLString)attributeValueCache.get(fAttributeCacheUsedCount++);
        } else{
            XMLString str = new XMLString();
            fAttributeCacheUsedCount++;
            attributeValueCache.add(str);
            return str;
        }
    }

    /**
     * Implements XMLBufferListener interface.
     */

    public void refresh(){
        refresh(0);
    }

    /**
     * receives callbacks from {@link XMLEntityReader } when buffer
     * is being changed.
     * @param refreshPosition
     */
    public void refresh(int refreshPosition){
        //If you are reading attributes and you got a callback
        //cache available attributes.
        if(fReadingAttributes){
            fAttributes.refresh();
        }
        if(fScannerState == SCANNER_STATE_CHARACTER_DATA){
            //since fTempString directly matches to the underlying main buffer
            //store the data into buffer
            fContentBuffer.append(fTempString);
            //clear the XMLString so that data can't be added again.
            fTempString.length = 0;
            fUsebuffer = true;
        }
    }

} // class XMLDocumentFragmentScannerImpl
