blob: 7a826dfcc9745952b46b85f42f3409218dea4aa8 [file] [log] [blame]
/*
* reserved comment block
* DO NOT REMOVE OR ALTER!
*/
/*
* 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 com.sun.org.apache.xerces.internal.impl;
import java.io.IOException;
import com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidatorFilter;
import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
import com.sun.org.apache.xerces.internal.util.XMLSymbols;
import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
import com.sun.org.apache.xerces.internal.xni.QName;
import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
import com.sun.org.apache.xerces.internal.xni.XNIException;
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
import javax.xml.stream.events.XMLEvent;
/**
* 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>
*
* @xerces.internal
*
* @author Elena Litani, IBM
* @author Michael Glavassevich, IBM
* @author Sunitha Reddy, Sun Microsystems
* @version $Id: XML11NSDocumentScannerImpl.java,v 1.6 2010-11-01 04:39:40 joehw Exp $
*/
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;
// 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;
/**
* 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_START_END_ELEMENT)
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) {
// Second chance. Check if this character is a high
// surrogate of a valid name start character.
if (!isValidNameStartHighSurrogate(c) || !sawSpace) {
reportFatalError(
"ElementUnterminated",
new Object[] { rawname });
}
}
// attributes
scanAttribute(fAttributes);
if (fSecurityManager != null && (!fSecurityManager.isNoLimit(fElementAttributeLimit)) &&
fAttributes.getLength() > fElementAttributeLimit){
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"ElementAttributeLimit",
new Object[]{rawname, new Integer(fElementAttributeLimit) },
XMLErrorReporter.SEVERITY_FATAL_ERROR );
}
} 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 (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();
}*/
fScanEndElement = true;
//pop the element off the stack..
fElementStack.popElement();
} else {
if(dtdGrammarUtil != null)
dtdGrammarUtil.startElement(fElementQName, fAttributes);
if (fDocumentHandler != null)
fDocumentHandler.startElement(fElementQName, fAttributes, null);
}
if (DEBUG_START_END_ELEMENT)
System.out.println("<<< scanStartElement(): " + empty);
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();
} else {
fDocumentHandler.startElement(fElementQName, fAttributes, null);
}
}
if (DEBUG_START_END_ELEMENT)
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.
*/
protected void scanAttribute(XMLAttributesImpl attributes)
throws IOException, XNIException {
if (DEBUG_START_END_ELEMENT)
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 attrIndex;
if (fBindNamespaces) {
attrIndex = attributes.getLength();
attributes.addAttributeNS(
fAttributeQName,
XMLSymbols.fCDATASymbol,
null);
} else {
int oldLen = attributes.getLength();
attrIndex =
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,
isVC,
fCurrentElement.rawname);
String value = fTempString.toString();
attributes.setValue(attrIndex, value);
attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
attributes.setSpecified(attrIndex, 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(
attrIndex,
fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS));
} else {
// attempt to bind attribute
if (fAttributeQName.prefix != null) {
attributes.setURI(
attrIndex,
fNamespaceContext.getURI(fAttributeQName.prefix));
}
}
}
if (DEBUG_START_END_ELEMENT)
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_START_END_ELEMENT)
System.out.println(">>> scanEndElement()");
// pop context
QName endElementName = fElementStack.popElement();
// 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(endElementName.rawname)) {
reportFatalError(
"ETagRequired",
new Object[] { endElementName.rawname });
}
// end
fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('>')) {
reportFatalError(
"ETagUnterminated",
new Object[] { endElementName.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[] { endElementName.rawname });
}
// call handler
if (fDocumentHandler != null) {
fDocumentHandler.endElement(endElementName, null);
/*if (fBindNamespaces) {
fNamespaceContext.popContext();
}*/
}
if(dtdGrammarUtil != null)
dtdGrammarUtil.endElement(endElementName);
return fMarkupDepth;
} // scanEndElement():int
public void reset(XMLComponentManager componentManager)
throws XMLConfigurationException {
super.reset(componentManager);
fPerformValidation = false;
fBindNamespaces = false;
}
/** Creates a content Driver. */
protected Driver createContentDriver() {
return new NS11ContentDriver();
} // createContentDriver():Driver
/** return the next state on the input
*
* @return int
*/
public int next() throws IOException, XNIException {
//since namespace context should still be valid when the parser is at the end element state therefore
//we pop the context only when next() has been called after the end element state was encountered. - nb.
if((fScannerLastState == XMLEvent.END_ELEMENT) && fBindNamespaces){
fScannerLastState = -1;
fNamespaceContext.popContext();
}
return fScannerLastState = super.next();
}
/**
* Driver to handle content scanning.
*/
protected final class NS11ContentDriver extends ContentDriver {
/**
* 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
* Driver. A return value of false indicates that
* the content Driver should continue as normal.
*/
protected boolean scanRootElementHook()
throws IOException, XNIException {
if (fExternalSubsetResolver != null && !fSeenDoctypeDecl
&& !fDisallowDoctype && (fValidation || fLoadExternalDTD)) {
scanStartElementName();
resolveExternalSubsetAndRead();
reconfigurePipeline();
if (scanStartElementAfterName()) {
setScannerState(SCANNER_STATE_TRAILING_MISC);
setDriver(fTrailingMiscDriver);
return true;
}
}
else {
reconfigurePipeline();
if (scanStartElement()) {
setScannerState(SCANNER_STATE_TRAILING_MISC);
setDriver(fTrailingMiscDriver);
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()) {
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);
}
} // reconfigurePipeline()
}
}