|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Security;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using MS.Internal.Xml.Cache;
using MS.Internal.Xml.XPath;
namespace System.Xml.XPath
{
// Provides a navigation interface API using XPath data model.
[DebuggerDisplay("{debuggerDisplayProxy}")]
public abstract class XPathNavigator : XPathItem, ICloneable, IXPathNavigable, IXmlNamespaceResolver
{
internal static readonly XPathNavigatorKeyComparer comparer = new XPathNavigatorKeyComparer();
//-----------------------------------------------
// Object
//-----------------------------------------------
public override string ToString()
{
return Value;
}
//-----------------------------------------------
// XPathItem
//-----------------------------------------------
public sealed override bool IsNode
{
get { return true; }
}
public override XmlSchemaType? XmlType
{
get
{
IXmlSchemaInfo? schemaInfo = SchemaInfo;
if (schemaInfo != null)
{
if (schemaInfo.Validity == XmlSchemaValidity.Valid)
{
XmlSchemaType? memberType = schemaInfo.MemberType;
if (memberType != null)
{
return memberType;
}
return schemaInfo.SchemaType;
}
}
return null;
}
}
public virtual void SetValue(string value)
{
throw new NotSupportedException();
}
public override object TypedValue
{
get
{
IXmlSchemaInfo? schemaInfo = SchemaInfo;
XmlSchemaType? schemaType;
XmlSchemaDatatype? datatype;
if (schemaInfo != null)
{
if (schemaInfo.Validity == XmlSchemaValidity.Valid)
{
schemaType = schemaInfo.MemberType ?? schemaInfo.SchemaType;
if (schemaType != null)
{
datatype = schemaType.Datatype;
if (datatype != null)
{
return schemaType.ValueConverter.ChangeType(Value, datatype.ValueType, this);
}
}
}
else
{
schemaType = schemaInfo.SchemaType;
if (schemaType != null)
{
datatype = schemaType.Datatype;
if (datatype != null)
{
return schemaType.ValueConverter.ChangeType(datatype.ParseValue(Value, NameTable, this), datatype.ValueType, this);
}
}
}
}
return Value;
}
}
public virtual void SetTypedValue(object typedValue)
{
ArgumentNullException.ThrowIfNull(typedValue);
switch (NodeType)
{
case XPathNodeType.Element:
case XPathNodeType.Attribute:
break;
default:
throw new InvalidOperationException(SR.Xpn_BadPosition);
}
string? value = null;
IXmlSchemaInfo? schemaInfo = SchemaInfo;
if (schemaInfo != null)
{
XmlSchemaType? schemaType = schemaInfo.SchemaType;
if (schemaType != null)
{
value = schemaType.ValueConverter.ToString(typedValue, this);
XmlSchemaDatatype? datatype = schemaType.Datatype;
datatype?.ParseValue(value, NameTable, this);
}
}
value ??= XmlUntypedConverter.Untyped.ToString(typedValue, this);
SetValue(value);
}
public override Type ValueType
{
get
{
IXmlSchemaInfo? schemaInfo = SchemaInfo;
XmlSchemaType? schemaType;
XmlSchemaDatatype? datatype;
if (schemaInfo != null)
{
if (schemaInfo.Validity == XmlSchemaValidity.Valid)
{
schemaType = schemaInfo.MemberType ?? schemaInfo.SchemaType;
if (schemaType != null)
{
datatype = schemaType.Datatype;
if (datatype != null)
{
return datatype.ValueType;
}
}
}
else
{
schemaType = schemaInfo.SchemaType;
if (schemaType != null)
{
datatype = schemaType.Datatype;
if (datatype != null)
{
return datatype.ValueType;
}
}
}
}
return typeof(string);
}
}
public override bool ValueAsBoolean
{
get
{
IXmlSchemaInfo? schemaInfo = SchemaInfo;
XmlSchemaType? schemaType;
XmlSchemaDatatype? datatype;
if (schemaInfo != null)
{
if (schemaInfo.Validity == XmlSchemaValidity.Valid)
{
schemaType = schemaInfo.MemberType ?? schemaInfo.SchemaType;
if (schemaType != null)
{
return schemaType.ValueConverter.ToBoolean(Value);
}
}
else
{
schemaType = schemaInfo.SchemaType;
if (schemaType != null)
{
datatype = schemaType.Datatype;
if (datatype != null)
{
return schemaType.ValueConverter.ToBoolean(datatype.ParseValue(Value, NameTable, this));
}
}
}
}
return XmlUntypedConverter.Untyped.ToBoolean(Value);
}
}
public override DateTime ValueAsDateTime
{
get
{
IXmlSchemaInfo? schemaInfo = SchemaInfo;
XmlSchemaType? schemaType;
XmlSchemaDatatype? datatype;
if (schemaInfo != null)
{
if (schemaInfo.Validity == XmlSchemaValidity.Valid)
{
schemaType = schemaInfo.MemberType ?? schemaInfo.SchemaType;
if (schemaType != null)
{
return schemaType.ValueConverter.ToDateTime(Value);
}
}
else
{
schemaType = schemaInfo.SchemaType;
if (schemaType != null)
{
datatype = schemaType.Datatype;
if (datatype != null)
{
return schemaType.ValueConverter.ToDateTime(datatype.ParseValue(Value, NameTable, this));
}
}
}
}
return XmlUntypedConverter.Untyped.ToDateTime(Value);
}
}
public override double ValueAsDouble
{
get
{
IXmlSchemaInfo? schemaInfo = SchemaInfo;
XmlSchemaType? schemaType;
XmlSchemaDatatype? datatype;
if (schemaInfo != null)
{
if (schemaInfo.Validity == XmlSchemaValidity.Valid)
{
schemaType = schemaInfo.MemberType ?? schemaInfo.SchemaType;
if (schemaType != null)
{
return schemaType.ValueConverter.ToDouble(Value);
}
}
else
{
schemaType = schemaInfo.SchemaType;
if (schemaType != null)
{
datatype = schemaType.Datatype;
if (datatype != null)
{
return schemaType.ValueConverter.ToDouble(datatype.ParseValue(Value, NameTable, this));
}
}
}
}
return XmlUntypedConverter.Untyped.ToDouble(Value);
}
}
public override int ValueAsInt
{
get
{
IXmlSchemaInfo? schemaInfo = SchemaInfo;
XmlSchemaType? schemaType;
XmlSchemaDatatype? datatype;
if (schemaInfo != null)
{
if (schemaInfo.Validity == XmlSchemaValidity.Valid)
{
schemaType = schemaInfo.MemberType ?? schemaInfo.SchemaType;
if (schemaType != null)
{
return schemaType.ValueConverter.ToInt32(Value);
}
}
else
{
schemaType = schemaInfo.SchemaType;
if (schemaType != null)
{
datatype = schemaType.Datatype;
if (datatype != null)
{
return schemaType.ValueConverter.ToInt32(datatype.ParseValue(Value, NameTable, this));
}
}
}
}
return XmlUntypedConverter.Untyped.ToInt32(Value);
}
}
public override long ValueAsLong
{
get
{
IXmlSchemaInfo? schemaInfo = SchemaInfo;
XmlSchemaType? schemaType;
XmlSchemaDatatype? datatype;
if (schemaInfo != null)
{
if (schemaInfo.Validity == XmlSchemaValidity.Valid)
{
schemaType = schemaInfo.MemberType ?? schemaInfo.SchemaType;
if (schemaType != null)
{
return schemaType.ValueConverter.ToInt64(Value);
}
}
else
{
schemaType = schemaInfo.SchemaType;
if (schemaType != null)
{
datatype = schemaType.Datatype;
if (datatype != null)
{
return schemaType.ValueConverter.ToInt64(datatype.ParseValue(Value, NameTable, this));
}
}
}
}
return XmlUntypedConverter.Untyped.ToInt64(Value);
}
}
public override object ValueAs(Type returnType, IXmlNamespaceResolver? nsResolver)
{
nsResolver ??= this;
IXmlSchemaInfo? schemaInfo = SchemaInfo;
XmlSchemaType? schemaType;
XmlSchemaDatatype? datatype;
if (schemaInfo != null)
{
if (schemaInfo.Validity == XmlSchemaValidity.Valid)
{
schemaType = schemaInfo.MemberType ?? schemaInfo.SchemaType;
if (schemaType != null)
{
return schemaType.ValueConverter.ChangeType(Value, returnType, nsResolver);
}
}
else
{
schemaType = schemaInfo.SchemaType;
if (schemaType != null)
{
datatype = schemaType.Datatype;
if (datatype != null)
{
return schemaType.ValueConverter.ChangeType(datatype.ParseValue(Value, NameTable, nsResolver), returnType, nsResolver);
}
}
}
}
return XmlUntypedConverter.Untyped.ChangeType(Value, returnType, nsResolver);
}
//-----------------------------------------------
// ICloneable
//-----------------------------------------------
object ICloneable.Clone()
{
return Clone();
}
//-----------------------------------------------
// IXPathNavigable
//-----------------------------------------------
public virtual XPathNavigator CreateNavigator()
{
return Clone();
}
//-----------------------------------------------
// IXmlNamespaceResolver
//-----------------------------------------------
public abstract XmlNameTable NameTable { get; }
public virtual string? LookupNamespace(string prefix)
{
if (prefix == null)
return null;
if (NodeType != XPathNodeType.Element)
{
XPathNavigator navSave = Clone();
// If current item is not an element, then try parent
if (navSave.MoveToParent())
return navSave.LookupNamespace(prefix);
}
else if (MoveToNamespace(prefix))
{
string namespaceURI = Value;
MoveToParent();
return namespaceURI;
}
// Check for "", "xml", and "xmlns" prefixes
if (prefix.Length == 0)
return string.Empty;
else if (prefix == "xml")
return XmlReservedNs.NsXml;
else if (prefix == "xmlns")
return XmlReservedNs.NsXmlNs;
return null;
}
public virtual string? LookupPrefix(string namespaceURI)
{
if (namespaceURI == null)
return null;
XPathNavigator navClone = Clone();
if (NodeType != XPathNodeType.Element)
{
// If current item is not an element, then try parent
if (navClone.MoveToParent())
return navClone.LookupPrefix(namespaceURI);
}
else
{
if (navClone.MoveToFirstNamespace(XPathNamespaceScope.All))
{
// Loop until a matching namespace is found
do
{
if (namespaceURI == navClone.Value)
return navClone.LocalName;
}
while (navClone.MoveToNextNamespace(XPathNamespaceScope.All));
}
}
// Check for default, "xml", and "xmlns" namespaces
if (namespaceURI == LookupNamespace(string.Empty))
return string.Empty;
else if (namespaceURI == XmlReservedNs.NsXml)
return "xml";
else if (namespaceURI == XmlReservedNs.NsXmlNs)
return "xmlns";
return null;
}
public virtual IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope)
{
XPathNodeType nt = NodeType;
if ((nt != XPathNodeType.Element && scope != XmlNamespaceScope.Local) || nt == XPathNodeType.Attribute || nt == XPathNodeType.Namespace)
{
XPathNavigator navSave = Clone();
// If current item is not an element, then try parent
if (navSave.MoveToParent())
return navSave.GetNamespacesInScope(scope);
}
Dictionary<string, string> dict = new Dictionary<string, string>();
// "xml" prefix always in scope
if (scope == XmlNamespaceScope.All)
dict["xml"] = XmlReservedNs.NsXml;
// Now add all in-scope namespaces
if (MoveToFirstNamespace((XPathNamespaceScope)scope))
{
do
{
string prefix = LocalName;
string ns = Value;
// Exclude xmlns="" declarations unless scope = Local
if (prefix.Length != 0 || ns.Length != 0 || scope == XmlNamespaceScope.Local)
dict[prefix] = ns;
}
while (MoveToNextNamespace((XPathNamespaceScope)scope));
MoveToParent();
}
return dict;
}
//-----------------------------------------------
// XPathNavigator
//-----------------------------------------------
// Returns an object of type IKeyComparer. Using this the navigators can be hashed
// on the basis of actual position it represents rather than the clr reference of
// the navigator object.
public static IEqualityComparer NavigatorComparer
{
get { return comparer; }
}
public abstract XPathNavigator Clone();
public abstract XPathNodeType NodeType { get; }
public abstract string LocalName { get; }
public abstract string Name { get; }
public abstract string NamespaceURI { get; }
public abstract string Prefix { get; }
public abstract string BaseURI { get; }
public abstract bool IsEmptyElement { get; }
public virtual string XmlLang
{
get
{
XPathNavigator navClone = Clone();
do
{
if (navClone.MoveToAttribute("lang", XmlReservedNs.NsXml))
return navClone.Value;
}
while (navClone.MoveToParent());
return string.Empty;
}
}
public virtual XmlReader ReadSubtree()
{
switch (NodeType)
{
case XPathNodeType.Root:
case XPathNodeType.Element:
break;
default:
throw new InvalidOperationException(SR.Xpn_BadPosition);
}
return CreateReader();
}
public virtual void WriteSubtree(XmlWriter writer)
{
ArgumentNullException.ThrowIfNull(writer);
writer.WriteNode(this, true);
}
public virtual object? UnderlyingObject
{
get { return null; }
}
public virtual bool HasAttributes
{
get
{
if (!MoveToFirstAttribute())
return false;
MoveToParent();
return true;
}
}
public virtual string GetAttribute(string localName, string namespaceURI)
{
string value;
if (!MoveToAttribute(localName, namespaceURI))
return "";
value = Value;
MoveToParent();
return value;
}
public virtual bool MoveToAttribute(string localName, string namespaceURI)
{
if (MoveToFirstAttribute())
{
do
{
if (localName == LocalName && namespaceURI == NamespaceURI)
return true;
}
while (MoveToNextAttribute());
MoveToParent();
}
return false;
}
public abstract bool MoveToFirstAttribute();
public abstract bool MoveToNextAttribute();
public virtual string GetNamespace(string name)
{
string value;
if (!MoveToNamespace(name))
{
if (name == "xml")
return XmlReservedNs.NsXml;
if (name == "xmlns")
return XmlReservedNs.NsXmlNs;
return string.Empty;
}
value = Value;
MoveToParent();
return value;
}
public virtual bool MoveToNamespace(string name)
{
if (MoveToFirstNamespace(XPathNamespaceScope.All))
{
do
{
if (name == LocalName)
return true;
}
while (MoveToNextNamespace(XPathNamespaceScope.All));
MoveToParent();
}
return false;
}
public abstract bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope);
public abstract bool MoveToNextNamespace(XPathNamespaceScope namespaceScope);
public bool MoveToFirstNamespace() { return MoveToFirstNamespace(XPathNamespaceScope.All); }
public bool MoveToNextNamespace() { return MoveToNextNamespace(XPathNamespaceScope.All); }
public abstract bool MoveToNext();
public abstract bool MoveToPrevious();
public virtual bool MoveToFirst()
{
switch (NodeType)
{
case XPathNodeType.Attribute:
case XPathNodeType.Namespace:
// MoveToFirst should only succeed for content-typed nodes
return false;
}
if (!MoveToParent())
return false;
return MoveToFirstChild();
}
public abstract bool MoveToFirstChild();
public abstract bool MoveToParent();
public virtual void MoveToRoot()
{
while (MoveToParent())
;
}
public abstract bool MoveTo(XPathNavigator other);
public abstract bool MoveToId(string id);
public virtual bool MoveToChild(string localName, string namespaceURI)
{
if (MoveToFirstChild())
{
do
{
if (NodeType == XPathNodeType.Element && localName == LocalName && namespaceURI == NamespaceURI)
return true;
}
while (MoveToNext());
MoveToParent();
}
return false;
}
public virtual bool MoveToChild(XPathNodeType type)
{
if (MoveToFirstChild())
{
int mask = GetContentKindMask(type);
do
{
if (((1 << (int)NodeType) & mask) != 0)
return true;
}
while (MoveToNext());
MoveToParent();
}
return false;
}
public virtual bool MoveToFollowing(string localName, string namespaceURI)
{
return MoveToFollowing(localName, namespaceURI, null);
}
public virtual bool MoveToFollowing(string localName, string namespaceURI, XPathNavigator? end)
{
XPathNavigator navSave = Clone();
if (end != null)
{
switch (end.NodeType)
{
case XPathNodeType.Attribute:
case XPathNodeType.Namespace:
// Scan until we come to the next content-typed node
// after the attribute or namespace node
end = end.Clone();
end.MoveToNonDescendant();
break;
}
}
switch (NodeType)
{
case XPathNodeType.Attribute:
case XPathNodeType.Namespace:
if (!MoveToParent())
{
return false;
}
break;
}
do
{
if (!MoveToFirstChild())
{
// Look for next sibling
while (true)
{
if (MoveToNext())
break;
if (!MoveToParent())
{
// Restore previous position and return false
MoveTo(navSave);
return false;
}
}
}
// Have we reached the end of the scan?
if (end != null && IsSamePosition(end))
{
// Restore previous position and return false
MoveTo(navSave);
return false;
}
}
while (NodeType != XPathNodeType.Element
|| localName != LocalName
|| namespaceURI != NamespaceURI);
return true;
}
public virtual bool MoveToFollowing(XPathNodeType type)
{
return MoveToFollowing(type, null);
}
public virtual bool MoveToFollowing(XPathNodeType type, XPathNavigator? end)
{
XPathNavigator navSave = Clone();
int mask = GetContentKindMask(type);
if (end != null)
{
switch (end.NodeType)
{
case XPathNodeType.Attribute:
case XPathNodeType.Namespace:
// Scan until we come to the next content-typed node
// after the attribute or namespace node
end = end.Clone();
end.MoveToNonDescendant();
break;
}
}
switch (NodeType)
{
case XPathNodeType.Attribute:
case XPathNodeType.Namespace:
if (!MoveToParent())
{
return false;
}
break;
}
do
{
if (!MoveToFirstChild())
{
// Look for next sibling
while (true)
{
if (MoveToNext())
break;
if (!MoveToParent())
{
// Restore previous position and return false
MoveTo(navSave);
return false;
}
}
}
// Have we reached the end of the scan?
if (end != null && IsSamePosition(end))
{
// Restore previous position and return false
MoveTo(navSave);
return false;
}
}
while (((1 << (int)NodeType) & mask) == 0);
return true;
}
public virtual bool MoveToNext(string localName, string namespaceURI)
{
XPathNavigator navClone = Clone();
while (MoveToNext())
{
if (NodeType == XPathNodeType.Element && localName == LocalName && namespaceURI == NamespaceURI)
return true;
}
MoveTo(navClone);
return false;
}
public virtual bool MoveToNext(XPathNodeType type)
{
XPathNavigator navClone = Clone();
int mask = GetContentKindMask(type);
while (MoveToNext())
{
if (((1 << (int)NodeType) & mask) != 0)
return true;
}
MoveTo(navClone);
return false;
}
public virtual bool HasChildren
{
get
{
if (MoveToFirstChild())
{
MoveToParent();
return true;
}
return false;
}
}
public abstract bool IsSamePosition(XPathNavigator other);
public virtual bool IsDescendant([NotNullWhen(true)] XPathNavigator? nav)
{
if (nav != null)
{
nav = nav.Clone();
while (nav.MoveToParent())
if (nav.IsSamePosition(this))
return true;
}
return false;
}
public virtual XmlNodeOrder ComparePosition(XPathNavigator? nav)
{
if (nav == null)
{
return XmlNodeOrder.Unknown;
}
if (IsSamePosition(nav))
return XmlNodeOrder.Same;
XPathNavigator n1 = this.Clone();
XPathNavigator n2 = nav.Clone();
int depth1 = GetDepth(n1.Clone());
int depth2 = GetDepth(n2.Clone());
if (depth1 > depth2)
{
while (depth1 > depth2)
{
n1.MoveToParent();
depth1--;
}
if (n1.IsSamePosition(n2))
return XmlNodeOrder.After;
}
if (depth2 > depth1)
{
while (depth2 > depth1)
{
n2.MoveToParent();
depth2--;
}
if (n1.IsSamePosition(n2))
return XmlNodeOrder.Before;
}
XPathNavigator parent1 = n1.Clone();
XPathNavigator parent2 = n2.Clone();
while (true)
{
if (!parent1.MoveToParent() || !parent2.MoveToParent())
return XmlNodeOrder.Unknown;
if (parent1.IsSamePosition(parent2))
{
if (n1.GetType().ToString() != "Microsoft.VisualStudio.Modeling.StoreNavigator")
{
Debug.Assert(CompareSiblings(n1.Clone(), n2.Clone()) != CompareSiblings(n2.Clone(), n1.Clone()), "IsSamePosition() on custom navigator returns inconsistent results");
}
return CompareSiblings(n1, n2);
}
n1.MoveToParent();
n2.MoveToParent();
}
}
public virtual IXmlSchemaInfo? SchemaInfo
{
get { return this as IXmlSchemaInfo; }
}
public virtual bool CheckValidity(XmlSchemaSet schemas, ValidationEventHandler validationEventHandler)
{
IXmlSchemaInfo? schemaInfo;
XmlSchemaType? schemaType = null;
XmlSchemaElement? schemaElement = null;
XmlSchemaAttribute? schemaAttribute = null;
switch (NodeType)
{
case XPathNodeType.Root:
if (schemas == null)
{
throw new InvalidOperationException(SR.XPathDocument_MissingSchemas);
}
schemaType = null;
break;
case XPathNodeType.Element:
if (schemas == null)
{
throw new InvalidOperationException(SR.XPathDocument_MissingSchemas);
}
schemaInfo = SchemaInfo;
if (schemaInfo != null)
{
schemaType = schemaInfo.SchemaType;
schemaElement = schemaInfo.SchemaElement;
}
if (schemaType == null
&& schemaElement == null)
{
throw new InvalidOperationException(SR.Format(SR.XPathDocument_NotEnoughSchemaInfo, null));
}
break;
case XPathNodeType.Attribute:
if (schemas == null)
{
throw new InvalidOperationException(SR.XPathDocument_MissingSchemas);
}
schemaInfo = SchemaInfo;
if (schemaInfo != null)
{
schemaType = schemaInfo.SchemaType;
schemaAttribute = schemaInfo.SchemaAttribute;
}
if (schemaType == null
&& schemaAttribute == null)
{
throw new InvalidOperationException(SR.Format(SR.XPathDocument_NotEnoughSchemaInfo, null));
}
break;
default:
throw new InvalidOperationException(SR.Format(SR.XPathDocument_ValidateInvalidNodeType, null));
}
Debug.Assert(schemaType != null || this.NodeType == XPathNodeType.Root, "schemaType != null || this.NodeType == XPathNodeType.Root");
XPathNavigatorReader reader = (XPathNavigatorReader)CreateReader();
CheckValidityHelper validityTracker = new CheckValidityHelper(validationEventHandler, reader);
validationEventHandler = new ValidationEventHandler(validityTracker.ValidationCallback);
XmlReader validatingReader = GetValidatingReader(reader, schemas, validationEventHandler, schemaType, schemaElement, schemaAttribute);
while (validatingReader.Read())
;
return validityTracker.IsValid;
}
private static XmlReader GetValidatingReader(XmlReader reader, XmlSchemaSet schemas, ValidationEventHandler validationEvent, XmlSchemaType? schemaType, XmlSchemaElement? schemaElement, XmlSchemaAttribute? schemaAttribute)
{
if (schemaAttribute != null)
{
return schemaAttribute.Validate(reader, null, schemas, validationEvent);
}
else if (schemaElement != null)
{
return schemaElement.Validate(reader, null, schemas, validationEvent);
}
else if (schemaType != null)
{
return schemaType.Validate(reader, null, schemas, validationEvent);
}
Debug.Assert(schemas != null);
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.ConformanceLevel = ConformanceLevel.Auto;
readerSettings.ValidationType = ValidationType.Schema;
readerSettings.Schemas = schemas;
readerSettings.ValidationEventHandler += validationEvent;
return XmlReader.Create(reader, readerSettings);
}
private sealed class CheckValidityHelper
{
private bool _isValid;
private readonly ValidationEventHandler _nextEventHandler;
private readonly XPathNavigatorReader _reader;
internal CheckValidityHelper(ValidationEventHandler nextEventHandler, XPathNavigatorReader reader)
{
_isValid = true;
_nextEventHandler = nextEventHandler;
_reader = reader;
}
internal void ValidationCallback(object? sender, ValidationEventArgs args)
{
Debug.Assert(args != null);
if (args.Severity == XmlSeverityType.Error)
_isValid = false;
XmlSchemaValidationException? exception = args.Exception as XmlSchemaValidationException;
if (exception != null && _reader != null)
exception.SetSourceObject(_reader.UnderlyingObject);
if (_nextEventHandler != null)
{
_nextEventHandler(sender, args);
}
else if (exception != null && args.Severity == XmlSeverityType.Error)
{
throw exception;
}
}
internal bool IsValid
{
get { return _isValid; }
}
}
public virtual XPathExpression Compile(string xpath)
{
return XPathExpression.Compile(xpath);
}
public virtual XPathNavigator? SelectSingleNode(string xpath)
{
return SelectSingleNode(XPathExpression.Compile(xpath));
}
public virtual XPathNavigator? SelectSingleNode(string xpath, IXmlNamespaceResolver? resolver)
{
return SelectSingleNode(XPathExpression.Compile(xpath, resolver));
}
public virtual XPathNavigator? SelectSingleNode(XPathExpression expression)
{
// PERF BUG: this actually caches _all_ matching nodes...
XPathNodeIterator iter = this.Select(expression);
if (iter.MoveNext())
{
return iter.Current;
}
return null;
}
public virtual XPathNodeIterator Select(string xpath)
{
return this.Select(XPathExpression.Compile(xpath));
}
public virtual XPathNodeIterator Select(string xpath, IXmlNamespaceResolver? resolver)
{
return this.Select(XPathExpression.Compile(xpath, resolver));
}
public virtual XPathNodeIterator Select(XPathExpression expr)
{
XPathNodeIterator? result = Evaluate(expr) as XPathNodeIterator;
if (result == null)
{
throw XPathException.Create(SR.Xp_NodeSetExpected);
}
return result;
}
public virtual object Evaluate(string xpath)
{
return Evaluate(XPathExpression.Compile(xpath), null);
}
public virtual object Evaluate(string xpath, IXmlNamespaceResolver? resolver)
{
return this.Evaluate(XPathExpression.Compile(xpath, resolver));
}
public virtual object Evaluate(XPathExpression expr)
{
return Evaluate(expr, null);
}
public virtual object Evaluate(XPathExpression expr, XPathNodeIterator? context)
{
CompiledXpathExpr? cexpr = expr as CompiledXpathExpr;
if (cexpr == null)
{
throw XPathException.Create(SR.Xp_BadQueryObject);
}
Query query = Query.Clone(cexpr.QueryTree);
query.Reset();
context ??= new XPathSingletonIterator(this.Clone(), moved: true);
object result = query.Evaluate(context);
if (result is XPathNodeIterator)
{
Debug.Assert(context.Current != null);
return new XPathSelectionIterator(context.Current, query);
}
return result;
}
public virtual bool Matches(XPathExpression expr)
{
CompiledXpathExpr? cexpr = expr as CompiledXpathExpr;
if (cexpr == null)
throw XPathException.Create(SR.Xp_BadQueryObject);
// We should clone query because some Query.MatchNode() alter expression state and this may brake
// SelectionIterators that are running using this Query
// Example of MatchNode() that alert the state is FilterQuery.MatchNode()
Query query = Query.Clone(cexpr.QueryTree);
try
{
return query.MatchNode(this) != null;
}
catch (XPathException)
{
throw XPathException.Create(SR.Xp_InvalidPattern, cexpr.Expression);
}
}
public virtual bool Matches(string xpath)
{
return Matches(CompileMatchPattern(xpath));
}
public virtual XPathNodeIterator SelectChildren(XPathNodeType type)
{
return new XPathChildIterator(this.Clone(), type);
}
public virtual XPathNodeIterator SelectChildren(string name, string namespaceURI)
{
return new XPathChildIterator(this.Clone(), name, namespaceURI);
}
public virtual XPathNodeIterator SelectAncestors(XPathNodeType type, bool matchSelf)
{
return new XPathAncestorIterator(this.Clone(), type, matchSelf);
}
public virtual XPathNodeIterator SelectAncestors(string name, string namespaceURI, bool matchSelf)
{
return new XPathAncestorIterator(this.Clone(), name, namespaceURI, matchSelf);
}
public virtual XPathNodeIterator SelectDescendants(XPathNodeType type, bool matchSelf)
{
return new XPathDescendantIterator(this.Clone(), type, matchSelf);
}
public virtual XPathNodeIterator SelectDescendants(string name, string namespaceURI, bool matchSelf)
{
return new XPathDescendantIterator(this.Clone(), name, namespaceURI, matchSelf);
}
public virtual bool CanEdit
{
get
{
return false;
}
}
public virtual XmlWriter PrependChild()
{
throw new NotSupportedException();
}
public virtual XmlWriter AppendChild()
{
throw new NotSupportedException();
}
public virtual XmlWriter InsertAfter()
{
throw new NotSupportedException();
}
public virtual XmlWriter InsertBefore()
{
throw new NotSupportedException();
}
public virtual XmlWriter CreateAttributes()
{
throw new NotSupportedException();
}
public virtual XmlWriter ReplaceRange(XPathNavigator lastSiblingToReplace)
{
throw new NotSupportedException();
}
public virtual void ReplaceSelf(string newNode)
{
XmlTextReader reader = CreateContextReader(newNode, false);
ReplaceSelf(reader);
}
public virtual void ReplaceSelf(XmlReader newNode)
{
ArgumentNullException.ThrowIfNull(newNode);
XPathNodeType type = NodeType;
if (type == XPathNodeType.Root
|| type == XPathNodeType.Attribute
|| type == XPathNodeType.Namespace)
{
throw new InvalidOperationException(SR.Xpn_BadPosition);
}
XmlWriter writer = ReplaceRange(this);
BuildSubtree(newNode, writer);
writer.Close();
}
public virtual void ReplaceSelf(XPathNavigator newNode)
{
ArgumentNullException.ThrowIfNull(newNode);
XmlReader reader = newNode.CreateReader();
ReplaceSelf(reader);
}
// Returns the markup representing the current node and all of its children.
public virtual string OuterXml
{
get
{
StringWriter stringWriter;
XmlWriterSettings writerSettings;
XmlWriter xmlWriter;
// Attributes and namespaces are not allowed at the top-level by the well-formed writer
if (NodeType == XPathNodeType.Attribute)
{
return $"{Name}=\"{Value}\"";
}
else if (NodeType == XPathNodeType.Namespace)
{
if (LocalName.Length == 0)
return $"xmlns=\"{Value}\"";
else
return $"xmlns:{LocalName}=\"{Value}\"";
}
stringWriter = new StringWriter(CultureInfo.InvariantCulture);
writerSettings = new XmlWriterSettings();
writerSettings.Indent = true;
writerSettings.OmitXmlDeclaration = true;
writerSettings.ConformanceLevel = ConformanceLevel.Auto;
xmlWriter = XmlWriter.Create(stringWriter, writerSettings);
try
{
xmlWriter.WriteNode(this, true);
}
finally
{
xmlWriter.Close();
}
return stringWriter.ToString();
}
set
{
ReplaceSelf(value);
}
}
// Returns the markup representing just the children of the current node.
public virtual string InnerXml
{
get
{
switch (NodeType)
{
case XPathNodeType.Root:
case XPathNodeType.Element:
StringWriter stringWriter;
XmlWriterSettings writerSettings;
XmlWriter xmlWriter;
stringWriter = new StringWriter(CultureInfo.InvariantCulture);
writerSettings = new XmlWriterSettings();
writerSettings.Indent = true;
writerSettings.OmitXmlDeclaration = true;
writerSettings.ConformanceLevel = ConformanceLevel.Auto;
xmlWriter = XmlWriter.Create(stringWriter, writerSettings);
try
{
if (MoveToFirstChild())
{
do
{
xmlWriter.WriteNode(this, true);
}
while (MoveToNext());
// Restore position
MoveToParent();
}
}
finally
{
xmlWriter.Close();
}
return stringWriter.ToString();
case XPathNodeType.Attribute:
case XPathNodeType.Namespace:
return Value;
default:
return string.Empty;
}
}
set
{
ArgumentNullException.ThrowIfNull(value);
switch (NodeType)
{
case XPathNodeType.Root:
case XPathNodeType.Element:
XPathNavigator edit = CreateNavigator();
while (edit.MoveToFirstChild())
{
edit.DeleteSelf();
}
if (value.Length != 0)
{
edit.AppendChild(value);
}
break;
case XPathNodeType.Attribute:
SetValue(value);
break;
default:
throw new InvalidOperationException(SR.Xpn_BadPosition);
}
}
}
public virtual void AppendChild(string newChild)
{
XmlReader reader = CreateContextReader(newChild, true);
AppendChild(reader);
}
public virtual void AppendChild(XmlReader newChild)
{
ArgumentNullException.ThrowIfNull(newChild);
XmlWriter writer = AppendChild();
BuildSubtree(newChild, writer);
writer.Close();
}
public virtual void AppendChild(XPathNavigator newChild)
{
ArgumentNullException.ThrowIfNull(newChild);
if (!IsValidChildType(newChild.NodeType))
{
throw new InvalidOperationException(SR.Xpn_BadPosition);
}
XmlReader reader = newChild.CreateReader();
AppendChild(reader);
}
public virtual void PrependChild(string newChild)
{
XmlReader reader = CreateContextReader(newChild, true);
PrependChild(reader);
}
public virtual void PrependChild(XmlReader newChild)
{
ArgumentNullException.ThrowIfNull(newChild);
XmlWriter writer = PrependChild();
BuildSubtree(newChild, writer);
writer.Close();
}
public virtual void PrependChild(XPathNavigator newChild)
{
ArgumentNullException.ThrowIfNull(newChild);
if (!IsValidChildType(newChild.NodeType))
{
throw new InvalidOperationException(SR.Xpn_BadPosition);
}
XmlReader reader = newChild.CreateReader();
PrependChild(reader);
}
public virtual void InsertBefore(string newSibling)
{
XmlReader reader = CreateContextReader(newSibling, false);
InsertBefore(reader);
}
public virtual void InsertBefore(XmlReader newSibling)
{
ArgumentNullException.ThrowIfNull(newSibling);
XmlWriter writer = InsertBefore();
BuildSubtree(newSibling, writer);
writer.Close();
}
public virtual void InsertBefore(XPathNavigator newSibling)
{
ArgumentNullException.ThrowIfNull(newSibling);
if (!IsValidSiblingType(newSibling.NodeType))
{
throw new InvalidOperationException(SR.Xpn_BadPosition);
}
XmlReader reader = newSibling.CreateReader();
InsertBefore(reader);
}
public virtual void InsertAfter(string newSibling)
{
XmlReader reader = CreateContextReader(newSibling, false);
InsertAfter(reader);
}
public virtual void InsertAfter(XmlReader newSibling)
{
ArgumentNullException.ThrowIfNull(newSibling);
XmlWriter writer = InsertAfter();
BuildSubtree(newSibling, writer);
writer.Close();
}
public virtual void InsertAfter(XPathNavigator newSibling)
{
ArgumentNullException.ThrowIfNull(newSibling);
if (!IsValidSiblingType(newSibling.NodeType))
{
throw new InvalidOperationException(SR.Xpn_BadPosition);
}
XmlReader reader = newSibling.CreateReader();
InsertAfter(reader);
}
public virtual void DeleteRange(XPathNavigator lastSiblingToDelete)
{
throw new NotSupportedException();
}
public virtual void DeleteSelf()
{
DeleteRange(this);
}
public virtual void PrependChildElement(string? prefix, string localName, string? namespaceURI, string? value)
{
XmlWriter writer = PrependChild();
writer.WriteStartElement(prefix, localName, namespaceURI);
if (value != null)
{
writer.WriteString(value);
}
writer.WriteEndElement();
writer.Close();
}
public virtual void AppendChildElement(string? prefix, string localName, string? namespaceURI, string? value)
{
XmlWriter writer = AppendChild();
writer.WriteStartElement(prefix, localName, namespaceURI);
if (value != null)
{
writer.WriteString(value);
}
writer.WriteEndElement();
writer.Close();
}
public virtual void InsertElementBefore(string? prefix, string localName, string? namespaceURI, string? value)
{
XmlWriter writer = InsertBefore();
writer.WriteStartElement(prefix, localName, namespaceURI);
if (value != null)
{
writer.WriteString(value);
}
writer.WriteEndElement();
writer.Close();
}
public virtual void InsertElementAfter(string? prefix, string localName, string? namespaceURI, string? value)
{
XmlWriter writer = InsertAfter();
writer.WriteStartElement(prefix, localName, namespaceURI);
if (value != null)
{
writer.WriteString(value);
}
writer.WriteEndElement();
writer.Close();
}
public virtual void CreateAttribute(string? prefix, string localName, string? namespaceURI, string? value)
{
XmlWriter writer = CreateAttributes();
writer.WriteStartAttribute(prefix, localName, namespaceURI);
if (value != null)
{
writer.WriteString(value);
}
writer.WriteEndAttribute();
writer.Close();
}
//-----------------------------------------------
// Internal
//-----------------------------------------------
internal bool MoveToPrevious(string localName, string namespaceURI)
{
XPathNavigator navClone = Clone();
string? atomizedLocalName = (localName != null) ? NameTable.Get(localName) : null;
while (MoveToPrevious())
{
if (NodeType == XPathNodeType.Element && atomizedLocalName == LocalName && namespaceURI == NamespaceURI)
return true;
}
MoveTo(navClone);
return false;
}
internal bool MoveToPrevious(XPathNodeType type)
{
XPathNavigator navClone = Clone();
int mask = GetContentKindMask(type);
while (MoveToPrevious())
{
if (((1 << (int)NodeType) & mask) != 0)
return true;
}
MoveTo(navClone);
return false;
}
internal bool MoveToNonDescendant()
{
// If current node is document, there is no next non-descendant
if (NodeType == XPathNodeType.Root)
return false;
// If sibling exists, it is the next non-descendant
if (MoveToNext())
return true;
// The current node is either an attribute, namespace, or last child node
XPathNavigator navSave = Clone();
if (!MoveToParent())
return false;
switch (navSave.NodeType)
{
case XPathNodeType.Attribute:
case XPathNodeType.Namespace:
// Next node in document order is first content-child of parent
if (MoveToFirstChild())
return true;
break;
}
while (!MoveToNext())
{
if (!MoveToParent())
{
// Restore original position and return false
MoveTo(navSave);
return false;
}
}
return true;
}
/// <summary>
/// Returns ordinal number of attribute, namespace or child node within its parent.
/// Order is reversed for attributes and child nodes to avoid O(N**2) running time.
/// This property is useful for debugging, and also used in UniqueId implementation.
/// </summary>
internal uint IndexInParent
{
get
{
XPathNavigator nav = this.Clone();
uint idx = 0;
switch (NodeType)
{
case XPathNodeType.Attribute:
while (nav.MoveToNextAttribute())
{
idx++;
}
break;
case XPathNodeType.Namespace:
while (nav.MoveToNextNamespace())
{
idx++;
}
break;
default:
while (nav.MoveToNext())
{
idx++;
}
break;
}
return idx;
}
}
// (R)oot
// (E)lement
// (A)ttribute
// (N)amespace
// (T)ext
// (S)ignificantWhitespace
// (W)hitespace
// (P)rocessingInstruction
// (C)omment
// (X) All
internal const string NodeTypeLetter = "REANTSWPCX";
internal const string UniqueIdTbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456";
// Requirements for id:
// 1. must consist of alphanumeric characters only
// 2. must begin with an alphabetic character
// 3. same id is generated for the same node
// 4. ids are unique
//
// id = node type letter + reverse path to root in terms of encoded IndexInParent integers from node to root separated by 0's if needed
internal virtual string UniqueId
{
get
{
XPathNavigator nav = this.Clone();
StringBuilder sb = new StringBuilder();
// Ensure distinguishing attributes, namespaces and child nodes
sb.Append(NodeTypeLetter[(int)NodeType]);
while (true)
{
uint idx = nav.IndexInParent;
if (!nav.MoveToParent())
{
break;
}
if (idx <= 0x1f)
{
sb.Append(UniqueIdTbl[(int)idx]);
}
else
{
sb.Append('0');
do
{
sb.Append(UniqueIdTbl[(int)(idx & 0x1f)]);
idx >>= 5;
} while (idx != 0);
sb.Append('0');
}
}
return sb.ToString();
}
}
private static CompiledXpathExpr CompileMatchPattern(string xpath)
{
bool hasPrefix;
Query query = new QueryBuilder().BuildPatternQuery(xpath, out hasPrefix);
return new CompiledXpathExpr(query, xpath, hasPrefix);
}
private static int GetDepth(XPathNavigator nav)
{
int depth = 0;
while (nav.MoveToParent())
{
depth++;
}
return depth;
}
// XPath based comparison for namespaces, attributes and other
// items with the same parent element.
//
// n2
// namespace(0) attribute(-1) other(-2)
// n1
// namespace(0) ?(0) before(-1) before(-2)
// attribute(1) after(1) ?(0) before(-1)
// other (2) after(2) after(1) ?(0)
private static XmlNodeOrder CompareSiblings(XPathNavigator n1, XPathNavigator n2)
{
int cmp = 0;
#if DEBUG
Debug.Assert(!n1.IsSamePosition(n2));
XPathNavigator p1 = n1.Clone(), p2 = n2.Clone();
Debug.Assert(p1.MoveToParent() && p2.MoveToParent() && p1.IsSamePosition(p2));
#endif
switch (n1.NodeType)
{
case XPathNodeType.Namespace:
break;
case XPathNodeType.Attribute:
cmp += 1;
break;
default:
cmp += 2;
break;
}
switch (n2.NodeType)
{
case XPathNodeType.Namespace:
if (cmp == 0)
{
while (n1.MoveToNextNamespace())
{
if (n1.IsSamePosition(n2))
{
return XmlNodeOrder.Before;
}
}
}
break;
case XPathNodeType.Attribute:
cmp -= 1;
if (cmp == 0)
{
while (n1.MoveToNextAttribute())
{
if (n1.IsSamePosition(n2))
{
return XmlNodeOrder.Before;
}
}
}
break;
default:
cmp -= 2;
if (cmp == 0)
{
while (n1.MoveToNext())
{
if (n1.IsSamePosition(n2))
{
return XmlNodeOrder.Before;
}
}
}
break;
}
return cmp < 0 ? XmlNodeOrder.Before : XmlNodeOrder.After;
}
internal static XmlNamespaceManager GetNamespaces(IXmlNamespaceResolver resolver)
{
XmlNamespaceManager mngr = new XmlNamespaceManager(new NameTable());
IDictionary<string, string> dictionary = resolver.GetNamespacesInScope(XmlNamespaceScope.All);
foreach (KeyValuePair<string, string> pair in dictionary)
{
//"xmlns " is always in the namespace manager so adding it would throw an exception
if (pair.Key != "xmlns")
mngr.AddNamespace(pair.Key, pair.Value);
}
return mngr;
}
// Get mask that will allow XPathNodeType content matching to be performed using only a shift and an and operation
internal const int AllMask = 0x7FFFFFFF;
internal const int NoAttrNmspMask = AllMask & ~(1 << (int)XPathNodeType.Attribute) & ~(1 << (int)XPathNodeType.Namespace);
internal const int TextMask = (1 << (int)XPathNodeType.Text) | (1 << (int)XPathNodeType.SignificantWhitespace) | (1 << (int)XPathNodeType.Whitespace);
internal static readonly int[] ContentKindMasks = {
(1 << (int) XPathNodeType.Root), // Root
(1 << (int) XPathNodeType.Element), // Element
0, // Attribute (not content)
0, // Namespace (not content)
TextMask, // Text
(1 << (int) XPathNodeType.SignificantWhitespace), // SignificantWhitespace
(1 << (int) XPathNodeType.Whitespace), // Whitespace
(1 << (int) XPathNodeType.ProcessingInstruction), // ProcessingInstruction
(1 << (int) XPathNodeType.Comment), // Comment
NoAttrNmspMask, // All
};
internal static int GetContentKindMask(XPathNodeType type)
{
return ContentKindMasks[(int)type];
}
internal static int GetKindMask(XPathNodeType type)
{
if (type == XPathNodeType.All)
return AllMask;
else if (type == XPathNodeType.Text)
return TextMask;
return (1 << (int)type);
}
internal static bool IsText(XPathNodeType type)
{
//return ((1 << (int) type) & TextMask) != 0;
return unchecked((uint)(type - XPathNodeType.Text)) <= (XPathNodeType.Whitespace - XPathNodeType.Text);
}
// Lax check for potential child item.
private bool IsValidChildType(XPathNodeType type)
{
switch (NodeType)
{
case XPathNodeType.Root:
switch (type)
{
case XPathNodeType.Element:
case XPathNodeType.SignificantWhitespace:
case XPathNodeType.Whitespace:
case XPathNodeType.ProcessingInstruction:
case XPathNodeType.Comment:
return true;
}
break;
case XPathNodeType.Element:
switch (type)
{
case XPathNodeType.Element:
case XPathNodeType.Text:
case XPathNodeType.SignificantWhitespace:
case XPathNodeType.Whitespace:
case XPathNodeType.ProcessingInstruction:
case XPathNodeType.Comment:
return true;
}
break;
}
return false;
}
// Lax check for potential sibling item.
private bool IsValidSiblingType(XPathNodeType type)
{
switch (NodeType)
{
case XPathNodeType.Element:
case XPathNodeType.Text:
case XPathNodeType.SignificantWhitespace:
case XPathNodeType.Whitespace:
case XPathNodeType.ProcessingInstruction:
case XPathNodeType.Comment:
switch (type)
{
case XPathNodeType.Element:
case XPathNodeType.Text:
case XPathNodeType.SignificantWhitespace:
case XPathNodeType.Whitespace:
case XPathNodeType.ProcessingInstruction:
case XPathNodeType.Comment:
return true;
}
break;
}
return false;
}
private XPathNavigatorReader CreateReader()
{
return XPathNavigatorReader.Create(this);
}
private XmlTextReader CreateContextReader(string xml, bool fromCurrentNode)
{
ArgumentNullException.ThrowIfNull(xml);
// We have to set the namespace context for the reader.
XPathNavigator editor = CreateNavigator();
// scope starts from parent.
XmlNamespaceManager mgr = new XmlNamespaceManager(NameTable);
if (!fromCurrentNode)
{
editor.MoveToParent(); // should always succeed.
}
if (editor.MoveToFirstNamespace(XPathNamespaceScope.All))
{
do
{
mgr.AddNamespace(editor.LocalName, editor.Value);
}
while (editor.MoveToNextNamespace(XPathNamespaceScope.All));
}
// BUGBUG: How can we preserve the whitespace setting
XmlParserContext context = new XmlParserContext(NameTable, mgr, null, XmlSpace.Default);
XmlTextReader reader = new XmlTextReader(xml, XmlNodeType.Element, context);
// BUGBUG: Whitespace handling??
reader.WhitespaceHandling = WhitespaceHandling.Significant;
return reader;
}
internal static void BuildSubtree(XmlReader reader, XmlWriter writer)
{
// important (perf) string literal...
string xmlnsUri = XmlReservedNs.NsXmlNs; // http://www.w3.org/2000/xmlns/
ReadState readState = reader.ReadState;
if (readState != ReadState.Initial
&& readState != ReadState.Interactive)
{
throw new ArgumentException(SR.Xml_InvalidOperation, nameof(reader));
}
int level = 0;
if (readState == ReadState.Initial)
{
if (!reader.Read())
return;
level++; // if start in initial, read everything (not just first)
}
do
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
bool isEmptyElement = reader.IsEmptyElement;
while (reader.MoveToNextAttribute())
{
if ((object)reader.NamespaceURI == (object)xmlnsUri)
{
if (reader.Prefix.Length == 0)
{
// Default namespace declaration "xmlns"
Debug.Assert(reader.LocalName == "xmlns");
writer.WriteAttributeString("", "xmlns", xmlnsUri, reader.Value);
}
else
{
Debug.Assert(reader.Prefix == "xmlns");
writer.WriteAttributeString("xmlns", reader.LocalName, xmlnsUri, reader.Value);
}
}
else
{
writer.WriteStartAttribute(reader.Prefix, reader.LocalName, reader.NamespaceURI);
writer.WriteString(reader.Value);
writer.WriteEndAttribute();
}
}
reader.MoveToElement();
if (isEmptyElement)
{
// there might still be a value, if there is a default value specified in the schema
writer.WriteEndElement();
}
else
{
level++;
}
break;
case XmlNodeType.EndElement:
writer.WriteFullEndElement();
//should not read beyond the level of the reader's original position.
level--;
break;
case XmlNodeType.Text:
case XmlNodeType.CDATA:
writer.WriteString(reader.Value);
break;
case XmlNodeType.SignificantWhitespace:
case XmlNodeType.Whitespace:
writer.WriteString(reader.Value);
break;
case XmlNodeType.Comment:
writer.WriteComment(reader.Value);
break;
case XmlNodeType.ProcessingInstruction:
writer.WriteProcessingInstruction(reader.LocalName, reader.Value);
break;
case XmlNodeType.EntityReference:
reader.ResolveEntity();
break;
case XmlNodeType.EndEntity:
case XmlNodeType.None:
case XmlNodeType.DocumentType:
case XmlNodeType.XmlDeclaration:
break;
case XmlNodeType.Attribute:
if ((object)reader.NamespaceURI == (object)xmlnsUri)
{
if (reader.Prefix.Length == 0)
{
// Default namespace declaration "xmlns"
Debug.Assert(reader.LocalName == "xmlns");
writer.WriteAttributeString("", "xmlns", xmlnsUri, reader.Value);
}
else
{
Debug.Assert(reader.Prefix == "xmlns");
writer.WriteAttributeString("xmlns", reader.LocalName, xmlnsUri, reader.Value);
}
}
else
{
writer.WriteStartAttribute(reader.Prefix, reader.LocalName, reader.NamespaceURI);
writer.WriteString(reader.Value);
writer.WriteEndAttribute();
}
break;
}
}
while (reader.Read() && (level > 0));
}
private object debuggerDisplayProxy { get { return new DebuggerDisplayProxy(this); } }
[DebuggerDisplay("{ToString()}")]
internal struct DebuggerDisplayProxy
{
private readonly XPathNavigator _nav;
public DebuggerDisplayProxy(XPathNavigator nav)
{
_nav = nav;
}
public override string ToString()
{
string result = _nav.NodeType.ToString();
switch (_nav.NodeType)
{
case XPathNodeType.Element:
result += $", Name=\"{_nav.Name}\"";
break;
case XPathNodeType.Attribute:
case XPathNodeType.Namespace:
case XPathNodeType.ProcessingInstruction:
result += $", Name=\"{_nav.Name}\"";
result += $", Value=\"{XmlConvert.EscapeValueForDebuggerDisplay(_nav.Value)}\"";
break;
case XPathNodeType.Text:
case XPathNodeType.Whitespace:
case XPathNodeType.SignificantWhitespace:
case XPathNodeType.Comment:
result += $", Value=\"{XmlConvert.EscapeValueForDebuggerDisplay(_nav.Value)}\"";
break;
}
return result;
}
}
}
}
|