The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.Runtime \ AttributeIterator

  1. //------------------------------------------------------------------------------
  2. // <copyright file="ContentIterators.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. using System;
  16. using System.Xml;
  17. using System.Xml.XPath;
  18. using System.Xml.Schema;
  19. using System.Diagnostics;
  20. using System.Collections;
  21. using System.ComponentModel;
  22. namespace System.Xml.Xsl.Runtime
  23. {
  24.    
  25.     /// <summary>
  26.     /// Iterate over all child content nodes (this is different from the QIL Content operator, which iterates over content + attributes).
  27.     /// </summary>
  28.     [EditorBrowsable(EditorBrowsableState.Never)]
  29.     public struct ContentIterator
  30.     {
  31.         private XPathNavigator navCurrent;
  32.         private bool needFirst;
  33.        
  34.         /// <summary>
  35.         /// Initialize the ContentIterator.
  36.         /// </summary>
  37.         public void Create(XPathNavigator context)
  38.         {
  39.             this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
  40.             this.needFirst = true;
  41.         }
  42.        
  43.         /// <summary>
  44.         /// Position the iterator on the next child content node. Return true if such a child exists and
  45.         /// set Current property. Otherwise, return false (Current property is undefined).
  46.         /// </summary>
  47.         public bool MoveNext()
  48.         {
  49.             if (this.needFirst) {
  50.                 this.needFirst = !this.navCurrent.MoveToFirstChild();
  51.                 return !this.needFirst;
  52.             }
  53.             return this.navCurrent.MoveToNext();
  54.         }
  55.        
  56.         /// <summary>
  57.         /// Return the current result navigator. This is only defined after MoveNext() has returned true.
  58.         /// </summary>
  59.         public XPathNavigator Current {
  60.             get { return this.navCurrent; }
  61.         }
  62.     }
  63.    
  64.    
  65.     /// <summary>
  66.     /// Iterate over all child elements with a matching name.
  67.     /// </summary>
  68.     [EditorBrowsable(EditorBrowsableState.Never)]
  69.     public struct ElementContentIterator
  70.     {
  71.         private string localName, ns;
  72.         private XPathNavigator navCurrent;
  73.         private bool needFirst;
  74.        
  75.         /// <summary>
  76.         /// Initialize the ElementContentIterator.
  77.         /// </summary>
  78.         public void Create(XPathNavigator context, string localName, string ns)
  79.         {
  80.             this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
  81.             this.localName = localName;
  82.             this.ns = ns;
  83.             this.needFirst = true;
  84.         }
  85.        
  86.         /// <summary>
  87.         /// Position the iterator on the next child element with a matching name. Return true if such a child exists and
  88.         /// set Current property. Otherwise, return false (Current property is undefined).
  89.         /// </summary>
  90.         public bool MoveNext()
  91.         {
  92.             if (this.needFirst) {
  93.                 this.needFirst = !this.navCurrent.MoveToChild(this.localName, this.ns);
  94.                 return !this.needFirst;
  95.             }
  96.             return this.navCurrent.MoveToNext(this.localName, this.ns);
  97.         }
  98.        
  99.         /// <summary>
  100.         /// Return the current result navigator. This is only defined after MoveNext() has returned true.
  101.         /// </summary>
  102.         public XPathNavigator Current {
  103.             get { return this.navCurrent; }
  104.         }
  105.     }
  106.    
  107.    
  108.     /// <summary>
  109.     /// Iterate over all child content nodes with a matching node kind.
  110.     /// </summary>
  111.     [EditorBrowsable(EditorBrowsableState.Never)]
  112.     public struct NodeKindContentIterator
  113.     {
  114.         private XPathNodeType nodeType;
  115.         private XPathNavigator navCurrent;
  116.         private bool needFirst;
  117.        
  118.         /// <summary>
  119.         /// Initialize the NodeKindContentIterator.
  120.         /// </summary>
  121.         public void Create(XPathNavigator context, XPathNodeType nodeType)
  122.         {
  123.             Debug.Assert(nodeType != XPathNodeType.Attribute && nodeType != XPathNodeType.Namespace);
  124.             this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
  125.             this.nodeType = nodeType;
  126.             this.needFirst = true;
  127.         }
  128.        
  129.         /// <summary>
  130.         /// Position the iterator on the next child content node with a matching node kind. Return true if such a child
  131.         /// exists and set Current property. Otherwise, return false (Current property is undefined).
  132.         /// </summary>
  133.         public bool MoveNext()
  134.         {
  135.             if (this.needFirst) {
  136.                 this.needFirst = !this.navCurrent.MoveToChild(this.nodeType);
  137.                 return !this.needFirst;
  138.             }
  139.             return this.navCurrent.MoveToNext(this.nodeType);
  140.         }
  141.        
  142.         /// <summary>
  143.         /// Return the current result navigator. This is only defined after MoveNext() has returned true.
  144.         /// </summary>
  145.         public XPathNavigator Current {
  146.             get { return this.navCurrent; }
  147.         }
  148.     }
  149.    
  150.    
  151.     /// <summary>
  152.     /// Iterate over all attributes.
  153.     /// </summary>
  154.     [EditorBrowsable(EditorBrowsableState.Never)]
  155.     public struct AttributeIterator
  156.     {
  157.         private XPathNavigator navCurrent;
  158.         private bool needFirst;
  159.        
  160.         /// <summary>
  161.         /// Initialize the AttributeIterator.
  162.         /// </summary>
  163.         public void Create(XPathNavigator context)
  164.         {
  165.             this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
  166.             this.needFirst = true;
  167.         }
  168.        
  169.         /// <summary>
  170.         /// Position the iterator on the attribute. Return true if such a child exists and set Current
  171.         /// property. Otherwise, return false (Current property is undefined).
  172.         /// </summary>
  173.         public bool MoveNext()
  174.         {
  175.             if (this.needFirst) {
  176.                 this.needFirst = !this.navCurrent.MoveToFirstAttribute();
  177.                 return !this.needFirst;
  178.             }
  179.             return this.navCurrent.MoveToNextAttribute();
  180.         }
  181.        
  182.         /// <summary>
  183.         /// Return the current result navigator. This is only defined after MoveNext() has returned true.
  184.         /// </summary>
  185.         public XPathNavigator Current {
  186.             get { return this.navCurrent; }
  187.         }
  188.     }
  189.    
  190.    
  191.     /// <summary>
  192.     /// Iterate over all namespace nodes.
  193.     /// </summary>
  194.     [EditorBrowsable(EditorBrowsableState.Never)]
  195.     public struct NamespaceIterator
  196.     {
  197.         private XPathNavigator navCurrent;
  198.         private XmlNavigatorStack navStack;
  199.        
  200.         /// <summary>
  201.         /// Initialize the NamespaceIterator.
  202.         /// </summary>
  203.         public void Create(XPathNavigator context)
  204.         {
  205.             // Push all of context's in-scope namespaces onto a stack in order to return them in document order
  206.             // (MoveToXXXNamespace methods return namespaces in reverse document order)
  207.             this.navStack.Reset();
  208.             if (context.MoveToFirstNamespace(XPathNamespaceScope.All)) {
  209.                 do {
  210.                     // Don't return the default namespace undeclaration
  211.                     if (context.LocalName.Length != 0 || context.Value.Length != 0)
  212.                         this.navStack.Push(context.Clone());
  213.                 }
  214.                 while (context.MoveToNextNamespace(XPathNamespaceScope.All));
  215.                
  216.                 context.MoveToParent();
  217.             }
  218.         }
  219.        
  220.         /// <summary>
  221.         /// Pop the top namespace from the stack and save it as navCurrent. If there are no more namespaces, return false.
  222.         /// </summary>
  223.         public bool MoveNext()
  224.         {
  225.             if (this.navStack.IsEmpty)
  226.                 return false;
  227.            
  228.             this.navCurrent = this.navStack.Pop();
  229.             return true;
  230.         }
  231.        
  232.         /// <summary>
  233.         /// Return the current result navigator. This is only defined after MoveNext() has returned true.
  234.         /// </summary>
  235.         public XPathNavigator Current {
  236.             get { return this.navCurrent; }
  237.         }
  238.     }
  239.    
  240.    
  241.     /// <summary>
  242.     /// Iterate over all attribute and child content nodes.
  243.     /// </summary>
  244.     [EditorBrowsable(EditorBrowsableState.Never)]
  245.     public struct AttributeContentIterator
  246.     {
  247.         private XPathNavigator navCurrent;
  248.         private bool needFirst;
  249.        
  250.         /// <summary>
  251.         /// Initialize the AttributeContentIterator.
  252.         /// </summary>
  253.         public void Create(XPathNavigator context)
  254.         {
  255.             this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, context);
  256.             this.needFirst = true;
  257.         }
  258.        
  259.         /// <summary>
  260.         /// Position the iterator on the next child content node with a matching node kind. Return true if such a child
  261.         /// exists and set Current property. Otherwise, return false (Current property is undefined).
  262.         /// </summary>
  263.         public bool MoveNext()
  264.         {
  265.             if (this.needFirst) {
  266.                 this.needFirst = !XmlNavNeverFilter.MoveToFirstAttributeContent(this.navCurrent);
  267.                 return !this.needFirst;
  268.             }
  269.             return XmlNavNeverFilter.MoveToNextAttributeContent(this.navCurrent);
  270.         }
  271.        
  272.         /// <summary>
  273.         /// Return the current result navigator. This is only defined after MoveNext() has returned true.
  274.         /// </summary>
  275.         public XPathNavigator Current {
  276.             get { return this.navCurrent; }
  277.         }
  278.     }
  279.    
  280.    
  281.     /// <summary>
  282.     /// Iterate over child content nodes or following-sibling nodes. Maintain document order by using a stack. Input
  283.     /// nodes are assumed to be in document order, but can contain one another (ContentIterator doesn't allow this).
  284.     /// </summary>
  285.     /// <remarks>
  286.     /// 1. Assume that the list I of input nodes is in document order, with no duplicates. There are N nodes in list I.
  287.     /// 2. For each node in list I, derive a list of nodes consisting of matching children or following-sibling nodes.
  288.     /// Call these lists S(1)...S(N).
  289.     /// 3. Let F be the first node in any list S(X), where X >= 1 and X < N
  290.     /// 4. There exists exactly one contiguous sequence of lists S(Y)...S(Z), where Y > X and Z <= N, such that the lists
  291.     /// S(X+1)...S(N) can be partitioned into these three groups:
  292.     /// a. 1st group (S(X+1)...S(Y-1)) -- All nodes in these lists precede F in document order
  293.     /// b. 2nd group (S(Y)...S(Z)) -- All nodes in these lists are duplicates of nodes in list S(X)
  294.     /// c. 3rd group (> S(Z)) -- All nodes in these lists succeed F in document order
  295.     /// 5. Given #4, node F can be returned once all nodes in the 1st group have been returned. Lists S(Y)...S(Z) can be
  296.     /// discarded. And only a single node in the 3rd group need be generated in order to guarantee that all nodes in
  297.     /// the 1st and 2nd groups have already been generated.
  298.     /// </remarks>
  299.     [EditorBrowsable(EditorBrowsableState.Never)]
  300.     public struct ContentMergeIterator
  301.     {
  302.         private XmlNavigatorFilter filter;
  303.         private XPathNavigator navCurrent, navNext;
  304.         private XmlNavigatorStack navStack;
  305.         private IteratorState state;
  306.        
  307.         private enum IteratorState
  308.         {
  309.             NeedCurrent = 0,
  310.             HaveCurrentNeedNext,
  311.             HaveCurrentNoNext,
  312.             HaveCurrentHaveNext
  313.         }
  314.        
  315.         /// <summary>
  316.         /// Initialize the ContentMergeIterator (merge multiple sets of content nodes in document order and remove duplicates).
  317.         /// </summary>
  318.         public void Create(XmlNavigatorFilter filter)
  319.         {
  320.             this.filter = filter;
  321.             this.navStack.Reset();
  322.             this.state = IteratorState.NeedCurrent;
  323.         }
  324.        
  325.         /// <summary>
  326.         /// Position this iterator to the next content or sibling node. Return IteratorResult.NoMoreNodes if there are
  327.         /// no more content or sibling nodes. Return IteratorResult.NeedInputNode if the next input node needs to be
  328.         /// fetched first. Return IteratorResult.HaveCurrent if the Current property is set to the next node in the
  329.         /// iteration.
  330.         /// </summary>
  331.         public IteratorResult MoveNext(XPathNavigator input)
  332.         {
  333.             return MoveNext(input, true);
  334.         }
  335.        
  336.         /// <summary>
  337.         /// Position this iterator to the next content or sibling node. Return IteratorResult.NoMoreNodes if there are
  338.         /// no more content or sibling nodes. Return IteratorResult.NeedInputNode if the next input node needs to be
  339.         /// fetched first. Return IteratorResult.HaveCurrent if the Current property is set to the next node in the
  340.         /// iteration.
  341.         /// </summary>
  342.         internal IteratorResult MoveNext(XPathNavigator input, bool isContent)
  343.         {
  344.             switch (this.state) {
  345.                 case IteratorState.NeedCurrent:
  346.                     // If there are no more input nodes, then iteration is complete
  347.                     if (input == null)
  348.                         return IteratorResult.NoMoreNodes;
  349.                    
  350.                     // Save the input node as the current node
  351.                     this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, input);
  352.                    
  353.                     // If matching child or sibling is found, then we have a current node
  354.                     if (isContent ? this.filter.MoveToContent(this.navCurrent) : this.filter.MoveToFollowingSibling(this.navCurrent))
  355.                         this.state = IteratorState.HaveCurrentNeedNext;
  356.                    
  357.                     return IteratorResult.NeedInputNode;
  358.                 case IteratorState.HaveCurrentNeedNext:
  359.                    
  360.                     if (input == null) {
  361.                         // There are no more input nodes, so enter HaveCurrentNoNext state and return Current
  362.                         this.state = IteratorState.HaveCurrentNoNext;
  363.                         return IteratorResult.HaveCurrentNode;
  364.                     }
  365.                    
  366.                     // Save the input node as the next node
  367.                     this.navNext = XmlQueryRuntime.SyncToNavigator(this.navNext, input);
  368.                    
  369.                     // If matching child or sibling is found,
  370.                     if (isContent ? this.filter.MoveToContent(this.navNext) : this.filter.MoveToFollowingSibling(this.navNext)) {
  371.                         // Then compare position of current and next nodes
  372.                         this.state = IteratorState.HaveCurrentHaveNext;
  373.                         return DocOrderMerge();
  374.                     }
  375.                    
  376.                     // Input node does not result in matching child or sibling, so get next input node
  377.                     return IteratorResult.NeedInputNode;
  378.                 case IteratorState.HaveCurrentNoNext:
  379.                 case IteratorState.HaveCurrentHaveNext:
  380.                    
  381.                     // If the current node has no more matching siblings,
  382.                     if (isContent ? !this.filter.MoveToNextContent(this.navCurrent) : !this.filter.MoveToFollowingSibling(this.navCurrent)) {
  383.                         if (this.navStack.IsEmpty) {
  384.                             if (this.state == IteratorState.HaveCurrentNoNext) {
  385.                                 // No more input nodes, so iteration is complete
  386.                                 return IteratorResult.NoMoreNodes;
  387.                             }
  388.                            
  389.                             // Make navNext the new current node and fetch a new navNext
  390.                             this.navCurrent = XmlQueryRuntime.SyncToNavigator(this.navCurrent, this.navNext);
  391.                             this.state = IteratorState.HaveCurrentNeedNext;
  392.                             return IteratorResult.NeedInputNode;
  393.                         }
  394.                        
  395.                         // Pop new current node from the stack
  396.                         this.navCurrent = this.navStack.Pop();
  397.                     }
  398.                    
  399.                     // If there is no next node, then no need to call DocOrderMerge; just return the current node
  400.                     if (this.state == IteratorState.HaveCurrentNoNext)
  401.                         return IteratorResult.HaveCurrentNode;
  402.                    
  403.                     // Compare positions of current and next nodes
  404.                     return DocOrderMerge();
  405.             }
  406.            
  407.             Debug.Assert(false, "Invalid IteratorState " + this.state);
  408.             return IteratorResult.NoMoreNodes;
  409.         }
  410.        
  411.         /// <summary>
  412.         /// Return the current result navigator. This is only defined after MoveNext() has returned IteratorResult.HaveCurrentNode.
  413.         /// </summary>
  414.         public XPathNavigator Current {
  415.             get { return this.navCurrent; }
  416.         }
  417.        
  418.         /// <summary>
  419.         /// If the context node-set returns a node that is contained in the subtree of the previous node,
  420.         /// then returning children of each node in "natural" order may not correspond to document order.
  421.         /// Therefore, in order to guarantee document order, keep a stack in order to push the sibling of
  422.         /// ancestor nodes. These siblings will not be returned until all of the descendants' children are
  423.         /// returned first.
  424.         /// </summary>
  425.         private IteratorResult DocOrderMerge()
  426.         {
  427.             XmlNodeOrder cmp;
  428.             Debug.Assert(this.state == IteratorState.HaveCurrentHaveNext);
  429.            
  430.             // Compare location of navCurrent with navNext
  431.             cmp = this.navCurrent.ComparePosition(this.navNext);
  432.            
  433.             // If navCurrent is before navNext in document order,
  434.             // If cmp = XmlNodeOrder.Unknown, then navCurrent is before navNext (since input is is doc order)
  435.             if (cmp == XmlNodeOrder.Before || cmp == XmlNodeOrder.Unknown) {
  436.                 // Then navCurrent can be returned (it is guaranteed to be first in document order)
  437.                 return IteratorResult.HaveCurrentNode;
  438.             }
  439.            
  440.             // If navCurrent is after navNext in document order, then delay returning navCurrent
  441.             // Otherwise, discard navNext since it is positioned to the same node as navCurrent
  442.             if (cmp == XmlNodeOrder.After) {
  443.                 this.navStack.Push(this.navCurrent);
  444.                 this.navCurrent = this.navNext;
  445.                 this.navNext = null;
  446.             }
  447.            
  448.             // Need next input node
  449.             this.state = IteratorState.HaveCurrentNeedNext;
  450.             return IteratorResult.NeedInputNode;
  451.         }
  452.     }
  453. }

Developer Fusion