XPathDocumentNavigator.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Xml / System / Xml / Cache / XPathDocumentNavigator.cs / 1 / XPathDocumentNavigator.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
using System; 
using System.IO; 
using System.Collections;
using System.Globalization; 
using System.Text;
using System.Diagnostics;
using System.Xml;
using System.Xml.XPath; 
using System.Xml.Schema;
 
namespace MS.Internal.Xml.Cache { 

    ///  
    /// This is the default XPath/XQuery data model cache implementation.  It will be used whenever
    /// the user does not supply his own XPathNavigator implementation.
    /// 
    internal sealed class XPathDocumentNavigator : XPathNavigator, IXmlLineInfo { 
        private XPathNode[] pageCurrent;
        private XPathNode[] pageParent; 
        private int idxCurrent; 
        private int idxParent;
        private string atomizedLocalName; 


        //-----------------------------------------------
        // Constructors 
        //-----------------------------------------------
 
        ///  
        /// Create a new navigator positioned on the specified current node.  If the current node is a namespace or a collapsed
        /// text node, then the parent is a virtualized parent (may be different than .Parent on the current node). 
        /// 
        public XPathDocumentNavigator(XPathNode[] pageCurrent, int idxCurrent, XPathNode[] pageParent, int idxParent) {
            Debug.Assert(pageCurrent != null && idxCurrent != 0);
            Debug.Assert((pageParent == null) == (idxParent == 0)); 
            this.pageCurrent = pageCurrent;
            this.pageParent = pageParent; 
            this.idxCurrent = idxCurrent; 
            this.idxParent = idxParent;
        } 

        /// 
        /// Copy constructor.
        ///  
        public XPathDocumentNavigator(XPathDocumentNavigator nav) : this(nav.pageCurrent, nav.idxCurrent, nav.pageParent, nav.idxParent) {
            this.atomizedLocalName = nav.atomizedLocalName; 
        } 

 
        //-----------------------------------------------
        // XPathItem
        //-----------------------------------------------
 
        /// 
        /// Get the string value of the current node, computed using data model dm:string-value rules. 
        /// If the node has a typed value, return the string representation of the value.  If the node 
        /// is not a parent type (comment, text, pi, etc.), get its simple text value.  Otherwise,
        /// concatenate all text node descendants of the current node. 
        /// 
        public override string Value {
            get {
                string value; 
                XPathNode[] page, pageEnd;
                int idx, idxEnd; 
 
                // Try to get the pre-computed string value of the node
                value = this.pageCurrent[this.idxCurrent].Value; 
                if (value != null)
                    return value;

            #if DEBUG 
                switch (this.pageCurrent[this.idxCurrent].NodeType) {
                    case XPathNodeType.Namespace: 
                    case XPathNodeType.Attribute: 
                    case XPathNodeType.Comment:
                    case XPathNodeType.ProcessingInstruction: 
                        Debug.Assert(false, "ReadStringValue() should have taken care of these node types.");
                        break;

                    case XPathNodeType.Text: 
                        Debug.Assert(this.idxParent != 0 && this.pageParent[this.idxParent].HasCollapsedText,
                                     "ReadStringValue() should have taken care of anything but collapsed text."); 
                        break; 
                }
            #endif 

                // If current node is collapsed text, then parent element has a simple text value
                if (this.idxParent != 0) {
                    Debug.Assert(this.pageCurrent[this.idxCurrent].NodeType == XPathNodeType.Text); 
                    return this.pageParent[this.idxParent].Value;
                } 
 
                // Must be node with complex content, so concatenate the string values of all text descendants
                string s = string.Empty; 
                StringBuilder bldr = null;

                // Get all text nodes which follow the current node in document order, but which are still descendants
                page = pageEnd = this.pageCurrent; 
                idx = idxEnd = this.idxCurrent;
                if (!XPathNodeHelper.GetNonDescendant(ref pageEnd, ref idxEnd)) { 
                    pageEnd = null; 
                    idxEnd = 0;
                } 

                while (XPathNodeHelper.GetTextFollowing(ref page, ref idx, pageEnd, idxEnd)) {
                    Debug.Assert(page[idx].NodeType == XPathNodeType.Element || page[idx].IsText);
 
                    if (s.Length == 0) {
                        s = page[idx].Value; 
                    } 
                    else {
                        if (bldr == null) { 
                            bldr = new StringBuilder();
                            bldr.Append(s);
                        }
                        bldr.Append(page[idx].Value); 
                    }
                } 
 
                return (bldr != null) ? bldr.ToString() : s;
            } 
        }


        //----------------------------------------------- 
        // XPathNavigator
        //----------------------------------------------- 
 
        /// 
        /// Create a copy of this navigator, positioned to the same node in the tree. 
        /// 
        public override XPathNavigator Clone() {
            return new XPathDocumentNavigator(this.pageCurrent, this.idxCurrent, this.pageParent, this.idxParent);
        } 

        ///  
        /// Get the XPath node type of the current node. 
        /// 
        public override XPathNodeType NodeType { 
            get { return this.pageCurrent[this.idxCurrent].NodeType; }
        }

        ///  
        /// Get the local name portion of the current node's name.
        ///  
        public override string LocalName { 
            get { return this.pageCurrent[this.idxCurrent].LocalName; }
        } 

        /// 
        /// Get the namespace portion of the current node's name.
        ///  
        public override string NamespaceURI {
            get { return this.pageCurrent[this.idxCurrent].NamespaceUri; } 
        } 

        ///  
        /// Get the name of the current node.
        /// 
        public override string Name {
            get { return this.pageCurrent[this.idxCurrent].Name; } 
        }
 
        ///  
        /// Get the prefix portion of the current node's name.
        ///  
        public override string Prefix {
            get { return this.pageCurrent[this.idxCurrent].Prefix; }
        }
 
        /// 
        /// Get the base URI of the current node. 
        ///  
        public override string BaseURI {
            get { 
                XPathNode[] page;
                int idx;

                if (this.idxParent != 0) { 
                    // Get BaseUri of parent for attribute, namespace, and collapsed text nodes
                    page = this.pageParent; 
                    idx = this.idxParent; 
                }
                else { 
                    page = this.pageCurrent;
                    idx = this.idxCurrent;
                }
 
                do {
                    switch (page[idx].NodeType) { 
                        case XPathNodeType.Element: 
                        case XPathNodeType.Root:
                        case XPathNodeType.ProcessingInstruction: 
                            // BaseUri is always stored with Elements, Roots, and PIs
                            return page[idx].BaseUri;
                    }
 
                    // Get BaseUri of parent
                    idx = page[idx].GetParent(out page); 
                } 
                while (idx != 0);
 
                return string.Empty;
            }
        }
 
        /// 
        /// Return true if this is an element which used a shortcut tag in its Xml 1.0 serialized form. 
        ///  
        public override bool IsEmptyElement {
            get { return this.pageCurrent[this.idxCurrent].AllowShortcutTag; } 
        }

        /// 
        /// Return the xml name table which was used to atomize all prefixes, local-names, and 
        /// namespace uris in the document.
        ///  
        public override XmlNameTable NameTable { 
            get { return this.pageCurrent[this.idxCurrent].Document.NameTable; }
        } 

        /// 
        /// Position the navigator on the first attribute of the current node and return true.  If no attributes
        /// can be found, return false. 
        /// 
        public override bool MoveToFirstAttribute() { 
            XPathNode[] page = this.pageCurrent; 
            int idx = this.idxCurrent;
 
            if (XPathNodeHelper.GetFirstAttribute(ref this.pageCurrent, ref this.idxCurrent)) {
                // Save element parent in order to make node-order comparison simpler
                this.pageParent = page;
                this.idxParent = idx; 
                return true;
            } 
 
            return false;
        } 

        /// 
        /// If positioned on an attribute, move to its next sibling attribute.  If no attributes can be found,
        /// return false. 
        /// 
        public override bool MoveToNextAttribute() { 
            return XPathNodeHelper.GetNextAttribute(ref this.pageCurrent, ref this.idxCurrent); 
        }
 
        /// 
        /// True if the current node has one or more attributes.
        /// 
        public override bool HasAttributes { 
            get { return this.pageCurrent[this.idxCurrent].HasAttribute; }
        } 
 
