mrglavas    2003/10/02 11:18:15

  Added:       java/src/org/apache/xerces/impl
                        XML11NSDocumentScannerImpl.java
               java/src/org/apache/xerces/impl/dtd XML11NSDTDValidator.java
  Log:
  Adding a specialized document scanner and DTD validator
  for namepace binding of XML 1.1 documents. This was inspired
  by the improvement that was gained from a similar change for
  XML 1.0.
  
  Preliminary performance testing showed a 5-10% 
  improvement depending on the characteristics of the document.
  
  Revision  Changes    Path
  1.1                  
xml-xerces/java/src/org/apache/xerces/impl/XML11NSDocumentScannerImpl.java
  
  Index: XML11NSDocumentScannerImpl.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999-2003 The Apache Software Foundation.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2002, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.impl;
  
  import java.io.IOException;
  
  import org.apache.xerces.impl.dtd.XMLDTDValidatorFilter;
  import org.apache.xerces.impl.msg.XMLMessageFormatter;
  import org.apache.xerces.util.XMLAttributesImpl;
  import org.apache.xerces.util.XMLSymbols;
  import org.apache.xerces.xni.NamespaceContext;
  import org.apache.xerces.xni.QName;
  import org.apache.xerces.xni.XMLDocumentHandler;
  import org.apache.xerces.xni.XNIException;
  import org.apache.xerces.xni.parser.XMLComponentManager;
  import org.apache.xerces.xni.parser.XMLConfigurationException;
  import org.apache.xerces.xni.parser.XMLDocumentSource;
  
  /**
   * The scanner acts as the source for the document
   * information which is communicated to the document handler.
   *
   * This class scans an XML document, checks if document has a DTD, and if
   * DTD is not found the scanner will remove the DTD Validator from the pipeline and 
perform
   * namespace binding.
   *
   * Note: This scanner should only be used when the namespace processing is on!
   *
   * <p>
   * This component requires the following features and properties from the
   * component manager that uses it:
   * <ul>
   *  <li>http://xml.org/sax/features/namespaces {true} -- if the value of this
   *      feature is set to false this scanner must not be used.</li>
   *  <li>http://xml.org/sax/features/validation</li>
   *  <li>http://apache.org/xml/features/nonvalidating/load-external-dtd</li>
   *  <li>http://apache.org/xml/features/scanner/notify-char-refs</li>
   *  <li>http://apache.org/xml/features/scanner/notify-builtin-refs</li>
   *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
   *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
   *  <li>http://apache.org/xml/properties/internal/entity-manager</li>
   *  <li>http://apache.org/xml/properties/internal/dtd-scanner</li>
   * </ul>
   *
   * @author Elena Litani, IBM
   * @author Michael Glavassevich, IBM
   */
  public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
  
        /** 
         * If is true, the dtd validator is no longer in the pipeline
         * and the scanner should bind namespaces 
         */
        protected boolean fBindNamespaces;
  
        /** 
         * If validating parser, make sure we report an error in the
         *  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;
  
        /**
         * The scanner is responsible for removing DTD validator
         * from the pipeline if it is not needed.
         * 
         * @param validator the DTD validator from the pipeline
         */
        public void setDTDValidator(XMLDTDValidatorFilter validator){
                fDTDValidator = validator;
        }
  
        /**
         * Scans a start element. This method will handle the binding of
         * namespace information and notifying the handler of the start
         * of the element.
         * <p>
         * <pre>
         * [44] EmptyElemTag ::= '&lt;' Name (S Attribute)* S? '/>'
         * [40] STag ::= '&lt;' Name (S Attribute)* S? '>'
         * </pre>
         * <p>
         * <strong>Note:</strong> This method assumes that the leading
         * '&lt;' character has been consumed.
         * <p>
         * <strong>Note:</strong> This method uses the fElementQName and
         * fAttributes variables. The contents of these variables will be
         * destroyed. The caller should copy important information out of
         * these variables before calling this method.
         *
         * @return True if element is empty. (i.e. It matches
         *          production [44].
         */
        protected boolean scanStartElement()
        throws IOException, XNIException {
                if (DEBUG_CONTENT_SCANNING) System.out.println(">>> 
scanStartElementNS()");
  
                // Note: namespace processing is on by default
                fEntityScanner.scanQName(fElementQName);
                // 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 {
                        // spaces
                        boolean sawSpace = fEntityScanner.skipSpaces();
  
                        // 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) || !sawSpace) {
                                reportFatalError("ElementUnterminated", new 
Object[]{rawname});
                        }
  
                        // attributes
                        scanAttribute(fAttributes);
  
                } 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;
                        }
                        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();
                        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);
                                }
                        }
                }
  
  
                // 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("<<< 
