1 //------------------------------------------------------------------------------
2 // <copyright file="XmlElement.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
9 using System.Xml.Schema;
10 using System.Xml.XPath;
11 using System.Collections;
12 using System.Diagnostics;
13 using System.Globalization;
15 namespace System.Xml {
16 // Represents an element.
17 public class XmlElement : XmlLinkedNode {
19 XmlAttributeCollection attributes;
20 XmlLinkedNode lastChild; // == this for empty elements otherwise it is the last child
22 internal XmlElement( XmlName name, bool empty, XmlDocument doc ): base( doc ) {
23 Debug.Assert(name!=null);
24 this.parentNode = null;
25 if ( !doc.IsLoading ) {
26 XmlDocument.CheckName( name.Prefix );
27 XmlDocument.CheckName( name.LocalName );
29 if (name.LocalName.Length == 0)
30 throw new ArgumentException(Res.GetString(Res.Xdom_Empty_LocalName));
33 this.lastChild = this;
37 protected internal XmlElement( string prefix, string localName, string namespaceURI, XmlDocument doc )
38 : this( doc.AddXmlName( prefix, localName, namespaceURI, null ), true, doc ) {
41 internal XmlName XmlName {
46 // Creates a duplicate of this node.
47 public override XmlNode CloneNode(bool deep) {
48 Debug.Assert( OwnerDocument != null );
49 XmlDocument doc = OwnerDocument;
50 bool OrigLoadingStatus = doc.IsLoading;
52 XmlElement element = doc.CreateElement( Prefix, LocalName, NamespaceURI );
53 doc.IsLoading = OrigLoadingStatus;
54 if ( element.IsEmpty != this.IsEmpty )
55 element.IsEmpty = this.IsEmpty;
58 foreach( XmlAttribute attr in Attributes ) {
59 XmlAttribute newAttr = (XmlAttribute)(attr.CloneNode(true));
60 if (attr is XmlUnspecifiedAttribute && attr.Specified == false)
61 ( ( XmlUnspecifiedAttribute )newAttr).SetSpecified(false);
62 element.Attributes.InternalAppendAttribute( newAttr );
66 element.CopyChildren( doc, this, deep );
71 // Gets the name of the node.
72 public override string Name {
73 get { return name.Name;}
76 // Gets the name of the current node without the namespace prefix.
77 public override string LocalName {
78 get { return name.LocalName;}
81 // Gets the namespace URI of this node.
82 public override string NamespaceURI {
83 get { return name.NamespaceURI;}
86 // Gets or sets the namespace prefix of this node.
87 public override string Prefix {
88 get { return name.Prefix;}
89 set { name = name.OwnerDocument.AddXmlName( value, LocalName, NamespaceURI, SchemaInfo ); }
92 // Gets the type of the current node.
93 public override XmlNodeType NodeType {
94 get { return XmlNodeType.Element;}
97 public override XmlNode ParentNode {
99 return this.parentNode;
103 // Gets the XmlDocument that contains this node.
104 public override XmlDocument OwnerDocument {
106 return name.OwnerDocument;
110 internal override bool IsContainer {
114 //the function is provided only at Load time to speed up Load process
115 internal override XmlNode AppendChildForLoad(XmlNode newChild, XmlDocument doc) {
116 XmlNodeChangedEventArgs args = doc.GetInsertEventArgsForLoad( newChild, this );
119 doc.BeforeEvent( args );
121 XmlLinkedNode newNode = (XmlLinkedNode)newChild;
123 if (lastChild == null
124 || lastChild == this) { // if LastNode == null
125 newNode.next = newNode;
126 lastChild = newNode; // LastNode = newNode;
127 newNode.SetParentForLoad(this);
130 XmlLinkedNode refNode = lastChild; // refNode = LastNode;
131 newNode.next = refNode.next;
132 refNode.next = newNode;
133 lastChild = newNode; // LastNode = newNode;
136 NestTextNodes(refNode, newNode);
139 newNode.SetParentForLoad(this);
144 doc.AfterEvent( args );
149 // Gets or sets whether the element does not have any children.
150 public bool IsEmpty {
152 return lastChild == this;
157 if (lastChild != this) {
163 if (lastChild == this) {
171 internal override XmlLinkedNode LastNode {
173 return lastChild == this ? null : lastChild;
181 internal override bool IsValidChildType( XmlNodeType type ) {
183 case XmlNodeType.Element:
184 case XmlNodeType.Text:
185 case XmlNodeType.EntityReference:
186 case XmlNodeType.Comment:
187 case XmlNodeType.Whitespace:
188 case XmlNodeType.SignificantWhitespace:
189 case XmlNodeType.ProcessingInstruction:
190 case XmlNodeType.CDATA:
199 // Gets a XmlAttributeCollection containing the list of attributes for this node.
200 public override XmlAttributeCollection Attributes {
202 if (attributes == null) {
203 lock ( OwnerDocument.objLock ) {
204 if ( attributes == null ) {
205 attributes = new XmlAttributeCollection(this);
214 // Gets a value indicating whether the current node
215 // has any attributes.
216 public virtual bool HasAttributes {
218 if ( this.attributes == null )
221 return this.attributes.Count > 0;
225 // Returns the value for the attribute with the specified name.
226 public virtual string GetAttribute(string name) {
227 XmlAttribute attr = GetAttributeNode(name);
233 // Sets the value of the attribute
234 // with the specified name.
235 public virtual void SetAttribute(string name, string value) {
236 XmlAttribute attr = GetAttributeNode(name);
238 attr = OwnerDocument.CreateAttribute(name);
240 Attributes.InternalAppendAttribute( attr );
247 // Removes an attribute by name.
248 public virtual void RemoveAttribute(string name) {
250 Attributes.RemoveNamedItem(name);
253 // Returns the XmlAttribute with the specified name.
254 public virtual XmlAttribute GetAttributeNode(string name) {
256 return Attributes[name];
260 // Adds the specified XmlAttribute.
261 public virtual XmlAttribute SetAttributeNode(XmlAttribute newAttr) {
262 if ( newAttr.OwnerElement != null )
263 throw new InvalidOperationException( Res.GetString(Res.Xdom_Attr_InUse) );
264 return(XmlAttribute) Attributes.SetNamedItem(newAttr);
267 // Removes the specified XmlAttribute.
268 public virtual XmlAttribute RemoveAttributeNode(XmlAttribute oldAttr) {
270 return(XmlAttribute) Attributes.Remove(oldAttr);
274 // Returns a XmlNodeList containing
275 // a list of all descendant elements that match the specified name.
276 public virtual XmlNodeList GetElementsByTagName(string name) {
277 return new XmlElementList( this, name );
284 // Returns the value for the attribute with the specified LocalName and NamespaceURI.
285 public virtual string GetAttribute(string localName, string namespaceURI) {
286 XmlAttribute attr = GetAttributeNode( localName, namespaceURI );
292 // Sets the value of the attribute with the specified name
294 public virtual string SetAttribute(string localName, string namespaceURI, string value) {
295 XmlAttribute attr = GetAttributeNode( localName, namespaceURI );
297 attr = OwnerDocument.CreateAttribute( string.Empty, localName, namespaceURI );
299 Attributes.InternalAppendAttribute( attr );
308 // Removes an attribute specified by LocalName and NamespaceURI.
309 public virtual void RemoveAttribute(string localName, string namespaceURI) {
310 //Debug.Assert(namespaceURI != null);
311 RemoveAttributeNode( localName, namespaceURI );
314 // Returns the XmlAttribute with the specified LocalName and NamespaceURI.
315 public virtual XmlAttribute GetAttributeNode(string localName, string namespaceURI) {
316 //Debug.Assert(namespaceURI != null);
318 return Attributes[ localName, namespaceURI ];
322 // Adds the specified XmlAttribute.
323 public virtual XmlAttribute SetAttributeNode(string localName, string namespaceURI) {
324 XmlAttribute attr = GetAttributeNode( localName, namespaceURI );
326 attr = OwnerDocument.CreateAttribute( string.Empty, localName, namespaceURI );
327 Attributes.InternalAppendAttribute( attr );
332 // Removes the XmlAttribute specified by LocalName and NamespaceURI.
333 public virtual XmlAttribute RemoveAttributeNode(string localName, string namespaceURI) {
334 //Debug.Assert(namespaceURI != null);
336 XmlAttribute attr = GetAttributeNode( localName, namespaceURI );
337 Attributes.Remove( attr );
343 // Returns a XmlNodeList containing
344 // a list of all descendant elements that match the specified name.
345 public virtual XmlNodeList GetElementsByTagName(string localName, string namespaceURI) {
346 //Debug.Assert(namespaceURI != null);
347 return new XmlElementList( this, localName, namespaceURI );
350 // Determines whether the current node has the specified attribute.
351 public virtual bool HasAttribute(string name) {
352 return GetAttributeNode(name) != null;
355 // Determines whether the current node has the specified
356 // attribute from the specified namespace.
357 public virtual bool HasAttribute(string localName, string namespaceURI) {
358 return GetAttributeNode(localName, namespaceURI) != null;
361 // Saves the current node to the specified XmlWriter.
362 public override void WriteTo(XmlWriter w) {
364 if (GetType() == typeof(XmlElement)) {
365 // Use the non-recursive version (for XmlElement only)
366 WriteElementTo(w, this);
369 // Use the (potentially) recursive version
370 WriteStartElement(w);
377 w.WriteFullEndElement();
382 // This method is copied from System.Xml.Linq.ElementWriter.WriteElement but adapted to DOM
383 private static void WriteElementTo(XmlWriter writer, XmlElement e) {
388 // Only use the inlined write logic for XmlElement, not for derived classes
389 if (e != null && e.GetType() == typeof(XmlElement)) {
391 e.WriteStartElement(writer);
392 // Write the element's content
394 // No content; use a short end element <a />
395 writer.WriteEndElement();
397 else if (e.lastChild == null) {
398 // No actual content; use a full end element <a></a>
399 writer.WriteFullEndElement();
402 // There are child node(s); move to first child
404 Debug.Assert(n != null);
409 // Use virtual dispatch (might recurse)
412 // Go back to the parent after writing the last child
413 while (n != root && n == n.ParentNode.LastChild) {
415 Debug.Assert(n != null);
416 writer.WriteFullEndElement();
421 Debug.Assert(n != null);
425 // Writes the start of the element (and its attributes) to the specified writer
426 private void WriteStartElement(XmlWriter w) {
427 w.WriteStartElement(Prefix, LocalName, NamespaceURI);
430 XmlAttributeCollection attrs = Attributes;
431 for (int i = 0; i < attrs.Count; i += 1) {
432 XmlAttribute attr = attrs[i];
438 // Saves all the children of the node to the specified XmlWriter.
439 public override void WriteContentTo(XmlWriter w) {
440 for (XmlNode node = FirstChild; node != null; node = node.NextSibling) {
445 // Removes the attribute node with the specified index from the attribute collection.
446 public virtual XmlNode RemoveAttributeAt(int i) {
448 return attributes.RemoveAt( i );
452 // Removes all attributes from the element.
453 public virtual void RemoveAllAttributes() {
455 attributes.RemoveAll();
459 // Removes all the children and/or attributes
460 // of the current node.
461 public override void RemoveAll() {
462 //remove all the children
464 //remove all the attributes
465 RemoveAllAttributes();
468 internal void RemoveAllChildren() {
472 public override IXmlSchemaInfo SchemaInfo {
478 // Gets or sets the markup representing just
479 // the children of this node.
480 public override string InnerXml {
482 return base.InnerXml;
486 XmlLoader loader = new XmlLoader();
487 loader.LoadInnerXmlElement( this, value );
491 // Gets or sets the concatenated values of the
492 // node and all its children.
493 public override string InnerText {
495 return base.InnerText;
498 XmlLinkedNode linkedNode = LastNode;
499 if (linkedNode != null && //there is one child
500 linkedNode.NodeType == XmlNodeType.Text && //which is text node
501 linkedNode.next == linkedNode ) // and it is the only child
503 //this branch is for perf reason, event fired when TextNode.Value is changed.
504 linkedNode.Value = value;
508 AppendChild( OwnerDocument.CreateTextNode( value ) );
513 public override XmlNode NextSibling {
515 if (this.parentNode != null
516 && this.parentNode.LastNode != this)
522 internal override void SetParent(XmlNode node) {
523 this.parentNode = node;
526 internal override XPathNodeType XPNodeType { get { return XPathNodeType.Element; } }
528 internal override string XPLocalName { get { return LocalName; } }
530 internal override string GetXPAttribute( string localName, string ns ) {
531 if ( ns == OwnerDocument.strReservedXmlns )
533 XmlAttribute attr = GetAttributeNode( localName, ns );