        /// 
        /// Position the navigator on the attribute with the specified name and return true.  If no matching 
        /// attribute can be found, return false.  Don't assume the name parts are atomized with respect
        /// to this document.
        /// 
        public override bool MoveToAttribute(string localName, string namespaceURI) { 
            XPathNode[] page = this.pageCurrent;
            int idx = this.idxCurrent; 
 
            if ((object) localName != (object) this.atomizedLocalName)
                this.atomizedLocalName = (localName != null) ? NameTable.Get(localName) : null; 

            if (XPathNodeHelper.GetAttribute(ref this.pageCurrent, ref this.idxCurrent, this.atomizedLocalName, namespaceURI)) {
                // Save element parent in order to make node-order comparison simpler
                this.pageParent = page; 
                this.idxParent = idx;
                return true; 
            } 

            return false; 
        }

        /// 
        /// Position the navigator on the namespace within the specified scope.  If no matching namespace 
        /// can be found, return false.
        ///  
        public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) { 
            XPathNode[] page;
            int idx; 

            if (namespaceScope == XPathNamespaceScope.Local) {
                // Get local namespaces only
                idx = XPathNodeHelper.GetLocalNamespaces(this.pageCurrent, this.idxCurrent, out page); 
            }
            else { 
                // Get all in-scope namespaces 
                idx = XPathNodeHelper.GetInScopeNamespaces(this.pageCurrent, this.idxCurrent, out page);
            } 

            while (idx != 0) {
                // Don't include the xmlns:xml namespace node if scope is ExcludeXml
                if (namespaceScope != XPathNamespaceScope.ExcludeXml || !page[idx].IsXmlNamespaceNode) { 
                    this.pageParent = this.pageCurrent;
                    this.idxParent = this.idxCurrent; 
                    this.pageCurrent = page; 
                    this.idxCurrent = idx;
                    return true; 
                }

                // Skip past xmlns:xml
                idx = page[idx].GetSibling(out page); 
            }
 
            return false; 
        }
 
        /// 
        /// Position the navigator on the next namespace within the specified scope.  If no matching namespace
        /// can be found, return false.
        ///  
        public override bool MoveToNextNamespace(XPathNamespaceScope scope) {
            XPathNode[] page = this.pageCurrent, pageParent; 
            int idx = this.idxCurrent, idxParent; 

            // If current node is not a namespace node, return false 
            if (page[idx].NodeType != XPathNodeType.Namespace)
                return false;

            while (true) { 
                // Get next namespace sibling
                idx = page[idx].GetSibling(out page); 
 
                // If there are no more nodes, return false
                if (idx == 0) 
                    return false;

                switch (scope) {
                    case XPathNamespaceScope.Local: 
                        // Once parent changes, there are no longer any local namespaces
                        idxParent = page[idx].GetParent(out pageParent); 
                        if (idxParent != this.idxParent || (object) pageParent != (object) this.pageParent) 
                            return false;
                        break; 

                    case XPathNamespaceScope.ExcludeXml:
                        // If node is xmlns:xml, then skip it
                        if (page[idx].IsXmlNamespaceNode) 
                            continue;
                        break; 
                } 

                // Found a matching next namespace node, so return it 
                break;
            }

            this.pageCurrent = page; 
            this.idxCurrent = idx;
            return true; 
        } 

        ///  
        /// If the current node is an attribute or namespace (not content), return false.  Otherwise,
        /// move to the next content node.  Return false if there are no more content nodes.
        /// 
        public override bool MoveToNext() { 
            return XPathNodeHelper.GetContentSibling(ref this.pageCurrent, ref this.idxCurrent);
        } 
 
        /// 
        /// If the current node is an attribute or namespace (not content), return false.  Otherwise, 
        /// move to the previous (sibling) content node.  Return false if there are no previous content nodes.
        /// 
        public override bool MoveToPrevious() {
            // If parent exists, then this is a namespace, an attribute, or a collapsed text node, all of which do 
            // not have previous siblings.
            if (this.idxParent != 0) 
                return false; 

            return XPathNodeHelper.GetPreviousContentSibling(ref this.pageCurrent, ref this.idxCurrent); 
        }

        /// 
        /// Move to the first content-typed child of the current node.  Return false if the current 
        /// node has no content children.
        ///  
        public override bool MoveToFirstChild() { 
            if (this.pageCurrent[this.idxCurrent].HasCollapsedText) {
                // Virtualize collapsed text nodes 
                this.pageParent = this.pageCurrent;
                this.idxParent = this.idxCurrent;
                this.idxCurrent = this.pageCurrent[this.idxCurrent].Document.GetCollapsedTextNode(out this.pageCurrent);
                return true; 
            }
 
            return XPathNodeHelper.GetContentChild(ref this.pageCurrent, ref this.idxCurrent); 
        }
 
        /// 
        /// Position the navigator on the parent of the current node.  If the current node has no parent,
        /// return false.
        ///  
        public override bool MoveToParent() {
            if (this.idxParent != 0) { 
                // 1. For attribute nodes, element parent is always stored in order to make node-order 
                //    comparison simpler.
                // 2. For namespace nodes, parent is always stored in navigator in order to virtualize 
                //    XPath 1.0 namespaces.
                // 3. For collapsed text nodes, element parent is always stored in navigator.
                Debug.Assert(this.pageParent != null);
                this.pageCurrent = this.pageParent; 
                this.idxCurrent = this.idxParent;
                this.pageParent = null; 
                this.idxParent = 0; 
                return true;
            } 

            return XPathNodeHelper.GetParent(ref this.pageCurrent, ref this.idxCurrent);
        }
 
        /// 
        /// Position this navigator to the same position as the "other" navigator.  If the "other" navigator 
        /// is not of the same type as this navigator, then return false. 
        /// 
        public override bool MoveTo(XPathNavigator other) { 
            XPathDocumentNavigator that = other as XPathDocumentNavigator;
            if (that != null) {
                this.pageCurrent = that.pageCurrent;
                this.idxCurrent = that.idxCurrent; 
                this.pageParent = that.pageParent;
                this.idxParent = that.idxParent; 
                return true; 
            }
            return false; 
        }

        /// 
        /// Position to the navigator to the element whose id is equal to the specified "id" string. 
        /// 
        public override bool MoveToId(string id) { 
            XPathNode[] page; 
            int idx;
 
            idx = this.pageCurrent[this.idxCurrent].Document.LookupIdElement(id, out page);
            if (idx != 0) {
                // Move to ID element and clear parent state
                Debug.Assert(page[idx].NodeType == XPathNodeType.Element); 
                this.pageCurrent = page;
                this.idxCurrent = idx; 
                this.pageParent = null; 
                this.idxParent = 0;
                return true; 
            }

            return false;
        } 

        ///  
        /// Returns true if this navigator is positioned to the same node as the "other" navigator.  Returns false 
        /// if not, or if the "other" navigator is not the same type as this navigator.
        ///  
        public override bool IsSamePosition(XPathNavigator other) {
            XPathDocumentNavigator that = other as XPathDocumentNavigator;
            if (that != null) {
                return this.idxCurrent == that.idxCurrent && this.pageCurrent == that.pageCurrent && 
                       this.idxParent == that.idxParent && this.pageParent == that.pageParent;
            } 
            return false; 
        }
 
        /// 
        /// Returns true if the current node has children.
        /// 
        public override bool HasChildren { 
            get { return this.pageCurrent[this.idxCurrent].HasContentChild; }
        } 
 
        /// 
        /// Position the navigator on the root node of the current document. 
        /// 
        public override void MoveToRoot() {
            if (this.idxParent != 0) {
                // Clear parent state 
                this.pageParent = null;
                this.idxParent = 0; 
            } 
            this.idxCurrent = this.pageCurrent[this.idxCurrent].GetRoot(out this.pageCurrent);
        } 

        /// 
        /// Move to the first element child of the current node with the specified name.  Return false
        /// if the current node has no matching element children. 
        /// 
        public override bool MoveToChild(string localName, string namespaceURI) { 
            if ((object) localName != (object) this.atomizedLocalName) 
                this.atomizedLocalName = (localName != null) ? NameTable.Get(localName) : null;
 
            return XPathNodeHelper.GetElementChild(ref this.pageCurrent, ref this.idxCurrent, this.atomizedLocalName, namespaceURI);
        }

        ///  
        /// Move to the first element sibling of the current node with the specified name.  Return false
        /// if the current node has no matching element siblings. 
        ///  
        public override bool MoveToNext(string localName, string namespaceURI) {
            if ((object) localName != (object) this.atomizedLocalName) 
                this.atomizedLocalName = (localName != null) ? NameTable.Get(localName) : null;

            return XPathNodeHelper.GetElementSibling(ref this.pageCurrent, ref this.idxCurrent, this.atomizedLocalName, namespaceURI);
        } 

