1 //------------------------------------------------------------------------------
2 // <copyright file="XmlDocument.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
11 using System.Collections;
12 using System.Diagnostics;
15 using System.Xml.Schema;
16 using System.Xml.XPath;
17 using System.Security;
18 using System.Security.Permissions;
19 using System.Globalization;
20 using System.Runtime.Versioning;
22 // Represents an entire document. An XmlDocument contains XML data.
23 public class XmlDocument: XmlNode {
24 private XmlImplementation implementation;
25 private DomNameTable domNameTable; // hash table of XmlName
26 private XmlLinkedNode lastChild;
27 private XmlNamedNodeMap entities;
28 private Hashtable htElementIdMap;
29 private Hashtable htElementIDAttrDecl; //key: id; object: the ArrayList of the elements that have the same id (connected or disconnected)
30 private SchemaInfo schemaInfo;
31 private XmlSchemaSet schemas; // schemas associated with the cache
32 private bool reportValidity;
33 //This variable represents the actual loading status. Since, IsLoading will
34 //be manipulated soemtimes for adding content to EntityReference this variable
35 //has been added which would always represent the loading status of document.
36 private bool actualLoadingStatus;
38 private XmlNodeChangedEventHandler onNodeInsertingDelegate;
39 private XmlNodeChangedEventHandler onNodeInsertedDelegate;
40 private XmlNodeChangedEventHandler onNodeRemovingDelegate;
41 private XmlNodeChangedEventHandler onNodeRemovedDelegate;
42 private XmlNodeChangedEventHandler onNodeChangingDelegate;
43 private XmlNodeChangedEventHandler onNodeChangedDelegate;
45 // false if there are no ent-ref present, true if ent-ref nodes are or were present (i.e. if all ent-ref were removed, the doc will not clear this flag)
46 internal bool fEntRefNodesPresent;
47 internal bool fCDataNodesPresent;
49 private bool preserveWhitespace;
50 private bool isLoading;
52 // special name strings for
53 internal string strDocumentName;
54 internal string strDocumentFragmentName;
55 internal string strCommentName;
56 internal string strTextName;
57 internal string strCDataSectionName;
58 internal string strEntityName;
59 internal string strID;
60 internal string strXmlns;
61 internal string strXml;
62 internal string strSpace;
63 internal string strLang;
64 internal string strEmpty;
66 internal string strNonSignificantWhitespaceName;
67 internal string strSignificantWhitespaceName;
68 internal string strReservedXmlns;
69 internal string strReservedXml;
71 internal String baseURI;
73 private XmlResolver resolver;
74 internal bool bSetResolver;
75 internal object objLock;
77 private XmlAttribute namespaceXml;
79 static internal EmptyEnumerator EmptyEnumerator = new EmptyEnumerator();
80 static internal IXmlSchemaInfo NotKnownSchemaInfo = new XmlSchemaInfo(XmlSchemaValidity.NotKnown);
81 static internal IXmlSchemaInfo ValidSchemaInfo = new XmlSchemaInfo(XmlSchemaValidity.Valid);
82 static internal IXmlSchemaInfo InvalidSchemaInfo = new XmlSchemaInfo(XmlSchemaValidity.Invalid);
84 // Initializes a new instance of the XmlDocument class.
85 public XmlDocument(): this( new XmlImplementation() ) {
88 // Initializes a new instance
89 // of the XmlDocument class with the specified XmlNameTable.
90 public XmlDocument( XmlNameTable nt ) : this( new XmlImplementation( nt ) ) {
93 protected internal XmlDocument( XmlImplementation imp ): base() {
96 domNameTable = new DomNameTable( this );
98 // force the following string instances to be default in the nametable
99 XmlNameTable nt = this.NameTable;
100 nt.Add( string.Empty );
101 strDocumentName = nt.Add( "#document" );
102 strDocumentFragmentName = nt.Add( "#document-fragment" );
103 strCommentName = nt.Add( "#comment" );
104 strTextName = nt.Add( "#text" );
105 strCDataSectionName = nt.Add( "#cdata-section" );
106 strEntityName = nt.Add( "#entity" );
107 strID = nt.Add( "id" );
108 strNonSignificantWhitespaceName = nt.Add( "#whitespace" );
109 strSignificantWhitespaceName = nt.Add( "#significant-whitespace" );
110 strXmlns = nt.Add( "xmlns" );
111 strXml = nt.Add( "xml" );
112 strSpace = nt.Add( "space" );
113 strLang = nt.Add( "lang" );
114 strReservedXmlns = nt.Add( XmlReservedNs.NsXmlNs );
115 strReservedXml = nt.Add( XmlReservedNs.NsXml );
116 strEmpty = nt.Add( String.Empty );
117 baseURI = String.Empty;
119 objLock = new object();
122 internal SchemaInfo DtdSchemaInfo {
123 get { return schemaInfo; }
124 set { schemaInfo = value; }
127 // NOTE: This does not correctly check start name char, but we cannot change it since it would be a breaking change.
128 internal static void CheckName( String name ) {
129 int endPos = ValidateNames.ParseNmtoken( name, 0 );
130 if (endPos < name.Length) {
131 throw new XmlException(Res.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(name, endPos));
135 internal XmlName AddXmlName(string prefix, string localName, string namespaceURI, IXmlSchemaInfo schemaInfo) {
136 XmlName n = domNameTable.AddName(prefix, localName, namespaceURI, schemaInfo);
137 Debug.Assert( (prefix == null) ? (n.Prefix.Length == 0) : (prefix == n.Prefix) );
138 Debug.Assert( n.LocalName == localName );
139 Debug.Assert( (namespaceURI == null) ? (n.NamespaceURI.Length == 0) : (n.NamespaceURI == namespaceURI) );
143 internal XmlName GetXmlName(string prefix, string localName, string namespaceURI, IXmlSchemaInfo schemaInfo) {
144 XmlName n = domNameTable.GetName(prefix, localName, namespaceURI, schemaInfo);
145 Debug.Assert(n == null || ((prefix == null) ? (n.Prefix.Length == 0) : (prefix == n.Prefix)));
146 Debug.Assert(n == null || n.LocalName == localName);
147 Debug.Assert(n == null || ((namespaceURI == null) ? (n.NamespaceURI.Length == 0) : (n.NamespaceURI == namespaceURI)));
151 internal XmlName AddAttrXmlName(string prefix, string localName, string namespaceURI, IXmlSchemaInfo schemaInfo) {
152 XmlName xmlName = AddXmlName(prefix, localName, namespaceURI, schemaInfo);
153 Debug.Assert( (prefix == null) ? (xmlName.Prefix.Length == 0) : (prefix == xmlName.Prefix) );
154 Debug.Assert( xmlName.LocalName == localName );
155 Debug.Assert( (namespaceURI == null) ? (xmlName.NamespaceURI.Length == 0) : (xmlName.NamespaceURI == namespaceURI) );
157 if ( !this.IsLoading ) {
158 // Use atomized versions instead of prefix, localName and nsURI
159 object oPrefix = xmlName.Prefix;
160 object oNamespaceURI = xmlName.NamespaceURI;
161 object oLocalName = xmlName.LocalName;
162 if ( ( oPrefix == (object)strXmlns || ( oPrefix == (object)strEmpty && oLocalName == (object)strXmlns ) ) ^ ( oNamespaceURI == (object)strReservedXmlns ) )
163 throw new ArgumentException( Res.GetString( Res.Xdom_Attr_Reserved_XmlNS, namespaceURI ) );
168 internal bool AddIdInfo( XmlName eleName, XmlName attrName ) {
169 //when XmlLoader call XmlDocument.AddInfo, the element.XmlName and attr.XmlName
170 //have already been replaced with the ones that don't have namespace values (or just
171 //string.Empty) because in DTD, the namespace is not supported
172 if ( htElementIDAttrDecl == null || htElementIDAttrDecl[eleName] == null ) {
173 if ( htElementIDAttrDecl == null )
174 htElementIDAttrDecl = new Hashtable();
175 htElementIDAttrDecl.Add(eleName, attrName);
181 private XmlName GetIDInfoByElement_(XmlName eleName)
183 //When XmlDocument is getting the IDAttribute for a given element,
184 //we need only compare the prefix and localname of element.XmlName with
185 //the registered htElementIDAttrDecl.
186 XmlName newName = GetXmlName(eleName.Prefix, eleName.LocalName, string.Empty, null);
187 if (newName != null) {
188 return (XmlName)(htElementIDAttrDecl[newName]);
193 internal XmlName GetIDInfoByElement( XmlName eleName ) {
194 if (htElementIDAttrDecl == null)
197 return GetIDInfoByElement_(eleName);
200 private WeakReference GetElement(ArrayList elementList, XmlElement elem) {
201 ArrayList gcElemRefs = new ArrayList();
202 foreach( WeakReference elemRef in elementList) {
203 if ( !elemRef.IsAlive)
204 //take notes on the garbage collected nodes
205 gcElemRefs.Add(elemRef);
207 if ((XmlElement)(elemRef.Target) == elem)
211 //Clear out the gced elements
212 foreach( WeakReference elemRef in gcElemRefs)
213 elementList.Remove(elemRef);
217 internal void AddElementWithId( string id, XmlElement elem ) {
218 if (htElementIdMap == null || !htElementIdMap.Contains(id)) {
219 if ( htElementIdMap == null )
220 htElementIdMap = new Hashtable();
221 ArrayList elementList = new ArrayList();
222 elementList.Add(new WeakReference(elem));
223 htElementIdMap.Add(id, elementList);
226 // there are other element(s) that has the same id
227 ArrayList elementList = (ArrayList)(htElementIdMap[id]);
228 if (GetElement(elementList, elem) == null)
229 elementList.Add(new WeakReference(elem));
233 internal void RemoveElementWithId( string id, XmlElement elem ) {
234 if (htElementIdMap != null && htElementIdMap.Contains(id)) {
235 ArrayList elementList = (ArrayList)(htElementIdMap[id]);
236 WeakReference elemRef = GetElement(elementList, elem);
237 if (elemRef != null) {
238 elementList.Remove(elemRef);
239 if (elementList.Count == 0)
240 htElementIdMap.Remove(id);
246 // Creates a duplicate of this node.
247 public override XmlNode CloneNode( bool deep ) {
248 XmlDocument clone = Implementation.CreateDocument();
249 clone.SetBaseURI(this.baseURI);
251 clone.ImportChildren( this, clone, deep );
256 // Gets the type of the current node.
257 public override XmlNodeType NodeType {
258 get { return XmlNodeType.Document; }
261 public override XmlNode ParentNode {
265 // Gets the node for the DOCTYPE declaration.
266 public virtual XmlDocumentType DocumentType {
267 get { return(XmlDocumentType) FindChild( XmlNodeType.DocumentType ); }
270 internal virtual XmlDeclaration Declaration {
272 if ( HasChildNodes ) {
273 XmlDeclaration dec = FirstChild as XmlDeclaration;
280 // Gets the XmlImplementation object for this document.
281 public XmlImplementation Implementation {
282 get { return this.implementation; }
285 // Gets the name of the node.
286 public override String Name {
287 get { return strDocumentName; }
290 // Gets the name of the current node without the namespace prefix.
291 public override String LocalName {
292 get { return strDocumentName; }
295 // Gets the root XmlElement for the document.
296 public XmlElement DocumentElement {
297 get { return(XmlElement)FindChild(XmlNodeType.Element); }
300 internal override bool IsContainer {
304 internal override XmlLinkedNode LastNode
306 get { return lastChild; }
307 set { lastChild = value; }
310 // Gets the XmlDocument that contains this node.
311 public override XmlDocument OwnerDocument
316 public XmlSchemaSet Schemas {
318 if (schemas == null) {
319 schemas = new XmlSchemaSet(NameTable);
329 internal bool CanReportValidity {
330 get { return reportValidity; }
333 internal bool HasSetResolver {
334 get { return bSetResolver; }
337 internal XmlResolver GetResolver() {
341 public virtual XmlResolver XmlResolver {
343 if ( value != null ) {
345 new NamedPermissionSet( "FullTrust" ).Demand();
347 catch ( SecurityException e ) {
348 throw new SecurityException( Res.GetString( Res.Xml_UntrustedCodeSettingResolver ), e );
356 XmlDocumentType dtd = this.DocumentType;
358 dtd.DtdSchemaInfo = null;
362 internal override bool IsValidChildType( XmlNodeType type ) {
364 case XmlNodeType.ProcessingInstruction:
365 case XmlNodeType.Comment:
366 case XmlNodeType.Whitespace:
367 case XmlNodeType.SignificantWhitespace:
370 case XmlNodeType.DocumentType:
371 if ( DocumentType != null )
372 throw new InvalidOperationException( Res.GetString(Res.Xdom_DualDocumentTypeNode) );
375 case XmlNodeType.Element:
376 if ( DocumentElement != null )
377 throw new InvalidOperationException( Res.GetString(Res.Xdom_DualDocumentElementNode) );
380 case XmlNodeType.XmlDeclaration:
381 if ( Declaration != null )
382 throw new InvalidOperationException( Res.GetString(Res.Xdom_DualDeclarationNode) );
389 // the function examines all the siblings before the refNode
390 // if any of the nodes has type equals to "nt", return true; otherwise, return false;
391 private bool HasNodeTypeInPrevSiblings( XmlNodeType nt, XmlNode refNode ) {
392 if ( refNode == null )
396 if ( refNode.ParentNode != null )
397 node = refNode.ParentNode.FirstChild;
398 while ( node != null ) {
399 if ( node.NodeType == nt )
401 if ( node == refNode )
403 node = node.NextSibling;
408 // the function examines all the siblings after the refNode
409 // if any of the nodes has the type equals to "nt", return true; otherwise, return false;
410 private bool HasNodeTypeInNextSiblings( XmlNodeType nt, XmlNode refNode ) {
411 XmlNode node = refNode;
412 while ( node != null ) {
413 if ( node.NodeType == nt )
415 node = node.NextSibling;
420 internal override bool CanInsertBefore( XmlNode newChild, XmlNode refChild ) {
421 if ( refChild == null )
422 refChild = FirstChild;
424 if ( refChild == null )
427 switch ( newChild.NodeType ) {
428 case XmlNodeType.XmlDeclaration:
429 return ( refChild == FirstChild );
431 case XmlNodeType.ProcessingInstruction:
432 case XmlNodeType.Comment:
433 return refChild.NodeType != XmlNodeType.XmlDeclaration;
435 case XmlNodeType.DocumentType: {
436 if ( refChild.NodeType != XmlNodeType.XmlDeclaration ) {
437 //if refChild is not the XmlDeclaration node, only need to go through the sibling before and including refChild to
438 // make sure no Element ( rootElem node ) before the current position
439 return !HasNodeTypeInPrevSiblings( XmlNodeType.Element, refChild.PreviousSibling );
444 case XmlNodeType.Element: {
445 if ( refChild.NodeType != XmlNodeType.XmlDeclaration ) {
446 //if refChild is not the XmlDeclaration node, only need to go through the siblings after and including the refChild to
447 // make sure no DocType node and XmlDeclaration node after the current posistion.
448 return !HasNodeTypeInNextSiblings( XmlNodeType.DocumentType, refChild );
457 internal override bool CanInsertAfter( XmlNode newChild, XmlNode refChild ) {
458 if ( refChild == null )
459 refChild = LastChild;
461 if ( refChild == null )
464 switch ( newChild.NodeType ) {
465 case XmlNodeType.ProcessingInstruction:
466 case XmlNodeType.Comment:
467 case XmlNodeType.Whitespace:
468 case XmlNodeType.SignificantWhitespace:
471 case XmlNodeType.DocumentType: {
472 //we will have to go through all the siblings before the refChild just to make sure no Element node ( rootElem )
473 // before the current position
474 return !HasNodeTypeInPrevSiblings( XmlNodeType.Element, refChild );
477 case XmlNodeType.Element: {
478 return !HasNodeTypeInNextSiblings( XmlNodeType.DocumentType, refChild.NextSibling );
486 // Creates an XmlAttribute with the specified name.
487 public XmlAttribute CreateAttribute( String name ) {
488 String prefix = String.Empty;
489 String localName = String.Empty;
490 String namespaceURI = String.Empty;
492 SplitName( name, out prefix, out localName );
494 SetDefaultNamespace( prefix, localName, ref namespaceURI );
496 return CreateAttribute( prefix, localName, namespaceURI );
499 internal void SetDefaultNamespace( String prefix, String localName, ref String namespaceURI ) {
500 if ( prefix == strXmlns || ( prefix.Length == 0 && localName == strXmlns ) ) {
501 namespaceURI = strReservedXmlns;
502 } else if ( prefix == strXml ) {
503 namespaceURI = strReservedXml;
507 // Creates a XmlCDataSection containing the specified data.
508 public virtual XmlCDataSection CreateCDataSection( String data ) {
509 fCDataNodesPresent = true;
510 return new XmlCDataSection( data, this );
513 // Creates an XmlComment containing the specified data.
514 public virtual XmlComment CreateComment( String data ) {
515 return new XmlComment( data, this );
518 // Returns a new XmlDocumentType object.
519 [PermissionSetAttribute( SecurityAction.InheritanceDemand, Name = "FullTrust" )]
520 public virtual XmlDocumentType CreateDocumentType( string name, string publicId, string systemId, string internalSubset ) {
521 return new XmlDocumentType( name, publicId, systemId, internalSubset, this );
524 // Creates an XmlDocumentFragment.
525 public virtual XmlDocumentFragment CreateDocumentFragment() {
526 return new XmlDocumentFragment( this );
529 // Creates an element with the specified name.
530 public XmlElement CreateElement( String name ) {
531 string prefix = String.Empty;
532 string localName = String.Empty;
533 SplitName( name, out prefix, out localName );
534 return CreateElement( prefix, localName, string.Empty );
538 internal void AddDefaultAttributes( XmlElement elem ) {
539 SchemaInfo schInfo = DtdSchemaInfo;
540 SchemaElementDecl ed = GetSchemaElementDecl( elem );
541 if ( ed != null && ed.AttDefs != null ) {
542 IDictionaryEnumerator attrDefs = ed.AttDefs.GetEnumerator();
543 while ( attrDefs.MoveNext() ) {
544 SchemaAttDef attdef = (SchemaAttDef)attrDefs.Value;
545 if ( attdef.Presence == SchemaDeclBase.Use.Default ||
546 attdef.Presence == SchemaDeclBase.Use.Fixed ) {
547 //build a default attribute and return
548 string attrPrefix = string.Empty;
549 string attrLocalname = attdef.Name.Name;
550 string attrNamespaceURI = string.Empty;
551 if ( schInfo.SchemaType == SchemaType.DTD )
552 attrPrefix = attdef.Name.Namespace;
554 attrPrefix = attdef.Prefix;
555 attrNamespaceURI = attdef.Name.Namespace;
557 XmlAttribute defattr = PrepareDefaultAttribute( attdef, attrPrefix, attrLocalname, attrNamespaceURI );
558 elem.SetAttributeNode( defattr );
564 private SchemaElementDecl GetSchemaElementDecl( XmlElement elem ) {
565 SchemaInfo schInfo = DtdSchemaInfo;
566 if ( schInfo != null ) {
567 //build XmlQualifiedName used to identify the element schema declaration
568 XmlQualifiedName qname = new XmlQualifiedName( elem.LocalName, schInfo.SchemaType == SchemaType.DTD ? elem.Prefix : elem.NamespaceURI );
569 //get the schema info for the element
570 SchemaElementDecl elemDecl;
571 if ( schInfo.ElementDecls.TryGetValue(qname, out elemDecl) ) {
578 //Will be used by AddDeafulatAttributes() and GetDefaultAttribute() methods
579 private XmlAttribute PrepareDefaultAttribute( SchemaAttDef attdef, string attrPrefix, string attrLocalname, string attrNamespaceURI ) {
580 SetDefaultNamespace( attrPrefix, attrLocalname, ref attrNamespaceURI );
581 XmlAttribute defattr = CreateDefaultAttribute( attrPrefix, attrLocalname, attrNamespaceURI );
582 //parsing the default value for the default attribute
583 defattr.InnerXml = attdef.DefaultValueRaw;
584 //during the expansion of the tree, the flag could be set to true, we need to set it back.
585 XmlUnspecifiedAttribute unspAttr = defattr as XmlUnspecifiedAttribute;
586 if ( unspAttr != null ) {
587 unspAttr.SetSpecified( false );
592 // Creates an XmlEntityReference with the specified name.
593 public virtual XmlEntityReference CreateEntityReference( String name ) {
594 return new XmlEntityReference( name, this );
597 // Creates a XmlProcessingInstruction with the specified name
599 public virtual XmlProcessingInstruction CreateProcessingInstruction( String target, String data ) {
600 return new XmlProcessingInstruction( target, data, this );
603 // Creates a XmlDeclaration node with the specified values.
604 public virtual XmlDeclaration CreateXmlDeclaration( String version, string encoding, string standalone ) {
605 return new XmlDeclaration( version, encoding, standalone, this );
608 // Creates an XmlText with the specified text.
609 public virtual XmlText CreateTextNode( String text ) {
610 return new XmlText( text, this );
613 // Creates a XmlSignificantWhitespace node.
614 public virtual XmlSignificantWhitespace CreateSignificantWhitespace( string text ) {
615 return new XmlSignificantWhitespace( text, this );
618 public override XPathNavigator CreateNavigator() {
619 return CreateNavigator(this);
622 internal protected virtual XPathNavigator CreateNavigator(XmlNode node) {
623 XmlNodeType nodeType = node.NodeType;
625 XmlNodeType parentType;
628 case XmlNodeType.EntityReference:
629 case XmlNodeType.Entity:
630 case XmlNodeType.DocumentType:
631 case XmlNodeType.Notation:
632 case XmlNodeType.XmlDeclaration:
634 case XmlNodeType.Text:
635 case XmlNodeType.CDATA:
636 case XmlNodeType.SignificantWhitespace:
637 parent = node.ParentNode;
638 if (parent != null) {
640 parentType = parent.NodeType;
641 if (parentType == XmlNodeType.Attribute) {
644 else if (parentType == XmlNodeType.EntityReference) {
645 parent = parent.ParentNode;
651 while (parent != null);
653 node = NormalizeText(node);
655 case XmlNodeType.Whitespace:
656 parent = node.ParentNode;
657 if (parent != null) {
659 parentType = parent.NodeType;
660 if (parentType == XmlNodeType.Document
661 || parentType == XmlNodeType.Attribute) {
664 else if (parentType == XmlNodeType.EntityReference) {
665 parent = parent.ParentNode;
671 while (parent != null);
673 node = NormalizeText(node);
678 return new DocumentXPathNavigator(this, node);
681 internal static bool IsTextNode( XmlNodeType nt ) {
683 case XmlNodeType.Text:
684 case XmlNodeType.CDATA:
685 case XmlNodeType.Whitespace:
686 case XmlNodeType.SignificantWhitespace:
693 private XmlNode NormalizeText( XmlNode n ) {
694 XmlNode retnode = null;
695 while( IsTextNode( n.NodeType ) ) {
697 n = n.PreviousSibling;
700 XmlNode intnode = retnode;
702 if ( intnode.ParentNode != null && intnode.ParentNode.NodeType == XmlNodeType.EntityReference ) {
703 if (intnode.ParentNode.PreviousSibling != null ) {
704 n = intnode.ParentNode.PreviousSibling;
708 intnode = intnode.ParentNode;
709 if( intnode == null )
720 while( n.NodeType == XmlNodeType.EntityReference ) {
727 // Creates a XmlWhitespace node.
728 public virtual XmlWhitespace CreateWhitespace( string text ) {
729 return new XmlWhitespace( text, this );
732 // Returns an XmlNodeList containing
733 // a list of all descendant elements that match the specified name.
734 public virtual XmlNodeList GetElementsByTagName( String name ) {
735 return new XmlElementList( this, name );
740 // Creates an XmlAttribute with the specified LocalName
742 public XmlAttribute CreateAttribute( String qualifiedName, String namespaceURI ) {
743 string prefix = String.Empty;
744 string localName = String.Empty;
746 SplitName( qualifiedName, out prefix, out localName );
747 return CreateAttribute( prefix, localName, namespaceURI );
750 // Creates an XmlElement with the specified LocalName and
752 public XmlElement CreateElement( String qualifiedName, String namespaceURI ) {
753 string prefix = String.Empty;
754 string localName = String.Empty;
755 SplitName( qualifiedName, out prefix, out localName );
756 return CreateElement( prefix, localName, namespaceURI );
759 // Returns a XmlNodeList containing
760 // a list of all descendant elements that match the specified name.
761 public virtual XmlNodeList GetElementsByTagName( String localName, String namespaceURI ) {
762 return new XmlElementList( this, localName, namespaceURI );
765 // Returns the XmlElement with the specified ID.
766 public virtual XmlElement GetElementById( string elementId ) {
767 if (htElementIdMap != null) {
768 ArrayList elementList = (ArrayList)(htElementIdMap[elementId]);
769 if (elementList != null) {
770 foreach (WeakReference elemRef in elementList) {
771 XmlElement elem = (XmlElement)elemRef.Target;
773 && elem.IsConnected())
781 // Imports a node from another document to this document.
782 public virtual XmlNode ImportNode( XmlNode node, bool deep ) {
783 return ImportNodeInternal( node, deep );
786 private XmlNode ImportNodeInternal( XmlNode node, bool deep ) {
787 XmlNode newNode = null;
789 if ( node == null ) {
790 throw new InvalidOperationException( Res.GetString(Res.Xdom_Import_NullNode) );
793 switch ( node.NodeType ) {
794 case XmlNodeType.Element:
795 newNode = CreateElement( node.Prefix, node.LocalName, node.NamespaceURI );
796 ImportAttributes( node, newNode );
798 ImportChildren( node, newNode, deep );
801 case XmlNodeType.Attribute:
802 Debug.Assert( ((XmlAttribute)node).Specified );
803 newNode = CreateAttribute( node.Prefix, node.LocalName, node.NamespaceURI );
804 ImportChildren( node, newNode, true );
807 case XmlNodeType.Text:
808 newNode = CreateTextNode( node.Value );
810 case XmlNodeType.Comment:
811 newNode = CreateComment( node.Value);
813 case XmlNodeType.ProcessingInstruction:
814 newNode = CreateProcessingInstruction( node.Name, node.Value );
816 case XmlNodeType.XmlDeclaration:
817 XmlDeclaration decl = (XmlDeclaration) node;
818 newNode = CreateXmlDeclaration( decl.Version, decl.Encoding, decl.Standalone );
820 case XmlNodeType.CDATA:
821 newNode = CreateCDataSection( node.Value );
823 case XmlNodeType.DocumentType:
824 XmlDocumentType docType = (XmlDocumentType)node;
825 newNode = CreateDocumentType( docType.Name, docType.PublicId, docType.SystemId, docType.InternalSubset );
827 case XmlNodeType.DocumentFragment:
828 newNode = CreateDocumentFragment();
830 ImportChildren( node, newNode, deep );
833 case XmlNodeType.EntityReference:
834 newNode = CreateEntityReference( node.Name );
835 // we don't import the children of entity reference because they might result in different
836 // children nodes given different namesapce context in the new document.
839 case XmlNodeType.Whitespace:
840 newNode = CreateWhitespace( node.Value );
843 case XmlNodeType.SignificantWhitespace:
844 newNode = CreateSignificantWhitespace( node.Value );
848 throw new InvalidOperationException( String.Format( CultureInfo.InvariantCulture, Res.GetString(Res.Xdom_Import), node.NodeType.ToString() ) );
855 private void ImportAttributes( XmlNode fromElem, XmlNode toElem ) {
856 int cAttr = fromElem.Attributes.Count;
857 for ( int iAttr = 0; iAttr < cAttr; iAttr++ ) {
858 if ( fromElem.Attributes[iAttr].Specified )
859 toElem.Attributes.SetNamedItem( ImportNodeInternal( fromElem.Attributes[iAttr], true ) );
863 private void ImportChildren( XmlNode fromNode, XmlNode toNode, bool deep ) {
864 Debug.Assert( toNode.NodeType != XmlNodeType.EntityReference );
865 for ( XmlNode n = fromNode.FirstChild; n != null; n = n.NextSibling ) {
866 toNode.AppendChild( ImportNodeInternal( n, deep ) );
870 // Microsoft extensions
872 // Gets the XmlNameTable associated with this
874 public XmlNameTable NameTable
876 get { return implementation.NameTable; }
879 // Creates a XmlAttribute with the specified Prefix, LocalName,
881 public virtual XmlAttribute CreateAttribute( string prefix, string localName, string namespaceURI ) {
882 return new XmlAttribute( AddAttrXmlName( prefix, localName, namespaceURI, null ), this );
885 protected internal virtual XmlAttribute CreateDefaultAttribute( string prefix, string localName, string namespaceURI ) {
886 return new XmlUnspecifiedAttribute( prefix, localName, namespaceURI, this );
889 public virtual XmlElement CreateElement( string prefix, string localName, string namespaceURI) {
890 XmlElement elem = new XmlElement( AddXmlName( prefix, localName, namespaceURI, null ), true, this );
892 AddDefaultAttributes( elem );
896 // Gets or sets a value indicating whether to preserve whitespace.
897 public bool PreserveWhitespace {
898 get { return preserveWhitespace;}
899 set { preserveWhitespace = value;}
902 // Gets a value indicating whether the node is read-only.
903 public override bool IsReadOnly {
907 internal XmlNamedNodeMap Entities {
909 if ( entities == null )
910 entities = new XmlNamedNodeMap( this );
913 set { entities = value; }
916 internal bool IsLoading {
917 get { return isLoading;}
918 set { isLoading = value; }
921 internal bool ActualLoadingStatus{
922 get { return actualLoadingStatus;}
923 set { actualLoadingStatus = value; }
927 // Creates a XmlNode with the specified XmlNodeType, Prefix, Name, and NamespaceURI.
928 public virtual XmlNode CreateNode( XmlNodeType type, string prefix, string name, string namespaceURI ) {
930 case XmlNodeType.Element:
932 return CreateElement( prefix, name, namespaceURI );
934 return CreateElement( name, namespaceURI );
936 case XmlNodeType.Attribute:
938 return CreateAttribute( prefix, name, namespaceURI );
940 return CreateAttribute( name, namespaceURI );
942 case XmlNodeType.Text:
943 return CreateTextNode( string.Empty );
945 case XmlNodeType.CDATA:
946 return CreateCDataSection( string.Empty );
948 case XmlNodeType.EntityReference:
949 return CreateEntityReference( name );
951 case XmlNodeType.ProcessingInstruction:
952 return CreateProcessingInstruction( name, string.Empty );
954 case XmlNodeType.XmlDeclaration:
955 return CreateXmlDeclaration( "1.0", null, null );
957 case XmlNodeType.Comment:
958 return CreateComment( string.Empty );
960 case XmlNodeType.DocumentFragment:
961 return CreateDocumentFragment();
963 case XmlNodeType.DocumentType:
964 return CreateDocumentType( name, string.Empty, string.Empty, string.Empty );
966 case XmlNodeType.Document:
967 return new XmlDocument();
969 case XmlNodeType.SignificantWhitespace:
970 return CreateSignificantWhitespace( string.Empty );
972 case XmlNodeType.Whitespace:
973 return CreateWhitespace( string.Empty );
976 throw new ArgumentException( Res.GetString( Res.Arg_CannotCreateNode, type ) );
980 // Creates an XmlNode with the specified node type, Name, and
982 public virtual XmlNode CreateNode( string nodeTypeString, string name, string namespaceURI ) {
983 return CreateNode( ConvertToNodeType( nodeTypeString ), name, namespaceURI );
986 // Creates an XmlNode with the specified XmlNodeType, Name, and
988 public virtual XmlNode CreateNode( XmlNodeType type, string name, string namespaceURI ) {
989 return CreateNode( type, null, name, namespaceURI );
992 // Creates an XmlNode object based on the information in the XmlReader.
993 // The reader must be positioned on a node or attribute.
994 [PermissionSetAttribute( SecurityAction.InheritanceDemand, Name = "FullTrust" )]
995 public virtual XmlNode ReadNode( XmlReader reader ) {
999 XmlLoader loader = new XmlLoader();
1000 node = loader.ReadCurrentNode( this, reader );
1008 internal XmlNodeType ConvertToNodeType( string nodeTypeString ) {
1009 if ( nodeTypeString == "element" ) {
1010 return XmlNodeType.Element;
1012 else if ( nodeTypeString == "attribute" ) {
1013 return XmlNodeType.Attribute;
1015 else if ( nodeTypeString == "text" ) {
1016 return XmlNodeType.Text;
1018 else if ( nodeTypeString == "cdatasection" ) {
1019 return XmlNodeType.CDATA;
1021 else if ( nodeTypeString == "entityreference" ) {
1022 return XmlNodeType.EntityReference;
1024 else if ( nodeTypeString == "entity" ) {
1025 return XmlNodeType.Entity;
1027 else if ( nodeTypeString == "processinginstruction" ) {
1028 return XmlNodeType.ProcessingInstruction;
1030 else if ( nodeTypeString == "comment" ) {
1031 return XmlNodeType.Comment;
1033 else if ( nodeTypeString == "document" ) {
1034 return XmlNodeType.Document;
1036 else if ( nodeTypeString == "documenttype" ) {
1037 return XmlNodeType.DocumentType;
1039 else if ( nodeTypeString == "documentfragment" ) {
1040 return XmlNodeType.DocumentFragment;
1042 else if ( nodeTypeString == "notation" ) {
1043 return XmlNodeType.Notation;
1045 else if ( nodeTypeString == "significantwhitespace" ) {
1046 return XmlNodeType.SignificantWhitespace;
1048 else if ( nodeTypeString == "whitespace" ) {
1049 return XmlNodeType.Whitespace;
1051 throw new ArgumentException( Res.GetString( Res.Xdom_Invalid_NT_String, nodeTypeString ) );
1055 private XmlTextReader SetupReader( XmlTextReader tr ) {
1056 tr.XmlValidatingReaderCompatibilityMode = true;
1057 tr.EntityHandling = EntityHandling.ExpandCharEntities;
1058 if ( this.HasSetResolver )
1059 tr.XmlResolver = GetResolver();
1063 // Loads the XML document from the specified URL.
1064 [ResourceConsumption(ResourceScope.Machine)]
1065 [ResourceExposure(ResourceScope.Machine)]
1066 public virtual void Load( string filename ) {
1067 XmlTextReader reader = SetupReader( new XmlTextReader( filename, NameTable ) );
1076 public virtual void Load( Stream inStream ) {
1077 XmlTextReader reader = SetupReader( new XmlTextReader( inStream, NameTable ) );
1082 reader.Impl.Close( false );
1086 // Loads the XML document from the specified TextReader.
1087 public virtual void Load( TextReader txtReader ) {
1088 XmlTextReader reader = SetupReader( new XmlTextReader( txtReader, NameTable ) );
1093 reader.Impl.Close( false );
1097 // Loads the XML document from the specified XmlReader.
1098 public virtual void Load( XmlReader reader ) {
1101 actualLoadingStatus = true;
1103 fEntRefNodesPresent = false;
1104 fCDataNodesPresent = false;
1105 reportValidity = true;
1107 XmlLoader loader = new XmlLoader();
1108 loader.Load( this, reader, preserveWhitespace );
1112 actualLoadingStatus = false;
1114 // Ensure the bit is still on after loading a dtd
1115 reportValidity = true;
1119 // Loads the XML document from the specified string.
1120 public virtual void LoadXml( string xml ) {
1121 XmlTextReader reader = SetupReader( new XmlTextReader( new StringReader( xml ), NameTable ));
1130 //TextEncoding is the one from XmlDeclaration if there is any
1131 internal Encoding TextEncoding {
1133 if ( Declaration != null )
1135 string value = Declaration.Encoding;
1136 if ( value.Length > 0 ) {
1137 return System.Text.Encoding.GetEncoding( value );
1144 public override string InnerText {
1146 throw new InvalidOperationException(Res.GetString(Res.Xdom_Document_Innertext));
1150 public override string InnerXml {
1152 return base.InnerXml;
1159 // Saves the XML document to the specified file.
1160 //Saves out the to the file with exact content in the XmlDocument.
1161 [ResourceConsumption(ResourceScope.Machine)]
1162 [ResourceExposure(ResourceScope.Machine)]
1163 public virtual void Save( string filename ) {
1164 if ( DocumentElement == null )
1165 throw new XmlException( Res.Xml_InvalidXmlDocument, Res.GetString( Res.Xdom_NoRootEle ) );
1166 XmlDOMTextWriter xw = new XmlDOMTextWriter( filename, TextEncoding );
1168 if ( preserveWhitespace == false )
1169 xw.Formatting = Formatting.Indented;
1178 //Saves out the to the file with exact content in the XmlDocument.
1179 public virtual void Save( Stream outStream ) {
1180 XmlDOMTextWriter xw = new XmlDOMTextWriter( outStream, TextEncoding );
1181 if ( preserveWhitespace == false )
1182 xw.Formatting = Formatting.Indented;
1187 // Saves the XML document to the specified TextWriter.
1189 //Saves out the file with xmldeclaration which has encoding value equal to
1190 //that of textwriter's encoding
1191 public virtual void Save( TextWriter writer ) {
1192 XmlDOMTextWriter xw = new XmlDOMTextWriter( writer );
1193 if ( preserveWhitespace == false )
1194 xw.Formatting = Formatting.Indented;
1198 // Saves the XML document to the specified XmlWriter.
1200 //Saves out the file with xmldeclaration which has encoding value equal to
1201 //that of textwriter's encoding
1202 public virtual void Save( XmlWriter w ) {
1203 XmlNode n = this.FirstChild;
1206 if( w.WriteState == WriteState.Start ) {
1207 if( n is XmlDeclaration ) {
1208 if( Standalone.Length == 0 )
1209 w.WriteStartDocument();
1210 else if( Standalone == "yes" )
1211 w.WriteStartDocument( true );
1212 else if( Standalone == "no" )
1213 w.WriteStartDocument( false );
1217 w.WriteStartDocument();
1220 while( n != null ) {
1221 //Debug.Assert( n.NodeType != XmlNodeType.XmlDeclaration );
1228 // Saves the node to the specified XmlWriter.
1230 //Writes out the to the file with exact content in the XmlDocument.
1231 public override void WriteTo( XmlWriter w ) {
1232 WriteContentTo( w );
1235 // Saves all the children of the node to the specified XmlWriter.
1237 //Writes out the to the file with exact content in the XmlDocument.
1238 public override void WriteContentTo( XmlWriter xw ) {
1239 foreach( XmlNode n in this ) {
1244 public void Validate(ValidationEventHandler validationEventHandler) {
1245 Validate(validationEventHandler, this);
1248 public void Validate(ValidationEventHandler validationEventHandler, XmlNode nodeToValidate) {
1249 if (this.schemas == null || this.schemas.Count == 0) { //Should we error
1250 throw new InvalidOperationException(Res.GetString(Res.XmlDocument_NoSchemaInfo));
1252 XmlDocument parentDocument = nodeToValidate.Document;
1253 if (parentDocument != this) {
1254 throw new ArgumentException(Res.GetString(Res.XmlDocument_NodeNotFromDocument, "nodeToValidate"));
1256 if (nodeToValidate == this) {
1257 reportValidity = false;
1259 DocumentSchemaValidator validator = new DocumentSchemaValidator(this, schemas, validationEventHandler);
1260 validator.Validate(nodeToValidate);
1261 if (nodeToValidate == this) {
1262 reportValidity = true;
1266 public event XmlNodeChangedEventHandler NodeInserting {
1268 onNodeInsertingDelegate += value;
1271 onNodeInsertingDelegate -= value;
1275 public event XmlNodeChangedEventHandler NodeInserted {
1277 onNodeInsertedDelegate += value;
1280 onNodeInsertedDelegate -= value;
1284 public event XmlNodeChangedEventHandler NodeRemoving {
1286 onNodeRemovingDelegate += value;
1289 onNodeRemovingDelegate -= value;
1293 public event XmlNodeChangedEventHandler NodeRemoved {
1295 onNodeRemovedDelegate += value;
1298 onNodeRemovedDelegate -= value;
1302 public event XmlNodeChangedEventHandler NodeChanging {
1304 onNodeChangingDelegate += value;
1307 onNodeChangingDelegate -= value;
1311 public event XmlNodeChangedEventHandler NodeChanged {
1313 onNodeChangedDelegate += value;
1316 onNodeChangedDelegate -= value;
1320 internal override XmlNodeChangedEventArgs GetEventArgs(XmlNode node, XmlNode oldParent, XmlNode newParent, string oldValue, string newValue, XmlNodeChangedAction action) {
1321 reportValidity = false;
1324 case XmlNodeChangedAction.Insert:
1325 if (onNodeInsertingDelegate == null && onNodeInsertedDelegate == null) {
1329 case XmlNodeChangedAction.Remove:
1330 if (onNodeRemovingDelegate == null && onNodeRemovedDelegate == null) {
1334 case XmlNodeChangedAction.Change:
1335 if (onNodeChangingDelegate == null && onNodeChangedDelegate == null) {
1340 return new XmlNodeChangedEventArgs( node, oldParent, newParent, oldValue, newValue, action );
1343 internal XmlNodeChangedEventArgs GetInsertEventArgsForLoad( XmlNode node, XmlNode newParent ) {
1344 if (onNodeInsertingDelegate == null && onNodeInsertedDelegate == null) {
1347 string nodeValue = node.Value;
1348 return new XmlNodeChangedEventArgs(node, null, newParent, nodeValue, nodeValue, XmlNodeChangedAction.Insert);
1351 internal override void BeforeEvent( XmlNodeChangedEventArgs args ) {
1352 if ( args != null ) {
1353 switch ( args.Action ) {
1354 case XmlNodeChangedAction.Insert:
1355 if ( onNodeInsertingDelegate != null )
1356 onNodeInsertingDelegate( this, args );
1359 case XmlNodeChangedAction.Remove:
1360 if ( onNodeRemovingDelegate != null )
1361 onNodeRemovingDelegate( this, args );
1364 case XmlNodeChangedAction.Change:
1365 if ( onNodeChangingDelegate != null )
1366 onNodeChangingDelegate( this, args );
1372 internal override void AfterEvent( XmlNodeChangedEventArgs args ) {
1373 if ( args != null ) {
1374 switch ( args.Action ) {
1375 case XmlNodeChangedAction.Insert:
1376 if ( onNodeInsertedDelegate != null )
1377 onNodeInsertedDelegate( this, args );
1380 case XmlNodeChangedAction.Remove:
1381 if ( onNodeRemovedDelegate != null )
1382 onNodeRemovedDelegate( this, args );
1385 case XmlNodeChangedAction.Change:
1386 if ( onNodeChangedDelegate != null )
1387 onNodeChangedDelegate( this, args );
1393 // The function such through schema info to find out if there exists a default attribute with passed in names in the passed in element
1394 // If so, return the newly created default attribute (with children tree);
1395 // Otherwise, return null.
1397 internal XmlAttribute GetDefaultAttribute( XmlElement elem, string attrPrefix, string attrLocalname, string attrNamespaceURI ) {
1398 SchemaInfo schInfo = DtdSchemaInfo;
1399 SchemaElementDecl ed = GetSchemaElementDecl( elem );
1400 if ( ed != null && ed.AttDefs != null ) {
1401 IDictionaryEnumerator attrDefs = ed.AttDefs.GetEnumerator();
1402 while ( attrDefs.MoveNext() ) {
1403 SchemaAttDef attdef = (SchemaAttDef)attrDefs.Value;
1404 if ( attdef.Presence == SchemaDeclBase.Use.Default ||
1405 attdef.Presence == SchemaDeclBase.Use.Fixed ) {
1406 if ( attdef.Name.Name == attrLocalname ) {
1407 if ( ( schInfo.SchemaType == SchemaType.DTD && attdef.Name.Namespace == attrPrefix ) ||
1408 ( schInfo.SchemaType != SchemaType.DTD && attdef.Name.Namespace == attrNamespaceURI ) ) {
1409 //find a def attribute with the same name, build a default attribute and return
1410 XmlAttribute defattr = PrepareDefaultAttribute( attdef, attrPrefix, attrLocalname, attrNamespaceURI );
1420 internal String Version {
1422 XmlDeclaration decl = Declaration;
1424 return decl.Version;
1429 internal String Encoding {
1431 XmlDeclaration decl = Declaration;
1433 return decl.Encoding;
1438 internal String Standalone {
1440 XmlDeclaration decl = Declaration;
1442 return decl.Standalone;
1447 internal XmlEntity GetEntityNode( String name ) {
1448 if ( DocumentType != null ) {
1449 XmlNamedNodeMap entites = DocumentType.Entities;
1450 if ( entites != null )
1451 return (XmlEntity)(entites.GetNamedItem( name ));
1456 public override IXmlSchemaInfo SchemaInfo {
1458 if (reportValidity) {
1459 XmlElement documentElement = DocumentElement;
1460 if (documentElement != null) {
1461 switch (documentElement.SchemaInfo.Validity) {
1462 case XmlSchemaValidity.Valid:
1463 return ValidSchemaInfo;
1464 case XmlSchemaValidity.Invalid:
1465 return InvalidSchemaInfo;
1469 return NotKnownSchemaInfo;
1473 public override String BaseURI {
1474 get { return baseURI; }
1477 internal void SetBaseURI( String inBaseURI ) {
1478 baseURI = inBaseURI;
1481 internal override XmlNode AppendChildForLoad( XmlNode newChild, XmlDocument doc ) {
1482 Debug.Assert( doc == this );
1484 if ( !IsValidChildType( newChild.NodeType ))
1485 throw new InvalidOperationException( Res.GetString(Res.Xdom_Node_Insert_TypeConflict) );
1487 if ( !CanInsertAfter( newChild, LastChild ) )
1488 throw new InvalidOperationException( Res.GetString(Res.Xdom_Node_Insert_Location) );
1490 XmlNodeChangedEventArgs args = GetInsertEventArgsForLoad( newChild, this );
1493 BeforeEvent( args );
1495 XmlLinkedNode newNode = (XmlLinkedNode) newChild;
1497 if ( lastChild == null ) {
1498 newNode.next = newNode;
1501 newNode.next = lastChild.next;
1502 lastChild.next = newNode;
1505 lastChild = newNode;
1506 newNode.SetParentForLoad( this );
1514 internal override XPathNodeType XPNodeType { get { return XPathNodeType.Root; } }
1516 internal bool HasEntityReferences {
1518 return fEntRefNodesPresent;
1522 internal XmlAttribute NamespaceXml {
1524 if (namespaceXml == null) {
1525 namespaceXml = new XmlAttribute(AddAttrXmlName(strXmlns, strXml, strReservedXmlns, null), this);
1526 namespaceXml.Value = strReservedXml;
1528 return namespaceXml;