/*
*/
/*
* Copyright 2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This class adds the functionality of namespace processing.
*
* This class has been modified as per the new design which is more suited to
* efficiently build pull parser. Lot of improvements have been done and
* the code has been added to support stax functionality/features.
*
*
* 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.
*
*
* @author Neeraj Bajaj, Sun Microsystems
* @author Venugopal Rao K, Sun Microsystems
* @author Elena Litani, IBM
*/
public class XMLNSDocumentScannerImpl
extends XMLDocumentScannerImpl {
/**
* 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;
/** Default value of this feature is false, when in Stax mode this should be true */
protected boolean fNotAddNSDeclAsAttribute = false;
/** DTD validator */
/** xmlns, default Namespace, declared */
private boolean fXmlnsDeclared = false;
/** Resets the fields of this scanner.
*/
super.reset(propertyManager);
fBindNamespaces = false;
fNotAddNSDeclAsAttribute = !((Boolean)propertyManager.getProperty(Constants.ADD_NAMESPACE_DECL_AS_ATTRIBUTE)).booleanValue();
}
throws XMLConfigurationException {
super.reset(componentManager);
fNotAddNSDeclAsAttribute = false ;
fPerformValidation = false;
fBindNamespaces = false;
}
/** return the next state on the input
*
* @return int
*/
//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.
fScannerLastState = -1;
}
return fScannerLastState = super.next();
}
/**
* The scanner is responsible for removing DTD validator
* from the pipeline if it is not needed.
*
* @param previous The filter component before DTDValidator
* @param dtdValidator
* The DTDValidator
* @param next The documentHandler after the DTDValidator
*/
fDTDValidator = dtd;
}
/**
* 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 ::= '<' Name (S Attribute)* S? '/>'
* [40] STag ::= '<' Name (S Attribute)* S? '>'
* </pre>
* <p>
* <strong>Note:</strong> This method assumes that the leading
* '<' 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(this.getClass().toString() +">>> scanStartElement()");
//when skipping is true and no more elements should be added
//get the stored element -- if everything goes right this should match the
//token in the buffer
if(DEBUG_SKIP_ALGORITHM){
}
//Be conservative -- if skipping fails -- stop.
if(fSkip){
if(DEBUG_SKIP_ALGORITHM){
}
}else{
//if skipping fails reposition the stack or fallback to normal way of processing
if(DEBUG_SKIP_ALGORITHM){
}
}
}
//we are still at the stage of adding elements
//the elements were not matched or
//fSkip is not set to true
//get the next element from the stack
// There are two variables,fNamespaces and fBindNamespaces
//StAX uses XMLNSDocumentScannerImpl so this distinction needs to be maintained
if (fNamespaces) {
} else {
}
if(DEBUG_SKIP_ALGORITHM){
if(fAdd){
System.out.println("Elements are being ADDED -- elemet added is = " + fElementQName.rawname + " at count = " + fElementStack.fCount);
}
}
}
//when the elements are being added , we need to check if we are set for skipping the elements
if(fAdd){
//this sets the value of fAdd variable
}
//xxx: We dont need another pointer, fCurrentElement, we can use fElementQName
if (fBindNamespaces) {
if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) {
if (fPerformValidation) {
"MSG_GRAMMAR_NOT_FOUND",
"RootElementTypeMustMatchDoctypedecl",
}
}
}
}
fEmptyElement = false;
if(!seekCloseOfStartTag()){
fReadingAttributes = true;
fAddDefaultAttr = true;
fXmlnsDeclared = false;
do {
"ElementAttributeLimit",
}
} while (!seekCloseOfStartTag());
fReadingAttributes=false;
}
if (fBindNamespaces) {
// REVISIT: is it required? forbit xmlns prefix for element
"ElementXMLNSPrefix",
}
// bind the element
// assign uri to the element
// make sure that object in the element stack is updated as well
}
"ElementPrefixUnbound",
}
// bind attributes (xmlns are already bound bellow)
// fLength = 0; //initialize structure
for (int i = 0; i < length; i++) {
// REVISIT: try removing the first "if" and see if it is faster.
//
// checkDuplicates(fAttributeQName, fAttributes);
continue;
}
"AttributePrefixUnbound",
}
// checkDuplicates(fAttributeQName, fAttributes);
}
}
if (length > 1) {
"AttributeNSNotUnique",
} else {
"AttributeNotUnique",
}
}
}
}
if (fEmptyElement) {
//decrease the markup depth..
fMarkupDepth--;
// check that this element was opened in the same entity
reportFatalError("ElementEntityMismatch",
}
// call handler
if (fDocumentHandler != null) {
if(DEBUG)
}
//We should not be popping out the context here in endELement becaause the namespace context is still
//valid when parser is at the endElement state.
fScanEndElement = true;
//if (fBindNamespaces) {
// fNamespaceContext.popContext();
//}
//pop the element off the stack..
} else {
if(dtdGrammarUtil != null)
if(fDocumentHandler != null){
//complete element and attributes are traversed in this function so we can send a callback
//here.
//<strong>we shouldn't be sending callback in scanDocument()</strong>
if(DEBUG)
}
}
if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +"<<< scanStartElement(): "+fEmptyElement);
return fEmptyElement;
} // scanStartElement():boolean
/**
* 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.
*/
throws IOException, XNIException {
// name
// equals
reportFatalError("EqRequiredInAttribute",
}
// content
int attrIndex = 0 ;
//REVISIT: one more case needs to be included: external PE and standalone is no
// REVISIT: it seems that this function should not take attributes, and length
//fTempString would store attribute value
///fTempString2 would store attribute non-normalized value
//this function doesn't use 'attIndex'. We are adding the attribute later
//after we have figured out that current attribute is not namespace declaration
//since scanAttributeValue doesn't use attIndex parameter therefore we
//can safely add the attribute later..
//fTempString.toString();
// record namespace declarations if any.
if (fBindNamespaces) {
// when it's of form xmlns="..." or xmlns:prefix="...",
// it's a namespace declaration. but prefix:xmlns="..." isn't.
// get the internalized value of this attribute
// 1. "xmlns" can't be bound to any namespace
"CantBindXMLNS",
new Object[]{fAttributeQName},
}
// 2. the namespace for "xmlns" can't be bound to any prefix
"CantBindXMLNS",
new Object[]{fAttributeQName},
}
// 3. "xml" can't be bound to any other namespace than it's own
"CantBindXML",
new Object[]{fAttributeQName},
}
}
// 4. the namespace for "xml" can't be bound to any other prefix
else {
"CantBindXML",
new Object[]{fAttributeQName},
}
}
//set it equal to XMLSymbols.PREFIX_XMLNS when namespace declaration
// is of type xmlns = "..", in this case prefix = "" and localname = XMLSymbols.PREFIX_XMLNS
//this special behavior is because of dependency on this behavior in DOM components
}
// http://www.w3.org/TR/1999/REC-xml-names-19990114/#dt-prefix
// We should only report an error if there is a prefix,
// that is, the local part is not "xmlns". -SG
"EmptyPrefixedAttName",
new Object[]{fAttributeQName},
}
// check for duplicate prefix bindings
if (((com.sun.org.apache.xerces.internal.util.NamespaceSupport) fNamespaceContext).containsPrefixInCurrentContext(prefix)) {
reportFatalError("AttributeNotUnique",
}
// declare prefix in context
// check for duplicate xmlns declarations
if (!declared) { // by convention, prefix == "xmlns" | "xml"
// error if duplicate declaration
if (fXmlnsDeclared) {
reportFatalError("AttributeNotUnique",
}
// xmlns declared
fXmlnsDeclared = true;
}
//xerces internals (XSAttributeChecker) has dependency on namespace declaration returned
//as part of XMLAttributes.
//addition of namespace declaration to the attribute list is controlled by fNotAddNSDeclAsAttribute
//feature. This is required in Stax where namespace declarations are not considered as attribute
return ;
}
}
}
//add the attributes to the list of attributes
if (fBindNamespaces) {
} else {
// WFC: Unique Att Spec
reportFatalError("AttributeNotUnique",
}
}
//attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
//removing as we are not using non-normalized values . -Venu
// attempt to bind attribute
}
} // scanAttribute(XMLAttributes)
/** Creates a content driver. */
return new NSContentDriver();
} // createContentDriver():Driver
/**
* Driver to handle content scanning.
*/
protected final class NSContentDriver
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 (scanStartElement()) {
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() {
//fDTDValidator will be null in Stax mode
fBindNamespaces = true;
}
fBindNamespaces = true;
// re-configure pipeline by removing DTDValidator
}
} // reconfigurePipeline()
}
} // class XMLNSDocumentScannerImpl