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 2003/10/02 20:18:15 UTC

cvs commit: xml-xerces/java/src/org/apache/xerces/impl/dtd XML11NSDTDValidator.java

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 apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 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 - neeraj.bajaj@sun.com
  
  		// this should work both for namespace processing true or false...
  
  		//REVISIT: if the string is not the same as expected.. we need to do better error handling..
  		//We can skip this for now... In any case if the string doesn't match -- document is not well formed.
  		if (!fEntityScanner.skipString(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 apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 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: xerces-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: xerces-cvs-help@xml.apache.org