Package org.apache.xerces.framework

Source Code of org.apache.xerces.framework.XMLDTDScanner

/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999,2000 The Apache Software Foundation.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment: 
*       "This product includes software developed by the
*        Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
*    not be used to endorse or promote products derived from this
*    software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    nor may "Apache" appear in their name, without prior written
*    permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.apache.org.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.xerces.framework;

import org.apache.xerces.readers.XMLEntityHandler;
import org.apache.xerces.readers.DefaultEntityHandler;
import org.apache.xerces.utils.QName;
import org.apache.xerces.utils.StringPool;
import org.apache.xerces.utils.XMLCharacterProperties;
import org.apache.xerces.utils.XMLMessages;
import org.apache.xerces.validators.common.Grammar;
import org.apache.xerces.validators.common.GrammarResolver;
import org.apache.xerces.validators.common.XMLAttributeDecl;
import org.apache.xerces.validators.common.XMLElementDecl;
import org.apache.xerces.validators.dtd.DTDGrammar;

import org.xml.sax.Locator;
import org.xml.sax.SAXParseException;

import java.util.StringTokenizer;
/**
* Default implementation of an XML DTD scanner.
* <p>
* Clients who wish to scan a DTD should implement
* XMLDTDScanner.EventHandler to provide the desired behavior
* when various DTD components are encountered.
* <p>
* To process the DTD, the client application should follow the
* following sequence:
* <ol>
<li>call scanDocTypeDecl() to scan the DOCTYPE declaration
<li>call getReadingExternalEntity() to determine if scanDocTypeDecl found an
*      external subset
* <li>if scanning an external subset, call scanDecls(true) to process the external subset
* </ol>
*
* @see XMLDTDScanner.EventHandler
* @version $Id: XMLDTDScanner.java,v 1.14 2000/08/04 20:55:23 ericye Exp $
*/
public final class XMLDTDScanner {
    //
    // Constants
    //
    //
    // [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ")
    //
    private static final char[] version_string = { 'v','e','r','s','i','o','n' };
    //
    // [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'
    //
    private static final char[] element_string = { 'E','L','E','M','E','N','T' };
    //
    // [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
    //
    private static final char[] empty_string = { 'E','M','P','T','Y' };
    private static final char[] any_string = { 'A','N','Y' };
    //
    // [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*'
    //                | '(' S? '#PCDATA' S? ')'
    //
    private static final char[] pcdata_string = { '#','P','C','D','A','T','A' };
    //
    // [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
    //
    private static final char[] attlist_string = { 'A','T','T','L','I','S','T' };
    //
    // [55] StringType ::= 'CDATA'
    //
    private static final char[] cdata_string = { 'C','D','A','T','A' };
    //
    // [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES'
    //                        | 'NMTOKEN' | 'NMTOKENS'
    //
    // Note: We search for common substrings always trying to move forward
    //
    //  'ID'      - Common prefix of ID, IDREF and IDREFS
    //  'REF'     - Common substring of IDREF and IDREFS after matching ID prefix
    //  'ENTIT'   - Common prefix of ENTITY and ENTITIES
    //  'IES'     - Suffix of ENTITIES
    //  'NMTOKEN' - Common prefix of NMTOKEN and NMTOKENS
    //
    private static final char[] id_string = { 'I','D' };
    private static final char[] ref_string = { 'R','E','F' };
    private static final char[] entit_string = { 'E','N','T','I','T' };
    private static final char[] ies_string = { 'I','E','S' };
    private static final char[] nmtoken_string = { 'N','M','T','O','K','E','N' };
    //
    // [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
    // [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID |  PublicID) S? '>'
    //
    private static final char[] notation_string = { 'N','O','T','A','T','I','O','N' };
    //
    // [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
    //
    private static final char[] required_string = { '#','R','E','Q','U','I','R','E','D' };
    private static final char[] implied_string = { '#','I','M','P','L','I','E','D' };
    private static final char[] fixed_string = { '#','F','I','X','E','D' };
    //
    // [62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>'
    //
    private static final char[] include_string = { 'I','N','C','L','U','D','E' };
    //
    // [63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
    //
    private static final char[] ignore_string = { 'I','G','N','O','R','E' };
    //
    // [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
    // [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
    //
    private static final char[] entity_string = { 'E','N','T','I','T','Y' };
    //
    // [75] ExternalID ::= 'SYSTEM' S SystemLiteral
    //                     | 'PUBLIC' S PubidLiteral S SystemLiteral
    // [83] PublicID ::= 'PUBLIC' S PubidLiteral
    //
    private static final char[] system_string = { 'S','Y','S','T','E','M' };
    private static final char[] public_string = { 'P','U','B','L','I','C' };
    //
    // [76] NDataDecl ::= S 'NDATA' S Name
    //
    private static final char[] ndata_string = { 'N','D','A','T','A' };
    //
    // [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' |  "'" EncName "'" )
    //
    private static final char[] encoding_string = { 'e','n','c','o','d','i','n','g' };
    //
    // Instance Variables
    //
    private DTDGrammar fDTDGrammar = null;
    private GrammarResolver fGrammarResolver = null;
    private boolean fNamespacesEnabled = false;
    private boolean fValidationEnabled = false;
    private XMLElementDecl fTempElementDecl = new XMLElementDecl();
    private XMLAttributeDecl fTempAttributeDecl = new XMLAttributeDecl();
    private QName fElementQName = new QName();
    private QName fAttributeQName = new QName();
    private QName fElementRefQName = new QName();
    private EventHandler fEventHandler = null;
    private XMLDocumentHandler.DTDHandler fDTDHandler = null;
    private StringPool fStringPool = null;
    private XMLErrorReporter fErrorReporter = null;
    private XMLEntityHandler fEntityHandler = null;
    private XMLEntityHandler.EntityReader fEntityReader = null;
    private XMLEntityHandler.CharBuffer fLiteralData = null;
    private int fReaderId = -1;
    private int fSystemLiteral = -1;
    private int fPubidLiteral = -1;
    private int[] fOpStack = null;
    private int[] fNodeIndexStack = null;
    private int[] fPrevNodeIndexStack = null;
    private int fScannerState = SCANNER_STATE_INVALID;
    private int fIncludeSectDepth = 0;
    private int fDoctypeReader = -1;
    private int fExternalSubsetReader = -1;
    private int fDefaultAttValueReader = -1;
    private int fDefaultAttValueElementType = -1;
    private int fDefaultAttValueAttrName = -1;
    private int fDefaultAttValueOffset = -1;
    private int fDefaultAttValueMark = -1;
    private int fEntityValueReader = -1;
    private int fEntityValueMark = -1;
    private int fXMLSymbol = -1;
    private int fXMLNamespace = -1;
    private int fXMLSpace = -1;
    private int fDefault = -1;
    private int fPreserve = -1;
    private int fScannerMarkupDepth = 0;
    private int fScannerParenDepth = 0;
    //
    // Constructors
    //
    public XMLDTDScanner(StringPool stringPool,
                         XMLErrorReporter errorReporter,
                         XMLEntityHandler entityHandler,
                         XMLEntityHandler.CharBuffer literalData) {
        fStringPool = stringPool;
        fErrorReporter = errorReporter;
        fEntityHandler = entityHandler;
        fLiteralData = literalData;
        init();
    }

    /**
     * Set the event handler
     *
     * @param eventHandler The place to send our callbacks.
     */
    public void setEventHandler(XMLDTDScanner.EventHandler eventHandler) {
        fEventHandler = eventHandler;
    }

    /** Set the DTD handler. */
    public void setDTDHandler(XMLDocumentHandler.DTDHandler dtdHandler) {
        fDTDHandler = dtdHandler;
    }

    /** Sets the grammar resolver. */
    public void setGrammarResolver(GrammarResolver resolver) {
        fGrammarResolver = resolver;
    }

    /** set fNamespacesEnabled  **/
    public void setNamespacesEnabled(boolean enabled) {
        fNamespacesEnabled = enabled;
    }
   
    /** set fValidationEnabled  **/
    public void setValidationEnabled(boolean enabled) {
        fValidationEnabled = enabled;
    }

    /**
     * Is the XMLDTDScanner reading from an external entity?
     *
     * This will be true, in particular if there was an external subset
     *
     * @return true if the XMLDTDScanner is reading from an external entity.
     */
    public boolean getReadingExternalEntity() {
        return fReaderId != fDoctypeReader;
    }
    /**
     * Is the scanner reading a ContentSpec?
     *
     * @return true if the scanner is reading a ContentSpec
     */
    public boolean getReadingContentSpec() {
        return getScannerState() == SCANNER_STATE_CONTENTSPEC;
    }
    /**
     * Report the markup nesting depth.  This allows a client to
     * perform validation checks for correct markup nesting.  This keeps
     * scanning and validation separate.
     *
     * @return the markup nesting depth
     */
    public int markupDepth() {
        return fScannerMarkupDepth;
    }
    private int increaseMarkupDepth() {
        return fScannerMarkupDepth++;
    }
    private int decreaseMarkupDepth() {
        return fScannerMarkupDepth--;
    }
    /**
     * Report the parenthesis nesting depth.  This allows a client to
     * perform validation checks for correct parenthesis balancing.  This keeps
     * scanning and validation separate.
     *
     * @return the parenthesis depth
     */
    public int parenDepth() {
        return fScannerParenDepth;
    }
    private void setParenDepth(int parenDepth) {
        fScannerParenDepth = parenDepth;
    }
    private void increaseParenDepth() {
        fScannerParenDepth++;
    }
    private void decreaseParenDepth() {
        fScannerParenDepth--;
    }
    //
    //
    //
    /**
     * Allow XMLDTDScanner to be reused.  This method is called from an
     * XMLParser reset method, which passes the StringPool to be used
     * by the reset DTD scanner instance.
     *
     * @param stringPool the string pool to be used by XMLDTDScanner. 
     */
    public void reset(StringPool stringPool, XMLEntityHandler.CharBuffer literalData) throws Exception {
        fStringPool = stringPool;
        fLiteralData = literalData;
        fEntityReader = null;
        fReaderId = -1;
        fSystemLiteral = -1;
        fPubidLiteral = -1;
        fOpStack = null;
        fNodeIndexStack = null;
        fPrevNodeIndexStack = null;
        fScannerState = SCANNER_STATE_INVALID;
        fIncludeSectDepth = 0;
        fDoctypeReader = -1;
        fExternalSubsetReader = -1;
        fDefaultAttValueReader = -1;
        fDefaultAttValueElementType = -1;
        fDefaultAttValueAttrName = -1;
        fDefaultAttValueOffset = -1;
        fDefaultAttValueMark = -1;
        fEntityValueReader = -1;
        fEntityValueMark = -1;
        fScannerMarkupDepth = 0;
        fScannerParenDepth = 0;
        init();
    }
    private void init() {
        fXMLSymbol = fStringPool.addSymbol("xml");
        fXMLNamespace = fStringPool.addSymbol("http://www.w3.org/XML/1998/namespace");

        fXMLSpace = fStringPool.addSymbol("xml:space");
        fDefault = fStringPool.addSymbol("default");
        fPreserve = fStringPool.addSymbol("preserve");
    }

    //
    // Interfaces
    //

    /**
     * This interface must be implemented by the users of the XMLDTDScanner class.
     * These methods form the abstraction between the implementation semantics and the
     * more generic task of scanning the DTD-specific XML grammar.
     */
    public interface EventHandler {

        /** Start of DTD. */
        public void callStartDTD() throws Exception;

        /** End of DTD. */
        public void callEndDTD() throws Exception;

        /**
         * Signal the Text declaration of an external entity.
         *
         * @param version the handle in the string pool for the version number
         * @param encoding the handle in the string pool for the encoding
         * @exception java.lang.Exception
         */
        public void callTextDecl(int version, int encoding) throws Exception;
        /**
         * Called when the doctype decl is scanned
         *
         * @param rootElementType handle of the rootElement
         * @param publicId StringPool handle of the public id
         * @param systemId StringPool handle of the system id
         * @exception java.lang.Exception
         */
        public void doctypeDecl(QName rootElement, int publicId, int systemId) throws Exception;
        /**
         * Called when the DTDScanner starts reading from the external subset
         *
         * @param publicId StringPool handle of the public id
         * @param systemId StringPool handle of the system id
         * @exception java.lang.Exception
         */
        public void startReadingFromExternalSubset(int publicId, int systemId) throws Exception;
        /**
         * Called when the DTDScanner stop reading from the external subset
         *
         * @exception java.lang.Exception
         */
        public void stopReadingFromExternalSubset() throws Exception;
        /**
         * Add an element declaration (forward reference)
         *
         * @param handle to the name of the element being declared
         * @return handle to the element whose declaration was added
         * @exception java.lang.Exception
         */
        public int addElementDecl(QName elementDecl) throws Exception;
        /**
         * Add an element declaration
         *
         * @param handle to the name of the element being declared
         * @param contentSpecType handle to the type name of the content spec
         * @param ContentSpec handle to the content spec node for the contentSpecType
         * @return handle to the element declaration that was added
         * @exception java.lang.Exception
         */
        public int addElementDecl(QName elementDecl, int contentSpecType, int contentSpec, boolean isExternal) throws Exception;
        /**
         * Add an attribute definition
         *
         * @param handle to the element whose attribute is being declared
         * @param attName StringPool handle to the attribute name being declared
         * @param attType type of the attribute
         * @param enumeration StringPool handle of the attribute's enumeration list (if any)
         * @param attDefaultType an integer value denoting the DefaultDecl value
         * @param attDefaultValue StringPool handle of this attribute's default value
         * @return handle to the attribute definition
         * @exception java.lang.Exception
         */
        public int addAttDef(QName elementDecl, QName attributeDecl,
                             int attType, boolean attList, int enumeration,
                             int attDefaultType, int attDefaultValue, boolean isExternal) throws Exception;
        /**
         * create an XMLContentSpec for a leaf
         *
         * @param nameIndex StringPool handle to the name (Element) for the node
         * @return handle to the newly create XMLContentSpec
         * @exception java.lang.Exception
         */
        public int addUniqueLeafNode(int nameIndex) throws Exception;
        /**
         * Create an XMLContentSpec for a single non-leaf
         *
         * @param nodeType the type of XMLContentSpec to create - from XMLContentSpec.CONTENTSPECNODE_*
         * @param nodeValue handle to an XMLContentSpec
         * @return handle to the newly create XMLContentSpec
         * @exception java.lang.Exception
         */
        public int addContentSpecNode(int nodeType, int nodeValue) throws Exception;
        /**
         * Create an XMLContentSpec for a two child leaf
         *
         * @param nodeType the type of XMLContentSpec to create - from XMLContentSpec.CONTENTSPECNODE_*
         * @param leftNodeIndex handle to an XMLContentSpec
         * @param rightNodeIndex handle to an XMLContentSpec
         * @return handle to the newly create XMLContentSpec
         * @exception java.lang.Exception
         */
        public int addContentSpecNode(int nodeType, int leftNodeIndex, int rightNodeIndex) throws Exception;
        /**
         * Create a string representation of an XMLContentSpec tree
         *
         * @param handle to an XMLContentSpec
         * @return String representation of the content spec tree
         * @exception java.lang.Exception
         */
        public String getContentSpecNodeAsString(int nodeIndex) throws Exception;
        /**
         * Start the scope of an entity declaration.
         *
         * @return <code>true</code> on success; otherwise
         *         <code>false</code> if the entity declaration is recursive.
         * @exception java.lang.Exception
         */
        public boolean startEntityDecl(boolean isPE, int entityName) throws Exception;
        /**
         * End the scope of an entity declaration.
         * @exception java.lang.Exception
         */
        public void endEntityDecl() throws Exception;
        /**
         * Add a declaration for an internal parameter entity
         *
         * @param name StringPool handle of the parameter entity name
         * @param value StringPool handle of the parameter entity value
         * @return handle to the parameter entity declaration
         * @exception java.lang.Exception
         */
        public int addInternalPEDecl(int name, int value) throws Exception;
        /**
         * Add a declaration for an external parameter entity
         *
         * @param name StringPool handle of the parameter entity name
         * @param publicId StringPool handle of the publicId
         * @param systemId StringPool handle of the systemId
         * @return handle to the parameter entity declaration
         * @exception java.lang.Exception
         */
        public int addExternalPEDecl(int name, int publicId, int systemId) throws Exception;
        /**
         * Add a declaration for an internal entity
         *
         * @param name StringPool handle of the entity name
         * @param value StringPool handle of the entity value
         * @return handle to the entity declaration
         * @exception java.lang.Exception
         */
        public int addInternalEntityDecl(int name, int value) throws Exception;
        /**
         * Add a declaration for an entity
         *
         * @param name StringPool handle of the entity name
         * @param publicId StringPool handle of the publicId
         * @param systemId StringPool handle of the systemId
         * @return handle to the entity declaration
         * @exception java.lang.Exception
         */
        public int addExternalEntityDecl(int name, int publicId, int systemId) throws Exception;
        /**
         * Add a declaration for an unparsed entity
         *
         * @param name StringPool handle of the entity name
         * @param publicId StringPool handle of the publicId
         * @param systemId StringPool handle of the systemId
         * @param notationName StringPool handle of the notationName
         * @return handle to the entity declaration
         * @exception java.lang.Exception
         */
        public int addUnparsedEntityDecl(int name, int publicId, int systemId, int notationName) throws Exception;
        /**
         * Called when the scanner start scanning an enumeration
         * @return StringPool handle to a string list that will hold the enumeration names
         * @exception java.lang.Exception
         */
        public int startEnumeration() throws Exception;
        /**
         * Add a name to an enumeration
         * @param enumIndex StringPool handle to the string list for the enumeration
         * @param elementType handle to the element that owns the attribute with the enumeration
         * @param attrName StringPool handle to the name of the attribut with the enumeration
         * @param nameIndex StringPool handle to the name to be added to the enumeration
         * @param isNotationType true if the enumeration is an enumeration of NOTATION names
         * @exception java.lang.Exception
         */
        public void addNameToEnumeration(int enumIndex, int elementType, int attrName, int nameIndex, boolean isNotationType) throws Exception;
        /**
         * Finish processing an enumeration
         *
         * @param enumIndex handle to the string list which holds the enumeration to be finshed.
         * @exception java.lang.Exception
         */
        public void endEnumeration(int enumIndex) throws Exception;
        /**
         * Add a declaration for a notation
         *
         * @param notationName
         * @param publicId
         * @param systemId
         * @return handle to the notation declaration
         * @exception java.lang.Exception
         */
        public int addNotationDecl(int notationName, int publicId, int systemId) throws Exception;
        /**
         * Called when a comment has been scanned
         *
         * @param data StringPool handle of the comment text
         * @exception java.lang.Exception
         */
        public void callComment(int data) throws Exception;
        /**
         * Called when a processing instruction has been scanned
         * @param piTarget StringPool handle of the PI target
         * @param piData StringPool handle of the PI data
         * @exception java.lang.Exception
         */
        public void callProcessingInstruction(int piTarget, int piData) throws Exception;
        /**
         * Supports DOM Level 2 internalSubset additions.
         * Called when the internal subset is completely scanned.
         */
        public void internalSubset(int internalSubset) throws Exception;
    }
    //
    //
    //
   