        ///  
        /// Move to the first content child of the current node with the specified type.  Return false 
        /// if the current node has no matching children.
        ///  
        public override bool MoveToChild(XPathNodeType type) {
            if (this.pageCurrent[this.idxCurrent].HasCollapsedText) {
                // Only XPathNodeType.Text and XPathNodeType.All matches collapsed text node
                if (type != XPathNodeType.Text && type != XPathNodeType.All) 
                    return false;
 
                // Virtualize collapsed text nodes 
                this.pageParent = this.pageCurrent;
                this.idxParent = this.idxCurrent; 
                this.idxCurrent = this.pageCurrent[this.idxCurrent].Document.GetCollapsedTextNode(out this.pageCurrent);
                return true;
            }
 
            return XPathNodeHelper.GetContentChild(ref this.pageCurrent, ref this.idxCurrent, type);
        } 
 
        /// 
        /// Move to the first content sibling of the current node with the specified type.  Return false 
        /// if the current node has no matching siblings.
        /// 
        public override bool MoveToNext(XPathNodeType type) {
            return XPathNodeHelper.GetContentSibling(ref this.pageCurrent, ref this.idxCurrent, type); 
        }
 
        ///  
        /// Move to the next element that:
        ///   1. Follows the current node in document order (includes descendants, unlike XPath following axis) 
        ///   2. Precedes "end" in document order (if end is null, then all following nodes in the document are considered)
        ///   3. Has the specified QName
        /// Return false if the current node has no matching following elements.
        ///  
        public override bool MoveToFollowing(string localName, string namespaceURI, XPathNavigator end) {
            XPathNode[] pageEnd; 
            int idxEnd; 

            if ((object) localName != (object) this.atomizedLocalName) 
                this.atomizedLocalName = (localName != null) ? NameTable.Get(localName) : null;

            // Get node on which scan ends (null if rest of document should be scanned)
            idxEnd = GetFollowingEnd(end as XPathDocumentNavigator, false, out pageEnd); 

            // If this navigator is positioned on a virtual node, then compute following of parent 
            if (this.idxParent != 0) { 
                if (!XPathNodeHelper.GetElementFollowing(ref this.pageParent, ref this.idxParent, pageEnd, idxEnd, this.atomizedLocalName, namespaceURI))
                    return false; 

                this.pageCurrent = this.pageParent;
                this.idxCurrent = this.idxParent;
                this.pageParent = null; 
                this.idxParent = 0;
                return true; 
            } 

            return XPathNodeHelper.GetElementFollowing(ref this.pageCurrent, ref this.idxCurrent, pageEnd, idxEnd, this.atomizedLocalName, namespaceURI); 
        }

        /// 
        /// Move to the next node that: 
        ///   1. Follows the current node in document order (includes descendants, unlike XPath following axis)
        ///   2. Precedes "end" in document order (if end is null, then all following nodes in the document are considered) 
        ///   3. Has the specified XPathNodeType 
        /// Return false if the current node has no matching following nodes.
        ///  
        public override bool MoveToFollowing(XPathNodeType type, XPathNavigator end) {
            XPathDocumentNavigator endTiny = end as XPathDocumentNavigator;
            XPathNode[] page, pageEnd;
            int idx, idxEnd; 

            // If searching for text, make sure to handle collapsed text nodes correctly 
            if (type == XPathNodeType.Text || type == XPathNodeType.All) { 
                if (this.pageCurrent[this.idxCurrent].HasCollapsedText) {
                    // Positioned on an element with collapsed text, so return the virtual text node, assuming it's before "end" 
                    if (endTiny != null && this.idxCurrent == endTiny.idxParent && this.pageCurrent == endTiny.pageParent) {
                        // "end" is positioned to a virtual attribute, namespace, or text node
                        return false;
                    } 

                    this.pageParent = this.pageCurrent; 
                    this.idxParent = this.idxCurrent; 
                    this.idxCurrent = this.pageCurrent[this.idxCurrent].Document.GetCollapsedTextNode(out this.pageCurrent);
                    return true; 
                }

                if (type == XPathNodeType.Text) {
                    // Get node on which scan ends (null if rest of document should be scanned, parent if positioned on virtual node) 
                    idxEnd = GetFollowingEnd(endTiny, true, out pageEnd);
 
                    // If this navigator is positioned on a virtual node, then compute following of parent 
                    if (this.idxParent != 0) {
                        page = this.pageParent; 
                        idx = this.idxParent;
                    }
                    else {
                        page = this.pageCurrent; 
                        idx = this.idxCurrent;
                    } 
 
                    // If ending node is a virtual node, and current node is its parent, then we're done
                    if (endTiny != null && endTiny.idxParent != 0 && idx == idxEnd && page == pageEnd) 
                        return false;

                    // Get all virtual (collapsed) and physical text nodes which follow the current node
                    if (!XPathNodeHelper.GetTextFollowing(ref page, ref idx, pageEnd, idxEnd)) 
                        return false;
 
                    if (page[idx].NodeType == XPathNodeType.Element) { 
                        // Virtualize collapsed text nodes
                        Debug.Assert(page[idx].HasCollapsedText); 
                        this.idxCurrent = page[idx].Document.GetCollapsedTextNode(out this.pageCurrent);
                        this.pageParent = page;
                        this.idxParent = idx;
                    } 
                    else {
                        // Physical text node 
                        Debug.Assert(page[idx].IsText); 
                        this.pageCurrent = page;
                        this.idxCurrent = idx; 
                        this.pageParent = null;
                        this.idxParent = 0;
                    }
                    return true; 
                }
            } 
 
            // Get node on which scan ends (null if rest of document should be scanned, parent + 1 if positioned on virtual node)
            idxEnd = GetFollowingEnd(endTiny, false, out pageEnd); 

            // If this navigator is positioned on a virtual node, then compute following of parent
            if (this.idxParent != 0) {
                if (!XPathNodeHelper.GetContentFollowing(ref this.pageParent, ref this.idxParent, pageEnd, idxEnd, type)) 
                    return false;
 
                this.pageCurrent = this.pageParent; 
                this.idxCurrent = this.idxParent;
                this.pageParent = null; 
                this.idxParent = 0;
                return true;
            }
 
            return XPathNodeHelper.GetContentFollowing(ref this.pageCurrent, ref this.idxCurrent, pageEnd, idxEnd, type);
        } 
 
        /// 
        /// Return an iterator that ranges over all children of the current node that match the specified XPathNodeType. 
        /// 
        public override XPathNodeIterator SelectChildren(XPathNodeType type) {
            return new XPathDocumentKindChildIterator(this, type);
        } 

        ///  
        /// Return an iterator that ranges over all children of the current node that match the specified QName. 
        /// 
        public override XPathNodeIterator SelectChildren(string name, string namespaceURI) { 
            // If local name is wildcard, then call XPathNavigator.SelectChildren
            if (name == null || name.Length == 0)
                return base.SelectChildren(name, namespaceURI);
 
            return new XPathDocumentElementChildIterator(this, name, namespaceURI);
        } 
 
        /// 
        /// Return an iterator that ranges over all descendants of the current node that match the specified 
        /// XPathNodeType.  If matchSelf is true, then also perform the match on the current node.
        /// 
        public override XPathNodeIterator SelectDescendants(XPathNodeType type, bool matchSelf) {
            return new XPathDocumentKindDescendantIterator(this, type, matchSelf); 
        }
 
        ///  
        /// Return an iterator that ranges over all descendants of the current node that match the specified
        /// QName.  If matchSelf is true, then also perform the match on the current node. 
        /// 
        public override XPathNodeIterator SelectDescendants(string name, string namespaceURI, bool matchSelf) {
            // If local name is wildcard, then call XPathNavigator.SelectDescendants
            if (name == null || name.Length == 0) 
                return base.SelectDescendants(name, namespaceURI, matchSelf);
 
            return new XPathDocumentElementDescendantIterator(this, name, namespaceURI, matchSelf); 
        }
 
