| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949 |
- //------------------------------------------------------------------------------
- // <copyright file="XPathDocumentNavigator.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">Microsoft</owner>
- //------------------------------------------------------------------------------
- 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 {
- /// <summary>
- /// This is the default XPath/XQuery data model cache implementation. It will be used whenever
- /// the user does not supply his own XPathNavigator implementation.
- /// </summary>
- internal sealed class XPathDocumentNavigator : XPathNavigator, IXmlLineInfo {
- private XPathNode[] pageCurrent;
- private XPathNode[] pageParent;
- private int idxCurrent;
- private int idxParent;
- private string atomizedLocalName;
- //-----------------------------------------------
- // Constructors
- //-----------------------------------------------
- /// <summary>
- /// 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).
- /// </summary>
- 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;
- }
- /// <summary>
- /// Copy constructor.
- /// </summary>
- public XPathDocumentNavigator(XPathDocumentNavigator nav) : this(nav.pageCurrent, nav.idxCurrent, nav.pageParent, nav.idxParent) {
- this.atomizedLocalName = nav.atomizedLocalName;
- }
- //-----------------------------------------------
- // XPathItem
- //-----------------------------------------------
- /// <summary>
- /// 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.
- /// </summary>
- 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
- //-----------------------------------------------
- /// <summary>
- /// Create a copy of this navigator, positioned to the same node in the tree.
- /// </summary>
- public override XPathNavigator Clone() {
- return new XPathDocumentNavigator(this.pageCurrent, this.idxCurrent, this.pageParent, this.idxParent);
- }
- /// <summary>
- /// Get the XPath node type of the current node.
- /// </summary>
- public override XPathNodeType NodeType {
- get { return this.pageCurrent[this.idxCurrent].NodeType; }
- }
- /// <summary>
- /// Get the local name portion of the current node's name.
- /// </summary>
- public override string LocalName {
- get { return this.pageCurrent[this.idxCurrent].LocalName; }
- }
- /// <summary>
- /// Get the namespace portion of the current node's name.
- /// </summary>
- public override string NamespaceURI {
- get { return this.pageCurrent[this.idxCurrent].NamespaceUri; }
- }
- /// <summary>
- /// Get the name of the current node.
- /// </summary>
- public override string Name {
- get { return this.pageCurrent[this.idxCurrent].Name; }
- }
- /// <summary>
- /// Get the prefix portion of the current node's name.
- /// </summary>
- public override string Prefix {
- get { return this.pageCurrent[this.idxCurrent].Prefix; }
- }
- /// <summary>
- /// Get the base URI of the current node.
- /// </summary>
- 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;
- }
- }
- /// <summary>
- /// Return true if this is an element which used a shortcut tag in its Xml 1.0 serialized form.
- /// </summary>
- public override bool IsEmptyElement {
- get { return this.pageCurrent[this.idxCurrent].AllowShortcutTag; }
- }
- /// <summary>
- /// Return the xml name table which was used to atomize all prefixes, local-names, and
- /// namespace uris in the document.
- /// </summary>
- public override XmlNameTable NameTable {
- get { return this.pageCurrent[this.idxCurrent].Document.NameTable; }
- }
- /// <summary>
- /// Position the navigator on the first attribute of the current node and return true. If no attributes
- /// can be found, return false.
- /// </summary>
- 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;
- }
- /// <summary>
- /// If positioned on an attribute, move to its next sibling attribute. If no attributes can be found,
- /// return false.
- /// </summary>
- public override bool MoveToNextAttribute() {
- return XPathNodeHelper.GetNextAttribute(ref this.pageCurrent, ref this.idxCurrent);
- }
- /// <summary>
- /// True if the current node has one or more attributes.
- /// </summary>
- public override bool HasAttributes {
- get { return this.pageCurrent[this.idxCurrent].HasAttribute; }
- }
- /// <summary>
- /// 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.
- /// </summary>
- 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;
- }
- /// <summary>
- /// Position the navigator on the namespace within the specified scope. If no matching namespace
- /// can be found, return false.
- /// </summary>
- 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;
- }
- /// <summary>
- /// Position the navigator on the next namespace within the specified scope. If no matching namespace
- /// can be found, return false.
- /// </summary>
- 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;
- }
- /// <summary>
- /// 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.
- /// </summary>
- public override bool MoveToNext() {
- return XPathNodeHelper.GetContentSibling(ref this.pageCurrent, ref this.idxCurrent);
- }
- /// <summary>
- /// 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.
- /// </summary>
- 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);
- }
- /// <summary>
- /// Move to the first content-typed child of the current node. Return false if the current
- /// node has no content children.
- /// </summary>
- 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);
- }
- /// <summary>
- /// Position the navigator on the parent of the current node. If the current node has no parent,
- /// return false.
- /// </summary>
- 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);
- }
- /// <summary>
- /// 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.
- /// </summary>
- 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;
- }
- /// <summary>
- /// Position to the navigator to the element whose id is equal to the specified "id" string.
- /// </summary>
- 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;
- }
- /// <summary>
- /// 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.
- /// </summary>
- 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;
- }
- /// <summary>
- /// Returns true if the current node has children.
- /// </summary>
- public override bool HasChildren {
- get { return this.pageCurrent[this.idxCurrent].HasContentChild; }
- }
- /// <summary>
- /// Position the navigator on the root node of the current document.
- /// </summary>
- 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);
- }
- /// <summary>
- /// 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.
- /// </summary>
- 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);
- }
- /// <summary>
- /// 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.
- /// </summary>
- 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);
- }
- /// <summary>
- /// Move to the first content child of the current node with the specified type. Return false
- /// if the current node has no matching children.
- /// </summary>
- 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);
- }
- /// <summary>
- /// Move to the first content sibling of the current node with the specified type. Return false
- /// if the current node has no matching siblings.
- /// </summary>
- public override bool MoveToNext(XPathNodeType type) {
- return XPathNodeHelper.GetContentSibling(ref this.pageCurrent, ref this.idxCurrent, type);
- }
- /// <summary>
- /// 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.
- /// </summary>
- 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);
- }
- /// <summary>
- /// 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.
- /// </summary>
- 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);
- }
- /// <summary>
- /// Return an iterator that ranges over all children of the current node that match the specified XPathNodeType.
- /// </summary>
- public override XPathNodeIterator SelectChildren(XPathNodeType type) {
- return new XPathDocumentKindChildIterator(this, type);
- }
- /// <summary>
- /// Return an iterator that ranges over all children of the current node that match the specified QName.
- /// </summary>
- 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);
- }
- /// <summary>
- /// 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.
- /// </summary>
- public override XPathNodeIterator SelectDescendants(XPathNodeType type, bool matchSelf) {
- return new XPathDocumentKindDescendantIterator(this, type, matchSelf);
- }
- /// <summary>
- /// 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.
- /// </summary>
- 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);
- }
- /// <summary>
- /// 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.
- /// </summary>
- 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;
- }
- /// <summary>
- /// Return true if the "other" navigator's current node is a descendant of this navigator's current node.
- /// </summary>
- 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;
- }
- /// <summary>
- /// 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.
- /// </summary>
- 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);
- }
- /// <summary>
- /// Construct a secondary location for this navigator. This location should only be used if
- /// primary locations previously compared equal.
- /// </summary>
- 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;
- }
- }
- /// <summary>
- /// Create a unique id for the current node. This is used by the generate-id() function.
- /// </summary>
- 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);
- }
- }
- public override object UnderlyingObject {
- get {
- // Since we don't have any underlying PUBLIC object
- // the best one we can return is a clone of the navigator.
- // Note that it should be a clone as the user might Move the returned navigator
- // around and thus cause unexpected behavior of the caller of this class (For example the validator)
- return this.Clone();
- }
- }
- //-----------------------------------------------
- // IXmlLineInfo
- //-----------------------------------------------
- /// <summary>
- /// Return true if line number information is recorded in the cache.
- /// </summary>
- public bool HasLineInfo() {
- return this.pageCurrent[this.idxCurrent].Document.HasLineInfo;
- }
- /// <summary>
- /// Return the source line number of the current node.
- /// </summary>
- 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;
- }
- }
- /// <summary>
- /// Return the source line position of the current node.
- /// </summary>
- 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
- //-----------------------------------------------
- /// <summary>
- /// Get hashcode based on current position of the navigator.
- /// </summary>
- public int GetPositionHashCode() {
- return this.idxCurrent ^ this.idxParent;
- }
- /// <summary>
- /// Return true if navigator is positioned to an element having the specified name.
- /// </summary>
- 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);
- }
- /// <summary>
- /// 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).
- /// </summary>
- public bool IsContentKindMatch(XPathNodeType typ) {
- return (((1 << (int) this.pageCurrent[this.idxCurrent].NodeType) & GetContentKindMask(typ)) != 0);
- }
- /// <summary>
- /// 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).
- /// </summary>
- public bool IsKindMatch(XPathNodeType typ) {
- return (((1 << (int) this.pageCurrent[this.idxCurrent].NodeType) & GetKindMask(typ)) != 0);
- }
- /// <summary>
- /// "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.
- /// </summary>
- 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;
- }
- }
- }
|