e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
877 lines
39 KiB
C#
877 lines
39 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="XmlLoader.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">[....]</owner>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Xml {
|
|
using System.IO;
|
|
using System.Collections;
|
|
using System.Diagnostics;
|
|
using System.Text;
|
|
using System.Xml.Schema;
|
|
using System.Globalization;
|
|
|
|
internal class XmlLoader {
|
|
XmlDocument doc;
|
|
XmlReader reader;
|
|
bool preserveWhitespace;
|
|
|
|
|
|
public XmlLoader() {
|
|
}
|
|
|
|
internal void Load(XmlDocument doc, XmlReader reader, bool preserveWhitespace ) {
|
|
this.doc = doc;
|
|
// perf: unwrap XmlTextReader if no one derived from it
|
|
if ( reader.GetType() == typeof( System.Xml.XmlTextReader ) ) {
|
|
this.reader = ((XmlTextReader)reader).Impl;
|
|
}
|
|
else {
|
|
this.reader = reader;
|
|
}
|
|
this.preserveWhitespace = preserveWhitespace;
|
|
if (doc == null)
|
|
throw new ArgumentException(Res.GetString(Res.Xdom_Load_NoDocument));
|
|
if (reader == null)
|
|
throw new ArgumentException(Res.GetString(Res.Xdom_Load_NoReader));
|
|
doc.SetBaseURI( reader.BaseURI );
|
|
if (reader.Settings != null
|
|
&& reader.Settings.ValidationType == ValidationType.Schema) {
|
|
doc.Schemas = reader.Settings.Schemas;
|
|
}
|
|
if ( this.reader.ReadState != ReadState.Interactive ) {
|
|
if ( !this.reader.Read() )
|
|
return;
|
|
}
|
|
LoadDocSequence( doc);
|
|
}
|
|
|
|
//The function will start loading the document from where current XmlReader is pointing at.
|
|
private void LoadDocSequence( XmlDocument parentDoc ) {
|
|
Debug.Assert( this.reader != null );
|
|
Debug.Assert( parentDoc != null );
|
|
XmlNode node = null;
|
|
while ( ( node = LoadNode(true) ) != null ) {
|
|
parentDoc.AppendChildForLoad( node, parentDoc );
|
|
if ( !this.reader.Read() )
|
|
return;
|
|
}
|
|
}
|
|
|
|
internal XmlNode ReadCurrentNode(XmlDocument doc, XmlReader reader) {
|
|
this.doc = doc;
|
|
this.reader = reader;
|
|
// WS are optional only for loading (see XmlDocument.PreserveWhitespace)
|
|
this.preserveWhitespace = true;
|
|
if (doc == null)
|
|
throw new ArgumentException(Res.GetString(Res.Xdom_Load_NoDocument));
|
|
if (reader == null)
|
|
throw new ArgumentException(Res.GetString(Res.Xdom_Load_NoReader));
|
|
|
|
if( reader.ReadState == ReadState.Initial ) {
|
|
reader.Read();
|
|
}
|
|
if( reader.ReadState == ReadState.Interactive ) {
|
|
XmlNode n = LoadNode(true);
|
|
|
|
// Move to the next node
|
|
if ( n.NodeType != XmlNodeType.Attribute )
|
|
reader.Read();
|
|
|
|
return n;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private XmlNode LoadNode(bool skipOverWhitespace) {
|
|
XmlReader r = this.reader;
|
|
XmlNode parent = null;
|
|
XmlElement element;
|
|
IXmlSchemaInfo schemaInfo;
|
|
do {
|
|
XmlNode node = null;
|
|
switch ( r.NodeType ) {
|
|
case XmlNodeType.Element:
|
|
bool fEmptyElement = r.IsEmptyElement;
|
|
element = doc.CreateElement( r.Prefix, r.LocalName, r.NamespaceURI );
|
|
element.IsEmpty = fEmptyElement;
|
|
|
|
if ( r.MoveToFirstAttribute() ) {
|
|
XmlAttributeCollection attributes = element.Attributes;
|
|
do {
|
|
XmlAttribute attr = LoadAttributeNode();
|
|
attributes.Append( attr ); // special case for load
|
|
}
|
|
while( r.MoveToNextAttribute() );
|
|
r.MoveToElement();
|
|
}
|
|
|
|
// recursively load all children.
|
|
if ( !fEmptyElement ) {
|
|
if ( parent != null ) {
|
|
parent.AppendChildForLoad( element, doc );
|
|
}
|
|
parent = element;
|
|
continue;
|
|
}
|
|
else {
|
|
schemaInfo = r.SchemaInfo;
|
|
if (schemaInfo != null) {
|
|
element.XmlName = doc.AddXmlName(element.Prefix, element.LocalName, element.NamespaceURI, schemaInfo);
|
|
}
|
|
node = element;
|
|
break;
|
|
}
|
|
|
|
case XmlNodeType.EndElement:
|
|
if ( parent == null ) {
|
|
return null;
|
|
}
|
|
Debug.Assert( parent.NodeType == XmlNodeType.Element );
|
|
schemaInfo = r.SchemaInfo;
|
|
if (schemaInfo != null) {
|
|
element = parent as XmlElement;
|
|
if (element != null) {
|
|
element.XmlName = doc.AddXmlName(element.Prefix, element.LocalName, element.NamespaceURI, schemaInfo);
|
|
}
|
|
}
|
|
if ( parent.ParentNode == null ) {
|
|
return parent;
|
|
}
|
|
parent = parent.ParentNode;
|
|
continue;
|
|
|
|
case XmlNodeType.EntityReference:
|
|
node = LoadEntityReferenceNode( false );
|
|
break;
|
|
|
|
case XmlNodeType.EndEntity:
|
|
Debug.Assert( parent == null );
|
|
return null;
|
|
|
|
case XmlNodeType.Attribute:
|
|
node = LoadAttributeNode();
|
|
break;
|
|
|
|
case XmlNodeType.Text:
|
|
node = doc.CreateTextNode( r.Value );
|
|
break;
|
|
|
|
case XmlNodeType.SignificantWhitespace:
|
|
node = doc.CreateSignificantWhitespace( r.Value );
|
|
break;
|
|
|
|
case XmlNodeType.Whitespace:
|
|
if ( preserveWhitespace ) {
|
|
node = doc.CreateWhitespace( r.Value );
|
|
break;
|
|
}
|
|
else if (parent == null && !skipOverWhitespace) {
|
|
// if called from LoadEntityReferenceNode, just return null
|
|
return null;
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
case XmlNodeType.CDATA:
|
|
node = doc.CreateCDataSection( r.Value );
|
|
break;
|
|
|
|
|
|
case XmlNodeType.XmlDeclaration:
|
|
node = LoadDeclarationNode();
|
|
break;
|
|
|
|
case XmlNodeType.ProcessingInstruction:
|
|
node = doc.CreateProcessingInstruction( r.Name, r.Value );
|
|
break;
|
|
|
|
case XmlNodeType.Comment:
|
|
node = doc.CreateComment( r.Value );
|
|
break;
|
|
|
|
case XmlNodeType.DocumentType:
|
|
node = LoadDocumentTypeNode();
|
|
break;
|
|
|
|
default:
|
|
throw UnexpectedNodeType( r.NodeType );
|
|
}
|
|
|
|
Debug.Assert( node != null );
|
|
if ( parent != null ) {
|
|
parent.AppendChildForLoad( node, doc );
|
|
}
|
|
else {
|
|
return node;
|
|
}
|
|
}
|
|
while ( r.Read());
|
|
|
|
// when the reader ended before full subtree is read, return whatever we have created so far
|
|
if ( parent != null ) {
|
|
while ( parent.ParentNode != null ) {
|
|
parent = parent.ParentNode;
|
|
}
|
|
}
|
|
return parent;
|
|
}
|
|
|
|
private XmlAttribute LoadAttributeNode() {
|
|
Debug.Assert(reader.NodeType == XmlNodeType.Attribute);
|
|
|
|
XmlReader r = reader;
|
|
if (r.IsDefault) {
|
|
return LoadDefaultAttribute();
|
|
}
|
|
|
|
XmlAttribute attr = doc.CreateAttribute(r.Prefix, r.LocalName, r.NamespaceURI);
|
|
IXmlSchemaInfo schemaInfo = r.SchemaInfo;
|
|
if (schemaInfo != null) {
|
|
attr.XmlName = doc.AddAttrXmlName(attr.Prefix, attr.LocalName, attr.NamespaceURI, schemaInfo);
|
|
}
|
|
while ( r.ReadAttributeValue() ) {
|
|
XmlNode node;
|
|
switch (r.NodeType) {
|
|
case XmlNodeType.Text:
|
|
node = doc.CreateTextNode( r.Value );
|
|
break;
|
|
case XmlNodeType.EntityReference:
|
|
node = doc.CreateEntityReference( r.LocalName );
|
|
if ( r.CanResolveEntity ) {
|
|
r.ResolveEntity();
|
|
LoadAttributeValue( node, false );
|
|
// Code internally relies on the fact that an EntRef nodes has at least one child (even an empty text node). Ensure that this holds true,
|
|
// if the reader does not present any children for the ent-ref
|
|
if ( node.FirstChild == null ) {
|
|
node.AppendChildForLoad( doc.CreateTextNode(string.Empty), doc );
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
throw UnexpectedNodeType( r.NodeType );
|
|
}
|
|
Debug.Assert( node != null );
|
|
attr.AppendChildForLoad( node, doc );
|
|
}
|
|
|
|
return attr;
|
|
}
|
|
|
|
private XmlAttribute LoadDefaultAttribute() {
|
|
Debug.Assert( reader.IsDefault );
|
|
|
|
XmlReader r = reader;
|
|
XmlAttribute attr = doc.CreateDefaultAttribute( r.Prefix, r.LocalName, r.NamespaceURI );
|
|
IXmlSchemaInfo schemaInfo = r.SchemaInfo;
|
|
if (schemaInfo != null) {
|
|
attr.XmlName = doc.AddAttrXmlName(attr.Prefix, attr.LocalName, attr.NamespaceURI, schemaInfo);
|
|
}
|
|
|
|
LoadAttributeValue( attr, false );
|
|
|
|
XmlUnspecifiedAttribute defAttr = attr as XmlUnspecifiedAttribute;
|
|
// If user overrides CreateDefaultAttribute, then attr will NOT be a XmlUnspecifiedAttribute instance.
|
|
if ( defAttr != null )
|
|
defAttr.SetSpecified( false );
|
|
|
|
return attr;
|
|
}
|
|
|
|
private void LoadAttributeValue( XmlNode parent, bool direct ) {
|
|
XmlReader r = reader;
|
|
while ( r.ReadAttributeValue() ) {
|
|
XmlNode node;
|
|
switch ( r.NodeType ) {
|
|
case XmlNodeType.Text:
|
|
node = direct ? new XmlText( r.Value, doc ) : doc.CreateTextNode( r.Value );
|
|
break;
|
|
case XmlNodeType.EndEntity:
|
|
return;
|
|
case XmlNodeType.EntityReference:
|
|
node = direct ? new XmlEntityReference( reader.LocalName, doc ) : doc.CreateEntityReference( reader.LocalName );
|
|
if ( r.CanResolveEntity ) {
|
|
r.ResolveEntity();
|
|
LoadAttributeValue( node, direct );
|
|
// Code internally relies on the fact that an EntRef nodes has at least one child (even an empty text node). Ensure that this holds true,
|
|
// if the reader does not present any children for the ent-ref
|
|
if ( node.FirstChild == null ) {
|
|
node.AppendChildForLoad( direct ? new XmlText( string.Empty ) : doc.CreateTextNode( string.Empty ), doc );
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
throw UnexpectedNodeType( r.NodeType );
|
|
}
|
|
Debug.Assert( node != null );
|
|
parent.AppendChildForLoad( node, doc );
|
|
}
|
|
return;
|
|
}
|
|
|
|
private XmlEntityReference LoadEntityReferenceNode( bool direct ) {
|
|
Debug.Assert( reader.NodeType == XmlNodeType.EntityReference );
|
|
XmlEntityReference eref = direct ? new XmlEntityReference( reader.Name, this.doc ) : doc.CreateEntityReference( reader.Name );
|
|
if ( reader.CanResolveEntity ) {
|
|
reader.ResolveEntity();
|
|
while ( reader.Read() && reader.NodeType != XmlNodeType.EndEntity ) {
|
|
XmlNode node = direct ? LoadNodeDirect() : LoadNode(false);
|
|
if ( node != null ) {
|
|
eref.AppendChildForLoad( node, doc );
|
|
}
|
|
}
|
|
// Code internally relies on the fact that an EntRef nodes has at least one child (even an empty text node). Ensure that this holds true,
|
|
// if the reader does not present any children for the ent-ref
|
|
if ( eref.LastChild == null )
|
|
eref.AppendChildForLoad( doc.CreateTextNode(string.Empty), doc );
|
|
}
|
|
return eref;
|
|
}
|
|
|
|
private XmlDeclaration LoadDeclarationNode() {
|
|
Debug.Assert( reader.NodeType == XmlNodeType.XmlDeclaration );
|
|
|
|
//parse data
|
|
string version = null;
|
|
string encoding = null;
|
|
string standalone = null;
|
|
|
|
// Try first to use the reader to get the xml decl "attributes". Since not all readers are required to support this, it is possible to have
|
|
// implementations that do nothing
|
|
while(reader.MoveToNextAttribute()) {
|
|
switch (reader.Name) {
|
|
case "version":
|
|
version = reader.Value;
|
|
break;
|
|
case "encoding":
|
|
encoding = reader.Value;
|
|
break;
|
|
case "standalone":
|
|
standalone = reader.Value;
|
|
break;
|
|
default:
|
|
Debug.Assert( false );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// For readers that do not break xml decl into attributes, we must parse the xml decl ourselfs. We use version attr, b/c xml decl MUST contain
|
|
// at least version attr, so if the reader implements them as attr, then version must be present
|
|
if ( version == null )
|
|
ParseXmlDeclarationValue( reader.Value, out version, out encoding, out standalone );
|
|
|
|
return doc.CreateXmlDeclaration( version, encoding, standalone );
|
|
}
|
|
|
|
private XmlDocumentType LoadDocumentTypeNode() {
|
|
Debug.Assert( reader.NodeType == XmlNodeType.DocumentType );
|
|
|
|
String publicId = null;
|
|
String systemId = null;
|
|
String internalSubset = reader.Value;
|
|
String localName = reader.LocalName;
|
|
while (reader.MoveToNextAttribute()) {
|
|
switch (reader.Name) {
|
|
case "PUBLIC" :
|
|
publicId = reader.Value;
|
|
break;
|
|
case "SYSTEM":
|
|
systemId = reader.Value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
XmlDocumentType dtNode = doc.CreateDocumentType( localName, publicId, systemId, internalSubset );
|
|
|
|
IDtdInfo dtdInfo = reader.DtdInfo;
|
|
if ( dtdInfo != null )
|
|
LoadDocumentType( dtdInfo, dtNode );
|
|
else {
|
|
//construct our own XmlValidatingReader to parse the DocumentType node so we could get Entities and notations information
|
|
ParseDocumentType( dtNode );
|
|
}
|
|
|
|
return dtNode;
|
|
}
|
|
|
|
// LoadNodeDirect does not use creator functions on XmlDocument. It is used loading nodes that are children of entity nodes,
|
|
// becaouse we do not want to let users extend these (if we would allow this, XmlDataDocument would have a problem, becaouse
|
|
// they do not know that those nodes should not be mapped). It can be also used for an optimized load path when if the
|
|
// XmlDocument is not extended if XmlDocumentType and XmlDeclaration handling is added.
|
|
private XmlNode LoadNodeDirect() {
|
|
XmlReader r = this.reader;
|
|
XmlNode parent = null;
|
|
do {
|
|
XmlNode node = null;
|
|
switch ( r.NodeType ) {
|
|
case XmlNodeType.Element:
|
|
bool fEmptyElement = reader.IsEmptyElement;
|
|
XmlElement element = new XmlElement( reader.Prefix, reader.LocalName, reader.NamespaceURI, this.doc );
|
|
element.IsEmpty = fEmptyElement;
|
|
|
|
if ( reader.MoveToFirstAttribute() ) {
|
|
XmlAttributeCollection attributes = element.Attributes;
|
|
do {
|
|
XmlAttribute attr = LoadAttributeNodeDirect();
|
|
attributes.Append( attr ); // special case for load
|
|
} while( r.MoveToNextAttribute() );
|
|
}
|
|
|
|
// recursively load all children.
|
|
if ( !fEmptyElement ) {
|
|
parent.AppendChildForLoad( element, doc );
|
|
parent = element;
|
|
continue;
|
|
}
|
|
else {
|
|
node = element;
|
|
break;
|
|
}
|
|
|
|
case XmlNodeType.EndElement:
|
|
Debug.Assert( parent.NodeType == XmlNodeType.Element );
|
|
if ( parent.ParentNode == null ) {
|
|
return parent;
|
|
}
|
|
parent = parent.ParentNode;
|
|
continue;
|
|
|
|
case XmlNodeType.EntityReference:
|
|
node = LoadEntityReferenceNode( true );
|
|
break;
|
|
|
|
case XmlNodeType.EndEntity:
|
|
continue;
|
|
|
|
case XmlNodeType.Attribute:
|
|
node = LoadAttributeNodeDirect();
|
|
break;
|
|
|
|
case XmlNodeType.SignificantWhitespace:
|
|
node = new XmlSignificantWhitespace( reader.Value, this.doc );
|
|
break;
|
|
|
|
case XmlNodeType.Whitespace:
|
|
if ( preserveWhitespace ) {
|
|
node = new XmlWhitespace( reader.Value, this.doc );
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case XmlNodeType.Text:
|
|
node = new XmlText( reader.Value, this.doc );
|
|
break;
|
|
|
|
case XmlNodeType.CDATA:
|
|
node = new XmlCDataSection( reader.Value, this.doc );
|
|
break;
|
|
|
|
case XmlNodeType.ProcessingInstruction:
|
|
node = new XmlProcessingInstruction( reader.Name, reader.Value, this.doc );
|
|
break;
|
|
|
|
case XmlNodeType.Comment:
|
|
node = new XmlComment( reader.Value, this.doc );
|
|
break;
|
|
|
|
default:
|
|
throw UnexpectedNodeType( reader.NodeType );
|
|
}
|
|
|
|
Debug.Assert( node != null );
|
|
if ( parent != null ) {
|
|
parent.AppendChildForLoad( node, doc );
|
|
}
|
|
else {
|
|
return node;
|
|
}
|
|
}
|
|
while ( r.Read());
|
|
|
|
return null;
|
|
}
|
|
|
|
private XmlAttribute LoadAttributeNodeDirect() {
|
|
XmlReader r = reader;
|
|
XmlAttribute attr;
|
|
if ( r.IsDefault ) {
|
|
XmlUnspecifiedAttribute defattr = new XmlUnspecifiedAttribute( r.Prefix, r.LocalName, r.NamespaceURI, this.doc );
|
|
LoadAttributeValue( defattr, true );
|
|
defattr.SetSpecified( false );
|
|
return defattr;
|
|
}
|
|
else {
|
|
attr = new XmlAttribute( r.Prefix, r.LocalName, r.NamespaceURI, this.doc );
|
|
LoadAttributeValue( attr, true );
|
|
return attr;
|
|
}
|
|
}
|
|
|
|
internal void ParseDocumentType ( XmlDocumentType dtNode ) {
|
|
XmlDocument doc = dtNode.OwnerDocument;
|
|
//if xmlresolver is set on doc, use that one, otherwise use the default one being created by xmlvalidatingreader
|
|
if ( doc.HasSetResolver )
|
|
ParseDocumentType( dtNode, true, doc.GetResolver() );
|
|
else
|
|
ParseDocumentType( dtNode, false, null );
|
|
}
|
|
|
|
private void ParseDocumentType ( XmlDocumentType dtNode, bool bUseResolver, XmlResolver resolver ) {
|
|
this.doc = dtNode.OwnerDocument;
|
|
XmlParserContext pc = new XmlParserContext(null, new XmlNamespaceManager( this.doc.NameTable ), null, null, null, null, this.doc.BaseURI, string.Empty, XmlSpace.None);
|
|
XmlTextReaderImpl tr = new XmlTextReaderImpl("", XmlNodeType.Element, pc);
|
|
tr.Namespaces = dtNode.ParseWithNamespaces;
|
|
if (bUseResolver) {
|
|
tr.XmlResolver = resolver;
|
|
}
|
|
|
|
IDtdParser dtdParser = DtdParser.Create();
|
|
XmlTextReaderImpl.DtdParserProxy proxy = new XmlTextReaderImpl.DtdParserProxy(tr);
|
|
|
|
IDtdInfo dtdInfo = dtdParser.ParseFreeFloatingDtd(this.doc.BaseURI, dtNode.Name, dtNode.PublicId, dtNode.SystemId, dtNode.InternalSubset, proxy );
|
|
LoadDocumentType( dtdInfo, dtNode );
|
|
}
|
|
|
|
private void LoadDocumentType( IDtdInfo dtdInfo, XmlDocumentType dtNode ) {
|
|
SchemaInfo schInfo = dtdInfo as SchemaInfo;
|
|
if (schInfo == null) {
|
|
throw new XmlException(Res.Xml_InternalError, string.Empty);
|
|
}
|
|
|
|
dtNode.DtdSchemaInfo = schInfo;
|
|
if (schInfo != null) {
|
|
//set the schema information into the document
|
|
doc.DtdSchemaInfo = schInfo;
|
|
|
|
// Notation hashtable
|
|
if (schInfo.Notations != null) {
|
|
foreach( SchemaNotation scNot in schInfo.Notations.Values ) {
|
|
dtNode.Notations.SetNamedItem(new XmlNotation( scNot.Name.Name, scNot.Pubid, scNot.SystemLiteral, doc ));
|
|
}
|
|
}
|
|
|
|
// Entity hashtables
|
|
if (schInfo.GeneralEntities != null) {
|
|
foreach( SchemaEntity scEnt in schInfo.GeneralEntities.Values ) {
|
|
XmlEntity ent = new XmlEntity( scEnt.Name.Name, scEnt.Text, scEnt.Pubid, scEnt.Url, scEnt.NData.IsEmpty ? null : scEnt.NData.Name, doc );
|
|
ent.SetBaseURI( scEnt.DeclaredURI );
|
|
dtNode.Entities.SetNamedItem( ent );
|
|
}
|
|
}
|
|
|
|
if (schInfo.ParameterEntities != null) {
|
|
foreach( SchemaEntity scEnt in schInfo.ParameterEntities.Values ) {
|
|
XmlEntity ent = new XmlEntity( scEnt.Name.Name, scEnt.Text, scEnt.Pubid, scEnt.Url, scEnt.NData.IsEmpty ? null : scEnt.NData.Name, doc );
|
|
ent.SetBaseURI( scEnt.DeclaredURI );
|
|
dtNode.Entities.SetNamedItem( ent );
|
|
}
|
|
}
|
|
doc.Entities = dtNode.Entities;
|
|
|
|
//extract the elements which has attribute defined as ID from the element declarations
|
|
IDictionaryEnumerator elementDecls = schInfo.ElementDecls.GetEnumerator();
|
|
if (elementDecls != null) {
|
|
elementDecls.Reset();
|
|
while (elementDecls.MoveNext()) {
|
|
SchemaElementDecl elementDecl = (SchemaElementDecl)elementDecls.Value;
|
|
if (elementDecl.AttDefs != null) {
|
|
IDictionaryEnumerator attDefs = elementDecl.AttDefs.GetEnumerator();
|
|
while (attDefs.MoveNext()) {
|
|
SchemaAttDef attdef = (SchemaAttDef)attDefs.Value;
|
|
if (attdef.Datatype.TokenizedType == XmlTokenizedType.ID) {
|
|
//we only register the XmlElement based on their Prefix/LocalName and skip the namespace
|
|
doc.AddIdInfo(
|
|
doc.AddXmlName(elementDecl.Prefix, elementDecl.Name.Name, string.Empty, null),
|
|
doc.AddAttrXmlName(attdef.Prefix, attdef.Name.Name, string.Empty, null));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#pragma warning restore 618
|
|
|
|
private XmlParserContext GetContext ( XmlNode node ) {
|
|
String lang = null;
|
|
XmlSpace spaceMode = XmlSpace.None;
|
|
XmlDocumentType docType = this.doc.DocumentType;
|
|
String baseURI = this.doc.BaseURI;
|
|
//constructing xmlnamespace
|
|
Hashtable prefixes = new Hashtable();
|
|
XmlNameTable nt = this.doc.NameTable;
|
|
XmlNamespaceManager mgr = new XmlNamespaceManager( nt );
|
|
bool bHasDefXmlnsAttr = false;
|
|
|
|
// Process all xmlns, xmlns:prefix, xml:space and xml:lang attributes
|
|
while ( node != null && node != doc ) {
|
|
if ( node is XmlElement && ((XmlElement)node).HasAttributes ) {
|
|
mgr.PushScope();
|
|
foreach( XmlAttribute attr in ((XmlElement)node).Attributes ) {
|
|
if ( attr.Prefix == doc.strXmlns && prefixes.Contains( attr.LocalName ) == false ) {
|
|
// Make sure the next time we will not add this prefix
|
|
prefixes.Add( attr.LocalName, attr.LocalName );
|
|
mgr.AddNamespace( attr.LocalName, attr.Value );
|
|
}
|
|
else if ( !bHasDefXmlnsAttr && attr.Prefix.Length == 0 && attr.LocalName == doc.strXmlns ) {
|
|
// Save the case xmlns="..." where xmlns is the LocalName
|
|
mgr.AddNamespace( String.Empty, attr.Value );
|
|
bHasDefXmlnsAttr = true;
|
|
}
|
|
else if ( spaceMode == XmlSpace.None && attr.Prefix == doc.strXml && attr.LocalName == doc.strSpace ) {
|
|
// Save xml:space context
|
|
if ( attr.Value=="default" )
|
|
spaceMode = XmlSpace.Default;
|
|
else if (attr.Value=="preserve")
|
|
spaceMode = XmlSpace.Preserve;
|
|
}
|
|
else if ( lang == null && attr.Prefix == doc.strXml && attr.LocalName == doc.strLang ) {
|
|
// Save xml:lag context
|
|
lang = attr.Value;
|
|
}
|
|
}
|
|
}
|
|
node = node.ParentNode;
|
|
}
|
|
return new XmlParserContext(
|
|
nt,
|
|
mgr,
|
|
( docType == null ) ? null : docType.Name,
|
|
( docType == null ) ? null : docType.PublicId,
|
|
( docType == null ) ? null : docType.SystemId,
|
|
( docType == null ) ? null : docType.InternalSubset,
|
|
baseURI,
|
|
lang,
|
|
spaceMode
|
|
);
|
|
}
|
|
|
|
|
|
|
|
internal XmlNamespaceManager ParsePartialContent( XmlNode parentNode, string innerxmltext, XmlNodeType nt) {
|
|
//the function shouldn't be used to set innerxml for XmlDocument node
|
|
Debug.Assert( parentNode.NodeType != XmlNodeType.Document );
|
|
this.doc = parentNode.OwnerDocument;
|
|
Debug.Assert( this.doc != null );
|
|
XmlParserContext pc = GetContext( parentNode );
|
|
this.reader = CreateInnerXmlReader( innerxmltext, nt, pc, this.doc );
|
|
try {
|
|
this.preserveWhitespace = true;
|
|
bool bOrigLoading = doc.IsLoading;
|
|
doc.IsLoading = true;
|
|
|
|
if ( nt == XmlNodeType.Entity ) {
|
|
XmlNode node = null;
|
|
while ( reader.Read() && ( node = LoadNodeDirect() ) != null ) {
|
|
parentNode.AppendChildForLoad( node, doc );
|
|
}
|
|
}
|
|
else {
|
|
XmlNode node = null;
|
|
while ( reader.Read() && ( node = LoadNode(true) ) != null ) {
|
|
parentNode.AppendChildForLoad( node, doc );
|
|
}
|
|
}
|
|
doc.IsLoading = bOrigLoading;
|
|
}
|
|
finally {
|
|
this.reader.Close();
|
|
}
|
|
return pc.NamespaceManager;
|
|
}
|
|
|
|
internal void LoadInnerXmlElement(XmlElement node, string innerxmltext ) {
|
|
//construct a tree underneth the node
|
|
XmlNamespaceManager mgr = ParsePartialContent( node, innerxmltext, XmlNodeType.Element );
|
|
//remove the duplicate namesapce
|
|
if ( node.ChildNodes.Count > 0 )
|
|
RemoveDuplicateNamespace( (XmlElement) node, mgr, false );
|
|
}
|
|
|
|
internal void LoadInnerXmlAttribute(XmlAttribute node, string innerxmltext ) {
|
|
ParsePartialContent( node, innerxmltext, XmlNodeType.Attribute );
|
|
}
|
|
|
|
|
|
private void RemoveDuplicateNamespace( XmlElement elem, XmlNamespaceManager mgr, bool fCheckElemAttrs ) {
|
|
//remove the duplicate attributes on current node first
|
|
mgr.PushScope();
|
|
XmlAttributeCollection attrs = elem.Attributes;
|
|
int cAttrs = attrs.Count;
|
|
if ( fCheckElemAttrs && cAttrs > 0 ) {
|
|
for ( int i = cAttrs - 1; i >= 0; --i ) {
|
|
XmlAttribute attr = attrs[i];
|
|
if ( attr.Prefix == doc.strXmlns ) {
|
|
string nsUri = mgr.LookupNamespace(attr.LocalName);
|
|
if ( nsUri != null ) {
|
|
if ( attr.Value == nsUri )
|
|
elem.Attributes.RemoveNodeAt(i);
|
|
}
|
|
else {
|
|
// Add this namespace, so it we will behave corectly when setting "<bar xmlns:p="BAR"><foo2 xmlns:p="FOO"/></bar>" as
|
|
// InnerXml on this foo elem where foo is like this "<foo xmlns:p="FOO"></foo>"
|
|
// If do not do this, then we will remove the inner p prefix definition and will let the 1st p to be in scope for
|
|
// the subsequent InnerXml_set or setting an EntRef inside.
|
|
mgr.AddNamespace( attr.LocalName, attr.Value );
|
|
}
|
|
}
|
|
else if ( attr.Prefix.Length == 0 && attr.LocalName == doc.strXmlns ) {
|
|
string nsUri = mgr.DefaultNamespace;
|
|
if ( nsUri != null ) {
|
|
if ( attr.Value == nsUri )
|
|
elem.Attributes.RemoveNodeAt(i);
|
|
}
|
|
else {
|
|
// Add this namespace, so it we will behave corectly when setting "<bar xmlns:p="BAR"><foo2 xmlns:p="FOO"/></bar>" as
|
|
// InnerXml on this foo elem where foo is like this "<foo xmlns:p="FOO"></foo>"
|
|
// If do not do this, then we will remove the inner p prefix definition and will let the 1st p to be in scope for
|
|
// the subsequent InnerXml_set or setting an EntRef inside.
|
|
mgr.AddNamespace( attr.LocalName, attr.Value );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//now recursively remove the duplicate attributes on the children
|
|
XmlNode child = elem.FirstChild;
|
|
while ( child != null ) {
|
|
XmlElement childElem = child as XmlElement;
|
|
if ( childElem != null )
|
|
RemoveDuplicateNamespace( childElem, mgr, true );
|
|
child = child.NextSibling;
|
|
}
|
|
mgr.PopScope();
|
|
}
|
|
|
|
private String EntitizeName(String name) {
|
|
return "&"+name+";";
|
|
}
|
|
|
|
//The function is called when expanding the entity when its children being asked
|
|
internal void ExpandEntity(XmlEntity ent) {
|
|
ParsePartialContent( ent, EntitizeName(ent.Name), XmlNodeType.Entity );
|
|
}
|
|
|
|
//The function is called when expanding the entity ref. ( inside XmlEntityReference.SetParent )
|
|
internal void ExpandEntityReference(XmlEntityReference eref)
|
|
{
|
|
//when the ent ref is not associated w/ an entity, append an empty string text node as child
|
|
this.doc = eref.OwnerDocument;
|
|
bool bOrigLoadingState = doc.IsLoading;
|
|
doc.IsLoading = true;
|
|
switch ( eref.Name ) {
|
|
case "lt":
|
|
eref.AppendChildForLoad( doc.CreateTextNode( "<" ), doc );
|
|
doc.IsLoading = bOrigLoadingState;
|
|
return;
|
|
case "gt":
|
|
eref.AppendChildForLoad( doc.CreateTextNode( ">" ), doc );
|
|
doc.IsLoading = bOrigLoadingState;
|
|
return;
|
|
case "amp":
|
|
eref.AppendChildForLoad( doc.CreateTextNode( "&" ), doc );
|
|
doc.IsLoading = bOrigLoadingState;
|
|
return;
|
|
case "apos":
|
|
eref.AppendChildForLoad( doc.CreateTextNode( "'" ), doc );
|
|
doc.IsLoading = bOrigLoadingState;
|
|
return;
|
|
case "quot":
|
|
eref.AppendChildForLoad( doc.CreateTextNode( "\"" ), doc );
|
|
doc.IsLoading = bOrigLoadingState;
|
|
return;
|
|
}
|
|
|
|
XmlNamedNodeMap entities = doc.Entities;
|
|
foreach ( XmlEntity ent in entities ) {
|
|
if ( Ref.Equal( ent.Name, eref.Name ) ) {
|
|
ParsePartialContent( eref, EntitizeName(eref.Name), XmlNodeType.EntityReference );
|
|
return;
|
|
}
|
|
}
|
|
//no fit so far
|
|
if( !( doc.ActualLoadingStatus ) ) {
|
|
eref.AppendChildForLoad( doc.CreateTextNode( "" ), doc );
|
|
doc.IsLoading = bOrigLoadingState;
|
|
}
|
|
else {
|
|
doc.IsLoading = bOrigLoadingState;
|
|
throw new XmlException( Res.Xml_UndeclaredParEntity, eref.Name );
|
|
}
|
|
}
|
|
|
|
#pragma warning disable 618
|
|
// Creates a XmlValidatingReader suitable for parsing InnerXml strings
|
|
private XmlReader CreateInnerXmlReader( String xmlFragment, XmlNodeType nt, XmlParserContext context, XmlDocument doc ) {
|
|
XmlNodeType contentNT = nt;
|
|
if ( contentNT == XmlNodeType.Entity || contentNT == XmlNodeType.EntityReference )
|
|
contentNT = XmlNodeType.Element;
|
|
|
|
XmlTextReaderImpl tr = new XmlTextReaderImpl( xmlFragment, contentNT, context );
|
|
tr.XmlValidatingReaderCompatibilityMode = true;
|
|
if ( doc.HasSetResolver ) {
|
|
tr.XmlResolver = doc.GetResolver();
|
|
}
|
|
if( !( doc.ActualLoadingStatus ) ) {
|
|
tr.DisableUndeclaredEntityCheck = true;
|
|
}
|
|
Debug.Assert( tr.EntityHandling == EntityHandling.ExpandCharEntities );
|
|
|
|
XmlDocumentType dtdNode = doc.DocumentType;
|
|
if ( dtdNode != null ) {
|
|
tr.Namespaces = dtdNode.ParseWithNamespaces;
|
|
if ( dtdNode.DtdSchemaInfo != null ) {
|
|
tr.SetDtdInfo( dtdNode.DtdSchemaInfo );
|
|
}
|
|
else {
|
|
IDtdParser dtdParser = DtdParser.Create();
|
|
XmlTextReaderImpl.DtdParserProxy proxy = new XmlTextReaderImpl.DtdParserProxy(tr);
|
|
|
|
IDtdInfo dtdInfo = dtdParser.ParseFreeFloatingDtd(context.BaseURI, context.DocTypeName, context.PublicId, context.SystemId, context.InternalSubset, proxy);
|
|
|
|
//
|
|
dtdNode.DtdSchemaInfo = dtdInfo as SchemaInfo;
|
|
tr.SetDtdInfo( dtdInfo );
|
|
}
|
|
}
|
|
|
|
if ( nt == XmlNodeType.Entity || nt == XmlNodeType.EntityReference ) {
|
|
tr.Read(); //this will skip the first element "wrapper"
|
|
tr.ResolveEntity();
|
|
}
|
|
return tr;
|
|
}
|
|
#pragma warning restore 618
|
|
|
|
internal static void ParseXmlDeclarationValue( string strValue, out string version, out string encoding, out string standalone ) {
|
|
version = null;
|
|
encoding = null;
|
|
standalone = null;
|
|
XmlTextReaderImpl tempreader = new XmlTextReaderImpl( strValue, (XmlParserContext)null);
|
|
try {
|
|
tempreader.Read();
|
|
//get version info.
|
|
if (tempreader.MoveToAttribute( "version" ))
|
|
version = tempreader.Value;
|
|
//get encoding info
|
|
if (tempreader.MoveToAttribute( "encoding" ))
|
|
encoding = tempreader.Value;
|
|
//get standalone info
|
|
if (tempreader.MoveToAttribute( "standalone" ))
|
|
standalone = tempreader.Value;
|
|
}
|
|
finally {
|
|
tempreader.Close();
|
|
}
|
|
}
|
|
|
|
static internal Exception UnexpectedNodeType( XmlNodeType nodetype ) {
|
|
return new InvalidOperationException( string.Format( CultureInfo.InvariantCulture, Res.GetString(Res.Xml_UnexpectedNodeType), nodetype.ToString()));
|
|
}
|
|
}
|
|
}
|