        /// 
        /// Returns:
        ///     XmlNodeOrder.Unknown -- This navigator and the "other" navigator are not of the same type, or the
        ///                             navigator's are not positioned on nodes in the same document. 
        ///     XmlNodeOrder.Before -- This navigator's current node is before the "other" navigator's current node
        ///                            in document order. 
        ///     XmlNodeOrder.After -- This navigator's current node is after the "other" navigator's current node 
        ///                           in document order.
        ///     XmlNodeOrder.Same -- This navigator is positioned on the same node as the "other" navigator. 
        /// 
        public override XmlNodeOrder ComparePosition(XPathNavigator other) {
            XPathDocumentNavigator that = other as XPathDocumentNavigator;
            if (that != null) { 
                XPathDocument thisDoc = this.pageCurrent[this.idxCurrent].Document;
                XPathDocument thatDoc = that.pageCurrent[that.idxCurrent].Document; 
                if ((object) thisDoc == (object) thatDoc) { 
                    int locThis = GetPrimaryLocation();
                    int locThat = that.GetPrimaryLocation(); 

                    if (locThis == locThat) {
                        locThis = GetSecondaryLocation();
                        locThat = that.GetSecondaryLocation(); 

                        if (locThis == locThat) 
                            return XmlNodeOrder.Same; 
                    }
                    return (locThis < locThat) ? XmlNodeOrder.Before : XmlNodeOrder.After; 
                }
            }
            return XmlNodeOrder.Unknown;
        } 

        ///  
        /// Return true if the "other" navigator's current node is a descendant of this navigator's current node. 
        /// 
        public override bool IsDescendant(XPathNavigator other) { 
            XPathDocumentNavigator that = other as XPathDocumentNavigator;
            if (that != null) {
                XPathNode[] pageThat;
                int idxThat; 

                // If that current node's parent is virtualized, then start with the virtual parent 
                if (that.idxParent != 0) { 
                    pageThat = that.pageParent;
                    idxThat = that.idxParent; 
                }
                else {
                    idxThat = that.pageCurrent[that.idxCurrent].GetParent(out pageThat);
                } 

                while (idxThat != 0) { 
                    if (idxThat == this.idxCurrent && pageThat == this.pageCurrent) 
                        return true;
                    idxThat = pageThat[idxThat].GetParent(out pageThat); 
                }
            }
            return false;
        } 

        ///  
        /// Construct a primary location for this navigator.  The location is an integer that can be 
        /// easily compared with other locations in the same document in order to determine the relative
        /// document order of two nodes.  If two locations compare equal, then secondary locations should 
        /// be compared.
        /// 
        private int GetPrimaryLocation() {
            // Is the current node virtualized? 
            if (this.idxParent == 0) {
                // No, so primary location should be derived from current node 
                return XPathNodeHelper.GetLocation(this.pageCurrent, this.idxCurrent); 
            }
 
            // Yes, so primary location should be derived from parent node
            return XPathNodeHelper.GetLocation(this.pageParent, this.idxParent);
        }
 
        /// 
        /// Construct a secondary location for this navigator.  This location should only be used if 
        /// primary locations previously compared equal. 
        /// 
        private int GetSecondaryLocation() { 
            // Is the current node virtualized?
            if (this.idxParent == 0) {
                // No, so secondary location is int.MinValue (always first)
                return int.MinValue; 
            }
 
            // Yes, so secondary location should be derived from current node 
            // This happens with attributes nodes, namespace nodes, collapsed text nodes, and atomic values
            switch (this.pageCurrent[this.idxCurrent].NodeType) { 
                case XPathNodeType.Namespace:
                    // Namespace nodes come first (make location negative, but greater than int.MinValue)
                    return int.MinValue + 1 + XPathNodeHelper.GetLocation(this.pageCurrent, this.idxCurrent);
 
                case XPathNodeType.Attribute:
                    // Attribute nodes come next (location is always positive) 
                    return XPathNodeHelper.GetLocation(this.pageCurrent, this.idxCurrent); 

                default: 
                    // Collapsed text nodes are always last
                    return int.MaxValue;
            }
        } 

        ///  
        /// Create a unique id for the current node.  This is used by the generate-id() function. 
        /// 
        internal override string UniqueId { 
            get {
                // 32-bit integer is split into 5-bit groups, the maximum number of groups is 7
                char[] buf = new char[1+7+1+7];
                int idx = 0; 
                int loc;
 
                // Ensure distinguishing attributes, namespaces and child nodes 
                buf[idx++] = NodeTypeLetter[(int)this.pageCurrent[this.idxCurrent].NodeType];
 
                // If the current node is virtualized, code its parent
                if (this.idxParent != 0) {
                    loc = (this.pageParent[0].PageInfo.PageNumber - 1) << 16 | (this.idxParent - 1);
                    do { 
                        buf[idx++] = UniqueIdTbl[loc & 0x1f];
                        loc >>= 5; 
                    } while (loc != 0); 
                    buf[idx++] = '0';
                } 

                // Code the node itself
                loc = (this.pageCurrent[0].PageInfo.PageNumber - 1) << 16 | (this.idxCurrent - 1);
                do { 
                    buf[idx++] = UniqueIdTbl[loc & 0x1f];
                    loc >>= 5; 
                } while (loc != 0); 

                return new string(buf, 0, idx); 
            }
        }

        //----------------------------------------------- 
        // IXmlLineInfo
        //----------------------------------------------- 
 
        /// 
        /// Return true if line number information is recorded in the cache. 
        /// 
        public bool HasLineInfo() {
            return this.pageCurrent[this.idxCurrent].Document.HasLineInfo;
        } 

        ///  
        /// Return the source line number of the current node. 
        /// 
        public int LineNumber { 
            get {
                // If the current node is a collapsed text node, then return parent element's line number
                if (this.idxParent != 0 && NodeType == XPathNodeType.Text)
                    return this.pageParent[this.idxParent].LineNumber; 

                return this.pageCurrent[this.idxCurrent].LineNumber; 
            } 
        }
 
        /// 
        /// Return the source line position of the current node.
        /// 
        public int LinePosition { 
            get {
                // If the current node is a collapsed text node, then get position from parent element 
                if (this.idxParent != 0 && NodeType == XPathNodeType.Text) 
                    return this.pageParent[this.idxParent].CollapsedLinePosition;
 
                return this.pageCurrent[this.idxCurrent].LinePosition;
            }
        }
 

        //----------------------------------------------- 
        // Helper methods 
        //-----------------------------------------------
 
        /// 
        /// Get hashcode based on current position of the navigator.
        /// 
        public int GetPositionHashCode() { 
            return this.idxCurrent ^ this.idxParent;
        } 
 
        /// 
        /// Return true if navigator is positioned to an element having the specified name. 
        /// 
        public bool IsElementMatch(string localName, string namespaceURI) {
            if ((object) localName != (object) this.atomizedLocalName)
                this.atomizedLocalName = (localName != null) ? NameTable.Get(localName) : null; 

            // Cannot be an element if parent is stored 
            if (this.idxParent != 0) 
                return false;
 
            return this.pageCurrent[this.idxCurrent].ElementMatch(this.atomizedLocalName, namespaceURI);
        }

        ///  
        /// Return true if navigator is positioned to a content node of the specified kind.  Whitespace/SignficantWhitespace/Text are
        /// all treated the same (i.e. they all match each other). 
        ///  
        public bool IsContentKindMatch(XPathNodeType typ) {
            return (((1 << (int) this.pageCurrent[this.idxCurrent].NodeType) & GetContentKindMask(typ)) != 0); 
        }

        /// 
        /// Return true if navigator is positioned to a node of the specified kind.  Whitespace/SignficantWhitespace/Text are 
        /// all treated the same (i.e. they all match each other).
        ///  
        public bool IsKindMatch(XPathNodeType typ) { 
            return (((1 << (int) this.pageCurrent[this.idxCurrent].NodeType) & GetKindMask(typ)) != 0);
        } 

        /// 
        /// "end" is positioned on a node which terminates a following scan.  Return the page and index of "end" if it
        /// is positioned to a non-virtual node.  If "end" is positioned to a virtual node: 
        ///    1. If useParentOfVirtual is true, then return the page and index of the virtual node's parent
        ///    2. If useParentOfVirtual is false, then return the page and index of the virtual node's parent + 1. 
        ///  
        private int GetFollowingEnd(XPathDocumentNavigator end, bool useParentOfVirtual, out XPathNode[] pageEnd) {
            // If ending navigator is positioned to a node in another document, then return null 
            if (end != null && this.pageCurrent[this.idxCurrent].Document == end.pageCurrent[end.idxCurrent].Document) {

                // If the ending navigator is not positioned on a virtual node, then return its current node
                if (end.idxParent == 0) { 
                    pageEnd = end.pageCurrent;
                    return end.idxCurrent; 
                } 

                // If the ending navigator is positioned on an attribute, namespace, or virtual text node, then use the 
                // next physical node instead, as the results will be the same.
                pageEnd = end.pageParent;
                return (useParentOfVirtual) ? end.idxParent : end.idxParent + 1;
            } 

            // No following, so set pageEnd to null and return an index of 0 
            pageEnd = null; 
            return 0;
        } 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
using System; 
using System.IO; 
using System.Collections;
using System.Globalization; 
using System.Text;
using System.Diagnostics;
using System.Xml;
using System.Xml.XPath; 
using System.Xml.Schema;
 
namespace MS.Internal.Xml.Cache { 

