2016-08-03 10:59:49 +00:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
// <copyright file="XmlElement.cs" company="Microsoft">
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
// </copyright>
|
2017-08-21 15:34:15 +00:00
|
|
|
// <owner current="true" primary="true">Microsoft</owner>
|
2016-08-03 10:59:49 +00:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Xml.Schema;
|
|
|
|
using System.Xml.XPath;
|
|
|
|
using System.Collections;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
|
|
namespace System.Xml {
|
|
|
|
// Represents an element.
|
|
|
|
public class XmlElement : XmlLinkedNode {
|
|
|
|
XmlName name;
|
|
|
|
XmlAttributeCollection attributes;
|
|
|
|
XmlLinkedNode lastChild; // == this for empty elements otherwise it is the last child
|
|
|
|
|
|
|
|
internal XmlElement( XmlName name, bool empty, XmlDocument doc ): base( doc ) {
|
|
|
|
Debug.Assert(name!=null);
|
|
|
|
this.parentNode = null;
|
|
|
|
if ( !doc.IsLoading ) {
|
|
|
|
XmlDocument.CheckName( name.Prefix );
|
|
|
|
XmlDocument.CheckName( name.LocalName );
|
|
|
|
}
|
|
|
|
if (name.LocalName.Length == 0)
|
|
|
|
throw new ArgumentException(Res.GetString(Res.Xdom_Empty_LocalName));
|
|
|
|
this.name = name;
|
|
|
|
if (empty) {
|
|
|
|
this.lastChild = this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected internal XmlElement( string prefix, string localName, string namespaceURI, XmlDocument doc )
|
|
|
|
: this( doc.AddXmlName( prefix, localName, namespaceURI, null ), true, doc ) {
|
|
|
|
}
|
|
|
|
|
|
|
|
internal XmlName XmlName {
|
|
|
|
get { return name; }
|
|
|
|
set { name = value; }
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a duplicate of this node.
|
|
|
|
public override XmlNode CloneNode(bool deep) {
|
|
|
|
Debug.Assert( OwnerDocument != null );
|
|
|
|
XmlDocument doc = OwnerDocument;
|
|
|
|
bool OrigLoadingStatus = doc.IsLoading;
|
|
|
|
doc.IsLoading = true;
|
|
|
|
XmlElement element = doc.CreateElement( Prefix, LocalName, NamespaceURI );
|
|
|
|
doc.IsLoading = OrigLoadingStatus;
|
|
|
|
if ( element.IsEmpty != this.IsEmpty )
|
|
|
|
element.IsEmpty = this.IsEmpty;
|
|
|
|
|
|
|
|
if (HasAttributes) {
|
|
|
|
foreach( XmlAttribute attr in Attributes ) {
|
|
|
|
XmlAttribute newAttr = (XmlAttribute)(attr.CloneNode(true));
|
|
|
|
if (attr is XmlUnspecifiedAttribute && attr.Specified == false)
|
|
|
|
( ( XmlUnspecifiedAttribute )newAttr).SetSpecified(false);
|
|
|
|
element.Attributes.InternalAppendAttribute( newAttr );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (deep)
|
|
|
|
element.CopyChildren( doc, this, deep );
|
|
|
|
|
|
|
|
return element;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets the name of the node.
|
|
|
|
public override string Name {
|
|
|
|
get { return name.Name;}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets the name of the current node without the namespace prefix.
|
|
|
|
public override string LocalName {
|
|
|
|
get { return name.LocalName;}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets the namespace URI of this node.
|
|
|
|
public override string NamespaceURI {
|
|
|
|
get { return name.NamespaceURI;}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets or sets the namespace prefix of this node.
|
|
|
|
public override string Prefix {
|
|
|
|
get { return name.Prefix;}
|
|
|
|
set { name = name.OwnerDocument.AddXmlName( value, LocalName, NamespaceURI, SchemaInfo ); }
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets the type of the current node.
|
|
|
|
public override XmlNodeType NodeType {
|
|
|
|
get { return XmlNodeType.Element;}
|
|
|
|
}
|
|
|
|
|
|
|
|
public override XmlNode ParentNode {
|
|
|
|
get {
|
|
|
|
return this.parentNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets the XmlDocument that contains this node.
|
|
|
|
public override XmlDocument OwnerDocument {
|
|
|
|
get {
|
|
|
|
return name.OwnerDocument;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override bool IsContainer {
|
|
|
|
get { return true;}
|
|
|
|
}
|
|
|
|
|
|
|
|
//the function is provided only at Load time to speed up Load process
|
|
|
|
internal override XmlNode AppendChildForLoad(XmlNode newChild, XmlDocument doc) {
|
|
|
|
XmlNodeChangedEventArgs args = doc.GetInsertEventArgsForLoad( newChild, this );
|
|
|
|
|
|
|
|
if (args != null)
|
|
|
|
doc.BeforeEvent( args );
|
|
|
|
|
|
|
|
XmlLinkedNode newNode = (XmlLinkedNode)newChild;
|
|
|
|
|
|
|
|
if (lastChild == null
|
|
|
|
|| lastChild == this) { // if LastNode == null
|
|
|
|
newNode.next = newNode;
|
|
|
|
lastChild = newNode; // LastNode = newNode;
|
|
|
|
newNode.SetParentForLoad(this);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
XmlLinkedNode refNode = lastChild; // refNode = LastNode;
|
|
|
|
newNode.next = refNode.next;
|
|
|
|
refNode.next = newNode;
|
|
|
|
lastChild = newNode; // LastNode = newNode;
|
|
|
|
if (refNode.IsText
|
|
|
|
&& newNode.IsText) {
|
|
|
|
NestTextNodes(refNode, newNode);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
newNode.SetParentForLoad(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (args != null)
|
|
|
|
doc.AfterEvent( args );
|
|
|
|
|
|
|
|
return newNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets or sets whether the element does not have any children.
|
|
|
|
public bool IsEmpty {
|
|
|
|
get {
|
|
|
|
return lastChild == this;
|
|
|
|
}
|
|
|
|
|
|
|
|
set {
|
|
|
|
if (value) {
|
|
|
|
if (lastChild != this) {
|
|
|
|
RemoveAllChildren();
|
|
|
|
lastChild = this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (lastChild == this) {
|
|
|
|
lastChild = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override XmlLinkedNode LastNode {
|
|
|
|
get {
|
|
|
|
return lastChild == this ? null : lastChild;
|
|
|
|
}
|
|
|
|
|
|
|
|
set {
|
|
|
|
lastChild = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override bool IsValidChildType( XmlNodeType type ) {
|
|
|
|
switch (type) {
|
|
|
|
case XmlNodeType.Element:
|
|
|
|
case XmlNodeType.Text:
|
|
|
|
case XmlNodeType.EntityReference:
|
|
|
|
case XmlNodeType.Comment:
|
|
|
|
case XmlNodeType.Whitespace:
|
|
|
|
case XmlNodeType.SignificantWhitespace:
|
|
|
|
case XmlNodeType.ProcessingInstruction:
|
|
|
|
case XmlNodeType.CDATA:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Gets a XmlAttributeCollection containing the list of attributes for this node.
|
|
|
|
public override XmlAttributeCollection Attributes {
|
|
|
|
get {
|
|
|
|
if (attributes == null) {
|
|
|
|
lock ( OwnerDocument.objLock ) {
|
|
|
|
if ( attributes == null ) {
|
|
|
|
attributes = new XmlAttributeCollection(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return attributes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets a value indicating whether the current node
|
|
|
|
// has any attributes.
|
|
|
|
public virtual bool HasAttributes {
|
|
|
|
get {
|
|
|
|
if ( this.attributes == null )
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
return this.attributes.Count > 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the value for the attribute with the specified name.
|
|
|
|
public virtual string GetAttribute(string name) {
|
|
|
|
XmlAttribute attr = GetAttributeNode(name);
|
|
|
|
if (attr != null)
|
|
|
|
return attr.Value;
|
|
|
|
return String.Empty;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sets the value of the attribute
|
|
|
|
// with the specified name.
|
|
|
|
public virtual void SetAttribute(string name, string value) {
|
|
|
|
XmlAttribute attr = GetAttributeNode(name);
|
|
|
|
if (attr == null) {
|
|
|
|
attr = OwnerDocument.CreateAttribute(name);
|
|
|
|
attr.Value = value;
|
|
|
|
Attributes.InternalAppendAttribute( attr );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
attr.Value = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Removes an attribute by name.
|
|
|
|
public virtual void RemoveAttribute(string name) {
|
|
|
|
if (HasAttributes)
|
|
|
|
Attributes.RemoveNamedItem(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the XmlAttribute with the specified name.
|
|
|
|
public virtual XmlAttribute GetAttributeNode(string name) {
|
|
|
|
if (HasAttributes)
|
|
|
|
return Attributes[name];
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adds the specified XmlAttribute.
|
|
|
|
public virtual XmlAttribute SetAttributeNode(XmlAttribute newAttr) {
|
|
|
|
if ( newAttr.OwnerElement != null )
|
|
|
|
throw new InvalidOperationException( Res.GetString(Res.Xdom_Attr_InUse) );
|
|
|
|
return(XmlAttribute) Attributes.SetNamedItem(newAttr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Removes the specified XmlAttribute.
|
|
|
|
public virtual XmlAttribute RemoveAttributeNode(XmlAttribute oldAttr) {
|
|
|
|
if (HasAttributes)
|
|
|
|
return(XmlAttribute) Attributes.Remove(oldAttr);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a XmlNodeList containing
|
|
|
|
// a list of all descendant elements that match the specified name.
|
|
|
|
public virtual XmlNodeList GetElementsByTagName(string name) {
|
|
|
|
return new XmlElementList( this, name );
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// DOM Level 2
|
|
|
|
//
|
|
|
|
|
|
|
|
// Returns the value for the attribute with the specified LocalName and NamespaceURI.
|
|
|
|
public virtual string GetAttribute(string localName, string namespaceURI) {
|
|
|
|
XmlAttribute attr = GetAttributeNode( localName, namespaceURI );
|
|
|
|
if (attr != null)
|
|
|
|
return attr.Value;
|
|
|
|
return String.Empty;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sets the value of the attribute with the specified name
|
|
|
|
// and namespace.
|
|
|
|
public virtual string SetAttribute(string localName, string namespaceURI, string value) {
|
|
|
|
XmlAttribute attr = GetAttributeNode( localName, namespaceURI );
|
|
|
|
if (attr == null) {
|
|
|
|
attr = OwnerDocument.CreateAttribute( string.Empty, localName, namespaceURI );
|
|
|
|
attr.Value = value;
|
|
|
|
Attributes.InternalAppendAttribute( attr );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
attr.Value = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Removes an attribute specified by LocalName and NamespaceURI.
|
|
|
|
public virtual void RemoveAttribute(string localName, string namespaceURI) {
|
|
|
|
//Debug.Assert(namespaceURI != null);
|
|
|
|
RemoveAttributeNode( localName, namespaceURI );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the XmlAttribute with the specified LocalName and NamespaceURI.
|
|
|
|
public virtual XmlAttribute GetAttributeNode(string localName, string namespaceURI) {
|
|
|
|
//Debug.Assert(namespaceURI != null);
|
|
|
|
if (HasAttributes)
|
|
|
|
return Attributes[ localName, namespaceURI ];
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adds the specified XmlAttribute.
|
|
|
|
public virtual XmlAttribute SetAttributeNode(string localName, string namespaceURI) {
|
|
|
|
XmlAttribute attr = GetAttributeNode( localName, namespaceURI );
|
|
|
|
if (attr == null) {
|
|
|
|
attr = OwnerDocument.CreateAttribute( string.Empty, localName, namespaceURI );
|
|
|
|
Attributes.InternalAppendAttribute( attr );
|
|
|
|
}
|
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Removes the XmlAttribute specified by LocalName and NamespaceURI.
|
|
|
|
public virtual XmlAttribute RemoveAttributeNode(string localName, string namespaceURI) {
|
|
|
|
//Debug.Assert(namespaceURI != null);
|
|
|
|
if (HasAttributes) {
|
|
|
|
XmlAttribute attr = GetAttributeNode( localName, namespaceURI );
|
|
|
|
Attributes.Remove( attr );
|
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a XmlNodeList containing
|
|
|
|
// a list of all descendant elements that match the specified name.
|
|
|
|
public virtual XmlNodeList GetElementsByTagName(string localName, string namespaceURI) {
|
|
|
|
//Debug.Assert(namespaceURI != null);
|
|
|
|
return new XmlElementList( this, localName, namespaceURI );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determines whether the current node has the specified attribute.
|
|
|
|
public virtual bool HasAttribute(string name) {
|
|
|
|
return GetAttributeNode(name) != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determines whether the current node has the specified
|
|
|
|
// attribute from the specified namespace.
|
|
|
|
public virtual bool HasAttribute(string localName, string namespaceURI) {
|
|
|
|
return GetAttributeNode(localName, namespaceURI) != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saves the current node to the specified XmlWriter.
|
|
|
|
public override void WriteTo(XmlWriter w) {
|
|
|
|
|
|
|
|
if (GetType() == typeof(XmlElement)) {
|
|
|
|
// Use the non-recursive version (for XmlElement only)
|
|
|
|
WriteElementTo(w, this);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Use the (potentially) recursive version
|
|
|
|
WriteStartElement(w);
|
|
|
|
|
|
|
|
if (IsEmpty) {
|
|
|
|
w.WriteEndElement();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
WriteContentTo(w);
|
|
|
|
w.WriteFullEndElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This method is copied from System.Xml.Linq.ElementWriter.WriteElement but adapted to DOM
|
|
|
|
private static void WriteElementTo(XmlWriter writer, XmlElement e) {
|
|
|
|
XmlNode root = e;
|
|
|
|
XmlNode n = e;
|
|
|
|
while (true) {
|
|
|
|
e = n as XmlElement;
|
|
|
|
// Only use the inlined write logic for XmlElement, not for derived classes
|
|
|
|
if (e != null && e.GetType() == typeof(XmlElement)) {
|
|
|
|
// Write the element
|
|
|
|
e.WriteStartElement(writer);
|
|
|
|
// Write the element's content
|
|
|
|
if (e.IsEmpty) {
|
|
|
|
// No content; use a short end element <a />
|
|
|
|
writer.WriteEndElement();
|
|
|
|
}
|
|
|
|
else if (e.lastChild == null) {
|
|
|
|
// No actual content; use a full end element <a></a>
|
|
|
|
writer.WriteFullEndElement();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// There are child node(s); move to first child
|
|
|
|
n = e.FirstChild;
|
|
|
|
Debug.Assert(n != null);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Use virtual dispatch (might recurse)
|
|
|
|
n.WriteTo(writer);
|
|
|
|
}
|
|
|
|
// Go back to the parent after writing the last child
|
|
|
|
while (n != root && n == n.ParentNode.LastChild) {
|
|
|
|
n = n.ParentNode;
|
|
|
|
Debug.Assert(n != null);
|
|
|
|
writer.WriteFullEndElement();
|
|
|
|
}
|
|
|
|
if (n == root)
|
|
|
|
break;
|
|
|
|
n = n.NextSibling;
|
|
|
|
Debug.Assert(n != null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Writes the start of the element (and its attributes) to the specified writer
|
|
|
|
private void WriteStartElement(XmlWriter w) {
|
|
|
|
w.WriteStartElement(Prefix, LocalName, NamespaceURI);
|
|
|
|
|
|
|
|
if (HasAttributes) {
|
|
|
|
XmlAttributeCollection attrs = Attributes;
|
|
|
|
for (int i = 0; i < attrs.Count; i += 1) {
|
|
|
|
XmlAttribute attr = attrs[i];
|
|
|
|
attr.WriteTo(w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saves all the children of the node to the specified XmlWriter.
|
|
|
|
public override void WriteContentTo(XmlWriter w) {
|
|
|
|
for (XmlNode node = FirstChild; node != null; node = node.NextSibling) {
|
|
|
|
node.WriteTo(w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Removes the attribute node with the specified index from the attribute collection.
|
|
|
|
public virtual XmlNode RemoveAttributeAt(int i) {
|
|
|
|
if (HasAttributes)
|
|
|
|
return attributes.RemoveAt( i );
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Removes all attributes from the element.
|
|
|
|
public virtual void RemoveAllAttributes() {
|
|
|
|
if (HasAttributes) {
|
|
|
|
attributes.RemoveAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Removes all the children and/or attributes
|
|
|
|
// of the current node.
|
|
|
|
public override void RemoveAll() {
|
|
|
|
//remove all the children
|
|
|
|
base.RemoveAll();
|
|
|
|
//remove all the attributes
|
|
|
|
RemoveAllAttributes();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void RemoveAllChildren() {
|
|
|
|
base.RemoveAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
public override IXmlSchemaInfo SchemaInfo {
|
|
|
|
get {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets or sets the markup representing just
|
|
|
|
// the children of this node.
|
|
|
|
public override string InnerXml {
|
|
|
|
get {
|
|
|
|
return base.InnerXml;
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
RemoveAllChildren();
|
|
|
|
XmlLoader loader = new XmlLoader();
|
|
|
|
loader.LoadInnerXmlElement( this, value );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets or sets the concatenated values of the
|
|
|
|
// node and all its children.
|
|
|
|
public override string InnerText {
|
|
|
|
get {
|
|
|
|
return base.InnerText;
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
XmlLinkedNode linkedNode = LastNode;
|
|
|
|
if (linkedNode != null && //there is one child
|
|
|
|
linkedNode.NodeType == XmlNodeType.Text && //which is text node
|
|
|
|
linkedNode.next == linkedNode ) // and it is the only child
|
|
|
|
{
|
|
|
|
//this branch is for perf reason, event fired when TextNode.Value is changed.
|
|
|
|
linkedNode.Value = value;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
RemoveAllChildren();
|
|
|
|
AppendChild( OwnerDocument.CreateTextNode( value ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public override XmlNode NextSibling {
|
|
|
|
get {
|
|
|
|
if (this.parentNode != null
|
|
|
|
&& this.parentNode.LastNode != this)
|
|
|
|
return next;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void SetParent(XmlNode node) {
|
|
|
|
this.parentNode = node;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override XPathNodeType XPNodeType { get { return XPathNodeType.Element; } }
|
|
|
|
|
|
|
|
internal override string XPLocalName { get { return LocalName; } }
|
|
|
|
|
|
|
|
internal override string GetXPAttribute( string localName, string ns ) {
|
|
|
|
if ( ns == OwnerDocument.strReservedXmlns )
|
|
|
|
return null;
|
|
|
|
XmlAttribute attr = GetAttributeNode( localName, ns );
|
|
|
|
if ( attr != null )
|
|
|
|
return attr.Value;
|
|
|
|
return string.Empty;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|