File: System\ServiceModel\Dispatcher\SeekableMessageNavigator.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// 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;
            }
        }
    }
}