    ///  
    /// This is the default XPath/XQuery data model cache implementation.  It will be used whenever
    /// the user does not supply his own XPathNavigator implementation.
    /// 
    internal sealed class XPathDocumentNavigator : XPathNavigator, IXmlLineInfo { 
        private XPathNode[] pageCurrent;
        private XPathNode[] pageParent; 
        private int idxCurrent; 
        private int idxParent;
        private string atomizedLocalName; 


        //-----------------------------------------------
        // Constructors 
        //-----------------------------------------------
 
        ///  
        /// Create a new navigator positioned on the specified current node.  If the current node is a namespace or a collapsed
        /// text node, then the parent is a virtualized parent (may be different than .Parent on the current node). 
        /// 
        public XPathDocumentNavigator(XPathNode[] pageCurrent, int idxCurrent, XPathNode[] pageParent, int idxParent) {
            Debug.Assert(pageCurrent != null && idxCurrent != 0);
            Debug.Assert((pageParent == null) == (idxParent == 0)); 
            this.pageCurrent = pageCurrent;
            this.pageParent = pageParent; 
            this.idxCurrent = idxCurrent; 
            this.idxParent = idxParent;
        } 

        /// 
        /// Copy constructor.
        ///  
        public XPathDocumentNavigator(XPathDocumentNavigator nav) : this(nav.pageCurrent, nav.idxCurrent, nav.pageParent, nav.idxParent) {
            this.atomizedLocalName = nav.atomizedLocalName; 
        } 

 
        //-----------------------------------------------
        // XPathItem
        //-----------------------------------------------
 
        /// 
        /// Get the string value of the current node, computed using data model dm:string-value rules. 
        /// If the node has a typed value, return the string representation of the value.  If the node 
        /// is not a parent type (comment, text, pi, etc.), get its simple text value.  Otherwise,
        /// concatenate all text node descendants of the current node. 
        /// 
        public override string Value {
            get {
                string value; 
                XPathNode[] page, pageEnd;
                int idx, idxEnd; 
 
                // Try to get the pre-computed string value of the node
                value = this.pageCurrent[this.idxCurrent].Value; 
                if (value != null)
                    return value;

            #if DEBUG 
                switch (this.pageCurrent[this.idxCurrent].NodeType) {
                    case XPathNodeType.Namespace: 
                    case XPathNodeType.Attribute: 
                    case XPathNodeType.Comment:
                    case XPathNodeType.ProcessingInstruction: 
                        Debug.Assert(false, "ReadStringValue() should have taken care of these node types.");
                        break;

                    case XPathNodeType.Text: 
                        Debug.Assert(this.idxParent != 0 && this.pageParent[this.idxParent].HasCollapsedText,
                                     "ReadStringValue() should have taken care of anything but collapsed text."); 
                        break; 
                }
            #endif 

                // If current node is collapsed text, then parent element has a simple text value
                if (this.idxParent != 0) {
                    Debug.Assert(this.pageCurrent[this.idxCurrent].NodeType == XPathNodeType.Text); 
                    return this.pageParent[this.idxParent].Value;
                } 
 
                // Must be node with complex content, so concatenate the string values of all text descendants
                string s = string.Empty; 
                StringBuilder bldr = null;

                // Get all text nodes which follow the current node in document order, but which are still descendants
                page = pageEnd = this.pageCurrent; 
                idx = idxEnd = this.idxCurrent;
                if (!XPathNodeHelper.GetNonDescendant(ref pageEnd, ref idxEnd)) { 
                    pageEnd = null; 
                    idxEnd = 0;
                } 

                while (XPathNodeHelper.GetTextFollowing(ref page, ref idx, pageEnd, idxEnd)) {
                    Debug.Assert(page[idx].NodeType == XPathNodeType.Element || page[idx].IsText);
 
                    if (s.Length == 0) {
                        s = page[idx].Value; 
                    } 
                    else {
                        if (bldr == null) { 
                            bldr = new StringBuilder();
                            bldr.Append(s);
                        }
                        bldr.Append(page[idx].Value); 
                    }
                } 
 
                return (bldr != null) ? bldr.ToString() : s;
            } 
        }


        //----------------------------------------------- 
        // XPathNavigator
        //----------------------------------------------- 
 
        /// 
        /// Create a copy of this navigator, positioned to the same node in the tree. 
        /// 
        public override XPathNavigator Clone() {
            return new XPathDocumentNavigator(this.pageCurrent, this.idxCurrent, this.pageParent, this.idxParent);
        } 

        ///  
        /// Get the XPath node type of the current node. 
        /// 
        public override XPathNodeType NodeType { 
            get { return this.pageCurrent[this.idxCurrent].NodeType; }
        }

        ///  
        /// Get the local name portion of the current node's name.
        ///  
        public override string LocalName { 
            get { return this.pageCurrent[this.idxCurrent].LocalName; }
        } 

        /// 
        /// Get the namespace portion of the current node's name.
        ///  
        public override string NamespaceURI {
            get { return this.pageCurrent[this.idxCurrent].NamespaceUri; } 
        } 

        ///  
        /// Get the name of the current node.
        /// 
        public override string Name {
            get { return this.pageCurrent[this.idxCurrent].Name; } 
        }
 
        ///  
        /// Get the prefix portion of the current node's name.
        ///  
        public override string Prefix {
            get { return this.pageCurrent[this.idxCurrent].Prefix; }
        }
 
        /// 
        /// Get the base URI of the current node. 
        ///  
        public override string BaseURI {
            get { 
                XPathNode[] page;
                int idx;

                if (this.idxParent != 0) { 
                    // Get BaseUri of parent for attribute, namespace, and collapsed text nodes
                    page = this.pageParent; 
                    idx = this.idxParent; 
                }
                else { 
                    page = this.pageCurrent;
                    idx = this.idxCurrent;
                }
 
                do {
                    switch (page[idx].NodeType) { 
                        case XPathNodeType.Element: 
                        case XPathNodeType.Root:
                        case XPathNodeType.ProcessingInstruction: 
                            // BaseUri is always stored with Elements, Roots, and PIs
                            return page[idx].BaseUri;
                    }
 
                    // Get BaseUri of parent
                    idx = page[idx].GetParent(out page); 
                } 
                while (idx != 0);
 
                return string.Empty;
            }
        }
 
        /// 
        /// Return true if this is an element which used a shortcut tag in its Xml 1.0 serialized form. 
        ///  
        public override bool IsEmptyElement {
            get { return this.pageCurrent[this.idxCurrent].AllowShortcutTag; } 
        }

        /// 
        /// Return the xml name table which was used to atomize all prefixes, local-names, and 
        /// namespace uris in the document.
        ///  
        public override XmlNameTable NameTable { 
            get { return this.pageCurrent[this.idxCurrent].Document.NameTable; }
        } 

        /// 
        /// Position the navigator on the first attribute of the current node and return true.  If no attributes
        /// can be found, return false. 
        /// 
        public override bool MoveToFirstAttribute() { 
            XPathNode[] page = this.pageCurrent; 
            int idx = this.idxCurrent;
 
            if (XPathNodeHelper.GetFirstAttribute(ref this.pageCurrent, ref this.idxCurrent)) {
                // Save element parent in order to make node-order comparison simpler
                this.pageParent = page;
                this.idxParent = idx; 
                return true;
            } 
 
            return false;
        } 

        /// 
        /// If positioned on an attribute, move to its next sibling attribute.  If no attributes can be found,
        /// return false. 
        /// 
        public override bool MoveToNextAttribute() { 
            return XPathNodeHelper.GetNextAttribute(ref this.pageCurrent, ref this.idxCurrent); 
        }
 
        /// 
        /// True if the current node has one or more attributes.
        /// 
        public override bool HasAttributes { 
            get { return this.pageCurrent[this.idxCurrent].HasAttribute; }
        } 
 
