You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xerces.apache.org by mr...@apache.org on 2004/04/09 00:24:52 UTC

cvs commit: xml-xerces/java/src/org/apache/xerces/impl XMLDocumentScannerImpl.java XML11NSDocumentScannerImpl.java XMLNSDocumentScannerImpl.java XMLDocumentFragmentScannerImpl.java

mrglavas    2004/04/08 15:24:52

  Modified:    java/src/org/apache/xerces/impl XMLDocumentScannerImpl.java
                        XML11NSDocumentScannerImpl.java
                        XMLNSDocumentScannerImpl.java
                        XMLDocumentFragmentScannerImpl.java
  Log:
  Infrastructure work in the scanners to support the SAX2 Extensions 1.1
  interface EntityResolver2, specifically getExternalSubset which
  allows an application to provide an external subset for a document
  which doesn't otherwise have one.
  
  The scanners are now able to handle all three cases where a resolver
  may be queried for external subset:
  
  1) Neither an external or internal subset exist.
  2) Only an internal subset exists.
  3) No DOCTYPE declaration in the document.
  
  In the third case, scanning of the root element is broken up into
  three segments:
  
  1) Scan the root element QName.
  2) Query the resolver with the root name and base URI, if it 
  locates an external subset then read it.
  3) Scan the remainder of the start tag.
  
  If an external subset is located the public id and system id
  from the XMLInputSource returned from the resolver are
  reported to doctypeDecl. This is what SAX expects.
  
  Revision  Changes    Path
  1.44      +109 -29   xml-xerces/java/src/org/apache/xerces/impl/XMLDocumentScannerImpl.java
  
  Index: XMLDocumentScannerImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/XMLDocumentScannerImpl.java,v
  retrieving revision 1.43
  retrieving revision 1.44
  diff -u -r1.43 -r1.44
  --- XMLDocumentScannerImpl.java	16 Mar 2004 22:03:22 -0000	1.43
  +++ XMLDocumentScannerImpl.java	8 Apr 2004 22:24:52 -0000	1.44
  @@ -20,6 +20,7 @@
   import java.io.EOFException;
   import java.io.IOException;
   
  +import org.apache.xerces.impl.dtd.XMLDTDDescription;
   import org.apache.xerces.impl.io.MalformedByteSequenceException;
   import org.apache.xerces.impl.validation.ValidationManager;
   import org.apache.xerces.util.NamespaceSupport;
  @@ -210,6 +211,9 @@
   
       /** String buffer. */
       private XMLStringBuffer fStringBuffer = new XMLStringBuffer();
  +    
  +    /** External subset source. */
  +    private XMLInputSource fExternalSubsetSource = null;
   
       //
       // Constructors
  @@ -263,8 +267,8 @@
           fDoctypePublicId = null;
           fDoctypeSystemId = null;
           fSeenDoctypeDecl = false;
  -		fScanningDTD = false;
  -       
  +        fScanningDTD = false;
  +        fExternalSubsetSource = null;
   
   		if (!fParserSettings) {
   			// parser settings have not been changed
  @@ -554,6 +558,14 @@
           }
   
           fHasExternalDTD = fDoctypeSystemId != null;
  +        
  +        // Attempt to locate an external subset with an external subset resolver.
  +        if (!fHasExternalDTD && fExternalSubsetResolver != null) {
  +            XMLDTDDescription desc = new XMLDTDDescription(null, 
  +                null, fEntityManager.getCurrentResourceIdentifier().getExpandedSystemId(), null, fDoctypeName);
  +            fExternalSubsetSource = fExternalSubsetResolver.getExternalSubset(desc);
  +            fHasExternalDTD = fExternalSubsetSource != null;
  +        }
   
           // call handler
           if (fDocumentHandler != null) {
  @@ -562,8 +574,12 @@
               //       subset) is parsed correctly but SAX2 requires that
               //       it knows the root element name and public and system
               //       identifier for the startDTD call. -Ac
  -            fDocumentHandler.doctypeDecl(fDoctypeName, fDoctypePublicId, fDoctypeSystemId,
  -                                         null);
  +            if (fExternalSubsetSource == null) {
  +                fDocumentHandler.doctypeDecl(fDoctypeName, fDoctypePublicId, fDoctypeSystemId, null);
  +            }
  +            else {
  +                fDocumentHandler.doctypeDecl(fDoctypeName, fExternalSubsetSource.getPublicId(), fExternalSubsetSource.getSystemId(), null);
  +            }
           }
   
           // is there an internal subset?
  @@ -805,22 +821,36 @@
                                   setDispatcher(fDTDDispatcher);
                                   return true;
                               }
  -                            if (fDoctypeSystemId != null && ((fValidation || fLoadExternalDTD) 
  +                            
  +                            // handle external subset
  +                            if (fDoctypeSystemId != null) {
  +                                if (((fValidation || fLoadExternalDTD) 
                                       && (fValidationManager == null || !fValidationManager.isCachedDTD()))) {
  -                                setScannerState(SCANNER_STATE_DTD_EXTERNAL);
  -                                setDispatcher(fDTDDispatcher);
  -                                return true;
  -                            } 
  -                            else {
  -                                // Send endDTD() call if: 
  -                                // a) systemId is null
  -                                // b) "load-external-dtd" and validation are false
  -                                // c) DTD grammar is cached
  -                                
  -                                // in XNI this results in 3 events:  doctypeDecl, startDTD, endDTD
  -                                // in SAX this results in 2 events: startDTD, endDTD
  -                                fDTDScanner.setInputSource(null);
  +                                    setScannerState(SCANNER_STATE_DTD_EXTERNAL);
  +                                    setDispatcher(fDTDDispatcher);
  +                                    return true;
  +                                }
                               }
  +                            else if (fExternalSubsetSource != null) {
  +                                if (((fValidation || fLoadExternalDTD) 
  +                                    && (fValidationManager == null || !fValidationManager.isCachedDTD()))) {
  +                                    // This handles the case of a DOCTYPE that had neither an internal subset or an external subset.
  +                                    fDTDScanner.setInputSource(fExternalSubsetSource);
  +                                    fExternalSubsetSource = null;
  +                                    setScannerState(SCANNER_STATE_DTD_EXTERNAL_DECLS);
  +                                    setDispatcher(fDTDDispatcher);
  +                                    return true;
  +                                }                       	
  +                            }
  +                            
  +                            // Send endDTD() call if: 
  +                            // a) systemId is null or if an external subset resolver could not locate an external subset.
  +                            // b) "load-external-dtd" and validation are false
  +                            // c) DTD grammar is cached
  +                                
  +                            // in XNI this results in 3 events:  doctypeDecl, startDTD, endDTD
  +                            // in SAX this results in 2 events: startDTD, endDTD
  +                            fDTDScanner.setInputSource(null);
                               setScannerState(SCANNER_STATE_PROLOG);
                               break;
                           }
  @@ -917,18 +947,29 @@
                                   fMarkupDepth--;
   
                                   // scan external subset next
  -                                if (fDoctypeSystemId != null && (fValidation || fLoadExternalDTD) 
  +                                if (fDoctypeSystemId != null) {
  +                                    if ((fValidation || fLoadExternalDTD) 
                                           && (fValidationManager == null || !fValidationManager.isCachedDTD())) {
  -                                    setScannerState(SCANNER_STATE_DTD_EXTERNAL);
  +                                        setScannerState(SCANNER_STATE_DTD_EXTERNAL);
  +                                        break;
  +                                    }
                                   }
  -
  -                                // break out of here
  -                                else {
  -                                    setScannerState(SCANNER_STATE_PROLOG);
  -                                    setDispatcher(fPrologDispatcher);
  -                                    fEntityManager.setEntityHandler(XMLDocumentScannerImpl.this);
  -                                    return true;
  +                                else if (fExternalSubsetSource != null) {
  +                                    if ((fValidation || fLoadExternalDTD) 
  +                                        && (fValidationManager == null || !fValidationManager.isCachedDTD())) {
  +                                        // This handles the case of a DOCTYPE that only had an internal subset.
  +                                        fDTDScanner.setInputSource(fExternalSubsetSource);
  +                                        fExternalSubsetSource = null;
  +                                        setScannerState(SCANNER_STATE_DTD_EXTERNAL_DECLS);
  +                                        break;
  +                                    }
                                   }
  +                                
  +                                // break out of this dispatcher.
  +                                setScannerState(SCANNER_STATE_PROLOG);
  +                                setDispatcher(fPrologDispatcher);
  +                                fEntityManager.setEntityHandler(XMLDocumentScannerImpl.this);
  +                                return true;
                               }
                               break;
                           }
  @@ -1062,7 +1103,17 @@
           protected boolean scanRootElementHook()
               throws IOException, XNIException {
   
  -            if (scanStartElement()) {
  +            if (fExternalSubsetResolver != null && !fSeenDoctypeDecl 
  +                && (fValidation || fLoadExternalDTD)) {
  +                scanStartElementName();
  +                resolveExternalSubsetAndRead();
  +                if (scanStartElementAfterName()) {
  +                    setScannerState(SCANNER_STATE_TRAILING_MISC);
  +                    setDispatcher(fTrailingMiscDispatcher);
  +                    return true;
  +                }
  +            }
  +            else if (scanStartElement()) {
                   setScannerState(SCANNER_STATE_TRAILING_MISC);
                   setDispatcher(fTrailingMiscDispatcher);
                   return true;
  @@ -1086,6 +1137,35 @@
               //throw e;
   
           } // endOfFileHook()
  +        
  +        /**
  +         * <p>Attempt to locate an external subset for a document that does not otherwise
  +         * have one. If an external subset is located, then it is scanned.</p>
  +         */
  +        protected void resolveExternalSubsetAndRead()
  +            throws IOException, XNIException {
  +            XMLDTDDescription desc = new XMLDTDDescription(null, 
  +                null, fEntityManager.getCurrentResourceIdentifier().getExpandedSystemId(), null, fElementQName.rawname);
  +            XMLInputSource src = fExternalSubsetResolver.getExternalSubset(desc);
  +            if (src != null) {
  +                fDoctypeName = fElementQName.rawname;
  +                fDoctypePublicId = src.getPublicId();
  +                fDoctypeSystemId = src.getSystemId();
  +                // call document handler
  +                if (fDocumentHandler != null) {
  +                    // This inserts a doctypeDecl event into the stream though no 
  +                    // DOCTYPE existed in the instance document.
  +                    fDocumentHandler.doctypeDecl(fDoctypeName, fDoctypePublicId, fDoctypeSystemId, null);
  +                }
  +                try {
  +                    fDTDScanner.setInputSource(src);
  +                    while (fDTDScanner.scanDTDExternalSubset(true));
  +                }
  +                finally {
  +                    fEntityManager.setEntityHandler(XMLDocumentScannerImpl.this);
  +                }
  +            }
  +        } // resolveExternalSubsetAndRead()
   
       } // class ContentDispatcher
   
  
  
  
  1.10      +271 -27   xml-xerces/java/src/org/apache/xerces/impl/XML11NSDocumentScannerImpl.java
  
  Index: XML11NSDocumentScannerImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/XML11NSDocumentScannerImpl.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- XML11NSDocumentScannerImpl.java	24 Feb 2004 23:03:46 -0000	1.9
  +++ XML11NSDocumentScannerImpl.java	8 Apr 2004 22:24:52 -0000	1.10
  @@ -80,6 +80,15 @@
   
       /** DTD validator */
       private XMLDTDValidatorFilter fDTDValidator;
  +    
  +    /** 
  +     * Saw spaces after element name or between attributes.
  +     * 
  +     * This is reserved for the case where scanning of a start element spans
  +     * several methods, as is the case when scanning the start of a root element 
  +     * where a DTD external subset may be read after scanning the element name.
  +     */
  +    private boolean fSawSpace;
   
       /**
        * The scanner is responsible for removing DTD validator
  @@ -304,24 +313,235 @@
           return empty;
   
       } // scanStartElement():boolean
  +    
  +    /**
  +     * Scans the name of an element in a start or empty tag. 
  +     * 
  +     * @see #scanStartElement()
  +     */
  +    protected void scanStartElementName ()
  +        throws IOException, XNIException {
  +        // Note: namespace processing is on by default
  +        fEntityScanner.scanQName(fElementQName);
  +        // Must skip spaces here because the DTD scanner
  +        // would consume them at the end of the external subset.
  +        fSawSpace = fEntityScanner.skipSpaces();
  +    } // scanStartElementName()
  +    
  +    /**
  +     * Scans the remainder of a start or empty tag after the element name.
  +     * 
  +     * @see #scanStartElement
  +     * @return True if element is empty.
  +     */    
  +    protected boolean scanStartElementAfterName()
  +        throws IOException, XNIException {
  +
  +        // REVISIT - [Q] Why do we need this local variable? -- mrglavas
  +        String rawname = fElementQName.rawname;
  +        if (fBindNamespaces) {
  +            fNamespaceContext.pushContext();
  +            if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) {
  +                if (fPerformValidation) {
  +                    fErrorReporter.reportError(
  +                        XMLMessageFormatter.XML_DOMAIN,
  +                        "MSG_GRAMMAR_NOT_FOUND",
  +                        new Object[] { rawname },
  +                        XMLErrorReporter.SEVERITY_ERROR);
  +
  +                    if (fDoctypeName == null
  +                        || !fDoctypeName.equals(rawname)) {
  +                        fErrorReporter.reportError(
  +                            XMLMessageFormatter.XML_DOMAIN,
  +                            "RootElementTypeMustMatchDoctypedecl",
  +                            new Object[] { fDoctypeName, rawname },
  +                            XMLErrorReporter.SEVERITY_ERROR);
  +                    }
  +                }
  +            }
  +        }
  +
  +        // push element stack
  +        fCurrentElement = fElementStack.pushElement(fElementQName);
  +
  +        // attributes
  +        boolean empty = false;
  +        fAttributes.removeAllAttributes();
  +        do {
  +        	
  +            // end tag?
  +            int c = fEntityScanner.peekChar();
  +            if (c == '>') {
  +                fEntityScanner.scanChar();
  +                break;
  +            } else if (c == '/') {
  +                fEntityScanner.scanChar();
  +                if (!fEntityScanner.skipChar('>')) {
  +                    reportFatalError(
  +                        "ElementUnterminated",
  +                        new Object[] { rawname });
  +                }
  +                empty = true;
  +                break;
  +            } else if (!isValidNameStartChar(c) || !fSawSpace) {
  +                // Second chance. Check if this character is a high
  +                // surrogate of a valid name start character.
  +                if (!isValidNameStartHighSurrogate(c) || !fSawSpace) {
  +                    reportFatalError(
  +                        "ElementUnterminated",
  +                        new Object[] { rawname });
  +                }
  +            }
  +
  +            // attributes
  +            scanAttribute(fAttributes);
  +            
  +            // spaces
  +            fSawSpace = fEntityScanner.skipSpaces();
  +
  +        } while (true);
  +
  +        if (fBindNamespaces) {
  +            // REVISIT: is it required? forbit xmlns prefix for element
  +            if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) {
  +                fErrorReporter.reportError(
  +                    XMLMessageFormatter.XMLNS_DOMAIN,
  +                    "ElementXMLNSPrefix",
  +                    new Object[] { fElementQName.rawname },
  +                    XMLErrorReporter.SEVERITY_FATAL_ERROR);
  +            }
  +
  +            // bind the element
  +            String prefix =
  +                fElementQName.prefix != null
  +                    ? fElementQName.prefix
  +                    : XMLSymbols.EMPTY_STRING;
  +            // assign uri to the element
  +            fElementQName.uri = fNamespaceContext.getURI(prefix);
  +            // make sure that object in the element stack is updated as well
  +            fCurrentElement.uri = fElementQName.uri;
  +
  +            if (fElementQName.prefix == null && fElementQName.uri != null) {
  +                fElementQName.prefix = XMLSymbols.EMPTY_STRING;
  +                // making sure that the object in the element stack is updated too.
  +                fCurrentElement.prefix = XMLSymbols.EMPTY_STRING;
  +            }
  +            if (fElementQName.prefix != null && fElementQName.uri == null) {
  +                fErrorReporter.reportError(
  +                    XMLMessageFormatter.XMLNS_DOMAIN,
  +                    "ElementPrefixUnbound",
  +                    new Object[] {
  +                        fElementQName.prefix,
  +                        fElementQName.rawname },
  +                    XMLErrorReporter.SEVERITY_FATAL_ERROR);
  +            }
  +
  +            // bind attributes (xmlns are already bound bellow)
  +            int length = fAttributes.getLength();
  +            for (int i = 0; i < length; i++) {
  +                fAttributes.getName(i, fAttributeQName);
  +
  +                String aprefix =
  +                    fAttributeQName.prefix != null
  +                        ? fAttributeQName.prefix
  +                        : XMLSymbols.EMPTY_STRING;
  +                String uri = fNamespaceContext.getURI(aprefix);
  +                // REVISIT: try removing the first "if" and see if it is faster.
  +                //
  +                if (fAttributeQName.uri != null
  +                    && fAttributeQName.uri == uri) {
  +                    continue;
  +                }
  +                if (aprefix != XMLSymbols.EMPTY_STRING) {
  +                    fAttributeQName.uri = uri;
  +                    if (uri == null) {
  +                        fErrorReporter.reportError(
  +                            XMLMessageFormatter.XMLNS_DOMAIN,
  +                            "AttributePrefixUnbound",
  +                            new Object[] {
  +                                fElementQName.rawname,
  +                                fAttributeQName.rawname,
  +                                aprefix },
  +                            XMLErrorReporter.SEVERITY_FATAL_ERROR);
  +                    }
  +                    fAttributes.setURI(i, uri);
  +                }
  +            }
  +
  +            if (length > 1) {
  +                QName name = fAttributes.checkDuplicatesNS();
  +                if (name != null) {
  +                    if (name.uri != null) {
  +                        fErrorReporter.reportError(
  +                            XMLMessageFormatter.XMLNS_DOMAIN,
  +                            "AttributeNSNotUnique",
  +                            new Object[] {
  +                                fElementQName.rawname,
  +                                name.localpart,
  +                                name.uri },
  +                            XMLErrorReporter.SEVERITY_FATAL_ERROR);
  +                    } else {
  +                        fErrorReporter.reportError(
  +                            XMLMessageFormatter.XMLNS_DOMAIN,
  +                            "AttributeNotUnique",
  +                            new Object[] {
  +                                fElementQName.rawname,
  +                                name.rawname },
  +                            XMLErrorReporter.SEVERITY_FATAL_ERROR);
  +                    }
  +                }
  +            }
  +        }
  +
  +        // call handler
  +        if (fDocumentHandler != null) {
  +            if (empty) {
  +
  +                //decrease the markup depth..
  +                fMarkupDepth--;
  +
  +                // check that this element was opened in the same entity
  +                if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
  +                    reportFatalError(
  +                        "ElementEntityMismatch",
  +                        new Object[] { fCurrentElement.rawname });
  +                }
  +
  +                fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
  +
  +                if (fBindNamespaces) {
  +                    fNamespaceContext.popContext();
  +                }
  +                //pop the element off the stack..
  +                fElementStack.popElement(fElementQName);
  +            } else {
  +                fDocumentHandler.startElement(fElementQName, fAttributes, null);
  +            }
  +        }
  +
  +        if (DEBUG_CONTENT_SCANNING)
  +            System.out.println("<<< scanStartElementAfterName(): " + empty);
  +        return empty;
  +        
  +    } // scanStartElementAfterName()
   
       /**
  -    * Scans an attribute.
  -    * <p>
  -    * <pre>
  -    * [41] Attribute ::= Name Eq AttValue
  -    * </pre>
  -    * <p>
  -    * <strong>Note:</strong> This method assumes that the next
  -    * character on the stream is the first character of the attribute
  -    * name.
  -    * <p>
  -    * <strong>Note:</strong> This method uses the fAttributeQName and
  -    * fQName variables. The contents of these variables will be
  -    * destroyed.
  -    *
  -    * @param attributes The attributes list for the scanned attribute.
  -    */
  +     * Scans an attribute.
  +     * <p>
  +     * <pre>
  +     * [41] Attribute ::= Name Eq AttValue
  +     * </pre>
  +     * <p>
  +     * <strong>Note:</strong> This method assumes that the next
  +     * character on the stream is the first character of the attribute
  +     * name.
  +     * <p>
  +     * <strong>Note:</strong> This method uses the fAttributeQName and
  +     * fQName variables. The contents of these variables will be
  +     * destroyed.
  +     *
  +     * @param attributes The attributes list for the scanned attribute.
  +     */
       protected void scanAttribute(XMLAttributesImpl attributes)
           throws IOException, XNIException {
           if (DEBUG_CONTENT_SCANNING)
  @@ -571,9 +791,41 @@
            */
           protected boolean scanRootElementHook()
               throws IOException, XNIException {
  +            
  +            if (fExternalSubsetResolver != null && !fSeenDoctypeDecl 
  +                && (fValidation || fLoadExternalDTD)) {
  +                scanStartElementName();
  +                resolveExternalSubsetAndRead();
  +                reconfigurePipeline();
  +                if (scanStartElementAfterName()) {
  +                    setScannerState(SCANNER_STATE_TRAILING_MISC);
  +                    setDispatcher(fTrailingMiscDispatcher);
  +                    return true;
  +                }
  +            }
  +            else {
  +                reconfigurePipeline();
  +                if (scanStartElement()) {
  +                    setScannerState(SCANNER_STATE_TRAILING_MISC);
  +                    setDispatcher(fTrailingMiscDispatcher);
  +                    return true;
  +                }
  +            }
  +            return false;
  +
  +        } // scanRootElementHook():boolean
  +        
  +        /**
  +         * Re-configures pipeline by removing the DTD validator 
  +         * if no DTD grammar exists. If no validator exists in the
  +         * pipeline or there is no DTD grammar, namespace binding
  +         * is performed by the scanner in the enclosing class.
  +         */
  +        private void reconfigurePipeline() {
               if (fDTDValidator == null) {
                   fBindNamespaces = true;
  -            } else if (!fDTDValidator.hasGrammar()) {
  +            }
  +            else if (!fDTDValidator.hasGrammar()) {
                   fBindNamespaces = true;
                   fPerformValidation = fDTDValidator.validate();
                   // re-configure pipeline
  @@ -585,14 +837,6 @@
                   fDTDValidator.setDocumentSource(null);
                   fDTDValidator.setDocumentHandler(null);
               }
  -
  -            if (scanStartElement()) {
  -                setScannerState(SCANNER_STATE_TRAILING_MISC);
  -                setDispatcher(fTrailingMiscDispatcher);
  -                return true;
  -            }
  -            return false;
  -
  -        } // scanRootElementHook():boolean
  +        } // reconfigurePipeline()
       }
   }
  
  
  
  1.22      +235 -52   xml-xerces/java/src/org/apache/xerces/impl/XMLNSDocumentScannerImpl.java
  
  Index: XMLNSDocumentScannerImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/XMLNSDocumentScannerImpl.java,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -r1.21 -r1.22
  --- XMLNSDocumentScannerImpl.java	24 Feb 2004 23:03:46 -0000	1.21
  +++ XMLNSDocumentScannerImpl.java	8 Apr 2004 22:24:52 -0000	1.22
  @@ -71,16 +71,20 @@
         *   scanner if DTD grammar is missing.*/
       protected boolean fPerformValidation;
   
  -    // protected String[] fUri= new String[4];
  -    // protected String[] fLocalpart = new String[4];
  -    // protected int fLength = 0;
  -
  -
       // private data
       //
   
       /** DTD validator */
       private XMLDTDValidatorFilter fDTDValidator;
  +    
  +    /** 
  +     * Saw spaces after element name or between attributes.
  +     * 
  +     * This is reserved for the case where scanning of a start element spans
  +     * several methods, as is the case when scanning the start of a root element 
  +     * where a DTD external subset may be read after scanning the element name.
  +     */
  +    private boolean fSawSpace;
   
   
       /**
  @@ -285,53 +289,208 @@
   
       } // scanStartElement():boolean
   
  -    /** private final void checkDuplicates(QName qname, XMLAttributesImpl attributes){
  +    /**
  +     * Scans the name of an element in a start or empty tag. 
  +     * 
  +     * @see #scanStartElement()
  +     */
  +    protected void scanStartElementName ()
  +        throws IOException, XNIException {
  +        // Note: namespace processing is on by default
  +        fEntityScanner.scanQName(fElementQName);
  +        // Must skip spaces here because the DTD scanner
  +        // would consume them at the end of the external subset.
  +        fSawSpace = fEntityScanner.skipSpaces();
  +    } // scanStartElementName()
  +
  +    /**
  +     * Scans the remainder of a start or empty tag after the element name.
  +     * 
  +     * @see #scanStartElement
  +     * @return True if element is empty.
  +     */
  +    protected boolean scanStartElementAfterName()
  +        throws IOException, XNIException {
  +    	
  +        // REVISIT - [Q] Why do we need this temp variable? -- mrglavas
  +        String rawname = fElementQName.rawname;
  +        if (fBindNamespaces) {
  +            fNamespaceContext.pushContext();
  +            if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) {
  +                if (fPerformValidation) {
  +                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  +                                               "MSG_GRAMMAR_NOT_FOUND",
  +                                               new Object[]{ rawname},
  +                                               XMLErrorReporter.SEVERITY_ERROR);
  +
  +                    if (fDoctypeName == null || !fDoctypeName.equals(rawname)) {
  +                        fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
  +                                                    "RootElementTypeMustMatchDoctypedecl",
  +                                                    new Object[]{fDoctypeName, rawname},
  +                                                    XMLErrorReporter.SEVERITY_ERROR);
  +                    }
  +                }
  +            }
  +        }
  +
  +        // push element stack
  +        fCurrentElement = fElementStack.pushElement(fElementQName);
  +
  +        // attributes
  +        boolean empty = false;
  +        fAttributes.removeAllAttributes();
  +        do {
  +        	
  +            // end tag?
  +            int c = fEntityScanner.peekChar();
  +            if (c == '>') {
  +                fEntityScanner.scanChar();
  +                break;
  +            }
  +            else if (c == '/') {
  +                fEntityScanner.scanChar();
  +                if (!fEntityScanner.skipChar('>')) {
  +                    reportFatalError("ElementUnterminated",
  +                                     new Object[]{rawname});
  +                }
  +                empty = true;
  +                break;
  +            }
  +            else if (!isValidNameStartChar(c) || !fSawSpace) {
  +                reportFatalError("ElementUnterminated", new Object[]{rawname});
  +            }
  +
  +            // attributes
  +            scanAttribute(fAttributes);
  +            
  +            // spaces
  +            fSawSpace = fEntityScanner.skipSpaces();
  +
  +        } while (true);
  +
  +        if (fBindNamespaces) {
  +            // REVISIT: is it required? forbit xmlns prefix for element
  +            if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) {
  +                fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
  +                                           "ElementXMLNSPrefix",
  +                                           new Object[]{fElementQName.rawname},
  +                                           XMLErrorReporter.SEVERITY_FATAL_ERROR);
  +            }
  +
  +            // bind the element
  +            String prefix = fElementQName.prefix != null
  +                            ? fElementQName.prefix : XMLSymbols.EMPTY_STRING;
  +            // assign uri to the element
  +            fElementQName.uri = fNamespaceContext.getURI(prefix);
  +            // make sure that object in the element stack is updated as well
  +            fCurrentElement.uri = fElementQName.uri;
   
  -        // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/>
  -        // search if such attribute already exists
  -        for (int i = 0; i < fLength; i++) {
  -            if (qname.uri == fUri[i] && fLocalpart[i].equals(qname.localpart)) {
  +            if (fElementQName.prefix == null && fElementQName.uri != null) {
  +                fElementQName.prefix = XMLSymbols.EMPTY_STRING;
  +                // making sure that the object in the element stack is updated too.
  +                fCurrentElement.prefix = XMLSymbols.EMPTY_STRING;
  +            }
  +            if (fElementQName.prefix != null && fElementQName.uri == null) {
                   fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
  -                                           "AttributeNSNotUnique",
  -                                           new Object[]{fElementQName.rawname,qname.rawname, qname.uri},
  +                                           "ElementPrefixUnbound",
  +                                           new Object[]{fElementQName.prefix, fElementQName.rawname},
                                              XMLErrorReporter.SEVERITY_FATAL_ERROR);
               }
  -        }
  -        int index = fLength;
  -        if (fLength++ == fUri.length) {
  -            String[] uris = new String[fUri.length + 4];
  -            String[] lps = new String [fUri.length + 4];
  -            System.arraycopy(fUri, 0, uris, 0, fUri.length);
  -            System.arraycopy(fLocalpart, 0, lps, 0, fLocalpart.length);
  -            fUri = uris;
  -            fLocalpart = lps;
   
  +            // bind attributes (xmlns are already bound bellow)
  +            int length = fAttributes.getLength();
  +            // fLength = 0; //initialize structure
  +            for (int i = 0; i < length; i++) {
  +                fAttributes.getName(i, fAttributeQName);
  +
  +                String aprefix = fAttributeQName.prefix != null
  +                                 ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
  +                String uri = fNamespaceContext.getURI(aprefix);
  +                // REVISIT: try removing the first "if" and see if it is faster.
  +                //
  +                if (fAttributeQName.uri != null && fAttributeQName.uri == uri) {
  +                    // checkDuplicates(fAttributeQName, fAttributes);
  +                    continue;
  +                }
  +                if (aprefix != XMLSymbols.EMPTY_STRING) {
  +                    fAttributeQName.uri = uri;
  +                    if (uri == null) {
  +                        fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
  +                                                   "AttributePrefixUnbound",
  +                                                   new Object[]{fElementQName.rawname,fAttributeQName.rawname,aprefix},
  +                                                   XMLErrorReporter.SEVERITY_FATAL_ERROR);
  +                    }
  +                    fAttributes.setURI(i, uri);
  +                    // checkDuplicates(fAttributeQName, fAttributes);
  +                }
  +            }
  +            
  +            if (length > 1) {
  +                QName name = fAttributes.checkDuplicatesNS();
  +                if (name != null) {
  +                    if (name.uri != null) {
  +                        fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
  +                                                   "AttributeNSNotUnique",
  +                                                   new Object[]{fElementQName.rawname, name.localpart, name.uri},
  +                                                   XMLErrorReporter.SEVERITY_FATAL_ERROR);
  +                    }
  +                    else {
  +                        fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
  +                                                   "AttributeNotUnique",
  +                                                   new Object[]{fElementQName.rawname, name.rawname}, 
  +                                                   XMLErrorReporter.SEVERITY_FATAL_ERROR);
  +                    }
  +                }
  +            }
           }
   
  -        fUri[index] = qname.uri;
  -        fLocalpart[index] = qname.localpart;
  -    } **/
   
  +        // call handler
  +        if (fDocumentHandler != null) {
  +            if (empty) {
  +
  +                //decrease the markup depth..
  +                fMarkupDepth--;
  +
  +                // check that this element was opened in the same entity
  +                if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
  +                    reportFatalError("ElementEntityMismatch",
  +                                     new Object[]{fCurrentElement.rawname});
  +                }
   
  +                fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
   
  +                if (fBindNamespaces) {
  +                    fNamespaceContext.popContext();
  +                }
  +                //pop the element off the stack..
  +                fElementStack.popElement(fElementQName);
  +            } else {
  +                fDocumentHandler.startElement(fElementQName, fAttributes, null);
  +            }
  +        }
  +
  +        if (DEBUG_CONTENT_SCANNING) System.out.println("<<< scanStartElementAfterName(): "+empty);
  +        return empty;
  +    } // scanStartElementAfterName()
   
       /**
  - * Scans an attribute.
  - * <p>
  - * <pre>
  - * [41] Attribute ::= Name Eq AttValue
  - * </pre>
  - * <p>
  - * <strong>Note:</strong> This method assumes that the next
  - * character on the stream is the first character of the attribute
  - * name.
  - * <p>
  - * <strong>Note:</strong> This method uses the fAttributeQName and
  - * fQName variables. The contents of these variables will be
  - * destroyed.
  - *
  - * @param attributes The attributes list for the scanned attribute.
  - */
  +     * Scans an attribute.
  +     * <p>
  +     * <pre>
  +     * [41] Attribute ::= Name Eq AttValue
  +     * </pre>
  +     * <p>
  +     * <strong>Note:</strong> This method assumes that the next
  +     * character on the stream is the first character of the attribute
  +     * name.
  +     * <p>
  +     * <strong>Note:</strong> This method uses the fAttributeQName and
  +     * fQName variables. The contents of these variables will be
  +     * destroyed.
  +     *
  +     * @param attributes The attributes list for the scanned attribute.
  +     */
       protected void scanAttribute(XMLAttributesImpl attributes)
       throws IOException, XNIException {
           if (DEBUG_CONTENT_SCANNING) System.out.println(">>> scanAttribute()");
  @@ -539,7 +698,8 @@
        * Dispatcher to handle content scanning.
        */
       protected final class NSContentDispatcher
  -    extends ContentDispatcher {
  +        extends ContentDispatcher {
  +        
           /**
            * Scan for root element hook. This method is a hook for
            * subclasses to add code that handles scanning for the root
  @@ -554,7 +714,38 @@
            *          the content dispatcher should continue as normal.
            */
           protected boolean scanRootElementHook()
  -        throws IOException, XNIException {
  +            throws IOException, XNIException {
  +       
  +            if (fExternalSubsetResolver != null && !fSeenDoctypeDecl 
  +                && (fValidation || fLoadExternalDTD)) {
  +                scanStartElementName();
  +                resolveExternalSubsetAndRead();
  +                reconfigurePipeline();
  +                if (scanStartElementAfterName()) {
  +                    setScannerState(SCANNER_STATE_TRAILING_MISC);
  +                    setDispatcher(fTrailingMiscDispatcher);
  +                    return true;
  +                }
  +            }
  +            else {
  +                reconfigurePipeline();
  +                if (scanStartElement()) {
  +                    setScannerState(SCANNER_STATE_TRAILING_MISC);
  +                    setDispatcher(fTrailingMiscDispatcher);
  +                    return true;
  +                }
  +            }
  +            return false;
  +
  +        } // scanRootElementHook():boolean
  +        
  +        /**
  +         * Re-configures pipeline by removing the DTD validator 
  +         * if no DTD grammar exists. If no validator exists in the
  +         * pipeline or there is no DTD grammar, namespace binding
  +         * is performed by the scanner in the enclosing class.
  +         */
  +        private void reconfigurePipeline() {
               if (fDTDValidator == null) {
                   fBindNamespaces = true;
               }
  @@ -570,15 +761,7 @@
                   fDTDValidator.setDocumentSource(null);
                   fDTDValidator.setDocumentHandler(null);
               }
  -
  -            if (scanStartElement()) {
  -                setScannerState(SCANNER_STATE_TRAILING_MISC);
  -                setDispatcher(fTrailingMiscDispatcher);
  -                return true;
  -            }
  -            return false;
  -
  -        } // scanRootElementHook():boolean
  +        } // reconfigurePipeline()
       }
   
   } // class XMLNSDocumentScannerImpl
  
  
  
  1.52      +139 -6    xml-xerces/java/src/org/apache/xerces/impl/XMLDocumentFragmentScannerImpl.java
  
  Index: XMLDocumentFragmentScannerImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/XMLDocumentFragmentScannerImpl.java,v
  retrieving revision 1.51
  retrieving revision 1.52
  diff -u -r1.51 -r1.52
  --- XMLDocumentFragmentScannerImpl.java	29 Mar 2004 03:53:27 -0000	1.51
  +++ XMLDocumentFragmentScannerImpl.java	8 Apr 2004 22:24:52 -0000	1.52
  @@ -115,6 +115,12 @@
       /** Feature identifier: notify built-in refereces. */
       protected static final String NOTIFY_BUILTIN_REFS =
           Constants.XERCES_FEATURE_PREFIX + Constants.NOTIFY_BUILTIN_REFS_FEATURE;
  +        
  +    // property identifiers
  +    
  +    /** Property identifier: entity resolver. */
  +    protected static final String ENTITY_RESOLVER =
  +        Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
       
       // recognized features and properties
   
  @@ -139,6 +145,7 @@
           SYMBOL_TABLE,
           ERROR_REPORTER,
           ENTITY_MANAGER,
  +        ENTITY_RESOLVER,
       };
   
       /** Property defaults. */
  @@ -146,6 +153,7 @@
           null,
           null,
           null,
  +        null,
       };
   
       // debugging
  @@ -185,6 +193,9 @@
       
       /** Standalone. */
       protected boolean fStandalone;
  +    
  +    /** External subset resolver. **/
  +    protected ExternalSubsetResolver fExternalSubsetResolver;
   
       // element information
   
  @@ -250,6 +261,15 @@
   
       /** External entity. */
       private XMLEntityManager.ExternalEntity fExternalEntity = new XMLEntityManager.ExternalEntity();
  +    
  +    /** 
  +     * Saw spaces after element name or between attributes.
  +     * 
  +     * This is reserved for the case where scanning of a start element spans
  +     * several methods, as is the case when scanning the start of a root element 
  +     * where a DTD external subset may be read after scanning the element name.
  +     */
  +    private boolean fSawSpace;
   
       //
       // Constructors
  @@ -293,6 +313,7 @@
           
           // reset entity scanner
           fEntityScanner = fEntityManager.getEntityScanner();
  +        
           // keep dispatching "events"
           fEntityManager.setEntityHandler(this);
           do {
  @@ -348,13 +369,25 @@
   		setDispatcher(fContentDispatcher);
           
   
  -        if (!fParserSettings) {
  +        if (fParserSettings) {
  +            // parser settings have changed. reset them.
  +        	
               // xerces features
               try {
                   fNotifyBuiltInRefs = componentManager.getFeature(NOTIFY_BUILTIN_REFS);
               } catch (XMLConfigurationException e) {
                   fNotifyBuiltInRefs = false;
               }
  +            
  +            // xerces properties
  +            try {
  +                Object resolver = componentManager.getProperty(ENTITY_RESOLVER);
  +                fExternalSubsetResolver = (resolver instanceof ExternalSubsetResolver) ?
  +                    (ExternalSubsetResolver) resolver : null;
  +            }
  +            catch (XMLConfigurationException e) {
  +                fExternalSubsetResolver = null;
  +            }
           }
   
       } // reset(XMLComponentManager)
  @@ -434,10 +467,16 @@
               if (suffixLength == Constants.ENTITY_MANAGER_PROPERTY.length() && 
                   propertyId.endsWith(Constants.ENTITY_MANAGER_PROPERTY)) {
                   fEntityManager = (XMLEntityManager)value;
  +                return;
  +            }
  +            if (suffixLength == Constants.ENTITY_RESOLVER_PROPERTY.length() && 
  +                propertyId.endsWith(Constants.ENTITY_RESOLVER_PROPERTY)) {
  +                fExternalSubsetResolver = (value instanceof ExternalSubsetResolver) ?
  +                    (ExternalSubsetResolver) value : null;
  +                return;
               }
  -            return;
           }
  -
  +        
       } // setProperty(String,Object)
   
       /** 
  @@ -785,6 +824,102 @@
           return empty;
   
       } // scanStartElement():boolean
  +    
  +    /**
  +     * Scans the name of an element in a start or empty tag. 
  +     * 
  +     * @see #scanStartElement()
  +     */
  +    protected void scanStartElementName ()
  +        throws IOException, XNIException {
  +        // name
  +        if (fNamespaces) {
  +            fEntityScanner.scanQName(fElementQName);
  +        }
  +        else {
  +            String name = fEntityScanner.scanName();
  +            fElementQName.setValues(null, name, name, null);
  +        }
  +        // Must skip spaces here because the DTD scanner
  +        // would consume them at the end of the external subset.
  +        fSawSpace = fEntityScanner.skipSpaces();
  +    } // scanStartElementName()
  +
  +    /**
  +     * Scans the remainder of a start or empty tag after the element name.
  +     * 
  +     * @see #scanStartElement
  +     * @return True if element is empty.
  +     */
  +    protected boolean scanStartElementAfterName()
  +        throws IOException, XNIException {
  +        String rawname = fElementQName.rawname;
  +
  +        // push element stack
  +        fCurrentElement = fElementStack.pushElement(fElementQName);
  +
  +        // attributes
  +        boolean empty = false;
  +        fAttributes.removeAllAttributes();
  +        do {
  +        	
  +            // end tag?
  +            int c = fEntityScanner.peekChar();
  +            if (c == '>') {
  +                fEntityScanner.scanChar();
  +                break;
  +            }
  +            else if (c == '/') {
  +                fEntityScanner.scanChar();
  +                if (!fEntityScanner.skipChar('>')) {
  +                    reportFatalError("ElementUnterminated",
  +                                     new Object[]{rawname});
  +                }
  +                empty = true;
  +                break;
  +            }
  +            else if (!isValidNameStartChar(c) || !fSawSpace) {
  +                // Second chance. Check if this character is a high
  +                // surrogate of a valid name start character.
  +                if (!isValidNameStartHighSurrogate(c) || !fSawSpace) {
  +                    reportFatalError("ElementUnterminated",
  +                                     new Object[] { rawname });
  +                }
  +            }
  +
  +            // attributes
  +            scanAttribute(fAttributes);
  +            
  +            // spaces
  +            fSawSpace = fEntityScanner.skipSpaces();
  +
  +        } while (true);
  +
  +        // call handler
  +        if (fDocumentHandler != null) {
  +            if (empty) {
  +
  +                //decrease the markup depth..
  +                fMarkupDepth--;
  +                // check that this element was opened in the same entity
  +                if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
  +                    reportFatalError("ElementEntityMismatch",
  +                                     new Object[]{fCurrentElement.rawname});
  +                }
  +
  +                fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
  +
  +                //pop the element off the stack..
  +                fElementStack.popElement(fElementQName);
  +            }
  +            else {
  +                fDocumentHandler.startElement(fElementQName, fAttributes, null);
  +            }
  +        }
  +
  +        if (DEBUG_CONTENT_SCANNING) System.out.println("<<< scanStartElementAfterName(): "+empty);
  +        return empty;
  +    } // scanStartElementAfterName()
   
       /** 
        * Scans an attribute.
  @@ -1050,8 +1185,6 @@
           return fMarkupDepth;
    
       } // scanEndElement():int
  -
  -    public static final String CHAR_REF = "characterReference";
   
       /**
        * Scans a character reference.
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: xerces-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: xerces-cvs-help@xml.apache.org