    /** Report a recoverable xml error. */
    protected void reportRecoverableXMLError(int majorCode, int minorCode,
                                             int stringIndex1)
        throws Exception {

        Object[] args = { fStringPool.toString(stringIndex1) };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);

    } // reportRecoverableXMLError(int,int,int)

    /** Report a recoverable xml error. */
    protected void reportRecoverableXMLError(int majorCode, int minorCode,
                                         String string1) throws Exception {

        Object[] args = { string1 };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);

    } // reportRecoverableXMLError(int,int,String)
   
    /** Report a recoverable xml error. */
    protected void reportRecoverableXMLError(int majorCode, int minorCode,
                                             String string1, String string2)
        throws Exception {

        Object[] args = { string1, string2 };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);

    } // reportRecoverableXMLError(int,int,String,String)
   
    private void reportFatalXMLError(int majorCode, int minorCode) throws Exception {
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   null,
                                   XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
    }
    private void reportFatalXMLError(int majorCode, int minorCode, int stringIndex1) throws Exception {
        Object[] args = { fStringPool.toString(stringIndex1) };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
    }
    private void reportFatalXMLError(int majorCode, int minorCode, String string1) throws Exception {
        Object[] args = { string1 };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
    }
    private void reportFatalXMLError(int majorCode, int minorCode, int stringIndex1, int stringIndex2) throws Exception {
        Object[] args = { fStringPool.toString(stringIndex1),
                          fStringPool.toString(stringIndex2) };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
    }
    private void reportFatalXMLError(int majorCode, int minorCode, String string1, String string2) throws Exception {
        Object[] args = { string1, string2 };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
    }
    private void reportFatalXMLError(int majorCode, int minorCode, String string1, String string2, String string3) throws Exception {
        Object[] args = { string1, string2, string3 };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
    }
    private void abortMarkup(int majorCode, int minorCode) throws Exception {
        reportFatalXMLError(majorCode, minorCode);
        skipPastEndOfCurrentMarkup();
    }
    private void abortMarkup(int majorCode, int minorCode, int stringIndex1) throws Exception {
        reportFatalXMLError(majorCode, minorCode, stringIndex1);
        skipPastEndOfCurrentMarkup();
    }
    private void abortMarkup(int majorCode, int minorCode, String string1) throws Exception {
        reportFatalXMLError(majorCode, minorCode, string1);
        skipPastEndOfCurrentMarkup();
    }
    private void abortMarkup(int majorCode, int minorCode, int stringIndex1, int stringIndex2) throws Exception {
        reportFatalXMLError(majorCode, minorCode, stringIndex1, stringIndex2);
        skipPastEndOfCurrentMarkup();
    }
    private void skipPastEndOfCurrentMarkup() throws Exception {
        fEntityReader.skipToChar('>');
        if (fEntityReader.lookingAtChar('>', true))
            decreaseMarkupDepth();
    }
    //
    //
    //
    static private final int SCANNER_STATE_INVALID = -1;
    static private final int SCANNER_STATE_END_OF_INPUT = 0;
    static private final int SCANNER_STATE_DOCTYPEDECL = 50;
    static private final int SCANNER_STATE_MARKUP_DECL = 51;
    static private final int SCANNER_STATE_TEXTDECL = 53;
    static private final int SCANNER_STATE_COMMENT = 54;
    static private final int SCANNER_STATE_PI = 55;
    static private final int SCANNER_STATE_DEFAULT_ATTRIBUTE_VALUE = 56;
    static private final int SCANNER_STATE_CONTENTSPEC = 57;
    static private final int SCANNER_STATE_ENTITY_VALUE = 58;
    static private final int SCANNER_STATE_SYSTEMLITERAL = 59;
    static private final int SCANNER_STATE_PUBIDLITERAL = 60;

    private int setScannerState(int scannerState) {
        int prevState = fScannerState;
        fScannerState = scannerState;
        return prevState;
    }
    private int getScannerState() {
        return fScannerState;
    }
    private void restoreScannerState(int scannerState) {
        if (fScannerState != SCANNER_STATE_END_OF_INPUT)
            fScannerState = scannerState;
    }
    /**
     * Change readers
     *
     * @param nextReader the new reader that the scanner will use
     * @param nextReaderId id of the reader to change to
     * @exception throws java.lang.Exception
     */
    public void readerChange(XMLEntityHandler.EntityReader nextReader, int nextReaderId) throws Exception {
        fEntityReader = nextReader;
        fReaderId = nextReaderId;
        if (fScannerState == SCANNER_STATE_DEFAULT_ATTRIBUTE_VALUE) {
            fDefaultAttValueOffset = fEntityReader.currentOffset();
            fDefaultAttValueMark = fDefaultAttValueOffset;
        } else if (fScannerState == SCANNER_STATE_ENTITY_VALUE) {
            fEntityValueMark = fEntityReader.currentOffset();
        }
    }
    /**
     * Handle the end of input
     *
     * @param entityName the handle in the string pool of the name of the entity which has reached end of input
     * @param moreToFollow if true, there is still input left to process in other readers
     * @exception java.lang.Exception
     */
    public void endOfInput(int entityNameIndex, boolean moreToFollow) throws Exception {
        if (fValidationEnabled ) {
            int readerDepth = fEntityHandler.getReaderDepth();
            if (getReadingContentSpec()) {
                int parenDepth = parenDepth();
                if (readerDepth != parenDepth) {
                    reportRecoverableXMLError(XMLMessages.MSG_IMPROPER_GROUP_NESTING,
                                                XMLMessages.VC_PROPER_GROUP_PE_NESTING,
                                                entityNameIndex);
                }
            } else {
                int markupDepth = markupDepth();
                if (readerDepth != markupDepth) {
                    reportRecoverableXMLError(XMLMessages.MSG_IMPROPER_DECLARATION_NESTING,
                                                XMLMessages.VC_PROPER_DECLARATION_PE_NESTING,
                                                entityNameIndex);
                }
            }
        }
        //REVISIT, why are we doing this?
        moreToFollow = fReaderId != fExternalSubsetReader;

//      System.out.println("current Scanner state " + getScannerState() +","+ fScannerState + moreToFollow);
        switch (fScannerState) {
        case SCANNER_STATE_INVALID:
            throw new RuntimeException("FWK004 XMLDTDScanner.endOfInput: cannot happen: 2"+"\n2");
        case SCANNER_STATE_END_OF_INPUT:
            break;
        case SCANNER_STATE_MARKUP_DECL:
            if (!moreToFollow && fIncludeSectDepth > 0) {
                reportFatalXMLError(XMLMessages.MSG_INCLUDESECT_UNTERMINATED,
                                    XMLMessages.P62_UNTERMINATED);
            }
            break;
        case SCANNER_STATE_DOCTYPEDECL:
            throw new RuntimeException("FWK004 XMLDTDScanner.endOfInput: cannot happen: 2.5"+"\n2.5");
//            break;
        case SCANNER_STATE_TEXTDECL:
// REVISIT            reportFatalXMLError(XMLMessages.MSG_ATTVAL0);
            break;
        case SCANNER_STATE_SYSTEMLITERAL:
            if (!moreToFollow) {
                reportFatalXMLError(XMLMessages.MSG_SYSTEMID_UNTERMINATED,
                                    XMLMessages.P11_UNTERMINATED);
            } else {
// REVISIT                reportFatalXMLError(XMLMessages.MSG_ATTVAL0);
            }
            break;
        case SCANNER_STATE_PUBIDLITERAL:
            if (!moreToFollow) {
                reportFatalXMLError(XMLMessages.MSG_PUBLICID_UNTERMINATED,
                                    XMLMessages.P12_UNTERMINATED);
            } else {
// REVISIT                reportFatalXMLError(XMLMessages.MSG_ATTVAL0);
            }
            break;
        case SCANNER_STATE_COMMENT:
            if (!moreToFollow && !getReadingExternalEntity()) {
                reportFatalXMLError(XMLMessages.MSG_COMMENT_UNTERMINATED,
                                    XMLMessages.P15_UNTERMINATED);
            } else {
                //
                // REVISIT - HACK !!!  code changed to pass incorrect OASIS test 'invalid--001'
                //  Uncomment the next line to conform to the spec...
                //
                //reportFatalXMLError(XMLMessages.MSG_COMMENT_NOT_IN_ONE_ENTITY,
                //                    XMLMessages.P78_NOT_WELLFORMED);
            }
            break;
        case SCANNER_STATE_PI:
            if (!moreToFollow) {
                reportFatalXMLError(XMLMessages.MSG_PI_UNTERMINATED,
                                    XMLMessages.P16_UNTERMINATED);
            } else {
                reportFatalXMLError(XMLMessages.MSG_PI_NOT_IN_ONE_ENTITY,
                                    XMLMessages.P78_NOT_WELLFORMED);
            }
            break;
        case SCANNER_STATE_DEFAULT_ATTRIBUTE_VALUE:
            if (!moreToFollow) {
                reportFatalXMLError(XMLMessages.MSG_ATTRIBUTE_VALUE_UNTERMINATED,
                                    XMLMessages.P10_UNTERMINATED,
                                    fDefaultAttValueElementType,
                                    fDefaultAttValueAttrName);
            } else if (fReaderId == fDefaultAttValueReader) {
// REVISIT                reportFatalXMLError(XMLMessages.MSG_ATTVAL0);
            } else {
                fEntityReader.append(fLiteralData, fDefaultAttValueMark, fDefaultAttValueOffset - fDefaultAttValueMark);
            }
            break;
        case SCANNER_STATE_CONTENTSPEC:
            break;
        case SCANNER_STATE_ENTITY_VALUE:
            if (fReaderId == fEntityValueReader) {
// REVISIT                reportFatalXMLError(XMLMessages.MSG_ATTVAL0);
            } else {
                fEntityReader.append(fLiteralData, fEntityValueMark, fEntityReader.currentOffset() - fEntityValueMark);
            }
            break;
        default:
            throw new RuntimeException("FWK004 XMLDTDScanner.endOfInput: cannot happen: 3"+"\n3");
        }
        if (!moreToFollow) {
            setScannerState(SCANNER_STATE_END_OF_INPUT);
        }
    }
    //
    // [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
    //
    private int scanCharRef() throws Exception {
        int valueOffset = fEntityReader.currentOffset();
        boolean hex = fEntityReader.lookingAtChar('x', true);
        int num = fEntityReader.scanCharRef(hex);
        if (num < 0) {
            switch (num) {
            case XMLEntityHandler.CHARREF_RESULT_SEMICOLON_REQUIRED:
                reportFatalXMLError(XMLMessages.MSG_SEMICOLON_REQUIRED_IN_CHARREF,
                                    XMLMessages.P66_SEMICOLON_REQUIRED);
                return -1;
            case XMLEntityHandler.CHARREF_RESULT_INVALID_CHAR:
                int majorCode = hex ? XMLMessages.MSG_HEXDIGIT_REQUIRED_IN_CHARREF :
                                      XMLMessages.MSG_DIGIT_REQUIRED_IN_CHARREF;
                int minorCode = hex ? XMLMessages.P66_HEXDIGIT_REQUIRED :
                                      XMLMessages.P66_DIGIT_REQUIRED;
                reportFatalXMLError(majorCode, minorCode);
                return -1;
            case XMLEntityHandler.CHARREF_RESULT_OUT_OF_RANGE:
                num = 0x110000; // this will cause the right error to be reported below...
                break;
            }
        }
        //
        //  [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF]        // any Unicode character, excluding the
        //               | [#xE000-#xFFFD] | [#x10000-#x10FFFF] // surrogate blocks, FFFE, and FFFF.
        //
        if (num < 0x20) {
            if (num == 0x09 || num == 0x0A || num == 0x0D) {
                return num;
            }
        } else if (num <= 0xD7FF || (num >= 0xE000 && (num <= 0xFFFD || (num >= 0x10000 && num <= 0x10FFFF)))) {
            return num;
        }
        int valueLength = fEntityReader.currentOffset() - valueOffset;
        reportFatalXMLError(XMLMessages.MSG_INVALID_CHARREF,
                            XMLMessages.WFC_LEGAL_CHARACTER,
                            fEntityReader.addString(valueOffset, valueLength));
        return -1;
    }
    //
    // From the standard:
    //
    // [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
    //
    // Called after scanning past '<!--'
    //
    private void scanComment() throws Exception
    {
        int commentOffset = fEntityReader.currentOffset();
        boolean sawDashDash = false;
        int previousState = setScannerState(SCANNER_STATE_COMMENT);
        while (fScannerState == SCANNER_STATE_COMMENT) {
            if (fEntityReader.lookingAtChar('-', false)) {
                int nextEndOffset = fEntityReader.currentOffset();
                int endOffset = 0;
                fEntityReader.lookingAtChar('-', true);
                int offset = fEntityReader.currentOffset();
                int count = 1;
                while (fEntityReader.lookingAtChar('-', true)) {
                    count++;
                    endOffset = nextEndOffset;
                    nextEndOffset = offset;
                    offset = fEntityReader.currentOffset();
                }
                if (count > 1) {
                    if (fEntityReader.lookingAtChar('>', true)) {
                        if (!sawDashDash && count > 2) {
                            reportFatalXMLError(XMLMessages.MSG_DASH_DASH_IN_COMMENT,
                                                XMLMessages.P15_DASH_DASH);
                            sawDashDash = true;
                        }
                        decreaseMarkupDepth();
                        int comment = fEntityReader.addString(commentOffset, endOffset - commentOffset);
                        fDTDGrammar.callComment(comment);
                        if (fDTDHandler != null) {
                            fDTDHandler.comment(comment);
                        }
                        restoreScannerState(previousState);
                        return;
                    } else if (!sawDashDash) {
                        reportFatalXMLError(XMLMessages.MSG_DASH_DASH_IN_COMMENT,
                                            XMLMessages.P15_DASH_DASH);
                        sawDashDash = true;
                    }
                }
            } else {
                if (!fEntityReader.lookingAtValidChar(true)) {
                    int invChar = fEntityReader.scanInvalidChar();
                    if (fScannerState != SCANNER_STATE_END_OF_INPUT) {
                        if (invChar >= 0) {
                            reportFatalXMLError(XMLMessages.MSG_INVALID_CHAR_IN_COMMENT,
                                                XMLMessages.P15_INVALID_CHARACTER,
                                                Integer.toHexString(invChar));
                        }
                    }
                }
            }
        }
        restoreScannerState(previousState);
    }
    //
    // From the standard:
    //
    // [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
    // [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
    //
    private void scanPI(int piTarget) throws Exception
    {
        String piTargetString = fStringPool.toString(piTarget);
        if (piTargetString.length() == 3 &&
            (piTargetString.charAt(0) == 'X' || piTargetString.charAt(0) == 'x') &&
            (piTargetString.charAt(1) == 'M' || piTargetString.charAt(1) == 'm') &&
            (piTargetString.charAt(2) == 'L' || piTargetString.charAt(2) == 'l')) {
            abortMarkup(XMLMessages.MSG_RESERVED_PITARGET,
                        XMLMessages.P17_RESERVED_PITARGET);
            return;
        }
        int prevState = setScannerState(SCANNER_STATE_PI);
        int piDataOffset = -1;
        int piDataLength = 0;
        if (!fEntityReader.lookingAtSpace(true)) {
            if (!fEntityReader.lookingAtChar('?', true) || !fEntityReader.lookingAtChar('>', true)) {
                if (fScannerState != SCANNER_STATE_END_OF_INPUT) {
                    abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_IN_PI,
                                XMLMessages.P16_WHITESPACE_REQUIRED);
                    restoreScannerState(prevState);
                }
                return;
            }
            decreaseMarkupDepth();
            restoreScannerState(prevState);
        } else {
            fEntityReader.skipPastSpaces();
            piDataOffset = fEntityReader.currentOffset();
            while (fScannerState == SCANNER_STATE_PI) {
                while (fEntityReader.lookingAtChar('?', false)) {
                    int offset = fEntityReader.currentOffset();
                    fEntityReader.lookingAtChar('?', true);
                    if (fEntityReader.lookingAtChar('>', true)) {
                        piDataLength = offset - piDataOffset;
                        decreaseMarkupDepth();
                        restoreScannerState(prevState);
                        break;
                    }
                }
                if (fScannerState != SCANNER_STATE_PI)
                    break;
                if (!fEntityReader.lookingAtValidChar(true)) {
                    int invChar = fEntityReader.scanInvalidChar();
                    if (fScannerState != SCANNER_STATE_END_OF_INPUT) {
                        if (invChar >= 0) {
                            reportFatalXMLError(XMLMessages.MSG_INVALID_CHAR_IN_PI,
                                                XMLMessages.P16_INVALID_CHARACTER,
                                                Integer.toHexString(invChar));
                        }
                        skipPastEndOfCurrentMarkup();
                        restoreScannerState(prevState);
                    }
                    return;
                }
            }
        }
        int piData = piDataLength == 0 ?
                     StringPool.EMPTY_STRING : fEntityReader.addString(piDataOffset, piDataLength);
        fDTDGrammar.callProcessingInstruction(piTarget, piData);
        if (fDTDHandler != null) {
            fDTDHandler.processingInstruction(piTarget, piData);
        }
    }
    //
    // From the standard:
    //
    // [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S?
    //                      ('[' (markupdecl | PEReference | S)* ']' S?)? '>'
    // [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl
    //                     | NotationDecl | PI | Comment
    //
    // Called after scanning '<!DOCTYPE'
    //
    /**
     * This routine is called after the &lt;!DOCTYPE portion of a DOCTYPE
     * line has been called.  scanDocTypeDecl goes onto scan the rest of the DOCTYPE
     * decl.  If an internal DTD subset exists, it is scanned. If an external DTD
     * subset exists, scanDocTypeDecl sets up the state necessary to process it.
     *
     * @return true if successful
     * @exception java.lang.Exception
     */
    public boolean scanDoctypeDecl() throws Exception
    {
        //System.out.println("XMLDTDScanner#scanDoctypeDecl()");

        fDTDGrammar = new DTDGrammar(fStringPool);
        fDTDGrammar.callStartDTD();
        increaseMarkupDepth();
        fEntityReader = fEntityHandler.getEntityReader();
        fReaderId = fEntityHandler.getReaderId();
        fDoctypeReader = fReaderId;
        setScannerState(SCANNER_STATE_DOCTYPEDECL);
        if (!fEntityReader.lookingAtSpace(true)) {
            abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_ROOT_ELEMENT_TYPE_IN_DOCTYPEDECL,
                        XMLMessages.P28_SPACE_REQUIRED);
            return false;
        }
        fEntityReader.skipPastSpaces();
        scanElementType(fEntityReader, ' ', fElementQName);
        if (fElementQName.rawname == -1) {
            abortMarkup(XMLMessages.MSG_ROOT_ELEMENT_TYPE_REQUIRED,
                        XMLMessages.P28_ROOT_ELEMENT_TYPE_REQUIRED);
            return false;
        }
        boolean lbrkt;
        boolean scanExternalSubset = false;
        int publicId = -1;
        int systemId = -1;
        if (fEntityReader.lookingAtSpace(true)) {
            fEntityReader.skipPastSpaces();
            if (!(lbrkt = fEntityReader.lookingAtChar('[', true)) && !fEntityReader.lookingAtChar('>', false)) {
                if (!scanExternalID(false)) {
                    skipPastEndOfCurrentMarkup();
                    return false;
                }
                scanExternalSubset = true;
                publicId = fPubidLiteral;
                systemId = fSystemLiteral;
                fEntityReader.skipPastSpaces();
                lbrkt = fEntityReader.lookingAtChar('[', true);
            }
        } else
            lbrkt = fEntityReader.lookingAtChar('[', true);
        fDTDGrammar.doctypeDecl(fElementQName, publicId, systemId);
        if (fDTDHandler != null) {
            fDTDHandler.startDTD(fElementQName, publicId, systemId);
        }
        if (lbrkt) {
            scanDecls(false);
            fEntityReader.skipPastSpaces();
        }
        if (!fEntityReader.lookingAtChar('>', true)) {
            if (fScannerState != SCANNER_STATE_END_OF_INPUT) {
                abortMarkup(XMLMessages.MSG_DOCTYPEDECL_UNTERMINATED,
                            XMLMessages.P28_UNTERMINATED,
                            fElementQName.rawname);
            }
            return false;
        }

        decreaseMarkupDepth();

        //System.out.println("  scanExternalSubset: "+scanExternalSubset);
        if (scanExternalSubset) {
            ((DefaultEntityHandler) fEntityHandler).startReadingFromExternalSubset( fStringPool.toString(publicId),
                                                                                    fStringPool.toString(systemId),
                                                                                    markupDepth());
            fDTDGrammar.startReadingFromExternalSubset(publicId, systemId);
        }
        else {
            fDTDGrammar.callEndDTD();
            if (fDTDHandler != null) {
                fDTDHandler.endDTD();
            }
        }

        fGrammarResolver.putGrammar("", fDTDGrammar);


        return true;
    }
    //
    // [75] ExternalID ::= 'SYSTEM' S SystemLiteral
    //                     | 'PUBLIC' S PubidLiteral S SystemLiteral
    // [83] PublicID ::= 'PUBLIC' S PubidLiteral
    //
    private boolean scanExternalID(boolean scanPublicID) throws Exception
    {
        fSystemLiteral = -1;
        fPubidLiteral = -1;
        int offset = fEntityReader.currentOffset();
        if (fEntityReader.skippedString(system_string)) {
            if (!fEntityReader.lookingAtSpace(true)) {
                reportFatalXMLError(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_SYSTEMLITERAL_IN_EXTERNALID,
                                    XMLMessages.P75_SPACE_REQUIRED);
                return false;
            }
            fEntityReader.skipPastSpaces();
            if( getReadingExternalEntity() == true ) {  //Are we in external subset?
               checkForPEReference(false);//If so Check for PE Ref
             }
            return scanSystemLiteral();
        }
        if (fEntityReader.skippedString(public_string)) {
            if (!fEntityReader.lookingAtSpace(true)) {
                reportFatalXMLError(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_PUBIDLITERAL_IN_EXTERNALID,
                                    XMLMessages.P75_SPACE_REQUIRED);
                return false;
            }
            fEntityReader.skipPastSpaces();
            if (!scanPubidLiteral())
                return false;
            if (scanPublicID) {
                //
                // [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID |  PublicID) S? '>'
                //
                if (!fEntityReader.lookingAtSpace(true))
                    return true; // no S, not an ExternalID
                fEntityReader.skipPastSpaces();
                if (fEntityReader.lookingAtChar('>', false)) // matches end of NotationDecl
                    return true;
            } else {
                if (!fEntityReader.lookingAtSpace(true)) {
                    reportFatalXMLError(XMLMessages.MSG_SPACE_REQUIRED_AFTER_PUBIDLITERAL_IN_EXTERNALID,
                                        XMLMessages.P75_SPACE_REQUIRED);
                    return false;
                }
                fEntityReader.skipPastSpaces();
            }
            return scanSystemLiteral();
        }
        reportFatalXMLError(XMLMessages.MSG_EXTERNALID_REQUIRED,
                            XMLMessages.P75_INVALID);
        return false;
    }
    //
    // [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
    //
    // REVISIT - need to look into uri escape mechanism for non-ascii characters.
    //
    private boolean scanSystemLiteral() throws Exception
    {
        boolean single;
        if (!(single = fEntityReader.lookingAtChar('\'', true)) && !fEntityReader.lookingAtChar('\"', true)) {
            reportFatalXMLError(XMLMessages.MSG_QUOTE_REQUIRED_IN_SYSTEMID,
                                XMLMessages.P11_QUOTE_REQUIRED);
            return false;
        }
        int prevState = setScannerState(SCANNER_STATE_SYSTEMLITERAL);
        int offset = fEntityReader.currentOffset();
        char qchar = single ? '\'' : '\"';
        boolean dataok = true;
        boolean fragment = false;
        while (!fEntityReader.lookingAtChar(qchar, false)) {
//ericye
//System.out.println("XMLDTDScanner#scanDoctypeDecl() 3333333, "+fReaderId+", " + fScannerState+", " +fExternalSubsetReader);
            if (fEntityReader.lookingAtChar('#', true)) {
                fragment = true;
            } else if (!fEntityReader.lookingAtValidChar(true)) {
//System.out.println("XMLDTDScanner#scanDoctypeDecl() 555555 scan state: " + fScannerState);
                dataok = false;
                int invChar = fEntityReader.scanInvalidChar();
                if (fScannerState == SCANNER_STATE_END_OF_INPUT)
                    return false;
                if (invChar >= 0) {
                    reportFatalXMLError(XMLMessages.MSG_INVALID_CHAR_IN_SYSTEMID,
                                        XMLMessages.P11_INVALID_CHARACTER,
                                        Integer.toHexString(invChar));
                }
            }
        }
        if (dataok) {
            fSystemLiteral = fEntityReader.addString(offset, fEntityReader.currentOffset() - offset);
            if (fragment) {
                // NOTE: RECOVERABLE ERROR
                Object[] args = { fStringPool.toString(fSystemLiteral) };
                fErrorReporter.reportError(fErrorReporter.getLocator(),
                                           XMLMessages.XML_DOMAIN,
                                           XMLMessages.MSG_URI_FRAGMENT_IN_SYSTEMID,
                                           XMLMessages.P11_URI_FRAGMENT,
                                           args,
                                           XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
            }
        }
        fEntityReader.lookingAtChar(qchar, true);
        restoreScannerState(prevState);
        return dataok;
    }
    //
    // [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
    // [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
    //
    private boolean scanPubidLiteral() throws Exception
    {
        boolean single;
        if (!(single = fEntityReader.lookingAtChar('\'', true)) && !fEntityReader.lookingAtChar('\"', true)) {
            reportFatalXMLError(XMLMessages.MSG_QUOTE_REQUIRED_IN_PUBLICID,
                                XMLMessages.P12_QUOTE_REQUIRED);
            return false;
        }
        char qchar = single ? '\'' : '\"';
        int prevState = setScannerState(SCANNER_STATE_PUBIDLITERAL);
        boolean dataok = true;
        while (true) {
            if (fEntityReader.lookingAtChar((char)0x09, true)) {
                dataok = false;
                reportFatalXMLError(XMLMessages.MSG_PUBIDCHAR_ILLEGAL,
                                    XMLMessages.P12_INVALID_CHARACTER, "9");
            }
            if (!fEntityReader.lookingAtSpace(true))
                break;
        }
        int offset = fEntityReader.currentOffset();
        int dataOffset = fLiteralData.length();
        int toCopy = offset;
        while (true) {
            if (fEntityReader.lookingAtChar(qchar, true)) {
                if (dataok && offset - toCopy > 0)
                    fEntityReader.append(fLiteralData, toCopy, offset - toCopy);
                break;
            }
            if (fEntityReader.lookingAtChar((char)0x09, true)) {
                dataok = false;
                reportFatalXMLError(XMLMessages.MSG_PUBIDCHAR_ILLEGAL,
                                    XMLMessages.P12_INVALID_CHARACTER, "9");
                continue;
            }
            if (fEntityReader.lookingAtSpace(true)) {
                if (dataok && offset - toCopy > 0)
                    fEntityReader.append(fLiteralData, toCopy, offset - toCopy);
                while (true) {
                    if (fEntityReader.lookingAtChar((char)0x09, true)) {
                        dataok = false;
                        reportFatalXMLError(XMLMessages.MSG_PUBIDCHAR_ILLEGAL,
                                            XMLMessages.P12_INVALID_CHARACTER, "9");
                        break;
                    } else if (!fEntityReader.lookingAtSpace(true)) {
                        break;
                    }
                }
                if (fEntityReader.lookingAtChar(qchar, true))
                    break;
                if (dataok) {
                    fLiteralData.append(' ');
                    offset = fEntityReader.currentOffset();
                    toCopy = offset;
                }
                continue;
            }
            if (!fEntityReader.lookingAtValidChar(true)) {
                int invChar = fEntityReader.scanInvalidChar();
                if (fScannerState == SCANNER_STATE_END_OF_INPUT)
                    return false;
                dataok = false;
                if (invChar >= 0) {
                    reportFatalXMLError(XMLMessages.MSG_INVALID_CHAR_IN_PUBLICID,
                                        XMLMessages.P12_INVALID_CHARACTER,
                                        Integer.toHexString(invChar));
                }
            }
            if (dataok)
                offset = fEntityReader.currentOffset();
        }
        if (dataok) {
            int dataLength = fLiteralData.length() - dataOffset;
            fPubidLiteral = fLiteralData.addString(dataOffset, dataLength);
            String publicId = fStringPool.toString(fPubidLiteral);
            int invCharIndex = validPublicId(publicId);
            if (invCharIndex >= 0) {
                reportFatalXMLError(XMLMessages.MSG_PUBIDCHAR_ILLEGAL,
                                    XMLMessages.P12_INVALID_CHARACTER,
                                    Integer.toHexString(publicId.charAt(invCharIndex)));
                return false;
            }
        }
        restoreScannerState(prevState);
        return dataok;
    }
    //
    // [??] intSubsetDecl = '[' (markupdecl | PEReference | S)* ']'
    //
    // [31] extSubsetDecl ::= ( markupdecl | conditionalSect | PEReference | S )*
    // [62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>'
    //
    // [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl
    //                     | NotationDecl | PI | Comment
    //
    // [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'
    //
    // [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
    //
    // [70] EntityDecl ::= GEDecl | PEDecl
    // [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
    // [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
    //
    // [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID |  PublicID) S? '>'
    //
    // [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
    //
    // [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
    //
    // [61] conditionalSect ::= includeSect | ignoreSect
    // [62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>'
    // [63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
    // [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)*
    // [65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*)
    //
    /**
     * Scan markup declarations
     *
     * @param extSubset true if the scanner is scanning an external subset, false
     *                  if it is scanning an internal subset
     * @exception java.lang.Exception
     */
    public void scanDecls(boolean extSubset) throws Exception
    {
        int subsetOffset = fEntityReader.currentOffset();
        if (extSubset)
            fExternalSubsetReader = fReaderId;
        fIncludeSectDepth = 0;
        boolean parseTextDecl = extSubset;
        int prevState = setScannerState(SCANNER_STATE_MARKUP_DECL);
        while (fScannerState == SCANNER_STATE_MARKUP_DECL) {

            boolean newParseTextDecl = false;
            if (!extSubset && fEntityReader.lookingAtChar(']', false)) {
                int subsetLength = fEntityReader.currentOffset() - subsetOffset;
                int internalSubset = fEntityReader.addString(subsetOffset, subsetLength);
                fDTDGrammar.internalSubset(internalSubset);
                if (fDTDHandler != null) {
                    fDTDHandler.internalSubset(internalSubset);
                }
                fEntityReader.lookingAtChar(']', true);
                restoreScannerState(prevState);
                return;
            }
            if (fEntityReader.lookingAtChar('<', true)) {
                int olddepth = markupDepth();
                increaseMarkupDepth();
                if (fEntityReader.lookingAtChar('!', true)) {
                    if (fEntityReader.lookingAtChar('-', true)) {
                        if (fEntityReader.lookingAtChar('-', true)) {
                            scanComment();
                        } else {
                            abortMarkup(XMLMessages.MSG_MARKUP_NOT_RECOGNIZED_IN_DTD,
                                        XMLMessages.P29_NOT_RECOGNIZED);
                        }
                    } else if (fEntityReader.lookingAtChar('[', true) && getReadingExternalEntity()) {
                        checkForPEReference(false);
                        if (fEntityReader.skippedString(include_string)) {
                            checkForPEReference(false);
                            if (!fEntityReader.lookingAtChar('[', true)) {
                                abortMarkup(XMLMessages.MSG_MARKUP_NOT_RECOGNIZED_IN_DTD,
                                            XMLMessages.P29_NOT_RECOGNIZED);
                            } else {
                                fIncludeSectDepth++;
                            }
                        } else if (fEntityReader.skippedString(ignore_string)) {
                            checkForPEReference(false);
                            if (!fEntityReader.lookingAtChar('[', true)) {
                                abortMarkup(XMLMessages.MSG_MARKUP_NOT_RECOGNIZED_IN_DTD,
                                            XMLMessages.P29_NOT_RECOGNIZED);
                            } else
                                scanIgnoreSectContents();
                        } else {
                            abortMarkup(XMLMessages.MSG_MARKUP_NOT_RECOGNIZED_IN_DTD,
                                        XMLMessages.P29_NOT_RECOGNIZED);
                        }
                    } else if (fEntityReader.skippedString(element_string)) {
                        scanElementDecl();
                    }
                    else if (fEntityReader.skippedString(attlist_string))
                        scanAttlistDecl();
                    else if (fEntityReader.skippedString(entity_string))
                        scanEntityDecl();
                    else if (fEntityReader.skippedString(notation_string))
                        scanNotationDecl();
                    else {
                        abortMarkup(XMLMessages.MSG_MARKUP_NOT_RECOGNIZED_IN_DTD,
                                    XMLMessages.P29_NOT_RECOGNIZED);
                    }
                } else if (fEntityReader.lookingAtChar('?', true)) {
                    int piTarget = fEntityReader.scanName(' ');
                    if (piTarget == -1) {
                        abortMarkup(XMLMessages.MSG_PITARGET_REQUIRED,
                                    XMLMessages.P16_REQUIRED);
                    } else if ("xml".equals(fStringPool.toString(piTarget))) {
                        if (fEntityReader.lookingAtSpace(true)) {
                            if (parseTextDecl) { // a TextDecl looks like a PI with the target 'xml'
                                scanTextDecl();
                            } else {
                                abortMarkup(XMLMessages.MSG_TEXTDECL_MUST_BE_FIRST,
                                            XMLMessages.P30_TEXTDECL_MUST_BE_FIRST);
                            }
                        } else { // a PI target matching 'xml'
                            abortMarkup(XMLMessages.MSG_RESERVED_PITARGET,
                                        XMLMessages.P17_RESERVED_PITARGET);
                        }
                    } else // PI
                        scanPI(piTarget);
                } else {
                    abortMarkup(XMLMessages.MSG_MARKUP_NOT_RECOGNIZED_IN_DTD,
                                XMLMessages.P29_NOT_RECOGNIZED);
                }
            } else if (fEntityReader.lookingAtSpace(true)) {
                fEntityReader.skipPastSpaces();
            } else if (fEntityReader.lookingAtChar('%', true)) {
                //
                // [69] PEReference ::= '%' Name ';'
                //
                int nameOffset = fEntityReader.currentOffset();
                fEntityReader.skipPastName(';');
                int nameLength = fEntityReader.currentOffset() - nameOffset;
                if (nameLength == 0) {
                    reportFatalXMLError(XMLMessages.MSG_NAME_REQUIRED_IN_PEREFERENCE,
                                        XMLMessages.P69_NAME_REQUIRED);
                } else if (!fEntityReader.lookingAtChar(';', true)) {
                    reportFatalXMLError(XMLMessages.MSG_SEMICOLON_REQUIRED_IN_PEREFERENCE,
                                        XMLMessages.P69_SEMICOLON_REQUIRED,
                                        fEntityReader.addString(nameOffset, nameLength));
                } else {
                    int peNameIndex = fEntityReader.addSymbol(nameOffset, nameLength);
                    newParseTextDecl = fEntityHandler.startReadingFromEntity(peNameIndex, markupDepth(), XMLEntityHandler.ENTITYREF_IN_DTD_AS_MARKUP);
                }
            } else if (fIncludeSectDepth > 0 && fEntityReader.lookingAtChar(']', true)) {
                if (!fEntityReader.lookingAtChar(']', true) || !fEntityReader.lookingAtChar('>', true)) {
                    abortMarkup(XMLMessages.MSG_INCLUDESECT_UNTERMINATED,
                                XMLMessages.P62_UNTERMINATED);
                } else
                    decreaseMarkupDepth();
                fIncludeSectDepth--;
            } else {
                if (!fEntityReader.lookingAtValidChar(false)) {
                    int invChar = fEntityReader.scanInvalidChar();
                    if (fScannerState == SCANNER_STATE_END_OF_INPUT)
                        break;
                    if (invChar >= 0) {
                        if (!extSubset) {
                            reportFatalXMLError(XMLMessages.MSG_INVALID_CHAR_IN_INTERNAL_SUBSET,
                                                XMLMessages.P28_INVALID_CHARACTER,
                                                Integer.toHexString(invChar));
                        } else {
                            reportFatalXMLError(XMLMessages.MSG_INVALID_CHAR_IN_EXTERNAL_SUBSET,
                                                XMLMessages.P30_INVALID_CHARACTER,
                                                Integer.toHexString(invChar));
                        }
                    }
                } else {
                    reportFatalXMLError(XMLMessages.MSG_MARKUP_NOT_RECOGNIZED_IN_DTD,
                                        XMLMessages.P29_NOT_RECOGNIZED);
                    fEntityReader.lookingAtValidChar(true);
                }
            }
            parseTextDecl = newParseTextDecl;
        }
        if (extSubset) {

            ((DefaultEntityHandler) fEntityHandler).stopReadingFromExternalSubset();

            fDTDGrammar.stopReadingFromExternalSubset();
            fDTDGrammar.callEndDTD();
            if (fDTDHandler != null) {
                fDTDHandler.endDTD();
            }
            // REVISIT: What should the namspace URI of a DTD be?
            fGrammarResolver.putGrammar("", fDTDGrammar);
        }
    }
    //
    // [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)*
    // [65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*)
    //
    private void scanIgnoreSectContents() throws Exception
    {
        int initialDepth = ++fIncludeSectDepth;
        while (true) {
            if (fEntityReader.lookingAtChar('<', true)) {
                //
                // These tests are split so that we handle cases like
                // '<<![' and '<!<![' which we might otherwise miss.
                //
                if (fEntityReader.lookingAtChar('!', true) && fEntityReader.lookingAtChar('[', true))
                    fIncludeSectDepth++;
            } else if (fEntityReader.lookingAtChar(']', true)) {
                //
                // The same thing goes for ']<![' and '<]]>', etc.
                //
                if (fEntityReader.lookingAtChar(']', true)) {
                    while (fEntityReader.lookingAtChar(']', true)) {
                        /* empty loop body */
                    }
                    if (fEntityReader.lookingAtChar('>', true)) {
                        if (fIncludeSectDepth-- == initialDepth) {
                            decreaseMarkupDepth();
                            return;
                        }
                    }
                }
            } else if (!fEntityReader.lookingAtValidChar(true)) {
                int invChar = fEntityReader.scanInvalidChar();
                if (fScannerState == SCANNER_STATE_END_OF_INPUT)
                    return;
                if (invChar >= 0) {
                    reportFatalXMLError(XMLMessages.MSG_INVALID_CHAR_IN_IGNORESECT,
                                        XMLMessages.P65_INVALID_CHARACTER,
                                        Integer.toHexString(invChar));
                }
            }
        }
    }
    //
    // From the standard:
    //
    // [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>'
    // [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ")
    // [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' |  "'" EncName "'" )
    // [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
    //
    private void scanTextDecl() throws Exception {
        int version = -1;
        int encoding = -1;
        final int TEXTDECL_START = 0;
        final int TEXTDECL_VERSION = 1;
        final int TEXTDECL_ENCODING = 2;
        final int TEXTDECL_FINISHED = 3;
        int prevState = setScannerState(SCANNER_STATE_TEXTDECL);
        int state = TEXTDECL_START;
        do {
            fEntityReader.skipPastSpaces();
            int offset = fEntityReader.currentOffset();
            if (state == TEXTDECL_START && fEntityReader.skippedString(version_string)) {
                state = TEXTDECL_VERSION;
            } else if (fEntityReader.skippedString(encoding_string)) {
                state = TEXTDECL_ENCODING;
            } else {
                abortMarkup(XMLMessages.MSG_ENCODINGDECL_REQUIRED,
                            XMLMessages.P77_ENCODINGDECL_REQUIRED);
                restoreScannerState(prevState);
                return;
            }
            int length = fEntityReader.currentOffset() - offset;
            fEntityReader.skipPastSpaces();
            if (!fEntityReader.lookingAtChar('=', true)) {
                int minorCode = state == TEXTDECL_VERSION ?
                                XMLMessages.P24_EQ_REQUIRED :
                                XMLMessages.P80_EQ_REQUIRED;
                abortMarkup(XMLMessages.MSG_EQ_REQUIRED_IN_TEXTDECL, minorCode,
                            fEntityReader.addString(offset, length));
                restoreScannerState(prevState);
                return;
            }
            fEntityReader.skipPastSpaces();
            int result = fEntityReader.scanStringLiteral();
            switch (result) {
            case XMLEntityHandler.STRINGLIT_RESULT_QUOTE_REQUIRED:
            {
                int minorCode = state == TEXTDECL_VERSION ?
                                XMLMessages.P24_QUOTE_REQUIRED :
                                XMLMessages.P80_QUOTE_REQUIRED;
                abortMarkup(XMLMessages.MSG_QUOTE_REQUIRED_IN_TEXTDECL, minorCode,
                            fEntityReader.addString(offset, length));
                restoreScannerState(prevState);
                return;
            }
            case XMLEntityHandler.STRINGLIT_RESULT_INVALID_CHAR:
                int invChar = fEntityReader.scanInvalidChar();
                if (fScannerState != SCANNER_STATE_END_OF_INPUT) {
                    if (invChar >= 0) {
                        int minorCode = state == TEXTDECL_VERSION ?
                                        XMLMessages.P26_INVALID_CHARACTER :
                                        XMLMessages.P81_INVALID_CHARACTER;
                        reportFatalXMLError(XMLMessages.MSG_INVALID_CHAR_IN_TEXTDECL, minorCode,
                                            Integer.toHexString(invChar));
                    }
                    skipPastEndOfCurrentMarkup();
                    restoreScannerState(prevState);
                }
                return;
            default:
                break;
            }
            switch (state) {
            case TEXTDECL_VERSION:
                //
                // version="..."
                //
                version = result;
                String versionString = fStringPool.toString(version);
                if (!"1.0".equals(versionString)) {
                    if (!validVersionNum(versionString)) {
                        abortMarkup(XMLMessages.MSG_VERSIONINFO_INVALID,
                                    XMLMessages.P26_INVALID_VALUE,
                                    versionString);
                        restoreScannerState(prevState);
                        return;
                    }
                    // NOTE: RECOVERABLE ERROR
                    Object[] args = { versionString };
                    fErrorReporter.reportError(fErrorReporter.getLocator(),
                                               XMLMessages.XML_DOMAIN,
                                               XMLMessages.MSG_VERSION_NOT_SUPPORTED,
                                               XMLMessages.P26_NOT_SUPPORTED,
                                               args,
                                               XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
                    // REVISIT - hope it is a compatible version...
                    // skipPastEndOfCurrentMarkup();
                    // return;
                }
                if (!fEntityReader.lookingAtSpace(true)) {
                    abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_IN_TEXTDECL,
                                XMLMessages.P80_WHITESPACE_REQUIRED);
                    restoreScannerState(prevState);
                    return;
                }
                break;
            case TEXTDECL_ENCODING:
                //
                // encoding = "..."
                //
                encoding = result;
                String encodingString = fStringPool.toString(encoding);
                if (!validEncName(encodingString)) {
                    abortMarkup(XMLMessages.MSG_ENCODINGDECL_INVALID,
                                XMLMessages.P81_INVALID_VALUE,
                                encodingString);
                    restoreScannerState(prevState);
                    return;
                }
                fEntityReader.skipPastSpaces();
                state = TEXTDECL_FINISHED;
                break;
            }
        } while (state != TEXTDECL_FINISHED);
        if (!fEntityReader.lookingAtChar('?', true) || !fEntityReader.lookingAtChar('>', true)) {
            abortMarkup(XMLMessages.MSG_TEXTDECL_UNTERMINATED,
                        XMLMessages.P77_UNTERMINATED);
            restoreScannerState(prevState);
            return;
        }
        decreaseMarkupDepth();
        fDTDGrammar.callTextDecl(version, encoding);
        if (fDTDHandler != null) {
            fDTDHandler.textDecl(version, encoding);
        }
        restoreScannerState(prevState);
    }

    private QName fElementDeclQName = new QName();

    /**
     * Scans an element declaration.
     * <pre>
     * [45] elementdecl ::= '&lt;!ELEMENT' S Name S contentspec S? '&gt;'
     * </pre>
     */
    private void scanElementDecl() throws Exception {
       
        if (!checkForPEReference(true)) {
            abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL,
                        XMLMessages.P45_SPACE_REQUIRED);
            return;
        }
        checkForElementTypeWithPEReference(fEntityReader, ' ', fElementQName);
        if (fElementQName.rawname == -1) {
            abortMarkup(XMLMessages.MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL,
                        XMLMessages.P45_ELEMENT_TYPE_REQUIRED);
            return;
        }
        if (fDTDHandler != null) {
            fElementDeclQName.setValues(fElementQName);
        }
        if (!checkForPEReference(true)) {
            abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_CONTENTSPEC_IN_ELEMENTDECL,
                        XMLMessages.P45_SPACE_REQUIRED,
                        fElementQName.rawname);
            return;
        }
        int contentSpecType = -1;
        int contentSpec = -1;
        if (fEntityReader.skippedString(empty_string)) {
            contentSpecType = XMLElementDecl.TYPE_EMPTY;
        } else if (fEntityReader.skippedString(any_string)) {
            contentSpecType = XMLElementDecl.TYPE_ANY;
        } else if (!fEntityReader.lookingAtChar('(', true)) {
            abortMarkup(XMLMessages.MSG_CONTENTSPEC_REQUIRED_IN_ELEMENTDECL,
                        XMLMessages.P45_CONTENTSPEC_REQUIRED,
                        fElementQName.rawname);
            return;
        } else {
            int contentSpecReader = fReaderId;
            int contentSpecReaderDepth = fEntityHandler.getReaderDepth();
            int prevState = setScannerState(SCANNER_STATE_CONTENTSPEC);
            int oldDepth = parenDepth();
            fEntityHandler.setReaderDepth(oldDepth);
            increaseParenDepth();
            checkForPEReference(false);
            boolean skippedPCDATA = fEntityReader.skippedString(pcdata_string);
            if (skippedPCDATA) {
                contentSpecType = XMLElementDecl.TYPE_MIXED;
                // REVISIT: Validation. Should we pass in QName?
                contentSpec = scanMixed(fElementQName);
            } else {
                contentSpecType = XMLElementDecl.TYPE_CHILDREN;
                // REVISIT: Validation. Should we pass in QName?
                contentSpec = scanChildren(fElementQName);
            }
            boolean success = contentSpec != -1;
            restoreScannerState(prevState);
            fEntityHandler.setReaderDepth(contentSpecReaderDepth);
            if (!success) {
                setParenDepth(oldDepth);
                skipPastEndOfCurrentMarkup();
                return;
            } else {
                if (parenDepth() != oldDepth) // REVISIT - should not be needed
                    // System.out.println("nesting depth mismatch");
                    ;
            }
        }
        checkForPEReference(false);
        if (!fEntityReader.lookingAtChar('>', true)) {
            abortMarkup(XMLMessages.MSG_ELEMENTDECL_UNTERMINATED,
                        XMLMessages.P45_UNTERMINATED,
                        fElementQName.rawname);
            return;
        }
        decreaseMarkupDepth();
        int elementIndex = fDTDGrammar.getElementDeclIndex(fElementQName, -1);
        boolean elementDeclIsExternal = getReadingExternalEntity();
        if (elementIndex == -1) {
            elementIndex = fDTDGrammar.addElementDecl(fElementQName, contentSpecType, contentSpec, elementDeclIsExternal);
            //System.out.println("XMLDTDScanner#scanElementDecl->DTDGrammar#addElementDecl: "+elementIndex+" ("+fElementQName.localpart+","+fStringPool.toString(fElementQName.localpart)+')');
        }
        else {
            //now check if we already add this element Decl by foward reference
            fDTDGrammar.getElementDecl(elementIndex, fTempElementDecl);
            if (fTempElementDecl.type == -1) {
                fTempElementDecl.type = contentSpecType;
                fTempElementDecl.contentSpecIndex = contentSpec;
                fDTDGrammar.setElementDeclDTD(elementIndex, fTempElementDecl);
                fDTDGrammar.setElementDeclIsExternal(elementIndex, elementDeclIsExternal);
            }
            else {
                //REVISIT, valiate VC duplicate element type.
                if ( fValidationEnabled )
                    //&&
                    // (elemenetDeclIsExternal==fDTDGrammar.getElementDeclIsExternal(elementIndex)
                {

                    reportRecoverableXMLError(
                        XMLMessages.MSG_ELEMENT_ALREADY_DECLARED,
                        XMLMessages.VC_UNIQUE_ELEMENT_TYPE_DECLARATION,
                        fStringPool.toString(fElementQName.rawname)
                        );
                }
            }
        }
        if (fDTDHandler != null) {
            fDTDGrammar.getElementDecl(elementIndex, fTempElementDecl);
            fDTDHandler.elementDecl(fElementDeclQName, contentSpecType, contentSpec, fDTDGrammar);
        }

    } // scanElementDecl()

   
    /**
     * Scans mixed content model. Called after scanning past '(' S? '#PCDATA'
     * <pre>
     * [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')'
     * </pre>
     */
    private int scanMixed(QName element) throws Exception {

        int valueIndex = -1// -1 is special value for #PCDATA
        int prevNodeIndex = -1;
        boolean starRequired = false;
        int[] valueSeen = new int[32];
        int valueCount = 0;
        boolean dupAttrType = false;
        int nodeIndex = -1;

        while (true) {
            if (fValidationEnabled) {
                for (int i=0; i<valueCount;i++) {
                    if ( valueSeen[i] == valueIndex) {
                        dupAttrType = true;
                        break;
                    }
                }
            }
            if (dupAttrType && fValidationEnabled) {
                reportRecoverableXMLError(XMLMessages.MSG_DUPLICATE_TYPE_IN_MIXED_CONTENT,
                                          XMLMessages.VC_NO_DUPLICATE_TYPES,
                                          valueIndex);
                dupAttrType = false;

            }
            else {
                try {
                    valueSeen[valueCount] = valueIndex;
                }
                catch (ArrayIndexOutOfBoundsException ae) {
                    int[] newArray = new int[valueSeen.length*2];
                    System.arraycopy(valueSeen,0,newArray,0,valueSeen.length);
                    valueSeen = newArray;
                    valueSeen[valueCount] = valueIndex;
                }
                valueCount++;

                nodeIndex = fDTDGrammar.addUniqueLeafNode(valueIndex);
            }

            checkForPEReference(false);
            if (!fEntityReader.lookingAtChar('|', true)) {
                if (!fEntityReader.lookingAtChar(')', true)) {
                    reportFatalXMLError(XMLMessages.MSG_CLOSE_PAREN_REQUIRED_IN_MIXED,
                                        XMLMessages.P51_CLOSE_PAREN_REQUIRED,
                                        element.rawname);
                    return -1;
                }
                decreaseParenDepth();
                if (nodeIndex == -1) {
                    nodeIndex = prevNodeIndex;
                } else if (prevNodeIndex != -1) {
                    nodeIndex = fDTDGrammar.addContentSpecNode(XMLContentSpec.CONTENTSPECNODE_CHOICE, prevNodeIndex, nodeIndex);
                }
                if (fEntityReader.lookingAtChar('*', true)) {
                    nodeIndex = fDTDGrammar.addContentSpecNode(XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE, nodeIndex);
                } else if (starRequired) {
                    reportFatalXMLError(XMLMessages.MSG_MIXED_CONTENT_UNTERMINATED,
                                        XMLMessages.P51_UNTERMINATED,
                                        fStringPool.toString(element.rawname),
                                        fDTDGrammar.getContentSpecNodeAsString(nodeIndex));
                    return -1;
                }
                return nodeIndex;
            }
            if (nodeIndex != -1) {
                if (prevNodeIndex != -1) {
                    nodeIndex = fDTDGrammar.addContentSpecNode(XMLContentSpec.CONTENTSPECNODE_CHOICE, prevNodeIndex, nodeIndex);
                }
                prevNodeIndex = nodeIndex;
            }
            starRequired = true;
            checkForPEReference(false);
            checkForElementTypeWithPEReference(fEntityReader, ')', fElementRefQName);
            valueIndex = fElementRefQName.rawname;
            if (valueIndex == -1) {
                reportFatalXMLError(XMLMessages.MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT,
                                    XMLMessages.P51_ELEMENT_TYPE_REQUIRED,
                                    element.rawname);
                return -1;
            }
        }

    } // scanMixed(QName):int
   
    /**
     * Scans a children content model.
     * <pre>
     * [47] children ::= (choice | seq) ('?' | '*' | '+')?
     * [49] choice ::= '(' S? cp ( S? '|' S? cp )* S? ')'
     * [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')'
     * [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
     * </pre>
     */
    private int scanChildren(QName element) throws Exception {
       
        int depth = 1;
        initializeContentModelStack(depth);
        while (true) {
            if (fEntityReader.lookingAtChar('(', true)) {
                increaseParenDepth();
                checkForPEReference(false);
                depth++;
                initializeContentModelStack(depth);
                continue;
            }
            checkForElementTypeWithPEReference(fEntityReader, ')', fElementRefQName);
            int valueIndex = fElementRefQName.rawname;
            if (valueIndex == -1) {
                reportFatalXMLError(XMLMessages.MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN,
                                    XMLMessages.P47_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED,
                                    element.rawname);
                return -1;
            }
            fNodeIndexStack[depth] = fDTDGrammar.addContentSpecNode(XMLContentSpec.CONTENTSPECNODE_LEAF, valueIndex);
            if (fEntityReader.lookingAtChar('?', true)) {
                fNodeIndexStack[depth] = fDTDGrammar.addContentSpecNode(XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE, fNodeIndexStack[depth]);
            } else if (fEntityReader.lookingAtChar('*', true)) {
                fNodeIndexStack[depth] = fDTDGrammar.addContentSpecNode(XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE, fNodeIndexStack[depth]);
            } else if (fEntityReader.lookingAtChar('+', true)) {
                fNodeIndexStack[depth] = fDTDGrammar.addContentSpecNode(XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE, fNodeIndexStack[depth]);
            }
            while (true) {
                checkForPEReference(false);
                if (fOpStack[depth] != XMLContentSpec.CONTENTSPECNODE_SEQ && fEntityReader.lookingAtChar('|', true)) {
                    if (fPrevNodeIndexStack[depth] != -1) {
                        fNodeIndexStack[depth] = fDTDGrammar.addContentSpecNode(fOpStack[depth], fPrevNodeIndexStack[depth], fNodeIndexStack[depth]);
                    }
                    fPrevNodeIndexStack[depth] = fNodeIndexStack[depth];
                    fOpStack[depth] = XMLContentSpec.CONTENTSPECNODE_CHOICE;
                    break;
                } else if (fOpStack[depth] != XMLContentSpec.CONTENTSPECNODE_CHOICE && fEntityReader.lookingAtChar(',', true)) {
                    if (fPrevNodeIndexStack[depth] != -1) {
                        fNodeIndexStack[depth] = fDTDGrammar.addContentSpecNode(fOpStack[depth], fPrevNodeIndexStack[depth], fNodeIndexStack[depth]);
                    }
                    fPrevNodeIndexStack[depth] = fNodeIndexStack[depth];
                    fOpStack[depth] = XMLContentSpec.CONTENTSPECNODE_SEQ;
                    break;
                } else {
                    if (!fEntityReader.lookingAtChar(')', true)) {
                        reportFatalXMLError(XMLMessages.MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN,
                                            XMLMessages.P47_CLOSE_PAREN_REQUIRED,
                                            element.rawname);
                    }
                    decreaseParenDepth();
                    if (fPrevNodeIndexStack[depth] != -1) {
                        fNodeIndexStack[depth] = fDTDGrammar.addContentSpecNode(fOpStack[depth], fPrevNodeIndexStack[depth], fNodeIndexStack[depth]);
                    }
                    int nodeIndex = fNodeIndexStack[depth--];
                    fNodeIndexStack[depth] = nodeIndex;
                    if (fEntityReader.lookingAtChar('?', true)) {
                        fNodeIndexStack[depth] = fDTDGrammar.addContentSpecNode(XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE, fNodeIndexStack[depth]);
                    } else if (fEntityReader.lookingAtChar('*', true)) {
                        fNodeIndexStack[depth] = fDTDGrammar.addContentSpecNode(XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE, fNodeIndexStack[depth]);
                    } else if (fEntityReader.lookingAtChar('+', true)) {
                        fNodeIndexStack[depth] = fDTDGrammar.addContentSpecNode(XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE, fNodeIndexStack[depth]);
                    }
                    if (depth == 0) {
                        return fNodeIndexStack[0];
                    }
                }
            }
            checkForPEReference(false);
        }

    } // scanChildren(QName):int

    //
    // [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
    // [53] AttDef ::= S Name S AttType S DefaultDecl
    // [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
    //
    private void scanAttlistDecl() throws Exception
    {
        if (!checkForPEReference(true)) {
            abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL,
                        XMLMessages.P52_SPACE_REQUIRED);
            return;
        }
        checkForElementTypeWithPEReference(fEntityReader, ' ', fElementQName);
        int elementTypeIndex = fElementQName.rawname;
        if (elementTypeIndex == -1) {
            abortMarkup(XMLMessages.MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL,
                        XMLMessages.P52_ELEMENT_TYPE_REQUIRED);
            return;
        }
        int elementIndex = fDTDGrammar.getElementDeclIndex(fElementQName, -1);
        if (elementIndex == -1) {
            elementIndex = fDTDGrammar.addElementDecl(fElementQName);
            //System.out.println("XMLDTDScanner#scanAttListDecl->DTDGrammar#addElementDecl: "+elementIndex+" ("+fElementQName.localpart+","+fStringPool.toString(fElementQName.localpart)+')');
        }
        boolean sawSpace = checkForPEReference(true);
        if (fEntityReader.lookingAtChar('>', true)) {
            decreaseMarkupDepth();
            return;
        }
        // REVISIT - review this code...
        if (!sawSpace) {
            if (fEntityReader.lookingAtSpace(true)) {
                fEntityReader.skipPastSpaces();
            } else
                reportFatalXMLError(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_ATTRIBUTE_NAME_IN_ATTDEF,
                                    XMLMessages.P53_SPACE_REQUIRED);
        } else {
            if (fEntityReader.lookingAtSpace(true)) {
                fEntityReader.skipPastSpaces();
            }
        }
        if (fEntityReader.lookingAtChar('>', true)) {
            decreaseMarkupDepth();
            return;
        }
        while (true) {
            checkForAttributeNameWithPEReference(fEntityReader, ' ', fAttributeQName);
            int attDefName = fAttributeQName.rawname;
            if (attDefName == -1) {
                abortMarkup(XMLMessages.MSG_ATTRIBUTE_NAME_REQUIRED_IN_ATTDEF,
                            XMLMessages.P53_NAME_REQUIRED,
                            fElementQName.rawname);
                return;
            }
            if (!checkForPEReference(true)) {
                abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_ATTTYPE_IN_ATTDEF,
                            XMLMessages.P53_SPACE_REQUIRED);
                return;
            }
            int attDefType = -1;
            boolean attDefList = false;
            int attDefEnumeration = -1;
            if (fEntityReader.skippedString(cdata_string)) {
                attDefType = XMLAttributeDecl.TYPE_CDATA;
            } else if (fEntityReader.skippedString(id_string)) {
                if (!fEntityReader.skippedString(ref_string)) {
                    attDefType = XMLAttributeDecl.TYPE_ID;
                } else if (!fEntityReader.lookingAtChar('S', true)) {
                    attDefType = XMLAttributeDecl.TYPE_IDREF;
                } else {
                    attDefType = XMLAttributeDecl.TYPE_IDREF;
                    attDefList = true;
                }
            } else if (fEntityReader.skippedString(entit_string)) {
                if (fEntityReader.lookingAtChar('Y', true)) {
                    attDefType = XMLAttributeDecl.TYPE_ENTITY;
                } else if (fEntityReader.skippedString(ies_string)) {
                    attDefType = XMLAttributeDecl.TYPE_ENTITY;
                    attDefList = true;
                } else {
                    abortMarkup(XMLMessages.MSG_ATTTYPE_REQUIRED_IN_ATTDEF,
                                XMLMessages.P53_ATTTYPE_REQUIRED,
                                elementTypeIndex, attDefName);
                    return;
                }
            } else if (fEntityReader.skippedString(nmtoken_string)) {
                if (fEntityReader.lookingAtChar('S', true)) {
                    attDefType = XMLAttributeDecl.TYPE_NMTOKEN;
                    attDefList = true;
                } else {
                    attDefType = XMLAttributeDecl.TYPE_NMTOKEN;
                }
            } else if (fEntityReader.skippedString(notation_string)) {
                if (!checkForPEReference(true)) {
                    abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_AFTER_NOTATION_IN_NOTATIONTYPE,
                                XMLMessages.P58_SPACE_REQUIRED,
                                elementTypeIndex, attDefName);
                    return;
                }
                if (!fEntityReader.lookingAtChar('(', true)) {
                    abortMarkup(XMLMessages.MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE,
                                XMLMessages.P58_OPEN_PAREN_REQUIRED,
                                elementTypeIndex, attDefName);
                    return;
                }
                increaseParenDepth();
                attDefType = XMLAttributeDecl.TYPE_NOTATION;
                attDefEnumeration = scanEnumeration(elementTypeIndex, attDefName, true);
                if (attDefEnumeration == -1) {
                    skipPastEndOfCurrentMarkup();
                    return;
                }
            } else if (fEntityReader.lookingAtChar('(', true)) {
                increaseParenDepth();
                attDefType = XMLAttributeDecl.TYPE_ENUMERATION;
                attDefEnumeration = scanEnumeration(elementTypeIndex, attDefName, false);
                if (attDefEnumeration == -1) {
                    skipPastEndOfCurrentMarkup();
                    return;
                }
            } else {
                abortMarkup(XMLMessages.MSG_ATTTYPE_REQUIRED_IN_ATTDEF,
                            XMLMessages.P53_ATTTYPE_REQUIRED,
                            elementTypeIndex, attDefName);
                return;
            }
            if (!checkForPEReference(true)) {
                abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_DEFAULTDECL_IN_ATTDEF,
                            XMLMessages.P53_SPACE_REQUIRED,
                            elementTypeIndex, attDefName);
                return;
            }
            int attDefDefaultType = -1;
            int attDefDefaultValue = -1;
            if (fEntityReader.skippedString(required_string)) {
                attDefDefaultType = XMLAttributeDecl.DEFAULT_TYPE_REQUIRED;
            } else if (fEntityReader.skippedString(implied_string)) {
                attDefDefaultType = XMLAttributeDecl.DEFAULT_TYPE_IMPLIED;
            } else {
                if (fEntityReader.skippedString(fixed_string)) {
                    if (!fEntityReader.lookingAtSpace(true)) {
                        abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_AFTER_FIXED_IN_DEFAULTDECL,
                                    XMLMessages.P60_SPACE_REQUIRED,
                                    elementTypeIndex, attDefName);
                        return;
                    }
                    fEntityReader.skipPastSpaces();
                    attDefDefaultType = XMLAttributeDecl.DEFAULT_TYPE_FIXED;
                } else
                    attDefDefaultType = XMLAttributeDecl.DEFAULT_TYPE_DEFAULT;

                //fElementQName.setValues(-1, elementTypeIndex, elementTypeIndex);

                // if attribute name has a prefix "xml", bind it to the XML Namespace.
                // since this is the only pre-defined namespace.
                /***
                if (fAttributeQName.prefix == fXMLSymbol) {
                    fAttributeQName.uri = fXMLNamespace;
                }
                else
                    fAttributeQName.setValues(-1, attDefName, attDefName);
                ****/

                attDefDefaultValue = scanDefaultAttValue(fElementQName, fAttributeQName,
                                                         attDefType,
                                                         attDefEnumeration);

                //normalize and check VC: Attribute Default Legal
                if (attDefDefaultValue != -1 && attDefType != XMLAttributeDecl.TYPE_CDATA ) {
                    attDefDefaultValue = normalizeDefaultAttValue( fAttributeQName, attDefDefaultValue,
                                                                attDefType, attDefEnumeration,
                                                                attDefList);
                }

                if (attDefDefaultValue == -1) {
                    skipPastEndOfCurrentMarkup();
                    return;
                }
            }
            if (attDefName == fXMLSpace) {
                boolean ok = false;
                if (attDefType == XMLAttributeDecl.TYPE_ENUMERATION) {
                    int index = attDefEnumeration;
                    if (index != -1) {
                        ok = (fStringPool.stringListLength(index) == 1 &&
                              (fStringPool.stringInList(index, fDefault) ||
                               fStringPool.stringInList(index, fPreserve))) ||
                            (fStringPool.stringListLength(index) == 2 &&
                             fStringPool.stringInList(index, fDefault) &&
                             fStringPool.stringInList(index, fPreserve));
                    }
                }
                if (!ok) {
                    reportFatalXMLError(XMLMessages.MSG_XML_SPACE_DECLARATION_ILLEGAL,
                                        XMLMessages.S2_10_DECLARATION_ILLEGAL,
                                        elementTypeIndex);
                }
            }
            sawSpace = checkForPEReference(true);
           
            // if attribute name has a prefix "xml", bind it to the XML Namespace.
            // since this is the only pre-defined namespace.
            if (fAttributeQName.prefix == fXMLSymbol) {
                fAttributeQName.uri = fXMLNamespace;
            }
           
            if (fEntityReader.lookingAtChar('>', true)) {
                int attDefIndex = addAttDef(fElementQName, fAttributeQName,
                                                        attDefType, attDefList, attDefEnumeration,
                                                        attDefDefaultType, attDefDefaultValue,
                                                        getReadingExternalEntity());
                //System.out.println("XMLDTDScanner#scanAttlistDecl->DTDGrammar#addAttDef: "+attDefIndex+
                //                   " ("+fElementQName.localpart+","+fStringPool.toString(fElementQName.rawname)+')'+
                //                   " ("+fAttributeQName.localpart+","+fStringPool.toString(fAttributeQName.rawname)+')');
                decreaseMarkupDepth();
                return;
            }
            // REVISIT - review this code...
            if (!sawSpace) {
                if (fEntityReader.lookingAtSpace(true)) {
                    fEntityReader.skipPastSpaces();
                } else
                    reportFatalXMLError(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_ATTRIBUTE_NAME_IN_ATTDEF,
                                        XMLMessages.P53_SPACE_REQUIRED);
            } else {
                if (fEntityReader.lookingAtSpace(true)) {
                    fEntityReader.skipPastSpaces();
                }
            }
            if (fEntityReader.lookingAtChar('>', true)) {
                int attDefIndex = addAttDef(fElementQName, fAttributeQName,
                                                        attDefType, attDefList, attDefEnumeration,
                                                        attDefDefaultType, attDefDefaultValue,
                                                        getReadingExternalEntity() );
                //System.out.println("XMLDTDScanner#scanAttlistDecl->DTDGrammar#addAttDef: "+attDefIndex+
                //                   " ("+fElementQName.localpart+","+fStringPool.toString(fElementQName.rawname)+')'+
                //                   " ("+fAttributeQName.localpart+","+fStringPool.toString(fAttributeQName.rawname)+')');
                decreaseMarkupDepth();
                return;
            }
            int attDefIndex = addAttDef(fElementQName, fAttributeQName,
                                                    attDefType, attDefList, attDefEnumeration,
                                                    attDefDefaultType, attDefDefaultValue,
                                                    getReadingExternalEntity());
            //System.out.println("XMLDTDScanner#scanAttlistDecl->DTDGrammar#addAttDef: "+attDefIndex+
            //                   " ("+fElementQName.localpart+","+fStringPool.toString(fElementQName.rawname)+')'+
            //                   " ("+fAttributeQName.localpart+","+fStringPool.toString(fAttributeQName.rawname)+')');
        }
    }

    private int addAttDef(QName element, QName attribute,
                          int attDefType, boolean attDefList, int attDefEnumeration,
                          int attDefDefaultType, int attDefDefaultValue,
                          boolean isExternal ) throws Exception {

        if (fDTDHandler != null) {
            String enumString = attDefEnumeration != -1 ? fStringPool.stringListAsString(attDefEnumeration) : null;
            fDTDHandler.attlistDecl(element, attribute,
                                    attDefType, attDefList,
                                    enumString,
                                    attDefDefaultType, attDefDefaultValue);
        }
        int elementIndex = fDTDGrammar.getElementDeclIndex(element, -1);
        if (elementIndex == -1) {
            // REPORT Internal error here
        }
        else {
            int attlistIndex = fDTDGrammar.getFirstAttributeDeclIndex(elementIndex);
            int dupID = -1;
            int dupNotation = -1;
            while (attlistIndex != -1) {
                fDTDGrammar.getAttributeDecl(attlistIndex, fTempAttributeDecl);

                // REVISIT: Validation. Attributes are also tuples.
                if (fStringPool.equalNames(fTempAttributeDecl.name.rawname, attribute.rawname)) {
                    /******
                    if (fWarningOnDuplicateAttDef) {
                        Object[] args = { fStringPool.toString(fElementType[elemChunk][elemIndex]),
                                          fStringPool.toString(attributeDecl.rawname) };
                        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                                   XMLMessages.XML_DOMAIN,
                                                   XMLMessages.MSG_DUPLICATE_ATTDEF,
                                                   XMLMessages.P53_DUPLICATE,
                                                   args,
                                                   XMLErrorReporter.ERRORTYPE_WARNING);
                    }
                    ******/
                    return -1;
                }

                if (fValidationEnabled) {
                    if (attDefType == XMLAttributeDecl.TYPE_ID &&
                        fTempAttributeDecl.type == XMLAttributeDecl.TYPE_ID ) {
                        dupID = fTempAttributeDecl.name.rawname;
                    }
                    if (attDefType == XMLAttributeDecl.TYPE_NOTATION
                        && fTempAttributeDecl.type == XMLAttributeDecl.TYPE_NOTATION) {
                        dupNotation = fTempAttributeDecl.name.rawname;
                    }
                }
                attlistIndex = fDTDGrammar.getNextAttributeDeclIndex(attlistIndex);
            }
            if (fValidationEnabled) {
                if (dupID != -1) {
                    Object[] args = { fStringPool.toString(element.rawname),
                                      fStringPool.toString(dupID),
                                      fStringPool.toString(attribute.rawname) };
                    fErrorReporter.reportError(fErrorReporter.getLocator(),
                                               XMLMessages.XML_DOMAIN,
                                               XMLMessages.MSG_MORE_THAN_ONE_ID_ATTRIBUTE,
                                               XMLMessages.VC_ONE_ID_PER_ELEMENT_TYPE,
                                               args,
                                               XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
                    return -1;
                }
                if (dupNotation != -1) {
                    Object[] args = { fStringPool.toString(element.rawname),
                                      fStringPool.toString(dupNotation),
                                      fStringPool.toString(attribute.rawname) };
                    fErrorReporter.reportError(fErrorReporter.getLocator(),
                                               XMLMessages.XML_DOMAIN,
                                               XMLMessages.MSG_MORE_THAN_ONE_NOTATION_ATTRIBUTE,
                                               XMLMessages.VC_ONE_NOTATION_PER_ELEMENT_TYPE,
                                               args,
                                               XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
                    return -1;
                }
            }
        }
        return fDTDGrammar.addAttDef(element, attribute,
                                     attDefType, attDefList, attDefEnumeration,
                                     attDefDefaultType, attDefDefaultValue,
                                     isExternal);

    }
    //
    // [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
    // [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
    //
    private int scanEnumeration(int elementType, int attrName, boolean isNotationType) throws Exception
    {
        int enumIndex = fDTDGrammar.startEnumeration();
        while (true) {
            checkForPEReference(false);
            int nameIndex = isNotationType ?
                            checkForNameWithPEReference(fEntityReader, ')') :
                            checkForNmtokenWithPEReference(fEntityReader, ')');
            if (nameIndex == -1) {
                if (isNotationType) {
                    reportFatalXMLError(XMLMessages.MSG_NAME_REQUIRED_IN_NOTATIONTYPE,
                                        XMLMessages.P58_NAME_REQUIRED,
                                        elementType,
                                        attrName);
                } else {
                    reportFatalXMLError(XMLMessages.MSG_NMTOKEN_REQUIRED_IN_ENUMERATION,
                                        XMLMessages.P59_NMTOKEN_REQUIRED,
                                        elementType,
                                        attrName);
                }
                fDTDGrammar.endEnumeration(enumIndex);
                return -1;
            }
            fDTDGrammar.addNameToEnumeration(enumIndex, elementType, attrName, nameIndex, isNotationType);
            /*****/
            if (isNotationType && !((DefaultEntityHandler)fEntityHandler).isNotationDeclared(nameIndex)) {
                Object[] args = { fStringPool.toString(elementType),
                    fStringPool.toString(attrName),
                    fStringPool.toString(nameIndex) };
                    ((DefaultEntityHandler)fEntityHandler).addRequiredNotation(nameIndex,
                                                       fErrorReporter.getLocator(),
                                                       XMLMessages.MSG_NOTATION_NOT_DECLARED_FOR_NOTATIONTYPE_ATTRIBUTE,
                                                       XMLMessages.VC_NOTATION_DECLARED,
                                                       args);
            }
            /*****/
            checkForPEReference(false);
            if (!fEntityReader.lookingAtChar('|', true)) {
                fDTDGrammar.endEnumeration(enumIndex);
                if (!fEntityReader.lookingAtChar(')', true)) {
                    if (isNotationType) {
                        reportFatalXMLError(XMLMessages.MSG_NOTATIONTYPE_UNTERMINATED,
                                            XMLMessages.P58_UNTERMINATED,
                                        elementType,
                                        attrName);
                    } else {
                        reportFatalXMLError(XMLMessages.MSG_ENUMERATION_UNTERMINATED,
                                            XMLMessages.P59_UNTERMINATED,
                                        elementType,
                                        attrName);
                    }
                    return -1;
                }
                decreaseParenDepth();
                return enumIndex;
            }
        }
    }
    //
    // [10] AttValue ::= '"' ([^<&"] | Reference)* '"'
    //                   | "'" ([^<&'] | Reference)* "'"
    //
    /**
     * Scan the default value in an attribute declaration
     *
     * @param elementType handle to the element that owns the attribute
     * @param attrName handle in the string pool for the attribute name
     * @return handle in the string pool for the default attribute value
     * @exception java.lang.Exception
     */
    public int scanDefaultAttValue(QName element, QName attribute) throws Exception
    {
        boolean single;
        if (!(single = fEntityReader.lookingAtChar('\'', true)) && !fEntityReader.lookingAtChar('\"', true)) {
            reportFatalXMLError(XMLMessages.MSG_QUOTE_REQUIRED_IN_ATTVALUE,
                                XMLMessages.P10_QUOTE_REQUIRED,
                                element.rawname,
                                attribute.rawname);
            return -1;
        }
        int previousState = setScannerState(SCANNER_STATE_DEFAULT_ATTRIBUTE_VALUE);
        char qchar = single ? '\'' : '\"';
        fDefaultAttValueReader = fReaderId;
        fDefaultAttValueElementType = element.rawname;
        fDefaultAttValueAttrName = attribute.rawname;
        boolean setMark = true;
        int dataOffset = fLiteralData.length();
        while (true) {
            fDefaultAttValueOffset = fEntityReader.currentOffset();
            if (setMark) {
                fDefaultAttValueMark = fDefaultAttValueOffset;
                setMark = false;
            }
            if (fEntityReader.lookingAtChar(qchar, true)) {
                if (fReaderId == fDefaultAttValueReader)
                    break;
                continue;
            }
            if (fEntityReader.lookingAtChar(' ', true)) {
                continue;
            }
            boolean skippedCR;
            if ((skippedCR = fEntityReader.lookingAtChar((char)0x0D, true)) || fEntityReader.lookingAtSpace(true)) {
                if (fDefaultAttValueOffset - fDefaultAttValueMark > 0)
                    fEntityReader.append(fLiteralData, fDefaultAttValueMark, fDefaultAttValueOffset - fDefaultAttValueMark);
                setMark = true;
                fLiteralData.append(' ');
                if (skippedCR)
                    fEntityReader.lookingAtChar((char)0x0A, true);
                continue;
            }
            if (fEntityReader.lookingAtChar('&', true)) {
                if (fDefaultAttValueOffset - fDefaultAttValueMark > 0)
                    fEntityReader.append(fLiteralData, fDefaultAttValueMark, fDefaultAttValueOffset - fDefaultAttValueMark);
                setMark = true;
                //
                // Check for character reference first.
                //
                if (fEntityReader.lookingAtChar('#', true)) {
                    int ch = scanCharRef();
                    if (ch != -1) {
                        if (ch < 0x10000)
                            fLiteralData.append((char)ch);
                        else {
                            fLiteralData.append((char)(((ch-0x00010000)>>10)+0xd800));
                            fLiteralData.append((char)(((ch-0x00010000)&0x3ff)+0xdc00));
                        }
                    }
                } else {
                    //
                    // Entity reference
                    //
                    int nameOffset = fEntityReader.currentOffset();
                    fEntityReader.skipPastName(';');
                    int nameLength = fEntityReader.currentOffset() - nameOffset;
                    if (nameLength == 0) {
                        reportFatalXMLError(XMLMessages.MSG_NAME_REQUIRED_IN_REFERENCE,
                                            XMLMessages.P68_NAME_REQUIRED);
                    } else if (!fEntityReader.lookingAtChar(';', true)) {
                        reportFatalXMLError(XMLMessages.MSG_SEMICOLON_REQUIRED_IN_REFERENCE,
                                            XMLMessages.P68_SEMICOLON_REQUIRED,
                                            fEntityReader.addString(nameOffset, nameLength));
                    } else {
                        int entityNameIndex = fEntityReader.addSymbol(nameOffset, nameLength);
                        fEntityHandler.startReadingFromEntity(entityNameIndex, markupDepth(), XMLEntityHandler.ENTITYREF_IN_DEFAULTATTVALUE);
                    }
                }
                continue;
            }
            if (fEntityReader.lookingAtChar('<', true)) {
                if (fDefaultAttValueOffset - fDefaultAttValueMark > 0)
                    fEntityReader.append(fLiteralData, fDefaultAttValueMark, fDefaultAttValueOffset - fDefaultAttValueMark);
                setMark = true;
                reportFatalXMLError(XMLMessages.MSG_LESSTHAN_IN_ATTVALUE,
                                    XMLMessages.WFC_NO_LESSTHAN_IN_ATTVALUE,
                                    element.rawname,
                                    attribute.rawname);
                continue;
            }
            //
            // REVISIT - HACK !!!  code added to pass incorrect OASIS test 'valid-sa-094'
            //  Remove this next section to conform to the spec...
            //
            if (!getReadingExternalEntity() && fEntityReader.lookingAtChar('%', true)) {
                int nameOffset = fEntityReader.currentOffset();
                fEntityReader.skipPastName(';');
                int nameLength = fEntityReader.currentOffset() - nameOffset;
                if (nameLength != 0 && fEntityReader.lookingAtChar(';', true)) {
                    reportFatalXMLError(XMLMessages.MSG_PEREFERENCE_WITHIN_MARKUP,
                                        XMLMessages.WFC_PES_IN_INTERNAL_SUBSET,
                                        fEntityReader.addString(nameOffset, nameLength));
                }
            }
            //
            // END HACK !!!
            //
            if (!fEntityReader.lookingAtValidChar(true)) {
                if (fDefaultAttValueOffset - fDefaultAttValueMark > 0)
                    fEntityReader.append(fLiteralData, fDefaultAttValueMark, fDefaultAttValueOffset - fDefaultAttValueMark);
                setMark = true;
                int invChar = fEntityReader.scanInvalidChar();
                if (fScannerState == SCANNER_STATE_END_OF_INPUT)
                    return -1;
                if (invChar >= 0) {
                    reportFatalXMLError(XMLMessages.MSG_INVALID_CHAR_IN_ATTVALUE,
                                        XMLMessages.P10_INVALID_CHARACTER,
                                        fStringPool.toString(element.rawname),
                                        fStringPool.toString(attribute.rawname),
                                        Integer.toHexString(invChar));
                }
                continue;
            }
        }
        restoreScannerState(previousState);
        int dataLength = fLiteralData.length() - dataOffset;
        if (dataLength == 0) {
            return fEntityReader.addString(fDefaultAttValueMark, fDefaultAttValueOffset - fDefaultAttValueMark);
        }
        if (fDefaultAttValueOffset - fDefaultAttValueMark > 0) {
            fEntityReader.append(fLiteralData, fDefaultAttValueMark, fDefaultAttValueOffset - fDefaultAttValueMark);
            dataLength = fLiteralData.length() - dataOffset;
        }
        return fLiteralData.addString(dataOffset, dataLength);
    }
    //
    // [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID |  PublicID) S? '>'
    //
    private void scanNotationDecl() throws Exception
    {
        if (!checkForPEReference(true)) {
            abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_NOTATIONDECL,
                        XMLMessages.P82_SPACE_REQUIRED);
            return;
        }
        int notationName = checkForNameWithPEReference(fEntityReader, ' ');
        if (notationName == -1) {
            abortMarkup(XMLMessages.MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL,
                        XMLMessages.P82_NAME_REQUIRED);
            return;
        }
        if (!checkForPEReference(true)) {
            abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_AFTER_NOTATION_NAME_IN_NOTATIONDECL,
                        XMLMessages.P82_SPACE_REQUIRED,
                        notationName);
            return;
        }
        if (!scanExternalID(true)) {
            skipPastEndOfCurrentMarkup();
            return;
        }
        checkForPEReference(false);
        if (!fEntityReader.lookingAtChar('>', true)) {
            abortMarkup(XMLMessages.MSG_NOTATIONDECL_UNTERMINATED,
                        XMLMessages.P82_UNTERMINATED,
                        notationName);
            return;
        }
        decreaseMarkupDepth();
        /****
        System.out.println(fStringPool.toString(notationName)+","
                           +fStringPool.toString(fPubidLiteral) + ","
                           +fStringPool.toString(fSystemLiteral) + ","
                           +getReadingExternalEntity());
        /****/

        int notationIndex = ((DefaultEntityHandler) fEntityHandler).addNotationDecl( notationName,
                                                                                     fPubidLiteral,
                                                                                     fSystemLiteral,
                                                                                     getReadingExternalEntity());
        fDTDGrammar.addNotationDecl(notationName, fPubidLiteral, fSystemLiteral);
        if (fDTDHandler != null) {
            fDTDHandler.notationDecl(notationName, fPubidLiteral, fSystemLiteral);
        }
    }
    //
    // [70] EntityDecl ::= GEDecl | PEDecl
    // [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
    // [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
    // [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
    // [74] PEDef ::= EntityValue | ExternalID
    // [75] ExternalID ::= 'SYSTEM' S SystemLiteral
    //                     | 'PUBLIC' S PubidLiteral S SystemLiteral
    // [76] NDataDecl ::= S 'NDATA' S Name
    //  [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"'
    //                      | "'" ([^%&'] | PEReference | Reference)* "'"
    //
    // Called after scanning 'ENTITY'
    //
    private void scanEntityDecl() throws Exception
    {
        boolean isPEDecl = false;
        boolean sawPERef = false;
        if (fEntityReader.lookingAtSpace(true)) {
            fEntityReader.skipPastSpaces();
            if (!fEntityReader.lookingAtChar('%', true)) {
                isPEDecl = false; // <!ENTITY x "x">
            } else if (fEntityReader.lookingAtSpace(true)) {
                checkForPEReference(false); // <!ENTITY % x "x">
                isPEDecl = true;
            } else if (!getReadingExternalEntity()) {
                reportFatalXMLError(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_PEDECL,
                                    XMLMessages.P72_SPACE);
                isPEDecl = true;
            } else if (fEntityReader.lookingAtChar('%', false)) {
                checkForPEReference(false); // <!ENTITY %%x; "x"> is legal
                isPEDecl = true;
            } else {
                sawPERef = true;
            }
        } else if (!getReadingExternalEntity() || !fEntityReader.lookingAtChar('%', true)) {
            // <!ENTITY[^ ]...> or <!ENTITY[^ %]...>
            reportFatalXMLError(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL,
                                XMLMessages.P70_SPACE);
            isPEDecl = false;
        } else if (fEntityReader.lookingAtSpace(false)) {
            // <!ENTITY% ...>
            reportFatalXMLError(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_PERCENT_IN_PEDECL,
                                XMLMessages.P72_SPACE);
            isPEDecl = false;
        } else {
            sawPERef = true;
        }
        if (sawPERef) {
            while (true) {
                int nameOffset = fEntityReader.currentOffset();
                fEntityReader.skipPastName(';');
                int nameLength = fEntityReader.currentOffset() - nameOffset;
                if (nameLength == 0) {
                    reportFatalXMLError(XMLMessages.MSG_NAME_REQUIRED_IN_PEREFERENCE,
                                        XMLMessages.P69_NAME_REQUIRED);
                } else if (!fEntityReader.lookingAtChar(';', true)) {
                    reportFatalXMLError(XMLMessages.MSG_SEMICOLON_REQUIRED_IN_PEREFERENCE,
                                        XMLMessages.P69_SEMICOLON_REQUIRED,
                                        fEntityReader.addString(nameOffset, nameLength));
                } else {
                    int peNameIndex = fEntityReader.addSymbol(nameOffset, nameLength);
                    int readerDepth = (fScannerState == SCANNER_STATE_CONTENTSPEC) ? parenDepth() : markupDepth();
                    fEntityHandler.startReadingFromEntity(peNameIndex, readerDepth, XMLEntityHandler.ENTITYREF_IN_DTD_WITHIN_MARKUP);
                }
                fEntityReader.skipPastSpaces();
                if (!fEntityReader.lookingAtChar('%', true))
                    break;
                if (!isPEDecl) {
                    if (fEntityReader.lookingAtSpace(true)) {
                        checkForPEReference(false);
                        isPEDecl = true;
                        break;
                    }
                    isPEDecl = fEntityReader.lookingAtChar('%', true);
                }
            }
        }
        int entityName = checkForNameWithPEReference(fEntityReader, ' ');
        if (entityName == -1) {
            abortMarkup(XMLMessages.MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL,
                        XMLMessages.P70_REQUIRED_NAME);
            return;
        }
        if (!fDTDGrammar.startEntityDecl(isPEDecl, entityName)) {
            skipPastEndOfCurrentMarkup();
            return;
        }
        if (!checkForPEReference(true)) {
            abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_AFTER_ENTITY_NAME_IN_ENTITYDECL,
                        XMLMessages.P70_REQUIRED_SPACE,
                        entityName);
            fDTDGrammar.endEntityDecl();
            return;
        }
        if (isPEDecl) {
            boolean single;
            if ((single = fEntityReader.lookingAtChar('\'', true)) || fEntityReader.lookingAtChar('\"', true)) {
                int value = scanEntityValue(single);
                if (value == -1) {
                    skipPastEndOfCurrentMarkup();
                    fDTDGrammar.endEntityDecl();
                    return;
                }
                checkForPEReference(false);
                if (!fEntityReader.lookingAtChar('>', true)) {
                    abortMarkup(XMLMessages.MSG_ENTITYDECL_UNTERMINATED,
                                XMLMessages.P72_UNTERMINATED,
                                entityName);
                    fDTDGrammar.endEntityDecl();
                    return;
                }
                decreaseMarkupDepth();
                fDTDGrammar.endEntityDecl();

                // a hack by Eric
                //REVISIT
                fDTDGrammar.addInternalPEDecl(entityName, value);
                if (fDTDHandler != null) {
                    fDTDHandler.internalPEDecl(entityName, value);
                }
                int entityIndex = ((DefaultEntityHandler) fEntityHandler).addInternalPEDecl(entityName,
                                                                                            value,
                                                                                            getReadingExternalEntity());

            } else {
                if (!scanExternalID(false)) {
                    skipPastEndOfCurrentMarkup();
                    fDTDGrammar.endEntityDecl();
                    return;
                }
                checkForPEReference(false);
                if (!fEntityReader.lookingAtChar('>', true)) {
                    abortMarkup(XMLMessages.MSG_ENTITYDECL_UNTERMINATED,
                                XMLMessages.P72_UNTERMINATED,
                                entityName);
                    fDTDGrammar.endEntityDecl();
                    return;
                }
                decreaseMarkupDepth();
                fDTDGrammar.endEntityDecl();

                //a hack by Eric
                //REVISIT
                fDTDGrammar.addExternalPEDecl(entityName, fPubidLiteral, fSystemLiteral);
                if (fDTDHandler != null) {
                    fDTDHandler.externalPEDecl(entityName, fPubidLiteral, fSystemLiteral);
                }
                int entityIndex = ((DefaultEntityHandler) fEntityHandler).addExternalPEDecl(entityName,
                                                                                            fPubidLiteral,
                                                                                            fSystemLiteral, getReadingExternalEntity());
            }
        } else {
            boolean single;
            if ((single = fEntityReader.lookingAtChar('\'', true)) || fEntityReader.lookingAtChar('\"', true)) {
                int value = scanEntityValue(single);
                if (value == -1) {
                    skipPastEndOfCurrentMarkup();
                    fDTDGrammar.endEntityDecl();
                    return;
                }
                checkForPEReference(false);
                if (!fEntityReader.lookingAtChar('>', true)) {
                    abortMarkup(XMLMessages.MSG_ENTITYDECL_UNTERMINATED,
                                XMLMessages.P71_UNTERMINATED,
                                entityName);
                    fDTDGrammar.endEntityDecl();
                    return;
                }
                decreaseMarkupDepth();
                fDTDGrammar.endEntityDecl();

                //a hack by Eric
                //REVISIT
                fDTDGrammar.addInternalEntityDecl(entityName, value);
                if (fDTDHandler != null) {
                    fDTDHandler.internalEntityDecl(entityName, value);
                }
                int entityIndex = ((DefaultEntityHandler) fEntityHandler).addInternalEntityDecl(entityName,
                                                                                                value,
                                                                                                getReadingExternalEntity());
            } else {
                if (!scanExternalID(false)) {
                    skipPastEndOfCurrentMarkup();
                    fDTDGrammar.endEntityDecl();
                    return;
                }
                boolean unparsed = false;
                if (fEntityReader.lookingAtSpace(true)) {
                    fEntityReader.skipPastSpaces();
                    unparsed = fEntityReader.skippedString(ndata_string);
                }
                if (!unparsed) {
                    checkForPEReference(false);
                    if (!fEntityReader.lookingAtChar('>', true)) {
                        abortMarkup(XMLMessages.MSG_ENTITYDECL_UNTERMINATED,
                                    XMLMessages.P72_UNTERMINATED,
                                    entityName);
                        fDTDGrammar.endEntityDecl();
                        return;
                    }
                    decreaseMarkupDepth();
                    fDTDGrammar.endEntityDecl();

                    //a hack by Eric
                    //REVISIT
                    fDTDGrammar.addExternalEntityDecl(entityName, fPubidLiteral, fSystemLiteral);
                    if (fDTDHandler != null) {
                        fDTDHandler.externalEntityDecl(entityName, fPubidLiteral, fSystemLiteral);
                    }
                    int entityIndex = ((DefaultEntityHandler) fEntityHandler).addExternalEntityDecl(entityName,
                                                                                                    fPubidLiteral,
                                                                                                    fSystemLiteral,
                                                                                                    getReadingExternalEntity());

                } else {
                    if (!fEntityReader.lookingAtSpace(true)) {
                        abortMarkup(XMLMessages.MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_UNPARSED_ENTITYDECL,
                                    XMLMessages.P76_SPACE_REQUIRED,
                                    entityName);
                        fDTDGrammar.endEntityDecl();
                        return;
                    }
                    fEntityReader.skipPastSpaces();
                    int ndataOffset = fEntityReader.currentOffset();
                    fEntityReader.skipPastName('>');
                    int ndataLength = fEntityReader.currentOffset() - ndataOffset;
                    if (ndataLength == 0) {
                        abortMarkup(XMLMessages.MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL,
                                    XMLMessages.P76_REQUIRED,
                                    entityName);
                        fDTDGrammar.endEntityDecl();
                        return;
                    }
                    int notationName = fEntityReader.addSymbol(ndataOffset, ndataLength);
                    checkForPEReference(false);
                    if (!fEntityReader.lookingAtChar('>', true)) {
                        abortMarkup(XMLMessages.MSG_ENTITYDECL_UNTERMINATED,
                                    XMLMessages.P72_UNTERMINATED,
                                    entityName);
                        fDTDGrammar.endEntityDecl();
                        return;
                    }
                    decreaseMarkupDepth();
                    fDTDGrammar.endEntityDecl();
                   
                    //a hack by Eric
                    //REVISIT
                    fDTDGrammar.addUnparsedEntityDecl(entityName, fPubidLiteral, fSystemLiteral, notationName);
                    if (fDTDHandler != null) {
                        fDTDHandler.unparsedEntityDecl(entityName, fPubidLiteral, fSystemLiteral, notationName);
                    }
                    /****
                    System.out.println("----addUnparsedEntity--- "+ fStringPool.toString(entityName)+","
                                       +fStringPool.toString(notationName)+","
                                       +fStringPool.toString(fPubidLiteral) + ","
                                       +fStringPool.toString(fSystemLiteral) + ","
                                       +getReadingExternalEntity());
                    /****/
                    int entityIndex = ((DefaultEntityHandler) fEntityHandler).addUnparsedEntityDecl(entityName,
                                                                                                    fPubidLiteral,
                                                                                                    fSystemLiteral,
                                                                                                    notationName,
                                                                                                    getReadingExternalEntity());
                }
            }
        }
    }
    //
    //  [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"'
    //                      | "'" ([^%&'] | PEReference | Reference)* "'"
    //
    private int scanEntityValue(boolean single) throws Exception
    {
        char qchar = single ? '\'' : '\"';
        fEntityValueMark = fEntityReader.currentOffset();
        int entityValue = fEntityReader.scanEntityValue(qchar, true);
        if (entityValue < 0)
            entityValue = scanComplexEntityValue(qchar, entityValue);
        return entityValue;
    }
    private int scanComplexEntityValue(char qchar, int result) throws Exception
    {
        int previousState = setScannerState(SCANNER_STATE_ENTITY_VALUE);
        fEntityValueReader = fReaderId;
        int dataOffset = fLiteralData.length();
        while (true) {
            switch (result) {
            case XMLEntityHandler.ENTITYVALUE_RESULT_FINISHED:
            {
                int offset = fEntityReader.currentOffset();
                fEntityReader.lookingAtChar(qchar, true);
                restoreScannerState(previousState);
                int dataLength = fLiteralData.length() - dataOffset;
                if (dataLength == 0) {
                    return fEntityReader.addString(fEntityValueMark, offset - fEntityValueMark);
                }
                if (offset - fEntityValueMark > 0) {
                    fEntityReader.append(fLiteralData, fEntityValueMark, offset - fEntityValueMark);
                    dataLength = fLiteralData.length() - dataOffset;
                }
                return fLiteralData.addString(dataOffset, dataLength);
            }
            case XMLEntityHandler.ENTITYVALUE_RESULT_REFERENCE:
            {
                int offset = fEntityReader.currentOffset();
                if (offset - fEntityValueMark > 0)
                    fEntityReader.append(fLiteralData, fEntityValueMark, offset - fEntityValueMark);
                fEntityReader.lookingAtChar('&', true);
                //
                // Check for character reference first.
                //
                if (fEntityReader.lookingAtChar('#', true)) {
                    int ch = scanCharRef();
                    if (ch != -1) {
                        if (ch < 0x10000)
                            fLiteralData.append((char)ch);
                        else {
                            fLiteralData.append((char)(((ch-0x00010000)>>10)+0xd800));
                            fLiteralData.append((char)(((ch-0x00010000)&0x3ff)+0xdc00));
                        }
                    }
                    fEntityValueMark = fEntityReader.currentOffset();
                } else {
                    //
                    // Entity reference
                    //
                    int nameOffset = fEntityReader.currentOffset();
                    fEntityReader.skipPastName(';');
                    int nameLength = fEntityReader.currentOffset() - nameOffset;
                    if (nameLength == 0) {
                        reportFatalXMLError(XMLMessages.MSG_NAME_REQUIRED_IN_REFERENCE,
                                            XMLMessages.P68_NAME_REQUIRED);
                        fEntityValueMark = fEntityReader.currentOffset();
                    } else if (!fEntityReader.lookingAtChar(';', true)) {
                        reportFatalXMLError(XMLMessages.MSG_SEMICOLON_REQUIRED_IN_REFERENCE,
                                            XMLMessages.P68_SEMICOLON_REQUIRED,
                                            fEntityReader.addString(nameOffset, nameLength));
                        fEntityValueMark = fEntityReader.currentOffset();
                    } else {
                        //
                        // 4.4.7 Bypassed
                        //
                        // When a general entity reference appears in the EntityValue in an
                        // entity declaration, it is bypassed and left as is.
                        //
                        fEntityValueMark = offset;
                    }
                }
                break;
            }
            case XMLEntityHandler.ENTITYVALUE_RESULT_PEREF:
            {
                int offset = fEntityReader.currentOffset();
                if (offset - fEntityValueMark > 0)
                    fEntityReader.append(fLiteralData, fEntityValueMark, offset - fEntityValueMark);
                fEntityReader.lookingAtChar('%', true);
                int nameOffset = fEntityReader.currentOffset();
                fEntityReader.skipPastName(';');
                int nameLength = fEntityReader.currentOffset() - nameOffset;
                if (nameLength == 0) {
                    reportFatalXMLError(XMLMessages.MSG_NAME_REQUIRED_IN_PEREFERENCE,
                                        XMLMessages.P69_NAME_REQUIRED);
                } else if (!fEntityReader.lookingAtChar(';', true)) {
                    reportFatalXMLError(XMLMessages.MSG_SEMICOLON_REQUIRED_IN_PEREFERENCE,
                                        XMLMessages.P69_SEMICOLON_REQUIRED,
                                        fEntityReader.addString(nameOffset, nameLength));
                } else if (!getReadingExternalEntity()) {
                    reportFatalXMLError(XMLMessages.MSG_PEREFERENCE_WITHIN_MARKUP,
                                        XMLMessages.WFC_PES_IN_INTERNAL_SUBSET,
                                        fEntityReader.addString(nameOffset, nameLength));
                } else {
                    int peNameIndex = fEntityReader.addSymbol(nameOffset, nameLength);
                    fEntityHandler.startReadingFromEntity(peNameIndex, markupDepth(), XMLEntityHandler.ENTITYREF_IN_ENTITYVALUE);
                }
                fEntityValueMark = fEntityReader.currentOffset();
                break;
            }
            case XMLEntityHandler.ENTITYVALUE_RESULT_INVALID_CHAR:
            {
                int offset = fEntityReader.currentOffset();
                if (offset - fEntityValueMark > 0)
                    fEntityReader.append(fLiteralData, fEntityValueMark, offset - fEntityValueMark);
                int invChar = fEntityReader.scanInvalidChar();
                if (fScannerState == SCANNER_STATE_END_OF_INPUT)
                    return -1;
                if (invChar >= 0) {
                    reportFatalXMLError(XMLMessages.MSG_INVALID_CHAR_IN_ENTITYVALUE,
                                        XMLMessages.P9_INVALID_CHARACTER,
                                        Integer.toHexString(invChar));
                }
                fEntityValueMark = fEntityReader.currentOffset();
                break;
            }
            case XMLEntityHandler.ENTITYVALUE_RESULT_END_OF_INPUT:
                // all the work is done by the previous reader, just invoke the next one now.
                break;
            default:
                break;
            }
            result = fEntityReader.scanEntityValue(fReaderId == fEntityValueReader ? qchar : -1, false);
        }
    }
    //
    //
    //
    private boolean checkForPEReference(boolean spaceRequired) throws Exception
    {
        boolean sawSpace = true;
        if (spaceRequired)
            sawSpace = fEntityReader.lookingAtSpace(true);
        fEntityReader.skipPastSpaces();
        if (!getReadingExternalEntity())
            return sawSpace;
        if (!fEntityReader.lookingAtChar('%', true))
            return sawSpace;
        while (true) {
            int nameOffset = fEntityReader.currentOffset();
            fEntityReader.skipPastName(';');
            int nameLength = fEntityReader.currentOffset() - nameOffset;
            if (nameLength == 0) {
                reportFatalXMLError(XMLMessages.MSG_NAME_REQUIRED_IN_PEREFERENCE,
                                    XMLMessages.P69_NAME_REQUIRED);
            } else if (!fEntityReader.lookingAtChar(';', true)) {
                reportFatalXMLError(XMLMessages.MSG_SEMICOLON_REQUIRED_IN_PEREFERENCE,
                                    XMLMessages.P69_SEMICOLON_REQUIRED,
                                    fEntityReader.addString(nameOffset, nameLength));
            } else {
                int peNameIndex = fEntityReader.addSymbol(nameOffset, nameLength);
                int readerDepth = (fScannerState == SCANNER_STATE_CONTENTSPEC) ? parenDepth() : markupDepth();
                fEntityHandler.startReadingFromEntity(peNameIndex, readerDepth, XMLEntityHandler.ENTITYREF_IN_DTD_WITHIN_MARKUP);
            }
            fEntityReader.skipPastSpaces();
            if (!fEntityReader.lookingAtChar('%', true))
                return true;
        }
    }
    //
    // content model stack
    //
    private void initializeContentModelStack(int depth) {
        if (fOpStack == null) {
            fOpStack = new int[8];
            fNodeIndexStack = new int[8];
            fPrevNodeIndexStack = new int[8];
        } else if (depth == fOpStack.length) {
            int[] newStack = new int[depth * 2];
            System.arraycopy(fOpStack, 0, newStack, 0, depth);
            fOpStack = newStack;
            newStack = new int[depth * 2];
            System.arraycopy(fNodeIndexStack, 0, newStack, 0, depth);
            fNodeIndexStack = newStack;
            newStack = new int[depth * 2];
            System.arraycopy(fPrevNodeIndexStack, 0, newStack, 0, depth);
            fPrevNodeIndexStack = newStack;
        }
        fOpStack[depth] = -1;
        fNodeIndexStack[depth] = -1;
        fPrevNodeIndexStack[depth] = -1;
    }

    private boolean validVersionNum(String version) {
        return XMLCharacterProperties.validVersionNum(version);
    }

    private boolean validEncName(String encoding) {
        return XMLCharacterProperties.validEncName(encoding);
    }

    private int validPublicId(String publicId) {
        return XMLCharacterProperties.validPublicId(publicId);
    }

    private void scanElementType(XMLEntityHandler.EntityReader entityReader,
                                char fastchar, QName element) throws Exception {

        if (!fNamespacesEnabled) {
            element.clear();
            element.localpart = entityReader.scanName(fastchar);
            element.rawname = element.localpart;
            return;
        }
        entityReader.scanQName(fastchar, element);
        if (entityReader.lookingAtChar(':', false)) {
            fErrorReporter.reportError(fErrorReporter.getLocator(),
                                       XMLMessages.XML_DOMAIN,
                                       XMLMessages.MSG_TWO_COLONS_IN_QNAME,
                                       XMLMessages.P5_INVALID_CHARACTER,
                                       null,
                                       XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
            entityReader.skipPastNmtoken(' ');
        }

    } // scanElementType(XMLEntityHandler.EntityReader,char,QName)

    public void checkForElementTypeWithPEReference(XMLEntityHandler.EntityReader entityReader,
                                                   char fastchar, QName element) throws Exception {

        if (!fNamespacesEnabled) {
            element.clear();
            element.localpart = entityReader.scanName(fastchar);
            element.rawname = element.localpart;
            return;
        }
        entityReader.scanQName(fastchar, element);
        if (entityReader.lookingAtChar(':', false)) {
            fErrorReporter.reportError(fErrorReporter.getLocator(),
                                       XMLMessages.XML_DOMAIN,
                                       XMLMessages.MSG_TWO_COLONS_IN_QNAME,
                                       XMLMessages.P5_INVALID_CHARACTER,
                                       null,
                                       XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
            entityReader.skipPastNmtoken(' ');
        }

    } // checkForElementTypeWithPEReference(XMLEntityHandler.EntityReader,char,QName)

    public void checkForAttributeNameWithPEReference(XMLEntityHandler.EntityReader entityReader,
                                                     char fastchar, QName attribute) throws Exception {

        if (!fNamespacesEnabled) {
            attribute.clear();
            attribute.localpart = entityReader.scanName(fastchar);
            attribute.rawname = attribute.localpart;
            return;
        }

        entityReader.scanQName(fastchar, attribute);
        if (entityReader.lookingAtChar(':', false)) {
            fErrorReporter.reportError(fErrorReporter.getLocator(),
                                       XMLMessages.XML_DOMAIN,
                                       XMLMessages.MSG_TWO_COLONS_IN_QNAME,
                                       XMLMessages.P5_INVALID_CHARACTER,
                                       null,
                                       XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
            entityReader.skipPastNmtoken(' ');
        }

    } // checkForAttributeNameWithPEReference(XMLEntityHandler.EntityReader,char,QName)

    public int checkForNameWithPEReference(XMLEntityHandler.EntityReader entityReader, char fastcheck) throws Exception {
        //
        // REVISIT - what does this have to do with PE references?
        //
        int valueIndex = entityReader.scanName(fastcheck);
        return valueIndex;
    }

    public int checkForNmtokenWithPEReference(XMLEntityHandler.EntityReader entityReader, char fastcheck) throws Exception {
        //
        // REVISIT - what does this have to do with PE references?
        //
        int nameOffset = entityReader.currentOffset();
        entityReader.skipPastNmtoken(fastcheck);
        int nameLength = entityReader.currentOffset() - nameOffset;
        if (nameLength == 0)
            return -1;
        int valueIndex = entityReader.addSymbol(nameOffset, nameLength);
        return valueIndex;
    }

    public int scanDefaultAttValue(QName element, QName attribute,
                                   int attType, int enumeration) throws Exception {
        /***/
        if (fValidationEnabled && attType == XMLAttributeDecl.TYPE_ID) {
            reportRecoverableXMLError(XMLMessages.MSG_ID_DEFAULT_TYPE_INVALID,
                                      XMLMessages.VC_ID_ATTRIBUTE_DEFAULT,
                                      fStringPool.toString(attribute.rawname));
        }
        /***/
        int defaultAttValue = scanDefaultAttValue(element, attribute);
        if (defaultAttValue == -1)
            return -1;
        // REVISIT
        /***
        if (attType != fCDATASymbol) {
            // REVISIT: Validation. Should we pass in the element or is this
            //          default attribute value normalization?
            defaultAttValue = fValidator.normalizeAttValue(null, attribute, defaultAttValue, attType, enumeration);
        }
        /***/
        return defaultAttValue;
    }

    public int normalizeDefaultAttValue( QName attribute, int defaultAttValue,
                                         int attType, int enumeration,
                                         boolean list) throws Exception {
            //
            // Normalize attribute based upon attribute type...
            //
            String attValue = fStringPool.toString(defaultAttValue);

            if (list) {
                StringTokenizer tokenizer = new StringTokenizer(attValue);
                StringBuffer sb = new StringBuffer(attValue.length());
                boolean ok = true;
                if (tokenizer.hasMoreTokens()) {
                    while (true) {
                        String nmtoken = tokenizer.nextToken();
                        if (attType == XMLAttributeDecl.TYPE_NMTOKEN) {
                            if (fValidationEnabled && !XMLCharacterProperties.validNmtoken(nmtoken)) {
                                ok = false;
                            }
                        }
                        else if (attType == XMLAttributeDecl.TYPE_IDREF || attType == XMLAttributeDecl.TYPE_ENTITY) {
                            if (fValidationEnabled && !XMLCharacterProperties.validName(nmtoken)) {
                                ok = false;
                            }
                            // REVISIT: a Hack!!! THis is to pass SUN test /invalid/attr11.xml and attr12.xml
                            // not consistent with XML1.0 spec VC: Attribute Default Legal
                            if (fValidationEnabled && attType == XMLAttributeDecl.TYPE_ENTITY)
                            if (! ((DefaultEntityHandler) fEntityHandler).isUnparsedEntity(defaultAttValue)) {
                                reportRecoverableXMLError(XMLMessages.MSG_ENTITY_INVALID,
                                                          XMLMessages.VC_ENTITY_NAME,
                                                          fStringPool.toString(attribute.rawname), nmtoken);
                            }

                        }
                        sb.append(nmtoken);
                        if (!tokenizer.hasMoreTokens()) {
                            break;
                        }
                        sb.append(' ');
                    }
                }
                String newAttValue = sb.toString();
                if (fValidationEnabled && (!ok || newAttValue.length() == 0)) {
                    reportRecoverableXMLError(XMLMessages.MSG_ATT_DEFAULT_INVALID,
                                              XMLMessages.VC_ATTRIBUTE_DEFAULT_LEGAL,
                                              fStringPool.toString(attribute.rawname), newAttValue);
                }
                if (!newAttValue.equals(attValue)) {
                    defaultAttValue = fStringPool.addString(newAttValue);
                }
                return defaultAttValue;
            }
            else {
                String newAttValue = attValue.trim();

                if (fValidationEnabled) {
                    // REVISIT - can we release the old string?
                    if (newAttValue != attValue) {
                       defaultAttValue = fStringPool.addSymbol(newAttValue);
                    }
                    else {
                       defaultAttValue = fStringPool.addSymbol(defaultAttValue);
                    }
                    if (attType == XMLAttributeDecl.TYPE_ENTITY ||
                        attType == XMLAttributeDecl.TYPE_ID ||
                        attType == XMLAttributeDecl.TYPE_IDREF ||
                        attType == XMLAttributeDecl.TYPE_NOTATION)  {

                        // REVISIT: A Hack!!! THis is to pass SUN test /invalid/attr11.xml and attr12.xml
                        // not consistent with XML1.0 spec VC: Attribute Default Legal
                        if (attType == XMLAttributeDecl.TYPE_ENTITY)
                        if (! ((DefaultEntityHandler) fEntityHandler).isUnparsedEntity(defaultAttValue)) {
                            reportRecoverableXMLError(XMLMessages.MSG_ENTITY_INVALID,
                                                      XMLMessages.VC_ENTITY_NAME,
                                                      fStringPool.toString(attribute.rawname), newAttValue);
                        }
                       
                        if (!XMLCharacterProperties.validName(newAttValue)) {
                            reportRecoverableXMLError(XMLMessages.MSG_ATT_DEFAULT_INVALID,
                                                      XMLMessages.VC_ATTRIBUTE_DEFAULT_LEGAL,
                                                      fStringPool.toString(attribute.rawname), newAttValue);
                        }

                    }
                    else if (attType == XMLAttributeDecl.TYPE_NMTOKEN ||
                             attType == XMLAttributeDecl.TYPE_ENUMERATION ) {

                        if (!XMLCharacterProperties.validNmtoken(newAttValue)) {
                            reportRecoverableXMLError(XMLMessages.MSG_ATT_DEFAULT_INVALID,
                                                      XMLMessages.VC_ATTRIBUTE_DEFAULT_LEGAL,
                                                      fStringPool.toString(attribute.rawname), newAttValue);
                        }
                    }
                   
                    if (attType == XMLAttributeDecl.TYPE_NOTATION ||
                        attType == XMLAttributeDecl.TYPE_ENUMERATION ) {

                        if ( !fStringPool.stringInList(enumeration, defaultAttValue) ) {
                            reportRecoverableXMLError(XMLMessages.MSG_ATT_DEFAULT_INVALID,
                                                      XMLMessages.VC_ATTRIBUTE_DEFAULT_LEGAL,
                                                      fStringPool.toString(attribute.rawname), newAttValue);
                        }
                    }

                }
                else if (newAttValue != attValue) {
                    // REVISIT - can we release the old string?
                    defaultAttValue = fStringPool.addSymbol(newAttValue);
                }
            }

            return defaultAttValue;
    }
    /***
    public boolean scanDoctypeDecl(boolean standalone) throws Exception {
        fStandaloneReader = standalone ? fEntityHandler.getReaderId() : -1;
        fDeclsAreExternal = false;
        if (!fDTDScanner.scanDoctypeDecl()) {
            return false;
        }
        if (fDTDScanner.getReadingExternalEntity()) {
            fDTDScanner.scanDecls(true);
        }
        fDTDHandler.endDTD();
        return true;
    }
    /***/

} // class XMLDTDScanner
TOP

Related Classes of org.apache.xerces.framework.XMLDTDScanner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.