/* * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright 2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.org.apache.xerces.internal.impl; import com.sun.xml.internal.stream.dtd.nonvalidating.DTDGrammar; import java.io.EOFException; import java.io.IOException; import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; import com.sun.org.apache.xerces.internal.util.SymbolTable; import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl; import com.sun.org.apache.xerces.internal.util.XMLChar; import com.sun.org.apache.xerces.internal.util.XMLStringBuffer; import com.sun.org.apache.xerces.internal.xni.XMLDTDContentModelHandler; import com.sun.org.apache.xerces.internal.xni.XMLDTDHandler; import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier; import com.sun.org.apache.xerces.internal.xni.XMLString; import com.sun.org.apache.xerces.internal.xni.XNIException; import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent; import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDScanner; import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource; import com.sun.org.apache.xerces.internal.xni.Augmentations; import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter; import com.sun.org.apache.xerces.internal.impl.XMLEntityHandler; import com.sun.org.apache.xerces.internal.impl.Constants; import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager; import com.sun.xml.internal.stream.Entity; /** * This class is responsible for scanning the declarations found * in the internal and external subsets of a DTD in an XML document. * The scanner acts as the sources for the DTD information which is * communicated to the DTD handlers. *
* This component requires the following features and properties from the * component manager that uses it: *
*
     * [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
     * 
     * * Note: Called after scanning past '<!--' */ protected final void scanComment() throws IOException, XNIException { fReportEntity = false; scanComment(fStringBuffer); fMarkUpDepth--; // call handler if (fDTDHandler != null) { fDTDHandler.comment(fStringBuffer, null); } fReportEntity = true; } // scanComment() /** * Scans an element declaration *
*
     * [45]    elementdecl    ::=    '<!ELEMENT' S Name S contentspec S? '>'
     * [46]    contentspec    ::=    'EMPTY' | 'ANY' | Mixed | children
     * 
     * * Note: Called after scanning past '<!ELEMENT' */ protected final void scanElementDecl() throws IOException, XNIException { // spaces fReportEntity = false; if (!skipSeparator(true, !scanningInternalSubset())) { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL", null); } // element name String name = fEntityScanner.scanName(); if (name == null) { reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL", null); } // spaces if (!skipSeparator(true, !scanningInternalSubset())) { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_CONTENTSPEC_IN_ELEMENTDECL", new Object[]{name}); } // content model if (fDTDContentModelHandler != null) { fDTDContentModelHandler.startContentModel(name, null); } String contentModel = null; fReportEntity = true; if (fEntityScanner.skipString("EMPTY")) { contentModel = "EMPTY"; // call handler if (fDTDContentModelHandler != null) { fDTDContentModelHandler.empty(null); } } else if (fEntityScanner.skipString("ANY")) { contentModel = "ANY"; // call handler if (fDTDContentModelHandler != null) { fDTDContentModelHandler.any(null); } } else { if (!fEntityScanner.skipChar('(')) { reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN", new Object[]{name}); } if (fDTDContentModelHandler != null) { fDTDContentModelHandler.startGroup(null); } fStringBuffer.clear(); fStringBuffer.append('('); fMarkUpDepth++; skipSeparator(false, !scanningInternalSubset()); // Mixed content model if (fEntityScanner.skipString("#PCDATA")) { scanMixed(name); } else { // children content scanChildren(name); } contentModel = fStringBuffer.toString(); } // call handler if (fDTDContentModelHandler != null) { fDTDContentModelHandler.endContentModel(null); } fReportEntity = false; skipSeparator(false, !scanningInternalSubset()); // end if (!fEntityScanner.skipChar('>')) { reportFatalError("ElementDeclUnterminated", new Object[]{name}); } fReportEntity = true; fMarkUpDepth--; // call handler if (fDTDHandler != null) { fDTDHandler.elementDecl(name, contentModel, null); } if (nonValidatingMode) nvGrammarInfo.elementDecl(name, contentModel, null); } // scanElementDecl() /** * scan Mixed content model * This assumes the content model has been parsed up to #PCDATA and * can simply append to fStringBuffer. *
     * [51]    Mixed    ::=    '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*'
     *                       | '(' S? '#PCDATA' S? ')'
     * 
     *
     * @param elName The element type name this declaration is about.
     *
     * Note: Called after scanning past '(#PCDATA'.
     */
    private final void scanMixed(String elName)
    throws IOException, XNIException {
        String childName = null;
        fStringBuffer.append("#PCDATA");
        // call handler
        if (fDTDContentModelHandler != null) {
            fDTDContentModelHandler.pcdata(null);
        }
        skipSeparator(false, !scanningInternalSubset());
        while (fEntityScanner.skipChar('|')) {
            fStringBuffer.append('|');
            // call handler
            if (fDTDContentModelHandler != null) {
                fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
                null);
            }
            skipSeparator(false, !scanningInternalSubset());
            childName = fEntityScanner.scanName();
            if (childName == null) {
                reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT",
                new Object[]{elName});
            }
            fStringBuffer.append(childName);
            // call handler
            if (fDTDContentModelHandler != null) {
                fDTDContentModelHandler.element(childName, null);
            }
            skipSeparator(false, !scanningInternalSubset());
        }
        // The following check must be done in a single call (as opposed to one
        // for ')' and then one for '*') to guarantee that callbacks are
        // properly nested. We do not want to trigger endEntity too early in
        // case we cross the boundary of an entity between the two characters.
        if (fEntityScanner.skipString(")*")) {
            fStringBuffer.append(")*");
            // call handler
            if (fDTDContentModelHandler != null) {
                fDTDContentModelHandler.endGroup(null);
                fDTDContentModelHandler.occurrence(XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE,
                null);
            }
        }
        else if (childName != null) {
            reportFatalError("MixedContentUnterminated",
            new Object[]{elName});
        }
        else if (fEntityScanner.skipChar(')')){
            fStringBuffer.append(')');
            // call handler
            if (fDTDContentModelHandler != null) {
                fDTDContentModelHandler.endGroup(null);
            }
        }
        else {
            reportFatalError("MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN",
            new Object[]{elName});
        }
        fMarkUpDepth--;
        // we are done
    }
    /**
     * scan children content model
     * This assumes it can simply append to fStringBuffer.
     * 
     * [47]    children  ::=    (choice | seq) ('?' | '*' | '+')?
     * [48]    cp        ::=    (Name | choice | seq) ('?' | '*' | '+')?
     * [49]    choice    ::=    '(' S? cp ( S? '|' S? cp )+ S? ')'
     * [50]    seq       ::=    '(' S? cp ( S? ',' S? cp )* S? ')'
     * 
     *
     * @param elName The element type name this declaration is about.
     *
     * Note: Called after scanning past the first open
     * paranthesis.
     */
    private final void scanChildren(String elName)
    throws IOException, XNIException {
        fContentDepth = 0;
        pushContentStack(0);
        int currentOp = 0;
        int c;
        while (true) {
            if (fEntityScanner.skipChar('(')) {
                fMarkUpDepth++;
                fStringBuffer.append('(');
                // call handler
                if (fDTDContentModelHandler != null) {
                    fDTDContentModelHandler.startGroup(null);
                }
                // push current op on stack and reset it
                pushContentStack(currentOp);
                currentOp = 0;
                skipSeparator(false, !scanningInternalSubset());
                continue;
            }
            skipSeparator(false, !scanningInternalSubset());
            String childName = fEntityScanner.scanName();
            if (childName == null) {
                reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
                new Object[]{elName});
                return;
            }
            // call handler
            if (fDTDContentModelHandler != null) {
                fDTDContentModelHandler.element(childName, null);
            }
            fStringBuffer.append(childName);
            c = fEntityScanner.peekChar();
            if (c == '?' || c == '*' || c == '+') {
                // call handler
                if (fDTDContentModelHandler != null) {
                    short oc;
                    if (c == '?') {
                        oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE;
                    }
                    else if (c == '*') {
                        oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE;
                    }
                    else {
                        oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE;
                    }
                    fDTDContentModelHandler.occurrence(oc, null);
                }
                fEntityScanner.scanChar();
                fStringBuffer.append((char)c);
            }
            while (true) {
                skipSeparator(false, !scanningInternalSubset());
                c = fEntityScanner.peekChar();
                if (c == ',' && currentOp != '|') {
                    currentOp = c;
                    // call handler
                    if (fDTDContentModelHandler != null) {
                        fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_SEQUENCE,
                        null);
                    }
                    fEntityScanner.scanChar();
                    fStringBuffer.append(',');
                    break;
                }
                else if (c == '|' && currentOp != ',') {
                    currentOp = c;
                    // call handler
                    if (fDTDContentModelHandler != null) {
                        fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
                        null);
                    }
                    fEntityScanner.scanChar();
                    fStringBuffer.append('|');
                    break;
                }
                else if (c != ')') {
                    reportFatalError("MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN",
                    new Object[]{elName});
                }
                // call handler
                if (fDTDContentModelHandler != null) {
                    fDTDContentModelHandler.endGroup(null);
                }
                // restore previous op
                currentOp = popContentStack();
                short oc;
                // The following checks must be done in a single call (as
                // opposed to one for ')' and then one for '?', '*', and '+')
                // to guarantee that callbacks are properly nested. We do not
                // want to trigger endEntity too early in case we cross the
                // boundary of an entity between the two characters.
                if (fEntityScanner.skipString(")?")) {
                    fStringBuffer.append(")?");
                    // call handler
                    if (fDTDContentModelHandler != null) {
                        oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE;
                        fDTDContentModelHandler.occurrence(oc, null);
                    }
                }
                else if (fEntityScanner.skipString(")+")) {
                    fStringBuffer.append(")+");
                    // call handler
                    if (fDTDContentModelHandler != null) {
                        oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE;
                        fDTDContentModelHandler.occurrence(oc, null);
                    }
                }
                else if (fEntityScanner.skipString(")*")) {
                    fStringBuffer.append(")*");
                    // call handler
                    if (fDTDContentModelHandler != null) {
                        oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE;
                        fDTDContentModelHandler.occurrence(oc, null);
                    }
                }
                else {
                    // no occurrence specified
                    fEntityScanner.scanChar();
                    fStringBuffer.append(')');
                }
                fMarkUpDepth--;
                if (fContentDepth == 0) {
                    return;
                }
            }
            skipSeparator(false, !scanningInternalSubset());
        }
    }
    /**
     * Scans an attlist declaration
     * *
     * [52]  AttlistDecl    ::=   '<!ATTLIST' S Name AttDef* S? '>'
     * [53]  AttDef         ::=   S Name S AttType S DefaultDecl
     * 
     * * Note: Called after scanning past '<!ATTLIST' */ protected final void scanAttlistDecl() throws IOException, XNIException { // spaces fReportEntity = false; if (!skipSeparator(true, !scanningInternalSubset())) { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL", null); } // element name String elName = fEntityScanner.scanName(); if (elName == null) { reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL", null); } // call handler if (fDTDHandler != null) { fDTDHandler.startAttlist(elName, null); } // spaces if (!skipSeparator(true, !scanningInternalSubset())) { // no space, is it the end yet? if (fEntityScanner.skipChar('>')) { // yes, stop here // call handler if (fDTDHandler != null) { fDTDHandler.endAttlist(null); } fMarkUpDepth--; return; } else { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ATTRIBUTE_NAME_IN_ATTDEF", new Object[]{elName}); } } // definitions while (!fEntityScanner.skipChar('>')) { String name = fEntityScanner.scanName(); if (name == null) { reportFatalError("AttNameRequiredInAttDef", new Object[]{elName}); } // spaces if (!skipSeparator(true, !scanningInternalSubset())) { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ATTTYPE_IN_ATTDEF", new Object[]{elName, name}); } // type String type = scanAttType(elName, name); // spaces if (!skipSeparator(true, !scanningInternalSubset())) { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_DEFAULTDECL_IN_ATTDEF", new Object[]{elName, name}); } // default decl String defaultType = scanAttDefaultDecl(elName, name, type, fLiteral, fLiteral2); // REVISIT: Should we do anything with the non-normalized // default attribute value? -Ac // yes--according to bug 5073. - neilg String[] enumr = null; if( fDTDHandler != null || nonValidatingMode){ if (fEnumerationCount != 0) { enumr = new String[fEnumerationCount]; System.arraycopy(fEnumeration, 0, enumr, 0, fEnumerationCount); } } // call handler // Determine whether the default value to be passed should be null. // REVISIT: should probably check whether fLiteral.ch is null instead. LM. if (defaultType!=null && (defaultType.equals("#REQUIRED") || defaultType.equals("#IMPLIED"))) { if (fDTDHandler != null){ fDTDHandler.attributeDecl(elName, name, type, enumr, defaultType, null, null, null); } if(nonValidatingMode){ nvGrammarInfo.attributeDecl(elName, name, type, enumr, defaultType, null, null, null); } } else { if (fDTDHandler != null){ fDTDHandler.attributeDecl(elName, name, type, enumr, defaultType, fLiteral, fLiteral2, null); } if(nonValidatingMode){ nvGrammarInfo.attributeDecl(elName, name, type, enumr, defaultType, fLiteral, fLiteral2, null); } } skipSeparator(false, !scanningInternalSubset()); } // call handler if (fDTDHandler != null) { fDTDHandler.endAttlist(null); } fMarkUpDepth--; fReportEntity = true; } // scanAttlistDecl() /** * Scans an attribute type definition *
*
     * [54]  AttType        ::=   StringType | TokenizedType | EnumeratedType
     * [55]  StringType     ::=   'CDATA'
     * [56]  TokenizedType  ::=   'ID'
     *                          | 'IDREF'
     *                          | 'IDREFS'
     *                          | 'ENTITY'
     *                          | 'ENTITIES'
     *                          | 'NMTOKEN'
     *                          | 'NMTOKENS'
     * [57]  EnumeratedType ::=    NotationType | Enumeration
     * [58]  NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
     * [59]  Enumeration    ::=    '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
     * 
     * * Note: Called after scanning past '<!ATTLIST' * * @param elName The element type name this declaration is about. * @param atName The attribute name this declaration is about. */ private final String scanAttType(String elName, String atName) throws IOException, XNIException { String type = null; fEnumerationCount = 0; /* * Watchout: the order here is important: when a string happens to * be a substring of another string, the longer one needs to be * looked for first!! */ if (fEntityScanner.skipString("CDATA")) { type = "CDATA"; } else if (fEntityScanner.skipString("IDREFS")) { type = "IDREFS"; } else if (fEntityScanner.skipString("IDREF")) { type = "IDREF"; } else if (fEntityScanner.skipString("ID")) { type = "ID"; } else if (fEntityScanner.skipString("ENTITY")) { type = "ENTITY"; } else if (fEntityScanner.skipString("ENTITIES")) { type = "ENTITIES"; } else if (fEntityScanner.skipString("NMTOKENS")) { type = "NMTOKENS"; } else if (fEntityScanner.skipString("NMTOKEN")) { type = "NMTOKEN"; } else if (fEntityScanner.skipString("NOTATION")) { type = "NOTATION"; // spaces if (!skipSeparator(true, !scanningInternalSubset())) { reportFatalError("MSG_SPACE_REQUIRED_AFTER_NOTATION_IN_NOTATIONTYPE", new Object[]{elName, atName}); } // open paren int c = fEntityScanner.scanChar(); if (c != '(') { reportFatalError("MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE", new Object[]{elName, atName}); } fMarkUpDepth++; do { skipSeparator(false, !scanningInternalSubset()); String aName = fEntityScanner.scanName(); if (aName == null) { reportFatalError("MSG_NAME_REQUIRED_IN_NOTATIONTYPE", new Object[]{elName, atName}); } ensureEnumerationSize(fEnumerationCount + 1); fEnumeration[fEnumerationCount++] = aName; skipSeparator(false, !scanningInternalSubset()); c = fEntityScanner.scanChar(); } while (c == '|'); if (c != ')') { reportFatalError("NotationTypeUnterminated", new Object[]{elName, atName}); } fMarkUpDepth--; } else { // Enumeration type = "ENUMERATION"; // open paren int c = fEntityScanner.scanChar(); if (c != '(') { // "OPEN_PAREN_REQUIRED_BEFORE_ENUMERATION_IN_ATTRDECL", reportFatalError("AttTypeRequiredInAttDef", new Object[]{elName, atName}); } fMarkUpDepth++; do { skipSeparator(false, !scanningInternalSubset()); String token = fEntityScanner.scanNmtoken(); if (token == null) { reportFatalError("MSG_NMTOKEN_REQUIRED_IN_ENUMERATION", new Object[]{elName, atName}); } ensureEnumerationSize(fEnumerationCount + 1); fEnumeration[fEnumerationCount++] = token; skipSeparator(false, !scanningInternalSubset()); c = fEntityScanner.scanChar(); } while (c == '|'); if (c != ')') { reportFatalError("EnumerationUnterminated", new Object[]{elName, atName}); } fMarkUpDepth--; } return type; } // scanAttType():String /** * Scans an attribute default declaration *
*
     * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
     * 
     *
     * @param name The name of the attribute being scanned.
     * @param defaultVal The string to fill in with the default value.
     */
    protected final String scanAttDefaultDecl(String elName, String atName,
    String type,
    XMLString defaultVal,
    XMLString nonNormalizedDefaultVal)
    throws IOException, XNIException {
        String defaultType = null;
        fString.clear();
        defaultVal.clear();
        if (fEntityScanner.skipString("#REQUIRED")) {
            defaultType = "#REQUIRED";
        }
        else if (fEntityScanner.skipString("#IMPLIED")) {
            defaultType = "#IMPLIED";
        }
        else {
            if (fEntityScanner.skipString("#FIXED")) {
                defaultType = "#FIXED";
                // spaces
                if (!skipSeparator(true, !scanningInternalSubset())) {
                    reportFatalError("MSG_SPACE_REQUIRED_AFTER_FIXED_IN_DEFAULTDECL",
                    new Object[]{elName, atName});
                }
            }
            // AttValue
            boolean isVC = !fStandalone  &&  (fSeenExternalDTD || fSeenExternalPE) ;
            scanAttributeValue(defaultVal, nonNormalizedDefaultVal, atName,
            fAttributes, 0, isVC);
        }
        return defaultType;
    } // ScanAttDefaultDecl
    /**
     * Scans an entity declaration
     * *
     * [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
     * 
     * * Note: Called after scanning past '<!ENTITY' */ private final void scanEntityDecl() throws IOException, XNIException { boolean isPEDecl = false; boolean sawPERef = false; fReportEntity = false; if (fEntityScanner.skipSpaces()) { if (!fEntityScanner.skipChar('%')) { isPEDecl = false; // } else if (skipSeparator(true, !scanningInternalSubset())) { // isPEDecl = true; } else if (scanningInternalSubset()) { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL", null); isPEDecl = true; } else if (fEntityScanner.peekChar() == '%') { // is legal skipSeparator(false, !scanningInternalSubset()); isPEDecl = true; } else { sawPERef = true; } } else if (scanningInternalSubset() || !fEntityScanner.skipChar('%')) { // or reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL", null); isPEDecl = false; } else if (fEntityScanner.skipSpaces()) { // reportFatalError("MSG_SPACE_REQUIRED_BEFORE_PERCENT_IN_PEDECL", null); isPEDecl = false; } else { sawPERef = true; } if (sawPERef) { while (true) { String peName = fEntityScanner.scanName(); if (peName == null) { reportFatalError("NameRequiredInPEReference", null); } else if (!fEntityScanner.skipChar(';')) { reportFatalError("SemicolonRequiredInPEReference", new Object[]{peName}); } else { startPE(peName, false); } fEntityScanner.skipSpaces(); if (!fEntityScanner.skipChar('%')) break; if (!isPEDecl) { if (skipSeparator(true, !scanningInternalSubset())) { isPEDecl = true; break; } isPEDecl = fEntityScanner.skipChar('%'); } } } // name String name = fEntityScanner.scanName(); if (name == null) { reportFatalError("MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL", null); } // spaces if (!skipSeparator(true, !scanningInternalSubset())) { reportFatalError("MSG_SPACE_REQUIRED_AFTER_ENTITY_NAME_IN_ENTITYDECL", new Object[]{name}); } // external id scanExternalID(fStrings, false); String systemId = fStrings[0]; String publicId = fStrings[1]; if (isPEDecl && systemId != null) { fSeenExternalPE = true; } String notation = null; // NDATA boolean sawSpace = skipSeparator(true, !scanningInternalSubset()); if (!isPEDecl && fEntityScanner.skipString("NDATA")) { // check whether there was space before NDATA if (!sawSpace) { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NDATA_IN_UNPARSED_ENTITYDECL", new Object[]{name}); } // spaces if (!skipSeparator(true, !scanningInternalSubset())) { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_UNPARSED_ENTITYDECL", new Object[]{name}); } notation = fEntityScanner.scanName(); if (notation == null) { reportFatalError("MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL", new Object[]{name}); } } // internal entity if (systemId == null) { scanEntityValue(name, isPEDecl, fLiteral, fLiteral2); // since we need it's value anyway, let's snag it so it doesn't get corrupted // if a new load takes place before we store the entity values fStringBuffer.clear(); fStringBuffer2.clear(); fStringBuffer.append(fLiteral.ch, fLiteral.offset, fLiteral.length); fStringBuffer2.append(fLiteral2.ch, fLiteral2.offset, fLiteral2.length); } // skip possible trailing space skipSeparator(false, !scanningInternalSubset()); // end if (!fEntityScanner.skipChar('>')) { reportFatalError("EntityDeclUnterminated", new Object[]{name}); } fMarkUpDepth--; // register entity and make callback if (isPEDecl) { name = "%" + name; } if (systemId != null) { String baseSystemId = fEntityScanner.getBaseSystemId(); if (notation != null) { fEntityStore.addUnparsedEntity(name, publicId, systemId, baseSystemId, notation); } else { fEntityStore.addExternalEntity(name, publicId, systemId, baseSystemId); } if (fDTDHandler != null) { //Venu Revisit : why false has been removed in expandSYstem fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId )); if (notation != null) { fDTDHandler.unparsedEntityDecl(name, fResourceIdentifier, notation, null); } else { fDTDHandler.externalEntityDecl(name, fResourceIdentifier, null); } } } else { fEntityStore.addInternalEntity(name, fStringBuffer.toString()); if (fDTDHandler != null) { fDTDHandler.internalEntityDecl(name, fStringBuffer, fStringBuffer2, null); } } fReportEntity = true; } // scanEntityDecl() /** * Scans an entity value. * * @param value The string to fill in with the value. * @param nonNormalizedValue The string to fill in with the * non-normalized value. * * Note: This method uses fString, fStringBuffer (through * the use of scanCharReferenceValue), and fStringBuffer2, anything in them * at the time of calling is lost. */ protected final void scanEntityValue(String entityName, boolean isPEDecl, XMLString value, XMLString nonNormalizedValue) throws IOException, XNIException { int quote = fEntityScanner.scanChar(); if (quote != '\'' && quote != '"') { reportFatalError("OpenQuoteMissingInDecl", null); } // store at which depth of entities we start int entityDepth = fEntityDepth; XMLString literal = fString; XMLString literal2 = fString; int countChar = 0; if (fLimitAnalyzer == null && fSecurityManager != null) { fLimitAnalyzer = fSecurityManager.getLimitAnalyzer(); fLimitAnalyzer.startEntity(entityName); } if (fEntityScanner.scanLiteral(quote, fString) != quote) { fStringBuffer.clear(); fStringBuffer2.clear(); do { if (isPEDecl && fLimitAnalyzer != null) { checkLimit("%" + entityName, fString.length + countChar); } countChar = 0; fStringBuffer.append(fString); fStringBuffer2.append(fString); if (fEntityScanner.skipChar('&')) { if (fEntityScanner.skipChar('#')) { fStringBuffer2.append(""); scanCharReferenceValue(fStringBuffer, fStringBuffer2); } else { fStringBuffer.append('&'); fStringBuffer2.append('&'); String eName = fEntityScanner.scanName(); if (eName == null) { reportFatalError("NameRequiredInReference", null); } else { fStringBuffer.append(eName); fStringBuffer2.append(eName); } if (!fEntityScanner.skipChar(';')) { reportFatalError("SemicolonRequiredInReference", new Object[]{eName}); } else { fStringBuffer.append(';'); fStringBuffer2.append(';'); } } } else if (fEntityScanner.skipChar('%')) { while (true) { fStringBuffer2.append('%'); String peName = fEntityScanner.scanName(); if (peName == null) { reportFatalError("NameRequiredInPEReference", null); } else if (!fEntityScanner.skipChar(';')) { reportFatalError("SemicolonRequiredInPEReference", new Object[]{peName}); } else { if (scanningInternalSubset()) { reportFatalError("PEReferenceWithinMarkup", new Object[]{peName}); } fStringBuffer2.append(peName); fStringBuffer2.append(';'); } startPE(peName, true); // REVISIT: [Q] Why do we skip spaces here? -Ac // REVISIT: This will make returning the non- // normalized value harder. -Ac fEntityScanner.skipSpaces(); if (!fEntityScanner.skipChar('%')) break; } } else { countChar++; int c = fEntityScanner.peekChar(); if (XMLChar.isHighSurrogate(c)) { scanSurrogates(fStringBuffer2); } else if (isInvalidLiteral(c)) { reportFatalError("InvalidCharInLiteral", new Object[]{Integer.toHexString(c)}); fEntityScanner.scanChar(); } // if it's not the delimiting quote or if it is but from a // different entity than the one this literal started from, // simply append the character to our buffer else if (c != quote || entityDepth != fEntityDepth) { fStringBuffer.append((char)c); fStringBuffer2.append((char)c); fEntityScanner.scanChar(); } } } while (fEntityScanner.scanLiteral(quote, fString) != quote); fStringBuffer.append(fString); fStringBuffer2.append(fString); literal = fStringBuffer; literal2 = fStringBuffer2; } else { if (isPEDecl) { checkLimit("%" + entityName, literal); } } value.setValues(literal); nonNormalizedValue.setValues(literal2); if (fLimitAnalyzer != null) { fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName); } if (!fEntityScanner.skipChar(quote)) { reportFatalError("CloseQuoteMissingInDecl", null); } } // scanEntityValue(XMLString,XMLString):void /** * Scans a notation declaration *
*
     * [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID|PublicID) S? '>'
     * [83]  PublicID    ::= 'PUBLIC' S PubidLiteral
     * 
     * * Note: Called after scanning past '<!NOTATION' */ private final void scanNotationDecl() throws IOException, XNIException { // spaces fReportEntity = false; if (!skipSeparator(true, !scanningInternalSubset())) { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_NOTATIONDECL", null); } // notation name String name = fEntityScanner.scanName(); if (name == null) { reportFatalError("MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL", null); } // spaces if (!skipSeparator(true, !scanningInternalSubset())) { reportFatalError("MSG_SPACE_REQUIRED_AFTER_NOTATION_NAME_IN_NOTATIONDECL", new Object[]{name}); } // external id scanExternalID(fStrings, true); String systemId = fStrings[0]; String publicId = fStrings[1]; String baseSystemId = fEntityScanner.getBaseSystemId(); if (systemId == null && publicId == null) { reportFatalError("ExternalIDorPublicIDRequired", new Object[]{name}); } // skip possible trailing space skipSeparator(false, !scanningInternalSubset()); // end if (!fEntityScanner.skipChar('>')) { reportFatalError("NotationDeclUnterminated", new Object[]{name}); } fMarkUpDepth--; fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId )); if (nonValidatingMode) nvGrammarInfo.notationDecl(name, fResourceIdentifier, null); // call handler if (fDTDHandler != null) { //Venu Revisit wby false has been removed. //fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId, false)); fDTDHandler.notationDecl(name, fResourceIdentifier, null); } fReportEntity = true; } // scanNotationDecl() /** * Scans a conditional section. If it's a section to ignore the whole * section gets scanned through and this method only returns after the * closing bracket has been found. When it's an include section though, it * returns to let the main loop take care of scanning it. In that case the * end of the section if handled by the main loop (scanDecls). *
*
     * [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*)
     * 
     * * Note: Called after scanning past '<![' */ private final void scanConditionalSect(int currPEDepth) throws IOException, XNIException { fReportEntity = false; skipSeparator(false, !scanningInternalSubset()); if (fEntityScanner.skipString("INCLUDE")) { skipSeparator(false, !scanningInternalSubset()); if(currPEDepth != fPEDepth && fValidation) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "INVALID_PE_IN_CONDITIONAL", new Object[]{ fEntityManager.fCurrentEntity.name}, XMLErrorReporter.SEVERITY_ERROR); } // call handler if (!fEntityScanner.skipChar('[')) { reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); } if (fDTDHandler != null) { fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_INCLUDE, null); } fIncludeSectDepth++; // just stop there and go back to the main loop fReportEntity = true; } else if (fEntityScanner.skipString("IGNORE")) { skipSeparator(false, !scanningInternalSubset()); if(currPEDepth != fPEDepth && fValidation) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "INVALID_PE_IN_CONDITIONAL", new Object[]{ fEntityManager.fCurrentEntity.name}, XMLErrorReporter.SEVERITY_ERROR); } // call handler if (fDTDHandler != null) { fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_IGNORE, null); } if (!fEntityScanner.skipChar('[')) { reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); } fReportEntity = true; int initialDepth = ++fIncludeSectDepth; if (fDTDHandler != null) { fIgnoreConditionalBuffer.clear(); } while (true) { if (fEntityScanner.skipChar('<')) { if (fDTDHandler != null) { fIgnoreConditionalBuffer.append('<'); } // // These tests are split so that we handle cases like // '<', etc. // if (fEntityScanner.skipChar(']')) { if (fDTDHandler != null) { fIgnoreConditionalBuffer.append(']'); } while (fEntityScanner.skipChar(']')) { /* empty loop body */ if (fDTDHandler != null) { fIgnoreConditionalBuffer.append(']'); } } if (fEntityScanner.skipChar('>')) { if (fIncludeSectDepth-- == initialDepth) { fMarkUpDepth--; // call handler if (fDTDHandler != null) { fLiteral.setValues(fIgnoreConditionalBuffer.ch, 0, fIgnoreConditionalBuffer.length - 2); fDTDHandler.ignoredCharacters(fLiteral, null); fDTDHandler.endConditional(null); } return; } else if(fDTDHandler != null) { fIgnoreConditionalBuffer.append('>'); } } } } else { int c = fEntityScanner.scanChar(); if (fScannerState == SCANNER_STATE_END_OF_INPUT) { reportFatalError("IgnoreSectUnterminated", null); return; } if (fDTDHandler != null) { fIgnoreConditionalBuffer.append((char)c); } } } } else { reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); } } // scanConditionalSect() /** * Dispatch an XML "event". * * @param complete True if this method is intended to scan * and dispatch as much as possible. * * @return True if there is more to scan. * * @throws IOException Thrown on i/o error. * @throws XNIException Thrown on parse error. * */ protected final boolean scanDecls(boolean complete) throws IOException, XNIException { skipSeparator(false, true); boolean again = true; //System.out.println("scanDecls"+fScannerState); while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) { again = complete; if (fEntityScanner.skipChar('<')) { fMarkUpDepth++; if (fEntityScanner.skipChar('?')) { fStringBuffer.clear(); scanPI(fStringBuffer); fMarkUpDepth--; // we're done with this decl } else if (fEntityScanner.skipChar('!')) { if (fEntityScanner.skipChar('-')) { if (!fEntityScanner.skipChar('-')) { reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); } else { scanComment(); } } else if (fEntityScanner.skipString("ELEMENT")) { scanElementDecl(); } else if (fEntityScanner.skipString("ATTLIST")) { scanAttlistDecl(); } else if (fEntityScanner.skipString("ENTITY")) { scanEntityDecl(); } else if (fEntityScanner.skipString("NOTATION")) { scanNotationDecl(); } else if (fEntityScanner.skipChar('[') && !scanningInternalSubset()) { scanConditionalSect(fPEDepth); } else { fMarkUpDepth--; reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); } } else { fMarkUpDepth--; reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); } } else if (fIncludeSectDepth > 0 && fEntityScanner.skipChar(']')) { // end of conditional section? if (!fEntityScanner.skipChar(']') || !fEntityScanner.skipChar('>')) { reportFatalError("IncludeSectUnterminated", null); } // call handler if (fDTDHandler != null) { fDTDHandler.endConditional(null); } // decreaseMarkupDepth(); fIncludeSectDepth--; fMarkUpDepth--; } else if (scanningInternalSubset() && fEntityScanner.peekChar() == ']') { // this is the end of the internal subset, let's stop here return false; } else if (fEntityScanner.skipSpaces()) { // simply skip } else { reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); } skipSeparator(false, true); } return fScannerState != SCANNER_STATE_END_OF_INPUT; } /** * Skip separator. This is typically just whitespace but it can also be one * or more parameter entity references. *
* If there are some it "expands them" by calling the corresponding entity * from the entity manager. *
* This is recursive and will process has many refs as possible. * * @param spaceRequired Specify whether some leading whitespace should be * found * @param lookForPERefs Specify whether parameter entity references should * be looked for * @return True if any leading whitespace was found or the end of a * parameter entity was crossed. */ private boolean skipSeparator(boolean spaceRequired, boolean lookForPERefs) throws IOException, XNIException { int depth = fPEDepth; boolean sawSpace = fEntityScanner.skipSpaces(); if (!lookForPERefs || !fEntityScanner.skipChar('%')) { return !spaceRequired || sawSpace || (depth != fPEDepth); } while (true) { String name = fEntityScanner.scanName(); if (name == null) { reportFatalError("NameRequiredInPEReference", null); } else if (!fEntityScanner.skipChar(';')) { reportFatalError("SemicolonRequiredInPEReference", new Object[]{name}); } startPE(name, false); fEntityScanner.skipSpaces(); if (!fEntityScanner.skipChar('%')) return true; } } /* * Element Children Content Stack */ private final void pushContentStack(int c) { if (fContentStack.length == fContentDepth) { int[] newStack = new int[fContentDepth * 2]; System.arraycopy(fContentStack, 0, newStack, 0, fContentDepth); fContentStack = newStack; } fContentStack[fContentDepth++] = c; } private final int popContentStack() { return fContentStack[--fContentDepth]; } /* * Parameter Entity Stack */ private final void pushPEStack(int depth, boolean report) { if (fPEStack.length == fPEDepth) { int[] newIntStack = new int[fPEDepth * 2]; System.arraycopy(fPEStack, 0, newIntStack, 0, fPEDepth); fPEStack = newIntStack; // report end/start calls boolean[] newBooleanStack = new boolean[fPEDepth * 2]; System.arraycopy(fPEReport, 0, newBooleanStack, 0, fPEDepth); fPEReport = newBooleanStack; } fPEReport[fPEDepth] = report; fPEStack[fPEDepth++] = depth; } /** pop the stack */ private final int popPEStack() { return fPEStack[--fPEDepth]; } /** look at the top of the stack */ private final boolean peekReportEntity() { return fPEReport[fPEDepth-1]; } /* * Utility method */ private final void ensureEnumerationSize(int size) { if (fEnumeration.length == size) { String[] newEnum = new String[size * 2]; System.arraycopy(fEnumeration, 0, newEnum, 0, size); fEnumeration = newEnum; } } // private methods private void init() { // reset state related data fStartDTDCalled = false; fExtEntityDepth = 0; fIncludeSectDepth = 0; fMarkUpDepth = 0; fPEDepth = 0; fStandalone = false; fSeenExternalDTD = false; fSeenExternalPE = false; // set starting state setScannerState(SCANNER_STATE_TEXT_DECL); //new SymbolTable()); } /** * Add the count of the content buffer and check if the accumulated * value exceeds the limit * @param entityName entity name * @param buffer content buffer */ private void checkLimit(String entityName, XMLString buffer) { checkLimit(entityName, buffer.length); } /** * Add the count and check limit * @param entityName entity name * @param len length of the buffer */ private void checkLimit(String entityName, int len) { if (fLimitAnalyzer == null) { fLimitAnalyzer = fSecurityManager.getLimitAnalyzer(); } fLimitAnalyzer.addValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName, len); if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)) { fSecurityManager.debugPrint(); reportFatalError("MaxEntitySizeLimit", new Object[]{entityName, fLimitAnalyzer.getValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT), fSecurityManager.getLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT), fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)}); } if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)) { fSecurityManager.debugPrint(); reportFatalError("TotalEntitySizeLimit", new Object[]{fLimitAnalyzer.getTotalValue(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT), fSecurityManager.getLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT), fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)}); } } public DTDGrammar getGrammar(){ return nvGrammarInfo; } } // class XMLDTDScannerImpl