|
//------------------------------------------------------------------------------
// <copyright file="XmlNode.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
namespace System.Xml {
using System;
using System.IO;
using System.Collections;
using System.Text;
using System.Diagnostics;
using System.Xml.Schema;
using System.Xml.XPath;
using MS.Internal.Xml.XPath;
using System.Globalization;
// Represents a single node in the document.
[DebuggerDisplay("{debuggerDisplayProxy}")]
public abstract class XmlNode : ICloneable, IEnumerable, IXPathNavigable {
internal XmlNode parentNode; //this pointer is reused to save the userdata information, need to prevent internal user access the pointer directly.
internal XmlNode () {
}
internal XmlNode( XmlDocument doc ) {
if ( doc == null )
throw new ArgumentException(Res.GetString(Res.Xdom_Node_Null_Doc));
this.parentNode = doc;
}
public virtual XPathNavigator CreateNavigator() {
XmlDocument thisAsDoc = this as XmlDocument;
if ( thisAsDoc != null ) {
return thisAsDoc.CreateNavigator( this );
}
XmlDocument doc = OwnerDocument;
Debug.Assert( doc != null );
return doc.CreateNavigator( this );
}
// Selects the first node that matches the xpath expression
public XmlNode SelectSingleNode( string xpath ) {
XmlNodeList list = SelectNodes(xpath);
// SelectNodes returns null for certain node types
return list != null ? list[0] : null;
}
// Selects the first node that matches the xpath expression and given namespace context.
public XmlNode SelectSingleNode( string xpath, XmlNamespaceManager nsmgr ) {
XPathNavigator xn = (this).CreateNavigator();
//if the method is called on node types like DocType, Entity, XmlDeclaration,
//the navigator returned is null. So just return null from here for those node types.
if( xn == null )
return null;
XPathExpression exp = xn.Compile(xpath);
exp.SetContext(nsmgr);
return new XPathNodeList(xn.Select(exp))[0];
}
// Selects all nodes that match the xpath expression
public XmlNodeList SelectNodes( string xpath ) {
XPathNavigator n = (this).CreateNavigator();
//if the method is called on node types like DocType, Entity, XmlDeclaration,
//the navigator returned is null. So just return null from here for those node types.
if( n == null )
return null;
return new XPathNodeList( n.Select(xpath) );
}
// Selects all nodes that match the xpath expression and given namespace context.
public XmlNodeList SelectNodes( string xpath, XmlNamespaceManager nsmgr ) {
XPathNavigator xn = (this).CreateNavigator();
//if the method is called on node types like DocType, Entity, XmlDeclaration,
//the navigator returned is null. So just return null from here for those node types.
if( xn == null )
return null;
XPathExpression exp = xn.Compile(xpath);
exp.SetContext(nsmgr);
return new XPathNodeList( xn.Select(exp) );
}
// Gets the name of the node.
public abstract string Name {
get;
}
// Gets or sets the value of the node.
public virtual string Value {
get { return null;}
set { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Res.GetString(Res.Xdom_Node_SetVal), NodeType.ToString()));}
}
// Gets the type of the current node.
public abstract XmlNodeType NodeType {
get;
}
// Gets the parent of this node (for nodes that can have parents).
public virtual XmlNode ParentNode {
get {
Debug.Assert(parentNode != null);
if (parentNode.NodeType != XmlNodeType.Document) {
return parentNode;
}
// Linear lookup through the children of the document
XmlLinkedNode firstChild = parentNode.FirstChild as XmlLinkedNode;
if (firstChild != null) {
XmlLinkedNode node = firstChild;
do {
if (node == this) {
return parentNode;
}
node = node.next;
}
while (node != null
&& node != firstChild);
}
return null;
}
}
// Gets all children of this node.
public virtual XmlNodeList ChildNodes {
get { return new XmlChildNodes(this);}
}
// Gets the node immediately preceding this node.
public virtual XmlNode PreviousSibling {
get { return null;}
}
// Gets the node immediately following this node.
public virtual XmlNode NextSibling {
get { return null;}
}
// Gets a XmlAttributeCollection containing the attributes
// of this node.
public virtual XmlAttributeCollection Attributes {
get { return null;}
}
// Gets the XmlDocument that contains this node.
public virtual XmlDocument OwnerDocument {
get {
Debug.Assert( parentNode != null );
if ( parentNode.NodeType == XmlNodeType.Document)
return (XmlDocument)parentNode;
return parentNode.OwnerDocument;
}
}
// Gets the first child of this node.
public virtual XmlNode FirstChild {
get {
XmlLinkedNode linkedNode = LastNode;
if (linkedNode != null)
return linkedNode.next;
return null;
}
}
// Gets the last child of this node.
public virtual XmlNode LastChild {
get { return LastNode;}
}
internal virtual bool IsContainer {
get { return false;}
}
internal virtual XmlLinkedNode LastNode {
get { return null;}
set {}
}
internal bool AncestorNode(XmlNode node) {
XmlNode n = this.ParentNode;
while (n != null && n != this) {
if (n == node)
return true;
n = n.ParentNode;
}
return false;
}
//trace to the top to find out its parent node.
internal bool IsConnected()
{
XmlNode parent = ParentNode;
while (parent != null && !( parent.NodeType == XmlNodeType.Document ))
parent = parent.ParentNode;
return parent != null;
}
// Inserts the specified node immediately before the specified reference node.
public virtual XmlNode InsertBefore(XmlNode newChild, XmlNode refChild) {
if (this == newChild || AncestorNode(newChild))
throw new ArgumentException(Res.GetString(Res.Xdom_Node_Insert_Child));
if (refChild == null)
return AppendChild(newChild);
if (!IsContainer)
throw new InvalidOperationException(Res.GetString(Res.Xdom_Node_Insert_Contain));
if (refChild.ParentNode != this)
throw new ArgumentException(Res.GetString(Res.Xdom_Node_Insert_Path));
if (newChild == refChild)
return newChild;
XmlDocument childDoc = newChild.OwnerDocument;
XmlDocument thisDoc = OwnerDocument;
if (childDoc != null && childDoc != thisDoc && childDoc != this)
throw new ArgumentException(Res.GetString(Res.Xdom_Node_Insert_Context));
if (!CanInsertBefore( newChild, refChild ))
throw new InvalidOperationException(Res.GetString(Res.Xdom_Node_Insert_Location));
if (newChild.ParentNode != null)
newChild.ParentNode.RemoveChild( newChild );
// special case for doc-fragment.
if (newChild.NodeType == XmlNodeType.DocumentFragment) {
XmlNode first = newChild.FirstChild;
XmlNode node = first;
if (node != null) {
newChild.RemoveChild( node );
InsertBefore( node, refChild );
// insert the rest of the children after this one.
InsertAfter( newChild, node );
}
return first;
}
if (!(newChild is XmlLinkedNode) || !IsValidChildType(newChild.NodeType))
throw new InvalidOperationException(Res.GetString(Res.Xdom_Node_Insert_TypeConflict));
XmlLinkedNode newNode = (XmlLinkedNode) newChild;
XmlLinkedNode refNode = (XmlLinkedNode) refChild;
string newChildValue = newChild.Value;
XmlNodeChangedEventArgs args = GetEventArgs( newChild, newChild.ParentNode, this, newChildValue, newChildValue, XmlNodeChangedAction.Insert );
if (args != null)
BeforeEvent( args );
if (refNode == FirstChild) {
newNode.next = refNode;
LastNode.next = newNode;
newNode.SetParent(this);
if (newNode.IsText) {
if (refNode.IsText) {
NestTextNodes(newNode, refNode);
}
}
}
else {
XmlLinkedNode prevNode = (XmlLinkedNode) refNode.PreviousSibling;
newNode.next = refNode;
prevNode.next = newNode;
newNode.SetParent(this);
if (prevNode.IsText) {
if (newNode.IsText) {
NestTextNodes(prevNode, newNode);
if (refNode.IsText) {
NestTextNodes(newNode, refNode);
}
}
else {
if (refNode.IsText) {
UnnestTextNodes(prevNode, refNode);
}
}
}
else {
if (newNode.IsText) {
if (refNode.IsText) {
NestTextNodes(newNode, refNode);
}
}
}
}
if (args != null)
AfterEvent( args );
return newNode;
}
// Inserts the specified node immediately after the specified reference node.
public virtual XmlNode InsertAfter(XmlNode newChild, XmlNode refChild) {
if (this == newChild || AncestorNode(newChild))
throw new ArgumentException(Res.GetString(Res.Xdom_Node_Insert_Child));
if (refChild == null)
return PrependChild(newChild);
if (!IsContainer)
throw new InvalidOperationException(Res.GetString(Res.Xdom_Node_Insert_Contain));
if (refChild.ParentNode != this)
throw new ArgumentException(Res.GetString(Res.Xdom_Node_Insert_Path));
if (newChild == refChild)
return newChild;
XmlDocument childDoc = newChild.OwnerDocument;
XmlDocument thisDoc = OwnerDocument;
if (childDoc != null && childDoc != thisDoc && childDoc != this)
throw new ArgumentException(Res.GetString(Res.Xdom_Node_Insert_Context));
if (!CanInsertAfter( newChild, refChild ))
throw new InvalidOperationException(Res.GetString(Res.Xdom_Node_Insert_Location));
if (newChild.ParentNode != null)
newChild.ParentNode.RemoveChild( newChild );
// special case for doc-fragment.
if (newChild.NodeType == XmlNodeType.DocumentFragment) {
XmlNode last = refChild;
XmlNode first = newChild.FirstChild;
XmlNode node = first;
while (node != null) {
XmlNode next = node.NextSibling;
newChild.RemoveChild( node );
InsertAfter( node, last );
last = node;
node = next;
}
return first;
}
if (!(newChild is XmlLinkedNode) || !IsValidChildType(newChild.NodeType))
throw new InvalidOperationException(Res.GetString(Res.Xdom_Node_Insert_TypeConflict));
XmlLinkedNode newNode = (XmlLinkedNode) newChild;
XmlLinkedNode refNode = (XmlLinkedNode) refChild;
string newChildValue = newChild.Value;
XmlNodeChangedEventArgs args = GetEventArgs( newChild, newChild.ParentNode, this, newChildValue, newChildValue, XmlNodeChangedAction.Insert );
if (args != null)
BeforeEvent( args );
if (refNode == LastNode) {
newNode.next = refNode.next;
refNode.next = newNode;
LastNode = newNode;
newNode.SetParent(this);
if (refNode.IsText) {
if (newNode.IsText) {
NestTextNodes(refNode, newNode);
}
}
}
else {
XmlLinkedNode nextNode = refNode.next;
newNode.next = nextNode;
refNode.next = newNode;
newNode.SetParent(this);
if (refNode.IsText) {
if (newNode.IsText) {
NestTextNodes(refNode, newNode);
if (nextNode.IsText) {
NestTextNodes(newNode, nextNode);
}
}
else {
if (nextNode.IsText) {
UnnestTextNodes(refNode, nextNode);
}
}
}
else {
if (newNode.IsText) {
if (nextNode.IsText) {
NestTextNodes(newNode, nextNode);
}
}
}
}
if (args != null)
AfterEvent( args );
return newNode;
}
// Replaces the child node oldChild with newChild node.
public virtual XmlNode ReplaceChild(XmlNode newChild, XmlNode oldChild) {
XmlNode nextNode = oldChild.NextSibling;
RemoveChild(oldChild);
XmlNode node = InsertBefore( newChild, nextNode );
return oldChild;
}
// Removes specified child node.
public virtual XmlNode RemoveChild(XmlNode oldChild) {
if (!IsContainer)
throw new InvalidOperationException(Res.GetString(Res.Xdom_Node_Remove_Contain));
if (oldChild.ParentNode != this)
throw new ArgumentException(Res.GetString(Res.Xdom_Node_Remove_Child));
XmlLinkedNode oldNode = (XmlLinkedNode) oldChild;
string oldNodeValue = oldNode.Value;
XmlNodeChangedEventArgs args = GetEventArgs( oldNode, this, null, oldNodeValue, oldNodeValue, XmlNodeChangedAction.Remove );
if (args != null)
BeforeEvent( args );
XmlLinkedNode lastNode = LastNode;
if (oldNode == FirstChild) {
if (oldNode == lastNode) {
LastNode = null;
oldNode.next = null;
oldNode.SetParent( null );
}
else {
XmlLinkedNode nextNode = oldNode.next;
if (nextNode.IsText) {
if (oldNode.IsText) {
UnnestTextNodes(oldNode, nextNode);
}
}
lastNode.next = nextNode;
oldNode.next = null;
oldNode.SetParent( null );
}
}
else {
if (oldNode == lastNode) {
XmlLinkedNode prevNode = (XmlLinkedNode) oldNode.PreviousSibling;
prevNode.next = oldNode.next;
LastNode = prevNode;
oldNode.next = null;
oldNode.SetParent(null);
}
else {
XmlLinkedNode prevNode = (XmlLinkedNode) oldNode.PreviousSibling;
XmlLinkedNode nextNode = oldNode.next;
if (nextNode.IsText) {
if (prevNode.IsText) {
NestTextNodes(prevNode, nextNode);
}
else {
if (oldNode.IsText) {
UnnestTextNodes(oldNode, nextNode);
}
}
}
prevNode.next = nextNode;
oldNode.next = null;
oldNode.SetParent(null);
}
}
if (args != null)
AfterEvent( args );
return oldChild;
}
// Adds the specified node to the beginning of the list of children of this node.
public virtual XmlNode PrependChild(XmlNode newChild) {
return InsertBefore(newChild, FirstChild);
}
// Adds the specified node to the end of the list of children of this node.
public virtual XmlNode AppendChild(XmlNode newChild) {
XmlDocument thisDoc = OwnerDocument;
if ( thisDoc == null ) {
thisDoc = this as XmlDocument;
}
if (!IsContainer)
throw new InvalidOperationException(Res.GetString(Res.Xdom_Node_Insert_Contain));
if (this == newChild || AncestorNode(newChild))
throw new ArgumentException(Res.GetString(Res.Xdom_Node_Insert_Child));
if (newChild.ParentNode != null)
newChild.ParentNode.RemoveChild( newChild );
XmlDocument childDoc = newChild.OwnerDocument;
if (childDoc != null && childDoc != thisDoc && childDoc != this)
throw new ArgumentException(Res.GetString(Res.Xdom_Node_Insert_Context));
// special case for doc-fragment.
if (newChild.NodeType == XmlNodeType.DocumentFragment) {
XmlNode first = newChild.FirstChild;
XmlNode node = first;
while (node != null) {
XmlNode next = node.NextSibling;
newChild.RemoveChild( node );
AppendChild( node );
node = next;
}
return first;
}
if (!(newChild is XmlLinkedNode) || !IsValidChildType(newChild.NodeType))
throw new InvalidOperationException(Res.GetString(Res.Xdom_Node_Insert_TypeConflict));
if (!CanInsertAfter( newChild, LastChild ))
throw new InvalidOperationException(Res.GetString(Res.Xdom_Node_Insert_Location));
string newChildValue = newChild.Value;
XmlNodeChangedEventArgs args = GetEventArgs( newChild, newChild.ParentNode, this, newChildValue, newChildValue, XmlNodeChangedAction.Insert );
if (args != null)
BeforeEvent( args );
XmlLinkedNode refNode = LastNode;
XmlLinkedNode newNode = (XmlLinkedNode) newChild;
if (refNode == null) {
newNode.next = newNode;
LastNode = newNode;
newNode.SetParent(this);
}
else {
newNode.next = refNode.next;
refNode.next = newNode;
LastNode = newNode;
newNode.SetParent(this);
if (refNode.IsText) {
if (newNode.IsText) {
NestTextNodes(refNode, newNode);
}
}
}
if (args != null)
AfterEvent( args );
return newNode;
}
//the function is provided only at Load time to speed up Load process
internal virtual XmlNode AppendChildForLoad(XmlNode newChild, XmlDocument doc) {
XmlNodeChangedEventArgs args = doc.GetInsertEventArgsForLoad( newChild, this );
if (args != null)
doc.BeforeEvent( args );
XmlLinkedNode refNode = LastNode;
XmlLinkedNode newNode = (XmlLinkedNode) newChild;
if (refNode == null) {
newNode.next = newNode;
LastNode = newNode;
newNode.SetParentForLoad(this);
}
else {
newNode.next = refNode.next;
refNode.next = newNode;
LastNode = newNode;
if (refNode.IsText
&& newNode.IsText) {
NestTextNodes(refNode, newNode);
}
else {
newNode.SetParentForLoad(this);
}
}
if (args != null)
doc.AfterEvent( args );
return newNode;
}
internal virtual bool IsValidChildType( XmlNodeType type ) {
return false;
}
internal virtual bool CanInsertBefore( XmlNode newChild, XmlNode refChild ) {
return true;
}
internal virtual bool CanInsertAfter( XmlNode newChild, XmlNode refChild ) {
return true;
}
// Gets a value indicating whether this node has any child nodes.
public virtual bool HasChildNodes {
get { return LastNode != null;}
}
// Creates a duplicate of this node.
public abstract XmlNode CloneNode(bool deep);
internal virtual void CopyChildren( XmlDocument doc, XmlNode container, bool deep ) {
for (XmlNode child = container.FirstChild; child != null; child = child.NextSibling) {
AppendChildForLoad( child.CloneNode(deep), doc );
}
}
// DOM Level 2
// Puts all XmlText nodes in the full depth of the sub-tree
// underneath this XmlNode into a "normal" form where only
// markup (e.g., tags, comments, processing instructions, CDATA sections,
// and entity references) separates XmlText nodes, that is, there
// are no adjacent XmlText nodes.
public virtual void Normalize() {
XmlNode firstChildTextLikeNode = null;
StringBuilder sb = new StringBuilder();
for ( XmlNode crtChild = this.FirstChild; crtChild != null; ) {
XmlNode nextChild = crtChild.NextSibling;
switch ( crtChild.NodeType ) {
case XmlNodeType.Text:
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace: {
sb.Append( crtChild.Value );
XmlNode winner = NormalizeWinner( firstChildTextLikeNode, crtChild );
if ( winner == firstChildTextLikeNode ) {
this.RemoveChild( crtChild );
}
else {
if ( firstChildTextLikeNode != null )
this.RemoveChild( firstChildTextLikeNode );
firstChildTextLikeNode = crtChild;
}
break;
}
case XmlNodeType.Element: {
crtChild.Normalize();
goto default;
}
default : {
if ( firstChildTextLikeNode != null ) {
firstChildTextLikeNode.Value = sb.ToString();
firstChildTextLikeNode = null;
}
sb.Remove( 0, sb.Length );
break;
}
}
crtChild = nextChild;
}
if ( firstChildTextLikeNode != null && sb.Length > 0 )
firstChildTextLikeNode.Value = sb.ToString();
}
private XmlNode NormalizeWinner( XmlNode firstNode, XmlNode secondNode ) {
//first node has the priority
if ( firstNode == null )
return secondNode;
Debug.Assert( firstNode.NodeType == XmlNodeType.Text
|| firstNode.NodeType == XmlNodeType.SignificantWhitespace
|| firstNode.NodeType == XmlNodeType.Whitespace
|| secondNode.NodeType == XmlNodeType.Text
|| secondNode.NodeType == XmlNodeType.SignificantWhitespace
|| secondNode.NodeType == XmlNodeType.Whitespace );
if ( firstNode.NodeType == XmlNodeType.Text )
return firstNode;
if ( secondNode.NodeType == XmlNodeType.Text )
return secondNode;
if ( firstNode.NodeType == XmlNodeType.SignificantWhitespace )
return firstNode;
if ( secondNode.NodeType == XmlNodeType.SignificantWhitespace )
return secondNode;
if ( firstNode.NodeType == XmlNodeType.Whitespace )
return firstNode;
if ( secondNode.NodeType == XmlNodeType.Whitespace )
return secondNode;
Debug.Assert( true, "shouldn't have fall through here." );
return null;
}
// Test if the DOM implementation implements a specific feature.
public virtual bool Supports(string feature, string version) {
if (String.Compare("XML", feature, StringComparison.OrdinalIgnoreCase) == 0) {
if (version == null || version == "1.0" || version == "2.0")
return true;
}
return false;
}
// Gets the namespace URI of this node.
public virtual string NamespaceURI {
get { return string.Empty;}
}
// Gets or sets the namespace prefix of this node.
public virtual string Prefix {
get { return string.Empty;}
set {}
}
// Gets the name of the node without the namespace prefix.
public abstract string LocalName {
get;
}
// Microsoft extensions
// Gets a value indicating whether the node is read-only.
public virtual bool IsReadOnly {
get {
XmlDocument doc = OwnerDocument;
return HasReadOnlyParent( this );
}
}
internal static bool HasReadOnlyParent( XmlNode n ) {
while (n != null) {
switch (n.NodeType) {
case XmlNodeType.EntityReference:
case XmlNodeType.Entity:
return true;
case XmlNodeType.Attribute:
n = ((XmlAttribute)n).OwnerElement;
break;
default:
n = n.ParentNode;
break;
}
}
return false;
}
// Creates a duplicate of this node.
public virtual XmlNode Clone() {
return this.CloneNode(true);
}
object ICloneable.Clone() {
return this.CloneNode(true);
}
// Provides a simple ForEach-style iteration over the
// collection of nodes in this XmlNamedNodeMap.
IEnumerator IEnumerable.GetEnumerator() {
return new XmlChildEnumerator(this);
}
public IEnumerator GetEnumerator() {
return new XmlChildEnumerator(this);
}
private void AppendChildText( StringBuilder builder ) {
for (XmlNode child = FirstChild; child != null; child = child.NextSibling) {
if (child.FirstChild == null) {
if (child.NodeType == XmlNodeType.Text || child.NodeType == XmlNodeType.CDATA
|| child.NodeType == XmlNodeType.Whitespace || child.NodeType == XmlNodeType.SignificantWhitespace)
builder.Append( child.InnerText );
}
else {
child.AppendChildText( builder );
}
}
}
// Gets or sets the concatenated values of the node and
// all its children.
public virtual string InnerText {
get {
XmlNode fc = FirstChild;
if (fc == null) {
return string.Empty;
}
if (fc.NextSibling == null) {
XmlNodeType nodeType = fc.NodeType;
switch (nodeType) {
case XmlNodeType.Text:
case XmlNodeType.CDATA:
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
return fc.Value;
}
}
StringBuilder builder = new StringBuilder();
AppendChildText( builder );
return builder.ToString();
}
set {
XmlNode firstChild = FirstChild;
if ( firstChild != null //there is one child
&& firstChild.NextSibling == null // and exactly one
&& firstChild.NodeType == XmlNodeType.Text )//which is a text node
{
//this branch is for perf reason and event fired when TextNode.Value is changed
firstChild.Value = value;
}
else {
RemoveAll();
AppendChild( OwnerDocument.CreateTextNode( value ) );
}
}
}
// Gets the markup representing this node and all its children.
public virtual string OuterXml {
get {
StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
XmlDOMTextWriter xw = new XmlDOMTextWriter( sw );
try {
WriteTo( xw );
}
finally {
xw.Close();
}
return sw.ToString();
}
}
// Gets or sets the markup representing just the children of this node.
public virtual string InnerXml {
get {
StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
XmlDOMTextWriter xw = new XmlDOMTextWriter( sw );
try {
WriteContentTo( xw );
}
finally {
xw.Close();
}
return sw.ToString();
}
set {
throw new InvalidOperationException( Res.GetString(Res.Xdom_Set_InnerXml ) );
}
}
public virtual IXmlSchemaInfo SchemaInfo {
get {
return XmlDocument.NotKnownSchemaInfo;
}
}
public virtual String BaseURI {
get {
XmlNode curNode = this.ParentNode; //save one while loop since if going to here, the nodetype of this node can't be document, entity and entityref
while ( curNode != null ) {
XmlNodeType nt = curNode.NodeType;
//EntityReference's children come from the dtd where they are defined.
//we need to investigate the same thing for entity's children if they are defined in an external dtd file.
if ( nt == XmlNodeType.EntityReference )
return ((XmlEntityReference)curNode).ChildBaseURI;
if ( nt == XmlNodeType.Document
|| nt == XmlNodeType.Entity
|| nt == XmlNodeType.Attribute )
return curNode.BaseURI;
curNode = curNode.ParentNode;
}
return String.Empty;
}
}
// Saves the current node to the specified XmlWriter.
public abstract void WriteTo(XmlWriter w);
// Saves all the children of the node to the specified XmlWriter.
public abstract void WriteContentTo(XmlWriter w);
// Removes all the children and/or attributes
// of the current node.
public virtual void RemoveAll() {
XmlNode child = FirstChild;
XmlNode sibling = null;
while (child != null) {
sibling = child.NextSibling;
RemoveChild( child );
child = sibling;
}
}
internal XmlDocument Document {
get {
if (NodeType == XmlNodeType.Document)
return (XmlDocument)this;
return OwnerDocument;
}
}
// Looks up the closest xmlns declaration for the given
// prefix that is in scope for the current node and returns
// the namespace URI in the declaration.
public virtual string GetNamespaceOfPrefix(string prefix) {
string namespaceName = GetNamespaceOfPrefixStrict(prefix);
return namespaceName != null ? namespaceName : string.Empty;
}
internal string GetNamespaceOfPrefixStrict(string prefix) {
XmlDocument doc = Document;
if (doc != null) {
prefix = doc.NameTable.Get(prefix);
if (prefix == null)
return null;
XmlNode node = this;
while (node != null) {
if (node.NodeType == XmlNodeType.Element) {
XmlElement elem = (XmlElement)node;
if (elem.HasAttributes) {
XmlAttributeCollection attrs = elem.Attributes;
if (prefix.Length == 0) {
for (int iAttr = 0; iAttr < attrs.Count; iAttr++) {
XmlAttribute attr = attrs[iAttr];
if (attr.Prefix.Length == 0) {
if (Ref.Equal(attr.LocalName, doc.strXmlns)) {
return attr.Value; // found xmlns
}
}
}
}
else {
for (int iAttr = 0; iAttr < attrs.Count; iAttr++) {
XmlAttribute attr = attrs[iAttr];
if (Ref.Equal(attr.Prefix, doc.strXmlns)) {
if (Ref.Equal(attr.LocalName, prefix)) {
return attr.Value; // found xmlns:prefix
}
}
else if (Ref.Equal(attr.Prefix, prefix)) {
return attr.NamespaceURI; // found prefix:attr
}
}
}
}
if (Ref.Equal(node.Prefix, prefix)) {
return node.NamespaceURI;
}
node = node.ParentNode;
}
else if (node.NodeType == XmlNodeType.Attribute) {
node = ((XmlAttribute)node).OwnerElement;
}
else {
node = node.ParentNode;
}
}
if (Ref.Equal(doc.strXml, prefix)) { // xmlns:xml
return doc.strReservedXml;
}
else if (Ref.Equal(doc.strXmlns, prefix)) { // xmlns:xmlns
return doc.strReservedXmlns;
}
}
return null;
}
// Looks up the closest xmlns declaration for the given namespace
// URI that is in scope for the current node and returns
// the prefix defined in that declaration.
public virtual string GetPrefixOfNamespace(string namespaceURI) {
string prefix = GetPrefixOfNamespaceStrict(namespaceURI);
return prefix != null ? prefix : string.Empty;
}
internal string GetPrefixOfNamespaceStrict(string namespaceURI) {
XmlDocument doc = Document;
if (doc != null) {
namespaceURI = doc.NameTable.Add(namespaceURI);
XmlNode node = this;
while (node != null) {
if (node.NodeType == XmlNodeType.Element) {
XmlElement elem = (XmlElement)node;
if (elem.HasAttributes) {
XmlAttributeCollection attrs = elem.Attributes;
for (int iAttr = 0; iAttr < attrs.Count; iAttr++) {
XmlAttribute attr = attrs[iAttr];
if (attr.Prefix.Length == 0) {
if (Ref.Equal(attr.LocalName, doc.strXmlns)) {
if (attr.Value == namespaceURI) {
return string.Empty; // found xmlns="namespaceURI"
}
}
}
else if (Ref.Equal(attr.Prefix, doc.strXmlns)) {
if (attr.Value == namespaceURI) {
return attr.LocalName; // found xmlns:prefix="namespaceURI"
}
}
else if (Ref.Equal(attr.NamespaceURI, namespaceURI)) {
return attr.Prefix; // found prefix:attr
// with prefix bound to namespaceURI
}
}
}
if (Ref.Equal(node.NamespaceURI, namespaceURI)) {
return node.Prefix;
}
node = node.ParentNode;
}
else if (node.NodeType == XmlNodeType.Attribute) {
node = ((XmlAttribute)node).OwnerElement;
}
else {
node = node.ParentNode;
}
}
if (Ref.Equal(doc.strReservedXml, namespaceURI)) { // xmlns:xml
return doc.strXml;
}
else if (Ref.Equal(doc.strReservedXmlns, namespaceURI)) { // xmlns:xmlns
return doc.strXmlns;
}
}
return null;
}
// Retrieves the first child element with the specified name.
public virtual XmlElement this[string name]
{
get {
for (XmlNode n = FirstChild; n != null; n = n.NextSibling) {
if (n.NodeType == XmlNodeType.Element && n.Name == name)
return(XmlElement) n;
}
return null;
}
}
// Retrieves the first child element with the specified LocalName and
// NamespaceURI.
public virtual XmlElement this[string localname, string ns]
{
get {
for (XmlNode n = FirstChild; n != null; n = n.NextSibling) {
if (n.NodeType == XmlNodeType.Element && n.LocalName == localname && n.NamespaceURI == ns)
return(XmlElement) n;
}
return null;
}
}
internal virtual void SetParent( XmlNode node ) {
if (node == null) {
this.parentNode = OwnerDocument;
}
else {
this.parentNode = node;
}
}
internal virtual void SetParentForLoad( XmlNode node ) {
this.parentNode = node;
}
internal static void SplitName( string name, out string prefix, out string localName ) {
int colonPos = name.IndexOf(':'); // ordinal compare
if (-1 == colonPos || 0 == colonPos || name.Length-1 == colonPos) {
prefix = string.Empty;
localName = name;
}
else {
prefix = name.Substring(0, colonPos);
localName = name.Substring(colonPos+1);
}
}
internal virtual XmlNode FindChild( XmlNodeType type ) {
for (XmlNode child = FirstChild; child != null; child = child.NextSibling) {
if (child.NodeType == type) {
return child;
}
}
return null;
}
internal virtual XmlNodeChangedEventArgs GetEventArgs( XmlNode node, XmlNode oldParent, XmlNode newParent, string oldValue, string newValue, XmlNodeChangedAction action ) {
XmlDocument doc = OwnerDocument;
if (doc != null) {
if ( ! doc.IsLoading ) {
if ( ( (newParent != null && newParent.IsReadOnly) || ( oldParent != null && oldParent.IsReadOnly ) ) )
throw new InvalidOperationException( Res.GetString(Res.Xdom_Node_Modify_ReadOnly));
}
return doc.GetEventArgs( node, oldParent, newParent, oldValue, newValue, action );
}
return null;
}
internal virtual void BeforeEvent( XmlNodeChangedEventArgs args ) {
if (args != null)
OwnerDocument.BeforeEvent( args );
}
internal virtual void AfterEvent( XmlNodeChangedEventArgs args ) {
if (args != null)
OwnerDocument.AfterEvent( args );
}
internal virtual XmlSpace XmlSpace {
get {
XmlNode node = this;
XmlElement elem = null;
do {
elem = node as XmlElement;
if (elem != null && elem.HasAttribute("xml:space")) {
switch (XmlConvert.TrimString(elem.GetAttribute("xml:space"))) {
case "default":
return XmlSpace.Default;
case "preserve":
return XmlSpace.Preserve;
default:
//should we throw exception if value is otherwise?
break;
}
}
node = node.ParentNode;
}
while (node != null);
return XmlSpace.None;
}
}
internal virtual String XmlLang {
get {
XmlNode node = this;
XmlElement elem = null;
do {
elem = node as XmlElement;
if ( elem != null ) {
if ( elem.HasAttribute( "xml:lang" ) )
return elem.GetAttribute( "xml:lang" );
}
node = node.ParentNode;
} while ( node != null );
return String.Empty;
}
}
internal virtual XPathNodeType XPNodeType {
get {
return (XPathNodeType)(-1);
}
}
internal virtual string XPLocalName {
get {
return string.Empty;
}
}
internal virtual string GetXPAttribute(string localName, string namespaceURI) {
return String.Empty;
}
internal virtual bool IsText {
get {
return false;
}
}
public virtual XmlNode PreviousText {
get {
return null;
}
}
internal static void NestTextNodes(XmlNode prevNode, XmlNode nextNode) {
Debug.Assert(prevNode.IsText);
Debug.Assert(nextNode.IsText);
nextNode.parentNode = prevNode;
}
internal static void UnnestTextNodes(XmlNode prevNode, XmlNode nextNode) {
Debug.Assert(prevNode.IsText);
Debug.Assert(nextNode.IsText);
nextNode.parentNode = prevNode.ParentNode;
}
private object debuggerDisplayProxy { get { return new DebuggerDisplayXmlNodeProxy(this); } }
}
[DebuggerDisplay("{ToString()}")]
internal struct DebuggerDisplayXmlNodeProxy {
private XmlNode node;
public DebuggerDisplayXmlNodeProxy(XmlNode node) {
this.node = node;
}
public override string ToString() {
XmlNodeType nodeType = node.NodeType;
string result = nodeType.ToString();
switch (nodeType) {
case XmlNodeType.Element:
case XmlNodeType.EntityReference:
result += ", Name=\"" + node.Name + "\"";
break;
case XmlNodeType.Attribute:
case XmlNodeType.ProcessingInstruction:
result += ", Name=\"" + node.Name + "\", Value=\"" + XmlConvert.EscapeValueForDebuggerDisplay(node.Value) + "\"";
break;
case XmlNodeType.Text:
case XmlNodeType.CDATA:
case XmlNodeType.Comment:
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
case XmlNodeType.XmlDeclaration:
result += ", Value=\"" + XmlConvert.EscapeValueForDebuggerDisplay(node.Value) + "\"";
break;
case XmlNodeType.DocumentType:
XmlDocumentType documentType = (XmlDocumentType)node;
result += ", Name=\"" + documentType.Name + "\", SYSTEM=\"" + documentType.SystemId + "\", PUBLIC=\"" + documentType.PublicId + "\", Value=\"" + XmlConvert.EscapeValueForDebuggerDisplay(documentType.InternalSubset) + "\"";
break;
default:
break;
}
return result;
}
}
}
|