        /// 
        /// Position the navigator on the attribute with the specified name and return true.  If no matching 
        /// attribute can be found, return false.  Don't assume the name parts are atomized with respect
        /// to this document.
        /// 
        public override bool MoveToAttribute(string localName, string namespaceURI) { 
            XPathNode[] page = this.pageCurrent;
            int idx = this.idxCurrent; 
 
            if ((object) localName != (object) this.atomizedLocalName)
                this.atomizedLocalName = (localName != null) ? NameTable.Get(localName) : null; 

            if (XPathNodeHelper.GetAttribute(ref this.pageCurrent, ref this.idxCurrent, this.atomizedLocalName, namespaceURI)) {
                // Save element parent in order to make node-order comparison simpler
                this.pageParent = page; 
                this.idxParent = idx;
                return true; 
            } 

            return false; 
        }

        /// 
        /// Position the navigator on the namespace within the specified scope.  If no matching namespace 
        /// can be found, return false.
        ///  
        public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) { 
            XPathNode[] page;
            int idx; 

            if (namespaceScope == XPathNamespaceScope.Local) {
                // Get local namespaces only
                idx = XPathNodeHelper.GetLocalNamespaces(this.pageCurrent, this.idxCurrent, out page); 
            }
            else { 
                // Get all in-scope namespaces 
                idx = XPathNodeHelper.GetInScopeNamespaces(this.pageCurrent, this.idxCurrent, out page);
            } 

            while (idx != 0) {
                // Don't include the xmlns:xml namespace node if scope is ExcludeXml
                if (namespaceScope != XPathNamespaceScope.ExcludeXml || !page[idx].IsXmlNamespaceNode) { 
                    this.pageParent = this.pageCurrent;
                    this.idxParent = this.idxCurrent; 
                    this.pageCurrent = page; 
                    this.idxCurrent = idx;
                    return true; 
                }

                // Skip past xmlns:xml
                idx = page[idx].GetSibling(out page); 
            }
 
            return false; 
        }
 
        /// 
        /// Position the navigator on the next namespace within the specified scope.  If no matching namespace
        /// can be found, return false.
        ///  
        public override bool MoveToNextNamespace(XPathNamespaceScope scope) {
            XPathNode[] page = this.pageCurrent, pageParent; 
            int idx = this.idxCurrent, idxParent; 

            // If current node is not a namespace node, return false 
            if (page[idx].NodeType != XPathNodeType.Namespace)
                return false;

            while (true) { 
                // Get next namespace sibling
                idx = page[idx].GetSibling(out page); 
 
                // If there are no more nodes, return false
                if (idx == 0) 
                    return false;

                switch (scope) {
                    case XPathNamespaceScope.Local: 
                        // Once parent changes, there are no longer any local namespaces
                        idxParent = page[idx].GetParent(out pageParent); 
                        if (idxParent != this.idxParent || (object) pageParent != (object) this.pageParent) 
                            return false;
                        break; 

                    case XPathNamespaceScope.ExcludeXml:
                        // If node is xmlns:xml, then skip it
                        if (page[idx].IsXmlNamespaceNode) 
                            continue;
                        break; 
                } 

                // Found a matching next namespace node, so return it 
                break;
            }

            this.pageCurrent = page; 
            this.idxCurrent = idx;
            return true; 
        } 

        ///  
        /// If the current node is an attribute or namespace (not content), return false.  Otherwise,
        /// move to the next content node.  Return false if there are no more content nodes.
        /// 
        public override bool MoveToNext() { 
            return XPathNodeHelper.GetContentSibling(ref this.pageCurrent, ref this.idxCurrent);
        } 
 
        /// 
        /// If the current node is an attribute or namespace (not content), return false.  Otherwise, 
        /// move to the previous (sibling) content node.  Return false if there are no previous content nodes.
        /// 
        public override bool MoveToPrevious() {
            // If parent exists, then this is a namespace, an attribute, or a collapsed text node, all of which do 
            // not have previous siblings.
            if (this.idxParent != 0) 
                return false; 

            return XPathNodeHelper.GetPreviousContentSibling(ref this.pageCurrent, ref this.idxCurrent); 
        }

        /// 
        /// Move to the first content-typed child of the current node.  Return false if the current 
        /// node has no content children.
        ///  
        public override bool MoveToFirstChild() { 
            if (this.pageCurrent[this.idxCurrent].HasCollapsedText) {
                // Virtualize collapsed text nodes 
                this.pageParent = this.pageCurrent;
                this.idxParent = this.idxCurrent;
                this.idxCurrent = this.pageCurrent[this.idxCurrent].Document.GetCollapsedTextNode(out this.pageCurrent);
                return true; 
            }
 
            return XPathNodeHelper.GetContentChild(ref this.pageCurrent, ref this.idxCurrent); 
        }
 
        /// 
        /// Position the navigator on the parent of the current node.  If the current node has no parent,
        /// return false.
        ///  
        public override bool MoveToParent() {
            if (this.idxParent != 0) { 
                // 1. For attribute nodes, element parent is always stored in order to make node-order 
                //    comparison simpler.
                // 2. For namespace nodes, parent is always stored in navigator in order to virtualize 
                //    XPath 1.0 namespaces.
                // 3. For collapsed text nodes, element parent is always stored in navigator.
                Debug.Assert(this.pageParent != null);
                this.pageCurrent = this.pageParent; 
                this.idxCurrent = this.idxParent;
                this.pageParent = null; 
                this.idxParent = 0; 
                return true;
            } 

            return XPathNodeHelper.GetParent(ref this.pageCurrent, ref this.idxCurrent);
        }
 
        /// 
        /// Position this navigator to the same position as the "other" navigator.  If the "other" navigator 
        /// is not of the same type as this navigator, then return false. 
        /// 
        public override bool MoveTo(XPathNavigator other) { 
            XPathDocumentNavigator that = other as XPathDocumentNavigator;
            if (that != null) {
                this.pageCurrent = that.pageCurrent;
                this.idxCurrent = that.idxCurrent; 
                this.pageParent = that.pageParent;
                this.idxParent = that.idxParent; 
                return true; 
            }
            return false; 
        }

        /// 
        /// Position to the navigator to the element whose id is equal to the specified "id" string. 
        /// 
        public override bool MoveToId(string id) { 
            XPathNode[] page; 
            int idx;
 
            idx = this.pageCurrent[this.idxCurrent].Document.LookupIdElement(id, out page);
            if (idx != 0) {
                // Move to ID element and clear parent state
                Debug.Assert(page[idx].NodeType == XPathNodeType.Element); 
                this.pageCurrent = page;
                this.idxCurrent = idx; 
                this.pageParent = null; 
                this.idxParent = 0;
                return true; 
            }

            return false;
        } 

        ///  
        /// Returns true if this navigator is positioned to the same node as the "other" navigator.  Returns false 
        /// if not, or if the "other" navigator is not the same type as this navigator.
        ///  
        public override bool IsSamePosition(XPathNavigator other) {
            XPathDocumentNavigator that = other as XPathDocumentNavigator;
            if (that != null) {
                return this.idxCurrent == that.idxCurrent && this.pageCurrent == that.pageCurrent && 
                       this.idxParent == that.idxParent && this.pageParent == that.pageParent;
            } 
            return false; 
        }
 
        /// 
        /// Returns true if the current node has children.
        /// 
        public override bool HasChildren { 
            get { return this.pageCurrent[this.idxCurrent].HasContentChild; }
        } 
 
        /// 
        /// Position the navigator on the root node of the current document. 
        /// 
        public override void MoveToRoot() {
            if (this.idxParent != 0) {
                // Clear parent state 
                this.pageParent = null;
                this.idxParent = 0; 
            } 
            this.idxCurrent = this.pageCurrent[this.idxCurrent].GetRoot(out this.pageCurrent);
        } 

        /// 
        /// Move to the first element child of the current node with the specified name.  Return false
        /// if the current node has no matching element children. 
        /// 
        public override bool MoveToChild(string localName, string namespaceURI) { 
            if ((object) localName != (object) this.atomizedLocalName) 
                this.atomizedLocalName = (localName != null) ? NameTable.Get(localName) : null;
 
            return XPathNodeHelper.GetElementChild(ref this.pageCurrent, ref this.idxCurrent, this.atomizedLocalName, namespaceURI);
        }

        ///  
        /// Move to the first element sibling of the current node with the specified name.  Return false
        /// if the current node has no matching element siblings. 
        ///  
        public override bool MoveToNext(string localName, string namespaceURI) {
            if ((object) localName != (object) this.atomizedLocalName) 
                this.atomizedLocalName = (localName != null) ? NameTable.Get(localName) : null;

            return XPathNodeHelper.GetElementSibling(ref this.pageCurrent, ref this.idxCurrent, this.atomizedLocalName, namespaceURI);
        } 

