|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.ServiceModel.Dispatcher
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;
using System.Xml;
using System.Xml.XPath;
// Anything marked
class SeekableMessageNavigator : SeekableXPathNavigator, INodeCounter
{
// Use a single template for constructing the common elements.
static Node[] BlankDom;
// Constants
const string XmlP = "xml";
const string XmlnsP = "xmlns";
const string SoapP = "s";
const string EnvelopeTag = MessageStrings.Envelope;
const string HeaderTag = MessageStrings.Header;
const string BodyTag = MessageStrings.Body;
// These constants are constructed from each other so new ones can be added without too much hastle.
const int NullIndex = 0;
const int RootIndex = NullIndex + 1;
const int EnvelopeIndex = RootIndex + 1;
const int SoapNSIndex = EnvelopeIndex + 1;
const int XmlNSIndex = SoapNSIndex + 1;
const int HeaderIndex = XmlNSIndex + 1;
const int FirstHeaderIndex = HeaderIndex + 1;
const int StartSize = 50; // Blank nodes to start with
const int GrowFactor = 2; // Multiplier to grow the node array by
const int StretchMax = 1000; // Use the multiplier until the node array gets this big
const int GrowInc = 1000; // Grow by this many nodes when StretchMax is exceeded
// Shared as dom
Message message;
MessageHeaders headers;
XmlSpace space;
StringBuilder stringBuilder;
Node[] nodes;
int bodyIndex;
int nextFreeIndex;
NameTable nameTable;
bool includeBody;
bool atomize;
// Shared as counter
int nodeCount;
int nodeCountMax;
// Instance
SeekableMessageNavigator dom;
SeekableMessageNavigator counter;
Stack<string> nsStack;
int location;
int specialParent; // Always null if we're not at a namespace node
static SeekableMessageNavigator()
{
BlankDom = new Node[HeaderIndex + 1];
// Root
BlankDom[RootIndex].type = XPathNodeType.Root;
BlankDom[RootIndex].firstChild = EnvelopeIndex;
BlankDom[RootIndex].prefix = string.Empty;
BlankDom[RootIndex].name = string.Empty;
BlankDom[RootIndex].val = string.Empty;
// Envelope
BlankDom[EnvelopeIndex].type = XPathNodeType.Element;
BlankDom[EnvelopeIndex].prefix = SoapP;
//BlankDom[EnvelopeIndex].ns = soapNS;
BlankDom[EnvelopeIndex].name = EnvelopeTag;
BlankDom[EnvelopeIndex].parent = RootIndex;
BlankDom[EnvelopeIndex].firstChild = HeaderIndex;
BlankDom[EnvelopeIndex].firstNamespace = SoapNSIndex;
// SOAP Namespace
BlankDom[SoapNSIndex].type = XPathNodeType.Namespace;
BlankDom[SoapNSIndex].name = SoapP;
//BlankDom[SoapNSIndex].val = soapNS;
BlankDom[SoapNSIndex].nextSibling = XmlNSIndex;
BlankDom[SoapNSIndex].parent = EnvelopeIndex;
// xmlns:xml Namespace
BlankDom[XmlNSIndex].type = XPathNodeType.Namespace;
BlankDom[XmlNSIndex].name = "xml";
BlankDom[XmlNSIndex].val = XmlUtil.XmlNs;
BlankDom[XmlNSIndex].prevSibling = SoapNSIndex;
BlankDom[XmlNSIndex].parent = RootIndex; // This one needs to be connected a little different
// Header
BlankDom[HeaderIndex].type = XPathNodeType.Element;
BlankDom[HeaderIndex].prefix = SoapP;
//BlankDom[HeaderIndex].ns = soapNS;
BlankDom[HeaderIndex].name = HeaderTag;
BlankDom[HeaderIndex].parent = EnvelopeIndex;
//BlankDom[HeaderIndex].nextSibling = bodyIndex;
//BlankDom[HeaderIndex].firstChild = this.bodyIndex != FirstHeaderIndex ? FirstHeaderIndex : NullIndex;
BlankDom[HeaderIndex].firstNamespace = SoapNSIndex;
}
internal SeekableMessageNavigator(SeekableMessageNavigator nav)
{
Fx.Assert(nav != null, "Navigator may not be null");
this.counter = nav.counter;
this.dom = nav.dom;
this.location = nav.location;
this.specialParent = nav.specialParent;
if (this.specialParent != NullIndex)
{
this.nsStack = nav.CloneNSStack();
}
}
internal SeekableMessageNavigator(Message msg, int countMax, XmlSpace space, bool includeBody, bool atomize)
{
Init(msg, countMax, space, includeBody, atomize);
}
// The base uri of the element
// This is usually associated with the URI of the original data's location
// WS, Microsoft, look into what readers from messages surface. If it's always null, we can save
// some memory
public override string BaseURI
{
get
{
LoadOnDemand();
string s = this.dom.nodes[this.location].baseUri;
return s == null ? string.Empty : s;
}
}
// Get/Set an opaque position value.
// This property will save and restore a navigator's position without cloning
// The two integers needed to uniquely identify this navigators location within the DOM are encoded in
// a long. The high 32 bits is the parent index, and the low 32 bits is the current node index.
public override long CurrentPosition
{
get
{
long p = this.specialParent;
p <<= 32;
p += this.location;
return p;
}
set
{
Position p = this.dom.DecodePosition(value);
// If we are at a namespace, collect the namespaces that have already been seen
if (p.parent != NullIndex)
{
if (this.nsStack == null)
{
this.nsStack = new Stack<string>();
}
else
{
this.nsStack.Clear();
}
int n = this.dom.nodes[p.parent].firstNamespace;
while (n != p.elem)
{
// PERF, Microsoft, we might be able to get rid of this check by tweaking the position
// validator
if (n == NullIndex)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryProcessingException(QueryProcessingError.InvalidNavigatorPosition, SR.GetString(SR.SeekableMessageNavInvalidPosition)));
}
this.nsStack.Push(this.dom.nodes[n].name);
n = this.dom.nodes[n].nextSibling;
}
}
this.location = p.elem;
this.specialParent = p.parent;
}
}
// Returns whether the current node has any defined attributes.
public override bool HasAttributes
{
get
{
LoadOnDemand();
return this.dom.nodes[this.location].firstAttribute != NullIndex;
}
}
// Returns whether the current node has any children.
public override bool HasChildren
{
get
{
LoadOnDemand();
return this.dom.nodes[this.location].firstChild != NullIndex;
}
}
// Returns whether the current node was defined as an empty element.
// This is not relevant to xpath, so we're saving memory by not recording the true value.
// If a reader does ever get wrapped around it, we'll have to fix this.
public override bool IsEmptyElement
{
get
{
// <a/> is an empty element. <a></a> is not.
return this.dom.nodes[this.location].empty;
}
}
// Return the local name of the current node.
public override string LocalName
{
get
{
string s = this.dom.nodes[this.location].name;
return s == null ? string.Empty : s;
}
}
// Returns the message that was used to construct this navigator's DOM
internal Message Message
{
get
{
return this.dom.message;
}
}
// Returns the qualified name of the current node
public override string Name
{
get
{
return GetName(this.location);
}
}
// Returns the namespace URI of the current node
public override string NamespaceURI
{
get
{
string s = this.dom.nodes[this.location].ns;
return s == null ? string.Empty : s;
}
}
public override XmlNameTable NameTable
{
get
{
// Delay atomizing for the cases where we typically don't
// need to do it (e.g. creating a navigator over a Message).
if (!this.dom.atomize)
{
this.dom.Atomize();
}
return this.dom.nameTable;
}
}
// Returns the type of the current node
public override XPathNodeType NodeType
{
get
{
return this.dom.nodes[this.location].type;
}
}
// Returns the prefix of the current node
public override string Prefix
{
get
{
LoadOnDemand();
string s = this.dom.nodes[this.location].prefix;
return s == null ? string.Empty : s;
}
}
// Returns the text value of the current node
public override string Value
{
get
{
return this.dom.GetValue(this.location);
}
}
// Returns the value of xml:lang that is currently in scope.
public override string XmlLang
{
get
{
LoadOnDemand();
string s = this.dom.nodes[this.location].xmlLang;
return s == null ? string.Empty : s;
}
}
#if NO
// The default value of xml:space for the DOM
internal XmlSpace DefaultXmlSpace
{
get
{
return this.dom.space;
}
}
internal SeekableMessageNavigator DomNavigator
{
get
{
return this.dom;
}
}
#endif
// Create a clone of the current navigator that shares its DOM and node counter
public override XPathNavigator Clone()
{
return new SeekableMessageNavigator(this);
}
// Compare the current position to that of another navigator.
public override XmlNodeOrder ComparePosition(XPathNavigator nav)
{
if (nav == null)
{
return XmlNodeOrder.Unknown;
}
// We can only compare the positions of navigators of the same type.
SeekableMessageNavigator smnav = nav as SeekableMessageNavigator;
if (smnav != null)
{
return ComparePosition(smnav);
}
return XmlNodeOrder.Unknown;
}
// Compare the current position to that of another navigator.
internal XmlNodeOrder ComparePosition(SeekableMessageNavigator nav)
{
if (nav == null)
{
return XmlNodeOrder.Unknown;
}
if (this.dom != nav.dom)
{
return XmlNodeOrder.Unknown;
}
return this.dom.ComparePosition(this.specialParent, this.location, nav.specialParent, nav.location);
}
// Compare two position values that are valid for this navigator's DOM
public override XmlNodeOrder ComparePosition(long pos1, long pos2)
{
Position p1 = this.dom.DecodePosition(pos1);
Position p2 = this.dom.DecodePosition(pos2);
return this.dom.ComparePosition(p1.parent, p1.elem, p2.parent, p2.elem);
}
// Compile: We don't need to override this. Seems like it could have even been static.
// Evaluate an xpath expression against the current navigator
public override object Evaluate(string xpath)
{
// We can only evaluate atomized navigators
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "Evaluate")));
}
return base.Evaluate(xpath);
}
// Evaluate an xpath expression against the current navigator
public override object Evaluate(XPathExpression expr)
{
// We can only evaluate atomized navigators
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "Evaluate")));
}
return base.Evaluate(expr);
}
// Evaluate an xpath expression against the current navigator
public override object Evaluate(XPathExpression expr, XPathNodeIterator context)
{
// We can only evaluate atomized navigators
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "Evaluate")));
}
return base.Evaluate(expr, context);
}
// Get the value of an attribute with a particular name and namespace
public override string GetAttribute(string name, string ns)
{
if (name == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name");
}
if (ns == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ns");
}
// We must be at an element node
if (this.NodeType != XPathNodeType.Element)
{
return string.Empty;
}
int a;
string ret = string.Empty;
Increase();
LoadOnDemand();
// Walk through the attributes looking for the one we want.
a = this.dom.nodes[this.location].firstAttribute;
while (a != NullIndex)
{
if (String.CompareOrdinal(this.dom.nodes[a].name, name) == 0 && String.CompareOrdinal(this.dom.nodes[a].ns, ns) == 0)
{
ret = this.dom.nodes[a].val;
break;
}
Increase();
a = this.dom.nodes[a].nextSibling;
}
return ret;
}
// Returns the local name of the node defined by the given navigator position
public override string GetLocalName(long pos)
{
string s = this.dom.nodes[this.dom.DecodePosition(pos).elem].name;
return s == null ? string.Empty : s;
}
// Returns the qualified name of the node defined by the given navigator position
public override string GetName(long pos)
{
return GetName(this.dom.DecodePosition(pos).elem);
}
// Returns the namespace uri of the node defined by the given navigator position
public override string GetNamespace(long pos)
{
string s = this.dom.nodes[this.dom.DecodePosition(pos).elem].ns;
return s == null ? string.Empty : s;
}
// Returns the namespace uri that is bound to the given prefix in the current scope, or the empty
// string if the prefix is not defined
public override string GetNamespace(string name)
{
if (name == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name");
}
if (this.NodeType != XPathNodeType.Element)
{
return string.Empty;
}
int ns;
Increase();
LoadOnDemand();
ns = this.dom.nodes[this.location].firstNamespace;
string ret = string.Empty;
while (ns != NullIndex)
{
Increase();
if (String.CompareOrdinal(this.dom.nodes[ns].name, name) == 0)
{
ret = this.dom.nodes[ns].val;
break;
}
ns = this.dom.nodes[ns].nextSibling;
}
return ret;
}
// Returns the node type of the node defined by the given navigator position
public override XPathNodeType GetNodeType(long pos)
{
return this.dom.nodes[this.dom.DecodePosition(pos).elem].type;
}
// Returns the string value of the node defined by the given navigator position
public override string GetValue(long pos)
{
string s = this.dom.GetValue(this.dom.DecodePosition(pos).elem);
return s == null ? string.Empty : s;
}
// Test whether the given navigator is positioned on a descendant of this navigator
public override bool IsDescendant(XPathNavigator nav)
{
if (nav == null)
{
return false;
}
// Navigator positions can only be compared if they are of the same type
SeekableMessageNavigator smnav = nav as SeekableMessageNavigator;
if (smnav != null)
{
return IsDescendant(smnav);
}
return false;
}
// Test whether the given navigator is positioned on a descendant of this navigator
internal bool IsDescendant(SeekableMessageNavigator nav)
{
if (nav == null)
{
return false;
}
if (this.dom != nav.dom)
{
return false;
}
// Namespaces and attributes are not considered descendants
XPathNodeType type = this.dom.nodes[nav.location].type;
if (type == XPathNodeType.Namespace || type == XPathNodeType.Attribute)
{
return false;
}
// Namespaces and attributes are not parents
type = this.dom.nodes[this.location].type;
if (type == XPathNodeType.Namespace || type == XPathNodeType.Attribute)
{
return false;
}
// Climb up the tree looking for the current navigator's position
int n = nav.location;
while (n != NullIndex)
{
Increase();
n = this.dom.nodes[n].parent;
if (n == this.location)
{
return true;
}
}
return false;
}
// Tests whether the given navigator is positioned on the same node as this navigator
public override bool IsSamePosition(XPathNavigator nav)
{
if (nav == null)
{
return false;
}
// Navigator positions can only be compared if they are of the same type
SeekableMessageNavigator smnav = nav as SeekableMessageNavigator;
if (smnav != null)
{
return IsSamePosition(smnav);
}
return false;
}
// Tests whether the given navigator is positioned on the same node as this navigator
internal bool IsSamePosition(SeekableMessageNavigator nav)
{
if (nav == null)
{
return false;
}
return this.dom == nav.dom && this.location == nav.location && this.specialParent == nav.specialParent;
}
// Test if the given xpath matches this navigator
public override bool Matches(string xpath)
{
// We can only match atomized navigators
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "Matches")));
}
return base.Matches(xpath);
}
// Test if the given xpath matches this navigator
public override bool Matches(XPathExpression expr)
{
// We can only match atomized navigators
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "Matches")));
}
return base.Matches(expr);
}
// Move this navigator to the same position as the given one
public override bool MoveTo(XPathNavigator nav)
{
if (nav == null)
{
return false;
}
// Can only move to the position of a navigator of the same type
SeekableMessageNavigator smnav = nav as SeekableMessageNavigator;
if (smnav != null)
{
return MoveTo(smnav);
}
return false;
}
// Move this navigator to the same position as the given one
internal bool MoveTo(SeekableMessageNavigator nav)
{
if (nav == null)
{
return false;
}
this.dom = nav.dom;
this.counter = nav.counter;
this.location = nav.location;
this.specialParent = nav.specialParent;
if (this.specialParent != NullIndex)
{
this.nsStack = nav.CloneNSStack();
}
return true;
}
// Move this navigator to an attribute with the given name and namespace
public override bool MoveToAttribute(string localName, string namespaceURI)
{
if (localName == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("localName");
}
if (namespaceURI == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("namespaceURI");
}
LoadOnDemand();
// We must be positioned on an element
if (this.dom.nodes[this.location].type != XPathNodeType.Element)
{
return false;
}
// Move through the attributes looking for one that matches
Increase();
int n = this.dom.nodes[this.location].firstAttribute;
while (n != NullIndex)
{
if (String.CompareOrdinal(this.dom.nodes[n].name, localName) == 0 &&
String.CompareOrdinal(this.dom.nodes[n].ns, namespaceURI) == 0)
{
// If we find it, we're done
this.location = n;
return true;
}
else
{
// Try the next one
Increase();
n = this.dom.nodes[n].nextSibling;
}
}
// Didn't find it
return false;
}
// Move the navigator to the first sibling of the current node
public override bool MoveToFirst()
{
// Not valid for attributes and namespaces
XPathNodeType t = this.dom.nodes[this.location].type;
if (t != XPathNodeType.Attribute && t != XPathNodeType.Namespace)
{
// Get the parent of the current node
Increase();
int p = this.dom.nodes[this.location].parent;
// If the parent was null, we're at the root (which has no siblings)
if (p != NullIndex)
{
// Move to the first child of our node's parent (ie, first sibling)
// Cool, huh? :-)
Increase();
this.location = this.dom.nodes[p].firstChild;
}
// The move is still considered successful if we were already there.
return true;
}
return false;
}
// Move the navigator to the first attribute of it's current element
public override bool MoveToFirstAttribute()
{
// Only valid for element nodes
if (this.dom.nodes[this.location].type != XPathNodeType.Element)
{
return false;
}
// Get the first attribute
LoadOnDemand();
int n = this.dom.nodes[this.location].firstAttribute;
// The move was only successful if there is at least one attribute
if (n != NullIndex)
{
Increase();
this.location = n;
return true;
}
return false;
}
// Move the navigator to the first child of the current node.
public override bool MoveToFirstChild()
{
// PERF, Microsoft, do we need this check? The null check may be enough
// Only valid for the root or an element node
if (this.location == RootIndex || this.dom.nodes[this.location].type == XPathNodeType.Element)
{
// Get the first child
LoadOnDemand();
int n = this.dom.nodes[this.location].firstChild;
// Only successful if there was at least one child
if (n != NullIndex)
{
Increase();
this.location = n;
return true;
}
}
return false;
}
// Move the navigator to the first namespace that fits the scope
public override bool MoveToFirstNamespace(XPathNamespaceScope scope)
{
// Only valid on element nodes
if (this.dom.nodes[this.location].type != XPathNodeType.Element)
{
return false;
}
// Start with a clean namespace stack
if (this.nsStack == null)
{
this.nsStack = new Stack<string>();
}
else
{
this.nsStack.Clear();
}
// Find the namespace
LoadOnDemand();
int n = FindNamespace(this.location, this.dom.nodes[this.location].firstNamespace, scope);
// If one was found, move there
if (n != NullIndex)
{
// Record the parent
this.specialParent = this.location;
Increase();
this.location = n;
return true;
}
return false;
}
// Move the navigator to the node with the given unique ID
public override bool MoveToId(string id)
{
// SOAP prohibits the inclusion of a DTD, so unique IDs cannot be defined.
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryProcessingException(QueryProcessingError.NotSupported, SR.GetString(SR.SeekableMessageNavIDNotSupported)));
}
// Move the navigator to the namespace manager the given prefix
public override bool MoveToNamespace(string name)
{
if (name == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name");
}
// Only valid from an element node
if (this.dom.nodes[this.location].type != XPathNodeType.Element)
{
return false;
}
// Start with a clean namespace stack
if (this.nsStack == null)
{
this.nsStack = new Stack<string>();
}
else
{
this.nsStack.Clear();
}
Increase();
LoadOnDemand();
// Look through the namespaces for the specified prefix
int n = this.dom.nodes[this.location].firstNamespace;
string nodeName;
string nodeVal;
int nsCount = 0;
while (n != NullIndex)
{
// Skip any already defined prefixes
nodeName = this.dom.nodes[n].name;
if (!this.nsStack.Contains(nodeName))
{
this.nsStack.Push(nodeName);
++nsCount;
nodeVal = this.dom.nodes[n].val;
if ((nodeName.Length > 0 || nodeVal.Length > 0) && String.CompareOrdinal(nodeName, name) == 0)
{
this.specialParent = this.location;
this.location = n;
return true;
}
}
// Try the next one
Increase();
n = this.dom.nodes[n].nextSibling;
}
// PERF, Microsoft, can we just clear?
// We didn't find it, so restore the namespace stack
for (int i = 0; i < nsCount; ++i)
{
this.nsStack.Pop();
}
return false;
}
// Move the navigator to the next sibling
public override bool MoveToNext()
{
// Not valid for attribute/namespace nodes. They have different 'next' functions.
XPathNodeType type = this.dom.nodes[this.location].type;
if (type == XPathNodeType.Attribute || type == XPathNodeType.Namespace)
{
return false;
}
// Successful if there is a next sibling.
int n = this.dom.nodes[this.location].nextSibling;
if (n != NullIndex)
{
Increase();
this.location = n;
return true;
}
return false;
}
// Move the navigator to the next attribute node
public override bool MoveToNextAttribute()
{
// Only valid on an attribute node
if (this.dom.nodes[this.location].type != XPathNodeType.Attribute)
{
return false;
}
// Successful if there is a next sibling
int n = this.dom.nodes[this.location].nextSibling;
if (n != NullIndex)
{
Increase();
this.location = n;
return true;
}
return false;
}
// Move the navigator to the next namespace node in the specified scope
public override bool MoveToNextNamespace(XPathNamespaceScope scope)
{
// Only valid from a namespace node
if (this.dom.nodes[this.location].type != XPathNodeType.Namespace)
{
return false;
}
// Successful if the a namespace was found in the given scope
int n = FindNamespace(this.specialParent, this.dom.nodes[this.location].nextSibling, scope);
if (n != NullIndex)
{
// Move to the namespace
Increase();
this.location = n;
return true;
}
return false;
}
// Move the navigator to the parent of the current node
public override bool MoveToParent()
{
// The root node doesn't have a parent
if (this.location == RootIndex)
{
return false;
}
// Use the special parent if it's not null
Increase();
if (this.specialParent != NullIndex)
{
Increase();
this.location = this.specialParent;
this.specialParent = NullIndex;
}
else
{
this.location = this.dom.nodes[this.location].parent;
}
return true;
}
// Move the navigator to the previous sibling of the current node
public override bool MoveToPrevious()
{
// Not valid for attribute/namespace nodes
int n = NullIndex;
XPathNodeType t = this.dom.nodes[this.location].type;
if (t != XPathNodeType.Attribute && t != XPathNodeType.Namespace)
{
n = this.dom.nodes[this.location].prevSibling;
}
// Successful if there was a previous sibling
if (n != NullIndex)
{
Increase();
this.location = n;
return true;
}
return false;
}
// Move the navigator to the root node
public override void MoveToRoot()
{
Increase();
this.location = RootIndex;
this.specialParent = NullIndex;
}
// Select nodes from the navigator using the given xpath
public override XPathNodeIterator Select(string xpath)
{
// Cannot select from an unatomized navigator
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "Select")));
}
return base.Select(xpath);
}
// Select nodes from the navigator using the given xpath
public override XPathNodeIterator Select(XPathExpression xpath)
{
// Cannot select from an unatomized navigator
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "Select")));
}
return base.Select(xpath);
}
// Select ancestor nodes from the navigator of a given type
public override XPathNodeIterator SelectAncestors(XPathNodeType type, bool matchSelf)
{
// Cannot select from an unatomized navigator
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "SelectAncestors")));
}
return base.SelectAncestors(type, matchSelf);
}
// Select ancestor nodes from the navigator with a particular name and namespace
public override XPathNodeIterator SelectAncestors(string name, string namespaceURI, bool matchSelf)
{
// Cannot select from an unatomized navigator
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "SelectAncestors")));
}
return base.SelectAncestors(name, namespaceURI, matchSelf);
}
// Select child nodes of a certain type from the navigator
public override XPathNodeIterator SelectChildren(XPathNodeType type)
{
// Cannot select from an unatomized navigator
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "SelectChildren")));
}
return base.SelectChildren(type);
}
// Select child nodes from the navigator with a certain name and namespace
public override XPathNodeIterator SelectChildren(string name, string namespaceURI)
{
// Cannot select from an unatomized navigator
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "SelectChildren")));
}
return base.SelectChildren(name, namespaceURI);
}
// Select descendant nodes of a certain type from the navigator
public override XPathNodeIterator SelectDescendants(XPathNodeType type, bool matchSelf)
{
// Cannot select from an unatomized navigator
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "SelectDescendants")));
}
return base.SelectDescendants(type, matchSelf);
}
// Selectg descendant nodes from the navigator with a certain name and namespace
public override XPathNodeIterator SelectDescendants(string name, string namespaceURI, bool matchSelf)
{
// Cannot select from an unatomized navigator
if (!this.dom.atomize)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.NotAtomized, SR.GetString(SR.SeekableMessageNavNonAtomized, "SelectDescendants")));
}
return base.SelectDescendants(name, namespaceURI, matchSelf);
}
// ToString: Don't need to override this
// Atomize the required strings if atomization was not enabled for this DOM
// Calling this function permanently enables atomization for the DOM
internal void Atomize()
{
if (!this.dom.atomize)
{
this.dom.atomize = true;
this.dom.nameTable = new NameTable();
this.dom.nameTable.Add(string.Empty);
this.dom.Atomize(RootIndex, this.nextFreeIndex);
}
}
// Set a new count for this navigator
// Set it to use itself for the counter so all clones of it will share the new counter
internal void ForkNodeCount(int count)
{
Fx.Assert(count > 0, "Maximum node count must be greater than zero");
this.nodeCount = count;
this.nodeCountMax = count;
this.counter = this;
}
// THREAD: Synchronize this function if multiple threads can try to re-initialize an instance at the same time. Also, if you reinitialize the navigator that holds information referenced by clones, the clones will be affected as well.
internal void Init(Message msg, int countMax, XmlSpace space, bool includeBody, bool atomize)
{
Fx.Assert(countMax > 0, "Maximum node count must be greater than zero");
this.counter = this;
this.nodeCount = countMax;
this.nodeCountMax = countMax;
Fx.Assert(msg != null, "Message may not be null");
this.dom = this;
this.location = RootIndex;
this.specialParent = NullIndex;
this.includeBody = includeBody;
this.message = msg;
this.headers = msg.Headers;
this.space = space;
this.atomize = false; // Will get fixed at the end of this function
int minSize = msg.Headers.Count + FirstHeaderIndex + 1;
if (this.nodes == null || this.nodes.Length < minSize)
{
this.nodes = new Node[minSize + StartSize];
}
else
{
Array.Clear(this.nodes, 1, this.nextFreeIndex - 1);
}
this.bodyIndex = minSize - 1;
this.nextFreeIndex = minSize;
// Use the static blank DOM to create the first few nodes
Array.Copy(BlankDom, this.nodes, HeaderIndex + 1);
string soapNS = msg.Version.Envelope.Namespace;
this.nodes[EnvelopeIndex].ns = soapNS;
this.nodes[SoapNSIndex].val = soapNS;
this.nodes[HeaderIndex].ns = soapNS;
this.nodes[HeaderIndex].nextSibling = bodyIndex;
this.nodes[HeaderIndex].firstChild = this.bodyIndex != FirstHeaderIndex ? FirstHeaderIndex : NullIndex;
// Headers
if (msg.Headers.Count > 0)
{
for (int i = FirstHeaderIndex, h = 0; h < msg.Headers.Count; ++i, ++h)
{
this.nodes[i].type = XPathNodeType.Element;
this.nodes[i].parent = HeaderIndex;
this.nodes[i].nextSibling = i + 1;
this.nodes[i].prevSibling = i - 1;
// Extract the header block stub data
MessageHeaderInfo header = msg.Headers[h];
this.nodes[i].ns = header.Namespace;
this.nodes[i].name = header.Name;
this.nodes[i].firstChild = -1;
}
this.nodes[FirstHeaderIndex].prevSibling = NullIndex;
this.nodes[this.bodyIndex - 1].nextSibling = NullIndex;
}
// Body
this.nodes[bodyIndex].type = XPathNodeType.Element;
this.nodes[bodyIndex].prefix = SoapP;
this.nodes[bodyIndex].ns = soapNS;
this.nodes[bodyIndex].name = BodyTag;
this.nodes[bodyIndex].parent = EnvelopeIndex;
this.nodes[bodyIndex].prevSibling = HeaderIndex;
this.nodes[bodyIndex].firstNamespace = SoapNSIndex;
this.nodes[bodyIndex].firstChild = -1; // Need to load
// Atomize
if (atomize)
{
Atomize();
}
}
// Add an attribute node
// Must be called on the DOM object
void AddAttribute(int node, int attr)
{
// Since order just needs to be consistant, we can just push it on the front of the list
this.nodes[attr].parent = node;
this.nodes[attr].nextSibling = this.nodes[node].firstAttribute;
this.nodes[node].firstAttribute = attr;
}
// Append a node to another's set of children
// Must be called on the DOM object
void AddChild(int parent, int child)
{
// What we do depends on whether there are alredy children
if (this.nodes[parent].firstChild == NullIndex)
{
// Make the node the only child
this.nodes[parent].firstChild = child;
this.nodes[child].parent = parent;
}
else
{
// Make the new child the last sibling of the first child
AddSibling(this.nodes[parent].firstChild, child);
}
}
// Add a namespace node
// Must be called on the DOM object
void AddNamespace(int node, int ns)
{
// Since order just needs to be consistant, we can just push it on the front of the list
this.nodes[ns].parent = node;
this.nodes[ns].nextSibling = this.nodes[node].firstNamespace;
this.nodes[node].firstNamespace = ns;
}
// Make a node the last sibling of another node
// Must be called on the DOM object
void AddSibling(int node1, int node2)
{
// Get the current last sibling
int i = LastSibling(node1);
// Link the node in after the last sibling
this.nodes[i].nextSibling = node2;
this.nodes[node2].prevSibling = i;
this.nodes[node2].parent = this.nodes[i].parent;
}
// Atomize the necessary strings within a range of nodes.
// Must be called on the DOM object
void Atomize(int first, int bound)
{
string s;
for (; first < bound; ++first)
{
s = this.nodes[first].prefix;
if (s != null)
{
this.nodes[first].prefix = this.nameTable.Add(s);
}
s = this.nodes[first].name;
if (s != null)
{
this.nodes[first].name = this.nameTable.Add(s);
}
s = this.nodes[first].ns;
if (s != null)
{
this.nodes[first].ns = this.nameTable.Add(s);
}
}
}
#if NO
// Verify that a given position is valid for the document, and throw if it is not.
// The node referenced must already have been constructed
// Must be called on the DOM object
void CheckValidPosition(int elem, int parent)
{
if (!IsValidPosition(elem, parent))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryProcessingException(QueryProcessingError.InvalidNavigatorPosition, SR.GetString(SR.SeekableMessageNavInvalidPosition)));
}
}
#endif
// Create a copy of the current namespace stack
Stack<string> CloneNSStack()
{
Stack<string> newStack = new Stack<string>();
foreach (string s in this.nsStack)
{
newStack.Push(s);
}
return newStack;
}
// Compare the locations of two nodes in the DOM
// This function assumes the nodes are both in the body or in the same header block
XmlNodeOrder CompareLocation(int loc1, int loc2)
{
if (loc1 == loc2)
{
return XmlNodeOrder.Same;
}
else if (loc1 < loc2)
{
return XmlNodeOrder.Before;
}
else
{
return XmlNodeOrder.After;
}
}
// Performs the actual position comparison.
XmlNodeOrder ComparePosition(int p1, int loc1, int p2, int loc2)
{
// Both namespaces of the same node
if (p1 == p2 && p1 != NullIndex)
{
return CompareLocation(loc1, loc2);
}
// Get the element of navigator 1
int thisNode;
if (p1 == NullIndex)
{
if (this.nodes[loc1].type == XPathNodeType.Attribute)
{
thisNode = this.nodes[loc1].parent;
}
else
{
thisNode = loc1;
}
}
else
{
thisNode = p1;
}
// Get the element of navigator 2
int thatNode;
if (p2 == NullIndex)
{
if (this.nodes[loc2].type == XPathNodeType.Attribute)
{
thatNode = this.nodes[loc2].parent;
}
else
{
thatNode = loc2;
}
}
else
{
thatNode = p2;
}
// If the elements are the same
if (thisNode == thatNode)
{
XPathNodeType type1 = this.nodes[loc1].type;
XPathNodeType type2 = this.nodes[loc2].type;
// We already know they are not both namespaces.
if (type1 == XPathNodeType.Namespace)
{
if (type2 == XPathNodeType.Attribute)
{
// Namespaces come before attributes
return XmlNodeOrder.Before;
}
else
{
// loc2 is the element itself
return XmlNodeOrder.After;
}
}
if (type2 == XPathNodeType.Namespace)
{
if (type1 == XPathNodeType.Attribute)
{
// Namespaces come before attributes
return XmlNodeOrder.After;
}
else
{
// loc1 is the element itself
return XmlNodeOrder.Before;
}
}
}
// Need to find out which upper level element is the parent of each node
// Upper level elements are in document order
int thisP = thisNode;
while (thisP > this.bodyIndex)
{
thisP = this.nodes[thisP].parent;
}
int thatP = thatNode;
while (thatP > this.bodyIndex)
{
thatP = this.nodes[thatP].parent;
}
if (thisP == thatP)
{
return CompareLocation(loc1, loc2);
}
else
{
return CompareLocation(thisP, thatP);
}
}
// Decompose the 'long' position into it's element and parent components
// Must be called on the DOM node
Position DecodePosition(long pos)
{
Position p = new Position((int)pos, (int)(pos >> 32));
// Make sure both nodes are valid
if (p.elem > NullIndex && p.elem < this.nextFreeIndex)
{
if (p.parent == NullIndex)
{
return p;
}
// If the parent is not null, make sure it is a valid parent and that the element is a namespace node
if (p.parent > NullIndex && p.parent < this.nextFreeIndex && this.nodes[p.parent].type == XPathNodeType.Element && this.nodes[p.elem].type == XPathNodeType.Namespace)
{
// Check that 'parent' is a descendant-or-self of elem->parent
int n = this.nodes[p.elem].parent;
int par = p.parent;
do
{
if (par == n)
{
return p;
}
par = this.nodes[par].parent;
} while (par != NullIndex);
}
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryProcessingException(QueryProcessingError.InvalidNavigatorPosition, SR.GetString(SR.SeekableMessageNavInvalidPosition)));
}
// Get the index of the next namespace that matches the scope
// This function populates the namespace stack too
// PERF, Microsoft, see if we can have this function set the current location too
int FindNamespace(int parent, int ns, XPathNamespaceScope scope)
{
bool done = false;
int nsCount = 0;
// ns is the first one we try
while (ns != NullIndex && !done)
{
Increase();
string nodeName, nodeVal;
// Skip any prefixes that are already defined
nodeName = this.dom.nodes[ns].name;
if (this.nsStack.Contains(nodeName))
{
ns = this.dom.nodes[ns].nextSibling;
continue;
}
this.nsStack.Push(nodeName);
++nsCount;
// Skip the node undefining the default namespace
nodeVal = this.dom.nodes[ns].val;
if (nodeName.Length == 0 && nodeVal.Length == 0)
{
continue;
}
// See if the current namespace is in our scope
switch (scope)
{
case XPathNamespaceScope.All:
done = true;
break;
case XPathNamespaceScope.ExcludeXml:
// If it's 'xml', keep looking
if (String.CompareOrdinal(nodeName, XmlP) == 0)
{
Increase();
ns = this.dom.nodes[ns].nextSibling;
}
else
{
done = true;
}
break;
case XPathNamespaceScope.Local:
// If the node's parent is not the element we're searching from, we've exhausted
// the locally defined namespaces
if (this.dom.nodes[ns].parent != parent)
{
ns = NullIndex;
}
else
{
done = true;
}
break;
}
}
// If we didn't find one, restore the namespace stack
if (ns == NullIndex)
{
for (int i = 0; i < nsCount; ++i)
{
this.nsStack.Pop();
}
}
return ns;
}
// Returns the qualified name of the given node
string GetName(int elem)
{
string p, n;
LoadOnDemand(elem);
p = this.dom.nodes[elem].prefix;
n = this.dom.nodes[elem].name;
if (p != null && p.Length > 0)
{
return p + ":" + n;
}
return n;
}
// Returns the string value of given node
// If the value is null, it attempts to acquire it
// Must only be called on the DOM object
string GetValue(int elem)
{
string val = this.nodes[elem].val;
if (val == null)
{
if (this.stringBuilder == null)
{
this.stringBuilder = new StringBuilder();
}
else
{
this.stringBuilder.Length = 0;
}
GetValueDriver(elem);
string s = this.stringBuilder.ToString();
this.nodes[elem].val = s;
return s;
}
else
{
return val;
}
}
// Recursive function that collects up the string value of element nodes
// Must only be called on the DOM object
void GetValueDriver(int elem)
{
string val;
this.dom.LoadOnDemand(elem);
switch (this.nodes[elem].type)
{
// The string value of Element/Root nodes is the concatination of the values of their children
case XPathNodeType.Element:
case XPathNodeType.Root:
val = this.nodes[elem].val;
if (val == null)
{
int n = this.nodes[elem].firstChild;
while (n != NullIndex)
{
Increase();
GetValueDriver(n);
n = this.nodes[n].nextSibling;
}
}
else
{
this.stringBuilder.Append(val);
}
break;
// String value cannot be 'computed' for other node types.
default:
this.stringBuilder.Append(this.nodes[elem].val);
break;
}
}
#if NO
// Checks if a node index is defined in the current DOM
// Must be called on the DOM object
bool IsValidNode(int n, bool allowNull)
{
if (allowNull)
{
return n >= NullIndex && n < this.dom.nextFreeIndex;
}
else
{
return n > NullIndex && n < this.dom.nextFreeIndex;
}
}
// Verify that a given position is valid for the document.
// The node referenced must already have been constructed
// Must be called on the DOM object
bool IsValidPosition(int elem, int parent)
{
// Make sure both nodes are valid
if ( elem > NullIndex && elem < this.nextFreeIndex)
{
if (parent == NullIndex)
{
return true;
}
// If the parent is not null, make sure it is a valid parent
if (parent > NullIndex && parent < this.nextFreeIndex)
{
// The parent must be an element node
if (this.nodes[parent].type != XPathNodeType.Element)
{
return false;
}
// If the parent is not null, the element must be a namespace node
if (this.nodes[elem].type != XPathNodeType.Namespace)
{
return false;
}
// Check that 'parent' is a descendant-or-self of elem->parent
int n = this.nodes[elem].parent;
while (parent != n)
{
parent = this.nodes[parent].parent;
if (parent == NullIndex)
{
return false;
}
}
return true;
}
}
return false;
}
#endif
// Find the last child of a node
// Must be called on the DOM object
int LastChild(int n)
{
// See if there are any children
n = this.nodes[n].firstChild;
if (n == NullIndex)
{
return NullIndex;
}
// Find the last sibling of the first child
return LastSibling(n);
}
// Find the last sibling of a node
// Must be called on the DOM object
int LastSibling(int n)
{
// Walk down the list unitl we get to the end.
while (this.nodes[n].nextSibling != NullIndex)
{
n = this.nodes[n].nextSibling;
}
return n;
}
// Load the children of the body element
// Must be called on the DOM object
void LoadBody()
{
if (!this.message.IsEmpty)
{
// Get the body reader
XmlReader reader = this.message.GetReaderAtBodyContents();
if (reader.ReadState == ReadState.Initial)
{
reader.Read();
}
// Record where we start so we can atomize later
int lower = this.nextFreeIndex;
// Load the children of the body element
ReadChildNodes(reader, this.bodyIndex, SoapNSIndex);
// Record where we finished
int upper = this.nextFreeIndex;
// Atomize if necessary
if (this.atomize)
{
Atomize(lower, upper);
}
}
}
// Load the given header node
// Must be called on the DOM object
void LoadHeader(int self)
{
// Get the header reader
XmlReader reader = this.headers.GetReaderAtHeader(self - FirstHeaderIndex);
if (reader.ReadState == ReadState.Initial)
{
reader.Read();
}
// Record where we started so we can atomize later
int lower = this.nextFreeIndex;
// Finish populating the header element
this.nodes[self].firstNamespace = SoapNSIndex;
this.nodes[self].prefix = this.atomize ? this.nameTable.Add(reader.Prefix) : reader.Prefix;
this.nodes[self].baseUri = reader.BaseURI;
this.nodes[self].xmlLang = reader.XmlLang;
// We are ignoring any siblings the reader may try to surface.
// NullIndex will be returned when then next element is the closing tag
if (!reader.IsEmptyElement)
{
ReadAttributes(self, reader);
reader.Read();
ReadChildNodes(reader, self, this.nodes[self].firstNamespace);
}
else
{
ReadAttributes(self, reader);
}
// Record where we finished
int upper = this.nextFreeIndex;
// Atomize if necessary
if (this.atomize)
{
Atomize(lower, upper);
}
}
// Make sure the current node is fully loaded into the DOM
// This function does not need to be called from the DOM object
void LoadOnDemand()
{
this.dom.LoadOnDemand(this.location);
}
// Make sure the given node is fully loaded into the DOM
// Must be called on the DOM object
// THREAD: Synchronize this function to prevent multiple concurrent loads of the same element.
// They will cause errors in the DOM structure.
void LoadOnDemand(int elem)
{
// Bail if we're not at a loadable item
if (elem > this.bodyIndex || elem < FirstHeaderIndex)
{
return;
}
// Check for the 'unloaded' marker
if (this.nodes[elem].firstChild == -1)
{
// Clear the 'unloaded' marker
this.nodes[elem].firstChild = 0;
// Load as appropriate
if (elem == this.bodyIndex)
{
if (this.includeBody)
{
LoadBody();
}
else
{
// Throw an exception if we try to navigate into the body.
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NavigatorInvalidBodyAccessException(SR.GetString(SR.SeekableMessageNavBodyForbidden)));
}
}
else
{
LoadHeader(elem);
}
}
else if (elem == this.bodyIndex && !this.includeBody)
{
// Throw an exception if we try to navigate into the body.
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NavigatorInvalidBodyAccessException(SR.GetString(SR.SeekableMessageNavBodyForbidden)));
}
}
// Get the index of a new unused node
// Perform an necessary resizing
// Must be called on the DOM object
// THREAD: Synchronize this function. If LoadOnDemand is synchronized, it may be possible to avoid synchronizing this one.
int NewNode()
{
// Resize if necessary
if (this.nextFreeIndex == this.nodes.Length)
{
// Compute the new size for the node array
int size;
if (this.nodes.Length <= StretchMax)
{
size = this.nodes.Length * GrowFactor;
}
else
{
size = this.nodes.Length + GrowInc;
}
// Resize the node array
Node[] tmp = new Node[size];
this.nodes.CopyTo(tmp, 0);
this.nodes = tmp;
}
// Return the index of the next free node
return this.nextFreeIndex++;
}
// Read in the attributes/namespaces and attach them to their element
// Must be called on the DOM object
void ReadAttributes(int elem, XmlReader reader)
{
int a, n;
while (reader.MoveToNextAttribute())
{
if (QueryDataModel.IsAttribute(reader.NamespaceURI))
{
a = NewNode();
this.nodes[a].type = XPathNodeType.Attribute;
this.nodes[a].prefix = reader.Prefix;
this.nodes[a].name = reader.LocalName;
this.nodes[a].ns = reader.NamespaceURI;
this.nodes[a].val = reader.Value;
this.nodes[a].baseUri = reader.BaseURI;
this.nodes[a].xmlLang = reader.XmlLang;
AddAttribute(elem, a);
}
else
{
string name = reader.Prefix.Length == 0 ? string.Empty : reader.LocalName;
// It is illegal to override the 'xml' and 'xmlns' prefixes
if (String.CompareOrdinal(name, XmlP) == 0 || String.CompareOrdinal(name, XmlnsP) == 0)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryProcessingException(QueryProcessingError.InvalidNamespacePrefix, SR.GetString(SR.SeekableMessageNavOverrideForbidden, reader.Name)));
}
n = NewNode();
this.nodes[n].type = XPathNodeType.Namespace;
this.nodes[n].name = name;
this.nodes[n].val = reader.Value;
this.nodes[n].baseUri = reader.BaseURI;
this.nodes[n].xmlLang = reader.XmlLang;
AddNamespace(elem, n);
}
}
}
// Load elements from a reader
// Must be called on the DOM object
int ReadChildNodes(XmlReader reader, int parent, int parentNS)
{
Fx.Assert(reader != null, "Reader cannot be null");
// Loop over all nodes being surfaced
int n = NullIndex;
do
{
// PERF, Microsoft, reorder cases so more common ones are earlier
switch (reader.NodeType)
{
case XmlNodeType.Element:
n = NewNode();
this.nodes[n].type = XPathNodeType.Element;
this.nodes[n].prefix = reader.Prefix;
this.nodes[n].name = reader.LocalName;
this.nodes[n].ns = reader.NamespaceURI;
this.nodes[n].firstNamespace = parentNS;
this.nodes[n].baseUri = reader.BaseURI;
this.nodes[n].xmlLang = reader.XmlLang;
// Empty elements don't surface closing tags so they need to be handled differently
if (!reader.IsEmptyElement)
{
ReadAttributes(n, reader);
reader.Read();
ReadChildNodes(reader, n, this.nodes[n].firstNamespace);
}
else
{
ReadAttributes(n, reader);
this.nodes[n].empty = true;
}
AddChild(parent, n);
break;
case XmlNodeType.Comment:
n = NewNode();
this.nodes[n].type = XPathNodeType.Comment;
this.nodes[n].val = reader.Value;
this.nodes[n].baseUri = reader.BaseURI;
this.nodes[n].xmlLang = reader.XmlLang;
AddChild(parent, n);
break;
case XmlNodeType.ProcessingInstruction:
n = NewNode();
this.nodes[n].type = XPathNodeType.ProcessingInstruction;
this.nodes[n].name = reader.LocalName;
this.nodes[n].val = reader.Value;
this.nodes[n].baseUri = reader.BaseURI;
this.nodes[n].xmlLang = reader.XmlLang;
AddChild(parent, n);
break;
case XmlNodeType.SignificantWhitespace:
if (reader.XmlSpace == XmlSpace.Preserve)
{
// If we're preserving whitespace, try to append it to a text node instead
// of creating a new node
n = LastChild(parent);
if (n != NullIndex && (this.nodes[n].type == XPathNodeType.Text ||
this.nodes[n].type == XPathNodeType.Whitespace ||
this.nodes[n].type == XPathNodeType.SignificantWhitespace))
{
this.nodes[n].val = this.nodes[n].val + reader.Value;
}
else
{
n = NewNode();
this.nodes[n].type = XPathNodeType.SignificantWhitespace;
this.nodes[n].val = reader.Value;
this.nodes[n].baseUri = reader.BaseURI;
this.nodes[n].xmlLang = reader.XmlLang;
AddChild(parent, n);
}
}
else
{
goto case XmlNodeType.Whitespace;
}
break;
case XmlNodeType.Whitespace:
if (this.space == XmlSpace.Preserve)
{
// If we're preserving whitespace, try to append it to a text node instead
// of creating a new node
n = LastChild(parent);
if (n != NullIndex && (this.nodes[n].type == XPathNodeType.Text ||
this.nodes[n].type == XPathNodeType.Whitespace ||
this.nodes[n].type == XPathNodeType.SignificantWhitespace))
{
this.nodes[n].val = this.nodes[n].val + reader.Value;
}
else
{
n = NewNode();
this.nodes[n].type = XPathNodeType.Whitespace;
this.nodes[n].val = reader.Value;
this.nodes[n].baseUri = reader.BaseURI;
this.nodes[n].xmlLang = reader.XmlLang;
AddChild(parent, n);
}
}
break;
case XmlNodeType.CDATA:
case XmlNodeType.Text:
// Try to append it to a text node instead of creating a new node
n = LastChild(parent);
if (n == NullIndex || (this.nodes[n].type != XPathNodeType.Text &&
this.nodes[n].type != XPathNodeType.Whitespace &&
this.nodes[n].type != XPathNodeType.SignificantWhitespace))
{
n = NewNode();
this.nodes[n].baseUri = reader.BaseURI;
this.nodes[n].xmlLang = reader.XmlLang;
AddChild(parent, n);
}
this.nodes[n].type = XPathNodeType.Text;
this.nodes[n].val = reader.Value;
break;
case XmlNodeType.EntityReference:
reader.ResolveEntity();
reader.Read();
ReadChildNodes(reader, parent, parentNS);
break;
case XmlNodeType.EndEntity:
case XmlNodeType.EndElement:
case XmlNodeType.None:
return n;
case XmlNodeType.DocumentType:
break;
case XmlNodeType.XmlDeclaration:
default:
break;
}
} while (reader.Read());
return n;
}
int INodeCounter.CounterMarker
{
get
{
return this.counter.nodeCount;
}
set
{
this.counter.nodeCount = value;
}
}
int INodeCounter.MaxCounter
{
set
{
this.counter.nodeCountMax = value;
}
}
int INodeCounter.ElapsedCount(int marker)
{
return marker - this.counter.nodeCount;
}
void Increase()
{
Fx.Assert(this.counter != null, "Counter reference is null");
if (this.counter.nodeCount > 0)
{
--this.counter.nodeCount;
}
else
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XPathNavigatorException(SR.GetString(SR.FilterNodeQuotaExceeded, this.counter.nodeCountMax)));
}
}
// PERF, Microsoft, find a better way to implement and have internal
void INodeCounter.Increase()
{
Increase();
}
void INodeCounter.IncreaseBy(int count)
{
this.counter.nodeCount -= (count - 1);
Increase();
}
struct Node
{
// Connectivity
internal int parent;
internal int firstAttribute;
internal int firstChild;
internal int firstNamespace;
internal int nextSibling;
internal int prevSibling;
// Data
internal string baseUri;
internal bool empty;
internal string name;
internal string ns;
internal string prefix;
internal string val;
internal string xmlLang;
// Type
internal XPathNodeType type;
}
struct Position
{
internal int elem;
internal int parent;
internal Position(int e, int p)
{
this.elem = e;
this.parent = p;
}
}
}
}
|