|
|
|
|
|
*/ |
|
/* |
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
|
* contributor license agreements. See the NOTICE file distributed with |
|
* this work for additional information regarding copyright ownership. |
|
* The ASF licenses this file to You 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 |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* 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. |
|
*/ |
|
|
|
|
|
package com.sun.org.apache.xerces.internal.impl; |
|
|
|
import java.io.IOException; |
|
import com.sun.org.apache.xerces.internal.xni.XMLString; |
|
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.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.XMLDocumentHandler; |
|
import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource; |
|
import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager; |
|
|
|
import javax.xml.stream.events.XMLEvent; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class XMLNSDocumentScannerImpl |
|
extends XMLDocumentScannerImpl { |
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean fBindNamespaces; |
|
|
|
|
|
* scanner if DTD grammar is missing.*/ |
|
protected boolean fPerformValidation; |
|
|
|
|
|
|
|
protected boolean fNotAddNSDeclAsAttribute = false; |
|
|
|
|
|
private XMLDTDValidatorFilter fDTDValidator; |
|
|
|
|
|
private boolean fXmlnsDeclared = false; |
|
|
|
|
|
*/ |
|
public void reset(PropertyManager propertyManager) { |
|
setPropertyManager(propertyManager); |
|
super.reset(propertyManager); |
|
fBindNamespaces = false; |
|
fNotAddNSDeclAsAttribute = !((Boolean)propertyManager.getProperty(Constants.ADD_NAMESPACE_DECL_AS_ATTRIBUTE)).booleanValue(); |
|
} |
|
|
|
public void reset(XMLComponentManager componentManager) |
|
throws XMLConfigurationException { |
|
super.reset(componentManager); |
|
fNotAddNSDeclAsAttribute = false ; |
|
fPerformValidation = false; |
|
fBindNamespaces = false; |
|
} |
|
|
|
/** 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(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setDTDValidator(XMLDTDValidatorFilter dtd){ |
|
fDTDValidator = dtd; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean scanStartElement() |
|
throws IOException, XNIException { |
|
|
|
if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanStartElement()"); |
|
|
|
if(fSkip && !fAdd){ |
|
//get the stored element -- if everything goes right this should match the |
|
//token in the buffer |
|
|
|
QName name = fElementStack.getNext(); |
|
|
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("Trying to skip String = " + name.rawname); |
|
} |
|
|
|
//Be conservative -- if skipping fails -- stop. |
|
fSkip = fEntityScanner.skipString(name.rawname); |
|
|
|
if(fSkip){ |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("Element SUCESSFULLY skipped = " + name.rawname); |
|
} |
|
fElementStack.push(); |
|
fElementQName = name; |
|
}else{ |
|
|
|
fElementStack.reposition(); |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("Element was NOT skipped, REPOSITIONING stack" ); |
|
} |
|
} |
|
} |
|
|
|
//we are still at the stage of adding elements |
|
//the elements were not matched or |
|
|
|
if(!fSkip || fAdd){ |
|
|
|
fElementQName = fElementStack.nextElement(); |
|
// There are two variables,fNamespaces and fBindNamespaces |
|
|
|
if (fNamespaces) { |
|
fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART); |
|
} else { |
|
String name = fEntityScanner.scanName(NameType.ELEMENTSTART); |
|
fElementQName.setValues(null, name, name, null); |
|
} |
|
|
|
if(DEBUG)System.out.println("Element scanned in start element is " + fElementQName.toString()); |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
if(fAdd){ |
|
System.out.println("Elements are being ADDED -- elemet added is = " + fElementQName.rawname + " at count = " + fElementStack.fCount); |
|
} |
|
} |
|
|
|
} |
|
|
|
|
|
if(fAdd){ |
|
|
|
fElementStack.matchElement(fElementQName); |
|
} |
|
|
|
|
|
fCurrentElement = fElementQName; |
|
|
|
String rawname = fElementQName.rawname; |
|
checkDepth(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); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
fEmptyElement = false; |
|
fAttributes.removeAllAttributes(); |
|
|
|
if(!seekCloseOfStartTag()){ |
|
fReadingAttributes = true; |
|
fAttributeCacheUsedCount =0; |
|
fStringBufferIndex =0; |
|
fAddDefaultAttr = true; |
|
fXmlnsDeclared = false; |
|
|
|
do { |
|
scanAttribute(fAttributes); |
|
if (fSecurityManager != null && (!fSecurityManager.isNoLimit(fElementAttributeLimit)) && |
|
fAttributes.getLength() > fElementAttributeLimit){ |
|
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, |
|
"ElementAttributeLimit", |
|
new Object[]{rawname, fElementAttributeLimit }, |
|
XMLErrorReporter.SEVERITY_FATAL_ERROR ); |
|
} |
|
|
|
} while (!seekCloseOfStartTag()); |
|
fReadingAttributes=false; |
|
} |
|
|
|
if (fBindNamespaces) { |
|
|
|
if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) { |
|
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, |
|
"ElementXMLNSPrefix", |
|
new Object[]{fElementQName.rawname}, |
|
XMLErrorReporter.SEVERITY_FATAL_ERROR); |
|
} |
|
|
|
|
|
String prefix = fElementQName.prefix != null |
|
? fElementQName.prefix : XMLSymbols.EMPTY_STRING; |
|
|
|
fElementQName.uri = fNamespaceContext.getURI(prefix); |
|
|
|
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); |
|
} |
|
|
|
|
|
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); |
|
// checkDuplicates(fAttributeQName, fAttributes); |
|
} |
|
} |
|
|
|
if (length > 1) { |
|
QName name = fAttributes.checkDuplicatesNS(); |
|
if (name != null) { |
|
if (name.uri != null) { |
|
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, |
|
"AttributeNSNotUnique", |
|
new Object[]{fElementQName.rawname, name.localpart, name.uri}, |
|
XMLErrorReporter.SEVERITY_FATAL_ERROR); |
|
} else { |
|
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, |
|
"AttributeNotUnique", |
|
new Object[]{fElementQName.rawname, name.rawname}, |
|
XMLErrorReporter.SEVERITY_FATAL_ERROR); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
if (fEmptyElement) { |
|
|
|
fMarkupDepth--; |
|
|
|
|
|
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) { |
|
reportFatalError("ElementEntityMismatch", |
|
new Object[]{fCurrentElement.rawname}); |
|
} |
|
|
|
if (fDocumentHandler != null) { |
|
if(DEBUG) |
|
System.out.println("emptyElement = " + fElementQName); |
|
|
|
fDocumentHandler.emptyElement(fElementQName, fAttributes, null); |
|
} |
|
|
|
//We should not be popping out the context here in endELement becaause the namespace context is still |
|
|
|
fScanEndElement = true; |
|
//if (fBindNamespaces) { |
|
// fNamespaceContext.popContext(); |
|
//} |
|
|
|
|
|
fElementStack.popElement(); |
|
|
|
} else { |
|
|
|
if(dtdGrammarUtil != null) |
|
dtdGrammarUtil.startElement(fElementQName,fAttributes); |
|
if(fDocumentHandler != null){ |
|
//complete element and attributes are traversed in this function so we can send a callback |
|
//here. |
|
|
|
if(DEBUG) |
|
System.out.println("startElement = " + fElementQName); |
|
fDocumentHandler.startElement(fElementQName, fAttributes, null); |
|
} |
|
} |
|
|
|
|
|
if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +"<<< scanStartElement(): "+fEmptyElement); |
|
return fEmptyElement; |
|
|
|
} // scanStartElement():boolean |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void scanAttribute(XMLAttributesImpl attributes) |
|
throws IOException, XNIException { |
|
if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanAttribute()"); |
|
|
|
|
|
fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTENAME); |
|
|
|
|
|
fEntityScanner.skipSpaces(); |
|
if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) { |
|
reportFatalError("EqRequiredInAttribute", |
|
new Object[]{fCurrentElement.rawname,fAttributeQName.rawname}); |
|
} |
|
fEntityScanner.skipSpaces(); |
|
|
|
|
|
int attrIndex = 0 ; |
|
|
|
|
|
|
|
boolean isVC = fHasExternalDTD && !fStandalone; |
|
|
|
// 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 |
|
|
|
XMLString tmpStr = getString(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
String localpart = fAttributeQName.localpart; |
|
String prefix = fAttributeQName.prefix != null |
|
? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING; |
|
boolean isNSDecl = fBindNamespaces & (prefix == XMLSymbols.PREFIX_XMLNS || |
|
prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS); |
|
|
|
scanAttributeValue(tmpStr, fTempString2, fAttributeQName.rawname, attributes, |
|
attrIndex, isVC, fCurrentElement.rawname, isNSDecl); |
|
|
|
String value = null; |
|
//fTempString.toString(); |
|
|
|
|
|
if (fBindNamespaces) { |
|
if (isNSDecl) { |
|
|
|
if (tmpStr.length > fXMLNameLimit) { |
|
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, |
|
"MaxXMLNameLimit", |
|
new Object[]{new String(tmpStr.ch,tmpStr.offset,tmpStr.length), |
|
tmpStr.length, fXMLNameLimit, |
|
fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.MAX_NAME_LIMIT)}, |
|
XMLErrorReporter.SEVERITY_FATAL_ERROR); |
|
} |
|
|
|
String uri = fSymbolTable.addSymbol(tmpStr.ch,tmpStr.offset,tmpStr.length); |
|
value = uri; |
|
|
|
if (prefix == XMLSymbols.PREFIX_XMLNS && localpart == XMLSymbols.PREFIX_XMLNS) { |
|
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, |
|
"CantBindXMLNS", |
|
new Object[]{fAttributeQName}, |
|
XMLErrorReporter.SEVERITY_FATAL_ERROR); |
|
} |
|
|
|
|
|
if (uri == NamespaceContext.XMLNS_URI) { |
|
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, |
|
"CantBindXMLNS", |
|
new Object[]{fAttributeQName}, |
|
XMLErrorReporter.SEVERITY_FATAL_ERROR); |
|
} |
|
|
|
|
|
if (localpart == XMLSymbols.PREFIX_XML) { |
|
if (uri != NamespaceContext.XML_URI) { |
|
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, |
|
"CantBindXML", |
|
new Object[]{fAttributeQName}, |
|
XMLErrorReporter.SEVERITY_FATAL_ERROR); |
|
} |
|
} |
|
|
|
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; |
|
//set it equal to XMLSymbols.PREFIX_XMLNS when namespace declaration |
|
// is of type xmlns = "..", in this case prefix = "" and localname = XMLSymbols.PREFIX_XMLNS |
|
|
|
if(prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS){ |
|
fAttributeQName.prefix = XMLSymbols.PREFIX_XMLNS; |
|
} |
|
// http://www.w3.org/TR/1999/REC-xml-names-19990114/#dt-prefix |
|
// We should only report an error if there is a prefix, |
|
|
|
if (uri == XMLSymbols.EMPTY_STRING && localpart != XMLSymbols.PREFIX_XMLNS) { |
|
fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, |
|
"EmptyPrefixedAttName", |
|
new Object[]{fAttributeQName}, |
|
XMLErrorReporter.SEVERITY_FATAL_ERROR); |
|
} |
|
|
|
|
|
if (((com.sun.org.apache.xerces.internal.util.NamespaceSupport) fNamespaceContext).containsPrefixInCurrentContext(prefix)) { |
|
reportFatalError("AttributeNotUnique", |
|
new Object[]{fCurrentElement.rawname, |
|
fAttributeQName.rawname}); |
|
} |
|
|
|
|
|
boolean declared = fNamespaceContext.declarePrefix(prefix, uri.length() != 0 ? uri : null); |
|
|
|
|
|
if (!declared) { // by convention, prefix == "xmlns" | "xml" |
|
|
|
if (fXmlnsDeclared) { |
|
reportFatalError("AttributeNotUnique", |
|
new Object[]{fCurrentElement.rawname, |
|
fAttributeQName.rawname}); |
|
} |
|
|
|
|
|
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 |
|
|
|
if(fNotAddNSDeclAsAttribute){ |
|
return ; |
|
} |
|
} |
|
} |
|
|
|
|
|
if (fBindNamespaces) { |
|
attrIndex = attributes.getLength(); |
|
attributes.addAttributeNS(fAttributeQName, XMLSymbols.fCDATASymbol, null); |
|
} else { |
|
int oldLen = attributes.getLength(); |
|
attrIndex = attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null); |
|
|
|
|
|
if (oldLen == attributes.getLength()) { |
|
reportFatalError("AttributeNotUnique", |
|
new Object[]{fCurrentElement.rawname, |
|
fAttributeQName.rawname}); |
|
} |
|
} |
|
|
|
attributes.setValue(attrIndex, value,tmpStr); |
|
//attributes.setNonNormalizedValue(attrIndex, fTempString2.toString()); |
|
|
|
attributes.setSpecified(attrIndex, true); |
|
|
|
|
|
if (fAttributeQName.prefix != null) { |
|
attributes.setURI(attrIndex, fNamespaceContext.getURI(fAttributeQName.prefix)); |
|
} |
|
|
|
if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +"<<< scanAttribute()"); |
|
} // scanAttribute(XMLAttributes) |
|
|
|
|
|
|
|
|
|
|
|
|
|
protected Driver createContentDriver() { |
|
return new NSContentDriver(); |
|
} // createContentDriver():Driver |
|
|
|
|
|
|
|
*/ |
|
protected final class NSContentDriver |
|
extends ContentDriver { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean scanRootElementHook() |
|
throws IOException, XNIException { |
|
|
|
reconfigurePipeline(); |
|
if (scanStartElement()) { |
|
setScannerState(SCANNER_STATE_TRAILING_MISC); |
|
setDriver(fTrailingMiscDriver); |
|
return true; |
|
} |
|
return false; |
|
|
|
} // scanRootElementHook():boolean |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void reconfigurePipeline() { |
|
|
|
if (fNamespaces && fDTDValidator == null) { |
|
fBindNamespaces = true; |
|
} |
|
else if (fNamespaces && !fDTDValidator.hasGrammar() ) { |
|
fBindNamespaces = true; |
|
fPerformValidation = fDTDValidator.validate(); |
|
|
|
XMLDocumentSource source = fDTDValidator.getDocumentSource(); |
|
XMLDocumentHandler handler = fDTDValidator.getDocumentHandler(); |
|
source.setDocumentHandler(handler); |
|
if (handler != null) |
|
handler.setDocumentSource(source); |
|
fDTDValidator.setDocumentSource(null); |
|
fDTDValidator.setDocumentHandler(null); |
|
} |
|
} // reconfigurePipeline() |
|
} |
|
|
|
} // class XMLNSDocumentScannerImpl |