        ///  
        /// Move to the first content child of the current node with the specified type.  Return false 
        /// if the current node has no matching children.
        ///  
        public override bool MoveToChild(XPathNodeType type) {
            if (this.pageCurrent[this.idxCurrent].HasCollapsedText) {
                // Only XPathNodeType.Text and XPathNodeType.All matches collapsed text node
                if (type != XPathNodeType.Text && type != XPathNodeType.All) 
                    return false;
 
                // Virtualize collapsed text nodes 
                this.pageParent = this.pageCurrent;
                this.idxParent = this.idxCurrent; 
                this.idxCurrent = this.pageCurrent[this.idxCurrent].Document.GetCollapsedTextNode(out this.pageCurrent);
                return true;
            }
 
            return XPathNodeHelper.GetContentChild(ref this.pageCurrent, ref this.idxCurrent, type);
        } 
 
        /// 
        /// Move to the first content sibling of the current node with the specified type.  Return false 
        /// if the current node has no matching siblings.
        /// 
        public override bool MoveToNext(XPathNodeType type) {
            return XPathNodeHelper.GetContentSibling(ref this.pageCurrent, ref this.idxCurrent, type); 
        }
 
        ///  
        /// Move to the next element that:
        ///   1. Follows the current node in document order (includes descendants, unlike XPath following axis) 
        ///   2. Precedes "end" in document order (if end is null, then all following nodes in the document are considered)
        ///   3. Has the specified QName
        /// Return false if the current node has no matching following elements.
        ///  
        public override bool MoveToFollowing(string localName, string namespaceURI, XPathNavigator end) {
            XPathNode[] pageEnd; 
            int idxEnd; 

            if ((object) localName != (object) this.atomizedLocalName) 
                this.atomizedLocalName = (localName != null) ? NameTable.Get(localName) : null;

            // Get node on which scan ends (null if rest of document should be scanned)
            idxEnd = GetFollowingEnd(end as XPathDocumentNavigator, false, out pageEnd); 

            // If this navigator is positioned on a virtual node, then compute following of parent 
            if (this.idxParent != 0) { 
                if (!XPathNodeHelper.GetElementFollowing(ref this.pageParent, ref this.idxParent, pageEnd, idxEnd, this.atomizedLocalName, namespaceURI))
                    return false; 

                this.pageCurrent = this.pageParent;
                this.idxCurrent = this.idxParent;
                this.pageParent = null; 
                this.idxParent = 0;
                return true; 
            } 

            return XPathNodeHelper.GetElementFollowing(ref this.pageCurrent, ref this.idxCurrent, pageEnd, idxEnd, this.atomizedLocalName, namespaceURI); 
        }

        /// 
        /// Move to the next node that: 
        ///   1. Follows the current node in document order (includes descendants, unlike XPath following axis)
        ///   2. Precedes "end" in document order (if end is null, then all following nodes in the document are considered) 
        ///   3. Has the specified XPathNodeType 
        /// Return false if the current node has no matching following nodes.
        ///  
        public override bool MoveToFollowing(XPathNodeType type, XPathNavigator end) {
            XPathDocumentNavigator endTiny = end as XPathDocumentNavigator;
            XPathNode[] page, pageEnd;
            int idx, idxEnd; 

            // If searching for text, make sure to handle collapsed text nodes correctly 
            if (type == XPathNodeType.Text || type == XPathNodeType.All) { 
                if (this.pageCurrent[this.idxCurrent].HasCollapsedText) {
                    // Positioned on an element with collapsed text, so return the virtual text node, assuming it's before "end" 
                    if (endTiny != null && this.idxCurrent == endTiny.idxParent && this.pageCurrent == endTiny.pageParent) {
                        // "end" is positioned to a virtual attribute, namespace, or text node
                        return false;
                    } 

                    this.pageParent = this.pageCurrent; 
                    this.idxParent = this.idxCurrent; 
                    this.idxCurrent = this.pageCurrent[this.idxCurrent].Document.GetCollapsedTextNode(out this.pageCurrent);
                    return true; 
                }

                if (type == XPathNodeType.Text) {
                    // Get node on which scan ends (null if rest of document should be scanned, parent if positioned on virtual node) 
                    idxEnd = GetFollowingEnd(endTiny, true, out pageEnd);
 
                    // If this navigator is positioned on a virtual node, then compute following of parent 
                    if (this.idxParent != 0) {
                        page = this.pageParent; 
                        idx = this.idxParent;
                    }
                    else {
                        page = this.pageCurrent; 
                        idx = this.idxCurrent;
                    } 
 
                    // If ending node is a virtual node, and current node is its parent, then we're done
                    if (endTiny != null && endTiny.idxParent != 0 && idx == idxEnd && page == pageEnd) 
                        return false;

                    // Get all virtual (collapsed) and physical text nodes which follow the current node
                    if (!XPathNodeHelper.GetTextFollowing(ref page, ref idx, pageEnd, idxEnd)) 
                        return false;
 
                    if (page[idx].NodeType == XPathNodeType.Element) { 
                        // Virtualize collapsed text nodes
                        Debug.Assert(page[idx].HasCollapsedText); 
                        this.idxCurrent = page[idx].Document.GetCollapsedTextNode(out this.pageCurrent);
                        this.pageParent = page;
                        this.idxParent = idx;
                    } 
                    else {
                        // Physical text node 
                        Debug.Assert(page[idx].IsText); 
                        this.pageCurrent = page;
                        this.idxCurrent = idx; 
                        this.pageParent = null;
                        this.idxParent = 0;
                    }
                    return true; 
                }
            } 
 
            // Get node on which scan ends (null if rest of document should be scanned, parent + 1 if positioned on virtual node)
            idxEnd = GetFollowingEnd(endTiny, false, out pageEnd); 

            // If this navigator is positioned on a virtual node, then compute following of parent
            if (this.idxParent != 0) {
                if (!XPathNodeHelper.GetContentFollowing(ref this.pageParent, ref this.idxParent, pageEnd, idxEnd, type)) 
                    return false;
 
                this.pageCurrent = this.pageParent; 
                this.idxCurrent = this.idxParent;
                this.pageParent = null; 
                this.idxParent = 0;
                return true;
            }
 
            return XPathNodeHelper.GetContentFollowing(ref this.pageCurrent, ref this.idxCurrent, pageEnd, idxEnd, type);
        } 
 
        /// 
        /// Return an iterator that ranges over all children of the current node that match the specified XPathNodeType. 
        /// 
        public override XPathNodeIterator SelectChildren(XPathNodeType type) {
            return new XPathDocumentKindChildIterator(this, type);
        } 

        ///  
        /// Return an iterator that ranges over all children of the current node that match the specified QName. 
        /// 
        public override XPathNodeIterator SelectChildren(string name, string namespaceURI) { 
            // If local name is wildcard, then call XPathNavigator.SelectChildren
            if (name == null || name.Length == 0)
                return base.SelectChildren(name, namespaceURI);
 
            return new XPathDocumentElementChildIterator(this, name, namespaceURI);
        } 
 
        /// 
        /// Return an iterator that ranges over all descendants of the current node that match the specified 
        /// XPathNodeType.  If matchSelf is true, then also perform the match on the current node.
        /// 
        public override XPathNodeIterator SelectDescendants(XPathNodeType type, bool matchSelf) {
            return new XPathDocumentKindDescendantIterator(this, type, matchSelf); 
        }
 
        ///  
        /// Return an iterator that ranges over all descendants of the current node that match the specified
        /// QName.  If matchSelf is true, then also perform the match on the current node. 
        /// 
        public override XPathNodeIterator SelectDescendants(string name, string namespaceURI, bool matchSelf) {
            // If local name is wildcard, then call XPathNavigator.SelectDescendants
            if (name == null || name.Length == 0) 
                return base.SelectDescendants(name, namespaceURI, matchSelf);
 
            return new XPathDocumentElementDescendantIterator(this, name, namespaceURI, matchSelf); 
        }
 
