e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1931 lines
73 KiB
C#
1931 lines
73 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="XPathNavigator.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">[....]</owner>
|
|
//------------------------------------------------------------------------------
|
|
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Xml.Schema;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.Contracts;
|
|
using System.Security;
|
|
using System.Security.Policy;
|
|
using System.Security.Permissions;
|
|
using System.Text;
|
|
using System.Xml;
|
|
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}")]
|
|
#if CONTRACTS_FULL
|
|
[ContractClass(typeof(XPathNavigatorContract))]
|
|
#endif
|
|
public abstract class XPathNavigator : XPathItem, ICloneable, IXPathNavigable, IXmlNamespaceResolver {
|
|
internal static readonly XPathNavigatorKeyComparer comparer = new XPathNavigatorKeyComparer();
|
|
|
|
//-----------------------------------------------
|
|
// Object
|
|
//-----------------------------------------------
|
|
|
|
public override string ToString() {
|
|
return Value;
|
|
}
|
|
|
|
//-----------------------------------------------
|
|
// XPathItem
|
|
//-----------------------------------------------
|
|
|
|
public override sealed 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;
|
|
if (schemaType == null) {
|
|
schemaType = 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) {
|
|
if (typedValue == null) {
|
|
throw new ArgumentNullException("typedValue");
|
|
}
|
|
switch (NodeType) {
|
|
case XPathNodeType.Element:
|
|
case XPathNodeType.Attribute:
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException(Res.GetString(Res.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;
|
|
if (datatype != null) {
|
|
datatype.ParseValue(value, NameTable, this);
|
|
}
|
|
}
|
|
}
|
|
if (value == null) {
|
|
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;
|
|
if (schemaType == null) {
|
|
schemaType = 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;
|
|
if (schemaType == null) {
|
|
schemaType = 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;
|
|
if (schemaType == null) {
|
|
schemaType = 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;
|
|
if (schemaType == null) {
|
|
schemaType = 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;
|
|
if (schemaType == null) {
|
|
schemaType = 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;
|
|
if (schemaType == null) {
|
|
schemaType = 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) {
|
|
if (nsResolver == null) {
|
|
nsResolver = this;
|
|
}
|
|
IXmlSchemaInfo schemaInfo = SchemaInfo;
|
|
XmlSchemaType schemaType;
|
|
XmlSchemaDatatype datatype;
|
|
if (schemaInfo != null) {
|
|
if (schemaInfo.Validity == XmlSchemaValidity.Valid) {
|
|
schemaType = schemaInfo.MemberType;
|
|
if (schemaType == null) {
|
|
schemaType = 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;
|
|
}
|
|
|
|
// This pragma disables a warning that the return type is not CLS-compliant, but generics are part of CLS in Whidbey.
|
|
#pragma warning disable 3002
|
|
public virtual IDictionary<string,string> GetNamespacesInScope(XmlNamespaceScope scope) {
|
|
#pragma warning restore 3002
|
|
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(Res.GetString(Res.Xpn_BadPosition));
|
|
}
|
|
return CreateReader();
|
|
}
|
|
|
|
public virtual void WriteSubtree(XmlWriter writer) {
|
|
if (null == writer)
|
|
throw new ArgumentNullException("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()) {
|
|
// Restore previous position and return false
|
|
// MoveTo(navSave);
|
|
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()) {
|
|
// Restore previous position and return false
|
|
// MoveTo(navSave);
|
|
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(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 incosistent 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(Res.GetString(Res.XPathDocument_MissingSchemas));
|
|
}
|
|
schemaType = null;
|
|
break;
|
|
case XPathNodeType.Element:
|
|
if (schemas == null) {
|
|
throw new InvalidOperationException(Res.GetString(Res.XPathDocument_MissingSchemas));
|
|
}
|
|
schemaInfo = SchemaInfo;
|
|
if (schemaInfo != null) {
|
|
schemaType = schemaInfo.SchemaType;
|
|
schemaElement = schemaInfo.SchemaElement;
|
|
}
|
|
if (schemaType == null
|
|
&& schemaElement == null) {
|
|
throw new InvalidOperationException(Res.GetString(Res.XPathDocument_NotEnoughSchemaInfo, null));
|
|
}
|
|
break;
|
|
case XPathNodeType.Attribute:
|
|
if (schemas == null) {
|
|
throw new InvalidOperationException(Res.GetString(Res.XPathDocument_MissingSchemas));
|
|
}
|
|
schemaInfo = SchemaInfo;
|
|
if (schemaInfo != null) {
|
|
schemaType = schemaInfo.SchemaType;
|
|
schemaAttribute = schemaInfo.SchemaAttribute;
|
|
}
|
|
if (schemaType == null
|
|
&& schemaAttribute == null) {
|
|
throw new InvalidOperationException(Res.GetString(Res.XPathDocument_NotEnoughSchemaInfo, null));
|
|
}
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException(Res.GetString(Res.XPathDocument_ValidateInvalidNodeType, null));
|
|
|
|
}
|
|
|
|
Debug.Assert( schemaType != null || this.NodeType == XPathNodeType.Root, "schemaType != null || this.NodeType == XPathNodeType.Root" );
|
|
|
|
XmlReader reader = CreateReader();
|
|
|
|
CheckValidityHelper validityTracker = new CheckValidityHelper( validationEventHandler, reader as XPathNavigatorReader );
|
|
validationEventHandler = new ValidationEventHandler( validityTracker.ValidationCallback );
|
|
XmlReader validatingReader = GetValidatingReader( reader, schemas, validationEventHandler, schemaType, schemaElement, schemaAttribute );
|
|
|
|
while( validatingReader.Read() )
|
|
;
|
|
|
|
return validityTracker.IsValid;
|
|
}
|
|
|
|
private 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, "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 );
|
|
}
|
|
|
|
class CheckValidityHelper {
|
|
bool isValid;
|
|
ValidationEventHandler nextEventHandler;
|
|
XPathNavigatorReader reader;
|
|
|
|
internal CheckValidityHelper( ValidationEventHandler nextEventHandler, XPathNavigatorReader reader ) {
|
|
this.isValid = true;
|
|
this.nextEventHandler = nextEventHandler;
|
|
this.reader = reader;
|
|
}
|
|
|
|
internal void ValidationCallback( object sender, ValidationEventArgs args ) {
|
|
Debug.Assert( args != null );
|
|
if ( args.Severity == XmlSeverityType.Error )
|
|
this.isValid = false;
|
|
XmlSchemaValidationException exception = args.Exception as XmlSchemaValidationException;
|
|
if (exception != null && reader != null)
|
|
exception.SetSourceObject(reader.UnderlyingObject);
|
|
|
|
if (this.nextEventHandler != null) {
|
|
this.nextEventHandler( sender, args );
|
|
}
|
|
else if (exception != null && args.Severity == XmlSeverityType.Error) {
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
internal bool IsValid {
|
|
get { return this.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) {
|
|
//
|
|
XPathNodeIterator iter = this.Select(expression);
|
|
if (iter.MoveNext()) {
|
|
return iter.Current;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public virtual XPathNodeIterator Select(string xpath) {
|
|
Contract.Ensures(Contract.Result<XPathNodeIterator>() != null);
|
|
|
|
return this.Select(XPathExpression.Compile(xpath));
|
|
}
|
|
|
|
public virtual XPathNodeIterator Select(string xpath, IXmlNamespaceResolver resolver) {
|
|
Contract.Ensures(Contract.Result<XPathNodeIterator>() != null);
|
|
|
|
return this.Select(XPathExpression.Compile(xpath, resolver));
|
|
}
|
|
|
|
public virtual XPathNodeIterator Select(XPathExpression expr) {
|
|
Contract.Ensures(Contract.Result<XPathNodeIterator>() != null);
|
|
|
|
XPathNodeIterator result = Evaluate(expr) as XPathNodeIterator;
|
|
if (result == null) {
|
|
throw XPathException.Create(Res.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(Res.Xp_BadQueryObject);
|
|
}
|
|
Query query = Query.Clone(cexpr.QueryTree);
|
|
query.Reset();
|
|
|
|
if (context == null) {
|
|
context = new XPathSingletonIterator(this.Clone(), /*moved:*/true);
|
|
}
|
|
|
|
object result = query.Evaluate(context);
|
|
|
|
if (result is XPathNodeIterator) {
|
|
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(Res.Xp_BadQueryObject);
|
|
|
|
// We should clone query because some Query.MatchNode() alter expression state and this may brake
|
|
// SelectionIterators that are runing using this Query
|
|
// Excample of MatchNode() that alret the state is FilterQuery.MatchNode()
|
|
Query query = Query.Clone(cexpr.QueryTree);
|
|
|
|
try {
|
|
return query.MatchNode(this) != null;
|
|
}
|
|
catch(XPathException) {
|
|
throw XPathException.Create(Res.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) {
|
|
XmlReader reader = CreateContextReader(newNode, false);
|
|
ReplaceSelf(reader);
|
|
}
|
|
|
|
public virtual void ReplaceSelf(XmlReader newNode) {
|
|
if (newNode == null) {
|
|
throw new ArgumentNullException("newNode");
|
|
}
|
|
XPathNodeType type = NodeType;
|
|
if (type == XPathNodeType.Root
|
|
|| type == XPathNodeType.Attribute
|
|
|| type == XPathNodeType.Namespace) {
|
|
throw new InvalidOperationException(Res.GetString(Res.Xpn_BadPosition));
|
|
}
|
|
XmlWriter writer = ReplaceRange(this);
|
|
BuildSubtree(newNode, writer);
|
|
writer.Close();
|
|
}
|
|
|
|
public virtual void ReplaceSelf(XPathNavigator newNode) {
|
|
if (newNode == null) {
|
|
throw new ArgumentNullException("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 string.Concat(Name, "=\"", Value, "\"");
|
|
}
|
|
else if (NodeType == XPathNodeType.Namespace) {
|
|
if (LocalName.Length == 0)
|
|
return string.Concat("xmlns=\"", Value, "\"");
|
|
else
|
|
return string.Concat("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 {
|
|
if (value == null) {
|
|
throw new ArgumentNullException("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(Res.GetString(Res.Xpn_BadPosition));
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void AppendChild(string newChild) {
|
|
XmlReader reader = CreateContextReader(newChild, true);
|
|
AppendChild(reader);
|
|
}
|
|
|
|
public virtual void AppendChild(XmlReader newChild) {
|
|
if (newChild == null) {
|
|
throw new ArgumentNullException("newChild");
|
|
}
|
|
XmlWriter writer = AppendChild();
|
|
BuildSubtree(newChild, writer);
|
|
writer.Close();
|
|
}
|
|
|
|
public virtual void AppendChild(XPathNavigator newChild) {
|
|
if (newChild == null) {
|
|
throw new ArgumentNullException("newChild");
|
|
}
|
|
if (!IsValidChildType(newChild.NodeType)) {
|
|
throw new InvalidOperationException(Res.GetString(Res.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) {
|
|
if (newChild == null) {
|
|
throw new ArgumentNullException("newChild");
|
|
}
|
|
XmlWriter writer = PrependChild();
|
|
BuildSubtree(newChild, writer);
|
|
writer.Close();
|
|
}
|
|
|
|
public virtual void PrependChild(XPathNavigator newChild) {
|
|
if (newChild == null) {
|
|
throw new ArgumentNullException("newChild");
|
|
}
|
|
if (!IsValidChildType(newChild.NodeType)) {
|
|
throw new InvalidOperationException(Res.GetString(Res.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) {
|
|
if (newSibling == null) {
|
|
throw new ArgumentNullException("newSibling");
|
|
}
|
|
XmlWriter writer = InsertBefore();
|
|
BuildSubtree(newSibling, writer);
|
|
writer.Close();
|
|
}
|
|
|
|
public virtual void InsertBefore(XPathNavigator newSibling) {
|
|
if (newSibling == null) {
|
|
throw new ArgumentNullException("newSibling");
|
|
}
|
|
if (!IsValidSiblingType(newSibling.NodeType)) {
|
|
throw new InvalidOperationException(Res.GetString(Res.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) {
|
|
if (newSibling == null) {
|
|
throw new ArgumentNullException("newSibling");
|
|
}
|
|
XmlWriter writer = InsertAfter();
|
|
BuildSubtree(newSibling, writer);
|
|
writer.Close();
|
|
}
|
|
|
|
public virtual void InsertAfter(XPathNavigator newSibling) {
|
|
if (newSibling == null) {
|
|
throw new ArgumentNullException("newSibling");
|
|
}
|
|
if (!IsValidSiblingType(newSibling.NodeType)) {
|
|
throw new InvalidOperationException(Res.GetString(Res.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();
|
|
|
|
localName = (localName != null) ? NameTable.Get(localName) : null;
|
|
while (MoveToPrevious()) {
|
|
if (NodeType == XPathNodeType.Element && (object) localName == (object) 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;
|
|
}
|
|
}
|
|
|
|
internal static readonly char[] NodeTypeLetter = new char[] {
|
|
'R', // Root
|
|
'E', // Element
|
|
'A', // Attribute
|
|
'N', // Namespace
|
|
'T', // Text
|
|
'S', // SignificantWhitespace
|
|
'W', // Whitespace
|
|
'P', // ProcessingInstruction
|
|
'C', // Comment
|
|
'X', // All
|
|
};
|
|
|
|
internal static readonly char[] UniqueIdTbl = new char[] {
|
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
|
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
|
|
'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4',
|
|
'5', '6'
|
|
};
|
|
|
|
// 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 seperated 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[idx]);
|
|
} else {
|
|
sb.Append('0');
|
|
do {
|
|
sb.Append(UniqueIdTbl[idx & 0x1f]);
|
|
idx >>= 5;
|
|
} while (idx != 0);
|
|
sb.Append('0');
|
|
}
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
|
|
private static XPathExpression 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 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 (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 XmlReader CreateReader() {
|
|
return XPathNavigatorReader.Create(this);
|
|
}
|
|
|
|
private XmlReader CreateContextReader(string xml, bool fromCurrentNode) {
|
|
if (xml == null) {
|
|
throw new ArgumentNullException("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));
|
|
}
|
|
//
|
|
XmlParserContext context = new XmlParserContext(NameTable, mgr, null, XmlSpace.Default);
|
|
XmlTextReader reader = new XmlTextReader(xml, XmlNodeType.Element, context);
|
|
//
|
|
reader.WhitespaceHandling = WhitespaceHandling.Significant;
|
|
return reader;
|
|
}
|
|
|
|
internal 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(Res.GetString(Res.Xml_InvalidOperation), "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 {
|
|
XPathNavigator nav;
|
|
public DebuggerDisplayProxy(XPathNavigator nav) {
|
|
this.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;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if CONTRACTS_FULL
|
|
[ContractClassFor(typeof(XPathNavigator))]
|
|
internal abstract class XPathNavigatorContract : XPathNavigator
|
|
{
|
|
public override XPathNavigator Clone()
|
|
{
|
|
Contract.Ensures(Contract.Result<XPathNavigator>() != null);
|
|
return default(XPathNavigator);
|
|
}
|
|
|
|
public override XmlNameTable NameTable {
|
|
get {
|
|
Contract.Ensures(Contract.Result<XmlNameTable>() != null);
|
|
return default(XmlNameTable);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|