scanStartElement(): "+empty);
                return empty;
  
        } // scanStartElement():boolean
  
        private final void checkDuplicates(QName qname, XMLAttributesImpl attributes){
  
                // 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)) {
                                
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                                                                                   
"AttributeNSNotUnique",
                                                                                   new 
Object[]{fElementQName.rawname,qname.rawname, qname.uri},
                                                                                   
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;
  
                }
  
                fUri[index] = qname.uri;
                fLocalpart[index] = qname.localpart;
        }
  
  
  
  
        /**
   * 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()");
  
                // name
                fEntityScanner.scanQName(fAttributeQName);
  
                // equals
                fEntityScanner.skipSpaces();
                if (!fEntityScanner.skipChar('=')) {
                        reportFatalError("EqRequiredInAttribute",
                                                         new 
Object[]{fCurrentElement.rawname,fAttributeQName.rawname});
                }
                fEntityScanner.skipSpaces();
  
                // content
                int oldLen = attributes.getLength();
                attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, 
null);
  
                // WFC: Unique Att Spec
                if (oldLen == attributes.getLength()) {
                        reportFatalError("AttributeNotUnique",
                                                         new 
Object[]{fCurrentElement.rawname,
                                                                 
fAttributeQName.rawname});
                }
  
                //REVISIT: one more case needs to be included: external PE and 
standalone is no
                boolean isVC =  fHasExternalDTD && !fStandalone;
  
                // REVISIT: it seems that this function should not take attributes, 
and length
                scanAttributeValue(this.fTempString, fTempString2,
                                                   fAttributeQName.rawname, attributes,
                                                   oldLen, 
isVC,fCurrentElement.rawname);
                String value = fTempString.toString();
                attributes.setValue(oldLen, value);
                attributes.setNonNormalizedValue(oldLen, fTempString2.toString());
                attributes.setSpecified(oldLen, true);
  
                // record namespace declarations if any.
                if (fBindNamespaces) {
  
                        String localpart = fAttributeQName.localpart;
                        String prefix = fAttributeQName.prefix != null
                                                        ? fAttributeQName.prefix : 
XMLSymbols.EMPTY_STRING;
                        // when it's of form xmlns="..." or xmlns:prefix="...",
                        // it's a namespace declaration. but prefix:xmlns="..." isn't.
                        if (prefix == XMLSymbols.PREFIX_XMLNS ||
                                prefix == XMLSymbols.EMPTY_STRING && localpart == 
XMLSymbols.PREFIX_XMLNS) {
  
                                // get the internalized value of this attribute
                                String uri = fSymbolTable.addSymbol(value);
  
                                // 1. "xmlns" can't be bound to any namespace
                                if (prefix == XMLSymbols.PREFIX_XMLNS && localpart == 
XMLSymbols.PREFIX_XMLNS) {
                                        
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                                                                                       
    "CantBindXMLNS",
                                                                                       
    new Object[]{fAttributeQName},
                                                                                       
    XMLErrorReporter.SEVERITY_FATAL_ERROR);
                                }
  
                                // 2. the namespace for "xmlns" can't be bound to any 
prefix
                                if (uri == NamespaceContext.XMLNS_URI) {
                                        
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                                                                                       
    "CantBindXMLNS",
                                                                                       
    new Object[]{fAttributeQName},
                                                                                       
    XMLErrorReporter.SEVERITY_FATAL_ERROR);
                                }
  
                                // 3. "xml" can't be bound to any other namespace than 
it's own
                                if (localpart == XMLSymbols.PREFIX_XML) {
                                        if (uri != NamespaceContext.XML_URI) {
                                                
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                                                                                       
            "CantBindXML",
                                                                                       
            new Object[]{fAttributeQName},
                                                                                       
            XMLErrorReporter.SEVERITY_FATAL_ERROR);
                                        }
                                }
                                // 4. the namespace for "xml" can't be bound to any 
other prefix
                                else {
                                        if (uri ==NamespaceContext.XML_URI) {
                                                
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                                                                                       
            "CantBindXML",
                                                                                       
            new Object[]{fAttributeQName},
                                                                                       
            XMLErrorReporter.SEVERITY_FATAL_ERROR);
                                        }
                                }
  
                                prefix = localpart != XMLSymbols.PREFIX_XMLNS ? 
localpart : XMLSymbols.EMPTY_STRING;
                                
                                // Declare prefix in context. Removing the association 
between a prefix and a 
                                // namespace name is permitted in XML 1.1, so if the 
uri value is the empty string, 
                                // the prefix is being unbound. -- mrglavas
                                fNamespaceContext.declarePrefix(prefix, uri.length() 
!= 0 ? uri : null);
                                // bind namespace attribute to a namespace
                                attributes.setURI(oldLen, 
fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS));
  
                        }
                        else {
                                // attempt to bind attribute
                                if (fAttributeQName.prefix != null) {
                                        attributes.setURI(oldLen, 
fNamespaceContext.getURI(fAttributeQName.prefix));
                                }
                        }
                }
  
                if (DEBUG_CONTENT_SCANNING) System.out.println("<<< scanAttribute()");
        } // scanAttribute(XMLAttributes)
  
  
  
        /**
         * Scans an end element.
         * <p>
         * <pre>
         * [42] ETag ::= '&lt;/' Name S? '>'
         * </pre>
         * <p>
         * <strong>Note:</strong> This method uses the fElementQName variable.
         * The contents of this variable will be destroyed. The caller should
         * copy the needed information out of this variable before calling
         * this method.
         *
         * @return The element depth.
         */
        protected int scanEndElement() throws IOException, XNIException {
                if (DEBUG_CONTENT_SCANNING) System.out.println(">>> scanEndElement()");
  
                // pop context
                fElementStack.popElement(fElementQName) ;
  
                // Take advantage of the fact that next string _should_ be 
"fElementQName.rawName",
                //In scanners most of the time is consumed on checks done for XML 
characters, we can
                // optimize on it and avoid the checks done for endElement,
                //we will also avoid symbol table lookup - [EMAIL PROTECTED]
  
                // this should work both for namespace processing true or false...
  
                //REVISIT: if the string is not the same as expected.. we need to do 
better error handling..
                //We can skip this for now... In any case if the string doesn't match 
-- document is not well formed.
                if (!fEntityScanner.skipString(fElementQName.rawname)) {
                        reportFatalError("ETagRequired", new 
Object[]{fElementQName.rawname});
                }
  
                // end
                fEntityScanner.skipSpaces();
                if (!fEntityScanner.skipChar('>')) {
                        reportFatalError("ETagUnterminated",
                                                         new 
Object[]{fElementQName.rawname});
                }
                fMarkupDepth--;
  
                //we have increased the depth for two markup "<" characters
                fMarkupDepth--;
  
                // check that this element was opened in the same entity
                if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
                        reportFatalError("ElementEntityMismatch",
                                                         new 
Object[]{fCurrentElement.rawname});
                }
  
                // call handler
                if (fDocumentHandler != null ) {
  
                        fDocumentHandler.endElement(fElementQName, null);
                        if (fBindNamespaces) {
                                fNamespaceContext.popContext();
                        }
  
                }
  
                return fMarkupDepth;
  
        } // scanEndElement():int
  
  
        public void reset(XMLComponentManager componentManager)
        throws XMLConfigurationException {
  
                super.reset(componentManager);
                fPerformValidation = false;
                fBindNamespaces = false;
        }
  
        /** Creates a content dispatcher. */
        protected Dispatcher createContentDispatcher() {
                return new NS11ContentDispatcher();
        } // createContentDispatcher():Dispatcher
  
        /**
         * Dispatcher to handle content scanning.
         */
        protected final class NS11ContentDispatcher
        extends ContentDispatcher {
                /**
                 * Scan for root element hook. This method is a hook for
                 * subclasses to add code that handles scanning for the root
                 * element. This method will also attempt to remove DTD validator
                 * from the pipeline, if there is no DTD grammar. If DTD validator
                 * is no longer in the pipeline bind namespaces in the scanner.
                 *
                 *
                 * @return True if the caller should stop and return true which
                 *          allows the scanner to switch to a new scanning
                 *          dispatcher. A return value of false indicates that
                 *          the content dispatcher should continue as normal.
                 */
                protected boolean scanRootElementHook()
                throws IOException, XNIException {
                        if (fDTDValidator == null) {
                                fBindNamespaces = true;
                        }
                        else if (!fDTDValidator.hasGrammar()) {
                                fBindNamespaces = true;
                                fPerformValidation = fDTDValidator.validate();
                                // re-configure pipeline
                                XMLDocumentSource source = 
fDTDValidator.getDocumentSource();
                                XMLDocumentHandler handler = 
fDTDValidator.getDocumentHandler();
                                source.setDocumentHandler(handler);
                                if (handler != null)
                                        handler.setDocumentSource(source);
                                fDTDValidator.setDocumentSource(null);
                                fDTDValidator.setDocumentHandler(null);
                        }
  
                        if (scanStartElement()) {
                                setScannerState(SCANNER_STATE_TRAILING_MISC);
                                setDispatcher(fTrailingMiscDispatcher);
                                return true;
                        }
                        return false;
  
                } // scanRootElementHook():boolean
        }
  }
  
  
  
  1.1                  
xml-xerces/java/src/org/apache/xerces/impl/dtd/XML11NSDTDValidator.java
  
  Index: XML11NSDTDValidator.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 1999-2003 The Apache Software Foundation.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2002, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.impl.dtd;
  
  import org.apache.xerces.impl.XMLErrorReporter;
  import org.apache.xerces.impl.msg.XMLMessageFormatter;
  import org.apache.xerces.util.XMLSymbols;
  import org.apache.xerces.xni.Augmentations;
  import org.apache.xerces.xni.NamespaceContext;
  import org.apache.xerces.xni.QName;
  import org.apache.xerces.xni.XMLAttributes;
  import org.apache.xerces.xni.XNIException;
  
  /**
   * The DTD validator. The validator implements a document
   * filter: receiving document events from the scanner; validating
   * the content and structure; augmenting the InfoSet, if applicable;
   * and notifying the parser of the information resulting from the
   * validation process.
   * <p> Formerly, this component also handled DTD events and grammar construction.
   * To facilitate the development of a meaningful DTD grammar caching/preparsing
   * framework, this functionality has been moved into the XMLDTDLoader
   * class.  Therefore, this class no longer implements the DTDFilter
   * or DTDContentModelFilter interfaces.
   * <p>
   * This component requires the following features and properties from the
   * component manager that uses it:
   * <ul>
   *  <li>http://xml.org/sax/features/namespaces</li>
   *  <li>http://xml.org/sax/features/validation</li>
   *  <li>http://apache.org/xml/features/validation/dynamic</li>
   *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
   *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
   *  <li>http://apache.org/xml/properties/internal/grammar-pool</li>
   *  <li>http://apache.org/xml/properties/internal/datatype-validator-factory</li>
   * </ul>
   *
   * @author Elena Litani, IBM
   * @author Michael Glavassevich, IBM
   *
   * @version $Id: XML11NSDTDValidator.java,v 1.1 2003/10/02 18:18:15 mrglavas Exp $
   */
  public class XML11NSDTDValidator extends XML11DTDValidator {
  
      /** Attribute QName. */
      private QName fAttributeQName = new QName();
  
      /** Bind namespaces */
      protected final void startNamespaceScope(QName element, XMLAttributes 
attributes, Augmentations augs)
          throws XNIException {
  
          // add new namespace context
          fNamespaceContext.pushContext();
  
          if (element.prefix == XMLSymbols.PREFIX_XMLNS) {
              fErrorReporter.reportError(
                  XMLMessageFormatter.XMLNS_DOMAIN,
                  "ElementXMLNSPrefix",
                  new Object[] { element.rawname },
                  XMLErrorReporter.SEVERITY_FATAL_ERROR);
          }
  
          // search for new namespace bindings
          int length = attributes.getLength();
          for (int i = 0; i < length; i++) {
              String localpart = attributes.getLocalName(i);
              String prefix = attributes.getPrefix(i);
              // when it's of form xmlns="..." or xmlns:prefix="...",
              // it's a namespace declaration. but prefix:xmlns="..." isn't.
              if (prefix == XMLSymbols.PREFIX_XMLNS || prefix == 
XMLSymbols.EMPTY_STRING
                  && localpart == XMLSymbols.PREFIX_XMLNS) {
  
                  // get the internalized value of this attribute
                  String uri = fSymbolTable.addSymbol(attributes.getValue(i));
  
                  // 1. "xmlns" can't be bound to any namespace
                  if (prefix == XMLSymbols.PREFIX_XMLNS && localpart == 
XMLSymbols.PREFIX_XMLNS) {
                      fErrorReporter.reportError(
                          XMLMessageFormatter.XMLNS_DOMAIN,
                          "CantBindXMLNS",
                          new Object[] { attributes.getQName(i)},
                          XMLErrorReporter.SEVERITY_FATAL_ERROR);
                  }
  
                  // 2. the namespace for "xmlns" can't be bound to any prefix
                  if (uri == NamespaceContext.XMLNS_URI) {
                      fErrorReporter.reportError(
                          XMLMessageFormatter.XMLNS_DOMAIN,
                          "CantBindXMLNS",
                          new Object[] { attributes.getQName(i)},
                          XMLErrorReporter.SEVERITY_FATAL_ERROR);
                  }
  
                  // 3. "xml" can't be bound to any other namespace than it's own
                  if (localpart == XMLSymbols.PREFIX_XML) {
                      if (uri != NamespaceContext.XML_URI) {
                          fErrorReporter.reportError(
                              XMLMessageFormatter.XMLNS_DOMAIN,
                              "CantBindXML",
                              new Object[] { attributes.getQName(i)},
                              XMLErrorReporter.SEVERITY_FATAL_ERROR);
                      }
                  }
                  // 4. the namespace for "xml" can't be bound to any other prefix
                  else {
                      if (uri == NamespaceContext.XML_URI) {
                          fErrorReporter.reportError(
                              XMLMessageFormatter.XMLNS_DOMAIN,
                              "CantBindXML",
                              new Object[] { attributes.getQName(i)},
                              XMLErrorReporter.SEVERITY_FATAL_ERROR);
                      }
                  }
  
                  prefix = localpart != XMLSymbols.PREFIX_XMLNS ? localpart : 
XMLSymbols.EMPTY_STRING;
  
                                // Declare prefix in context. Removing the association 
between a prefix and a 
                                // namespace name is permitted in XML 1.1, so if the 
uri value is the empty string, 
                                // the prefix is being unbound. -- mrglavas
                  fNamespaceContext.declarePrefix(prefix, uri.length() != 0 ? uri : 
null);
              }
          }
  
          // bind the element
          String prefix = element.prefix != null ? element.prefix : 
XMLSymbols.EMPTY_STRING;
          element.uri = fNamespaceContext.getURI(prefix);
          if (element.prefix == null && element.uri != null) {
              element.prefix = XMLSymbols.EMPTY_STRING;
          }
          if (element.prefix != null && element.uri == null) {
              fErrorReporter.reportError(
                  XMLMessageFormatter.XMLNS_DOMAIN,
                  "ElementPrefixUnbound",
                  new Object[] { element.prefix, element.rawname },
                  XMLErrorReporter.SEVERITY_FATAL_ERROR);
          }
  
          // bind the attributes
          for (int i = 0; i < length; i++) {
              attributes.getName(i, fAttributeQName);
              String aprefix = fAttributeQName.prefix != null ? fAttributeQName.prefix 
: XMLSymbols.EMPTY_STRING;
              String arawname = fAttributeQName.rawname;
              if (arawname == XMLSymbols.PREFIX_XMLNS) {
                  fAttributeQName.uri = 
fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS);
                  attributes.setName(i, fAttributeQName);
              } else if (aprefix != XMLSymbols.EMPTY_STRING) {
                  fAttributeQName.uri = fNamespaceContext.getURI(aprefix);
                  if (fAttributeQName.uri == null) {
                      fErrorReporter.reportError(
                          XMLMessageFormatter.XMLNS_DOMAIN,
                          "AttributePrefixUnbound",
                          new Object[] { element.rawname, arawname, aprefix },
                          XMLErrorReporter.SEVERITY_FATAL_ERROR);
                  }
                  attributes.setName(i, fAttributeQName);
              }
          }
  
          // verify that duplicate attributes don't exist
          // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/>
          int attrCount = attributes.getLength();
          for (int i = 0; i < attrCount - 1; i++) {
              String alocalpart = attributes.getLocalName(i);
              String auri = attributes.getURI(i);
              for (int j = i + 1; j < attrCount; j++) {
                  String blocalpart = attributes.getLocalName(j);
                  String buri = attributes.getURI(j);
                  if (alocalpart == blocalpart && auri == buri) {
                      fErrorReporter.reportError(
                          XMLMessageFormatter.XMLNS_DOMAIN,
                          "AttributeNSNotUnique",
                          new Object[] { element.rawname, alocalpart, auri },
                          XMLErrorReporter.SEVERITY_FATAL_ERROR);
                  }
              }
          }
  
      } // startNamespaceScope(QName,XMLAttributes)
  
      /** Handles end element. */
      protected void endNamespaceScope(QName element, Augmentations augs, boolean 
isEmpty)
          throws XNIException {
  
          // bind element
          String eprefix = element.prefix != null ? element.prefix : 
XMLSymbols.EMPTY_STRING;
          element.uri = fNamespaceContext.getURI(eprefix);
          if (element.uri != null) {
              element.prefix = eprefix;
          }
  
          // call handlers
          if (fDocumentHandler != null) {
              if (!isEmpty) {
                  fDocumentHandler.endElement(element, augs);
              }
          }
  
          // pop context
          fNamespaceContext.popContext();
  
      } // endNamespaceScope(QName,boolean)
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to