        /// 
        /// Returns:
        ///     XmlNodeOrder.Unknown -- This navigator and the "other" navigator are not of the same type, or the
        ///                             navigator's are not positioned on nodes in the same document. 
        ///     XmlNodeOrder.Before -- This navigator's current node is before the "other" navigator's current node
        ///                            in document order. 
        ///     XmlNodeOrder.After -- This navigator's current node is after the "other" navigator's current node 
        ///                           in document order.
        ///     XmlNodeOrder.Same -- This navigator is positioned on the same node as the "other" navigator. 
        /// 
        public override XmlNodeOrder ComparePosition(XPathNavigator other) {
            XPathDocumentNavigator that = other as XPathDocumentNavigator;
            if (that != null) { 
                XPathDocument thisDoc = this.pageCurrent[this.idxCurrent].Document;
                XPathDocument thatDoc = that.pageCurrent[that.idxCurrent].Document; 
                if ((object) thisDoc == (object) thatDoc) { 
                    int locThis = GetPrimaryLocation();
                    int locThat = that.GetPrimaryLocation(); 

                    if (locThis == locThat) {
                        locThis = GetSecondaryLocation();
                        locThat = that.GetSecondaryLocation(); 

                        if (locThis == locThat) 
                            return XmlNodeOrder.Same; 
                    }
                    return (locThis < locThat) ? XmlNodeOrder.Before : XmlNodeOrder.After; 
                }
            }
            return XmlNodeOrder.Unknown;
        } 

        ///  
        /// Return true if the "other" navigator's current node is a descendant of this navigator's current node. 
        /// 
        public override bool IsDescendant(XPathNavigator other) { 
            XPathDocumentNavigator that = other as XPathDocumentNavigator;
            if (that != null) {
                XPathNode[] pageThat;
                int idxThat; 

                // If that current node's parent is virtualized, then start with the virtual parent 
                if (that.idxParent != 0) { 
                    pageThat = that.pageParent;
                    idxThat = that.idxParent; 
                }
                else {
                    idxThat = that.pageCurrent[that.idxCurrent].GetParent(out pageThat);
                } 

                while (idxThat != 0) { 
                    if (idxThat == this.idxCurrent && pageThat == this.pageCurrent) 
                        return true;
                    idxThat = pageThat[idxThat].GetParent(out pageThat); 
                }
            }
            return false;
        } 

        ///  
        /// Construct a primary location for this navigator.  The location is an integer that can be 
        /// easily compared with other locations in the same document in order to determine the relative
        /// document order of two nodes.  If two locations compare equal, then secondary locations should 
        /// be compared.
        /// 
        private int GetPrimaryLocation() {
            // Is the current node virtualized? 
            if (this.idxParent == 0) {
                // No, so primary location should be derived from current node 
                return XPathNodeHelper.GetLocation(this.pageCurrent, this.idxCurrent); 
            }
 
            // Yes, so primary location should be derived from parent node
            return XPathNodeHelper.GetLocation(this.pageParent, this.idxParent);
        }
 
        /// 
        /// Construct a secondary location for this navigator.  This location should only be used if 
        /// primary locations previously compared equal. 
        /// 
        private int GetSecondaryLocation() { 
            // Is the current node virtualized?
            if (this.idxParent == 0) {
                // No, so secondary location is int.MinValue (always first)
                return int.MinValue; 
            }
 
            // Yes, so secondary location should be derived from current node 
            // This happens with attributes nodes, namespace nodes, collapsed text nodes, and atomic values
            switch (this.pageCurrent[this.idxCurrent].NodeType) { 
                case XPathNodeType.Namespace:
                    // Namespace nodes come first (make location negative, but greater than int.MinValue)
                    return int.MinValue + 1 + XPathNodeHelper.GetLocation(this.pageCurrent, this.idxCurrent);
 
                case XPathNodeType.Attribute:
                    // Attribute nodes come next (location is always positive) 
                    return XPathNodeHelper.GetLocation(this.pageCurrent, this.idxCurrent); 

                default: 
                    // Collapsed text nodes are always last
                    return int.MaxValue;
            }
        } 

        ///  
        /// Create a unique id for the current node.  This is used by the generate-id() function. 
        /// 
        internal override string UniqueId { 
            get {
                // 32-bit integer is split into 5-bit groups, the maximum number of groups is 7
                char[] buf = new char[1+7+1+7];
                int idx = 0; 
                int loc;
 
                // Ensure distinguishing attributes, namespaces and child nodes 
                buf[idx++] = NodeTypeLetter[(int)this.pageCurrent[this.idxCurrent].NodeType];
 
                // If the current node is virtualized, code its parent
                if (this.idxParent != 0) {
                    loc = (this.pageParent[0].PageInfo.PageNumber - 1) << 16 | (this.idxParent - 1);
                    do { 
                        buf[idx++] = UniqueIdTbl[loc & 0x1f];
                        loc >>= 5; 
                    } while (loc != 0); 
                    buf[idx++] = '0';
                } 

                // Code the node itself
                loc = (this.pageCurrent[0].PageInfo.PageNumber - 1) << 16 | (this.idxCurrent - 1);
                do { 
                    buf[idx++] = UniqueIdTbl[loc & 0x1f];
                    loc >>= 5; 
                } while (loc != 0); 

                return new string(buf, 0, idx); 
            }
        }

        //----------------------------------------------- 
        // IXmlLineInfo
        //----------------------------------------------- 
 
        /// 
        /// Return true if line number information is recorded in the cache. 
        /// 
        public bool HasLineInfo() {
            return this.pageCurrent[this.idxCurrent].Document.HasLineInfo;
        } 

        ///  
        /// Return the source line number of the current node. 
        /// 
        public int LineNumber { 
            get {
                // If the current node is a collapsed text node, then return parent element's line number
                if (this.idxParent != 0 && NodeType == XPathNodeType.Text)
                    return this.pageParent[this.idxParent].LineNumber; 

                return this.pageCurrent[this.idxCurrent].LineNumber; 
            } 
        }
 
        /// 
        /// Return the source line position of the current node.
        /// 
        public int LinePosition { 
            get {
                // If the current node is a collapsed text node, then get position from parent element 
                if (this.idxParent != 0 && NodeType == XPathNodeType.Text) 
                    return this.pageParent[this.idxParent].CollapsedLinePosition;
 
                return this.pageCurrent[this.idxCurrent].LinePosition;
            }
        }
 

        //----------------------------------------------- 
        // Helper methods 
        //-----------------------------------------------
 
        /// 
        /// Get hashcode based on current position of the navigator.
        /// 
        public int GetPositionHashCode() { 
            return this.idxCurrent ^ this.idxParent;
        } 
 
        /// 
        /// Return true if navigator is positioned to an element having the specified name. 
        /// 
        public bool IsElementMatch(string localName, string namespaceURI) {
            if ((object) localName != (object) this.atomizedLocalName)
                this.atomizedLocalName = (localName != null) ? NameTable.Get(localName) : null; 

            // Cannot be an element if parent is stored 
            if (this.idxParent != 0) 
                return false;
 
            return this.pageCurrent[this.idxCurrent].ElementMatch(this.atomizedLocalName, namespaceURI);
        }

        ///  
        /// Return true if navigator is positioned to a content node of the specified kind.  Whitespace/SignficantWhitespace/Text are
        /// all treated the same (i.e. they all match each other). 
        ///  
        public bool IsContentKindMatch(XPathNodeType typ) {
            return (((1 << (int) this.pageCurrent[this.idxCurrent].NodeType) & GetContentKindMask(typ)) != 0); 
        }

        /// 
        /// Return true if navigator is positioned to a node of the specified kind.  Whitespace/SignficantWhitespace/Text are 
        /// all treated the same (i.e. they all match each other).
        ///  
        public bool IsKindMatch(XPathNodeType typ) { 
            return (((1 << (int) this.pageCurrent[this.idxCurrent].NodeType) & GetKindMask(typ)) != 0);
        } 

        /// 
        /// "end" is positioned on a node which terminates a following scan.  Return the page and index of "end" if it
        /// is positioned to a non-virtual node.  If "end" is positioned to a virtual node: 
        ///    1. If useParentOfVirtual is true, then return the page and index of the virtual node's parent
        ///    2. If useParentOfVirtual is false, then return the page and index of the virtual node's parent + 1. 
        ///  
        private int GetFollowingEnd(XPathDocumentNavigator end, bool useParentOfVirtual, out XPathNode[] pageEnd) {
            // If ending navigator is positioned to a node in another document, then return null 
            if (end != null && this.pageCurrent[this.idxCurrent].Document == end.pageCurrent[end.idxCurrent].Document) {

                // If the ending navigator is not positioned on a virtual node, then return its current node
                if (end.idxParent == 0) { 
                    pageEnd = end.pageCurrent;
                    return end.idxCurrent; 
                } 

                // If the ending navigator is positioned on an attribute, namespace, or virtual text node, then use the 
                // next physical node instead, as the results will be the same.
                pageEnd = end.pageParent;
                return (useParentOfVirtual) ? end.idxParent : end.idxParent + 1;
            } 

            // No following, so set pageEnd to null and return an index of 0 
            pageEnd = null; 
            return 0;
        } 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK