1 //------------------------------------------------------------------------------
2 // <copyright file="DodSequenceMerge.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
8 using System.Collections.Generic;
9 using System.Xml.XPath;
10 using System.Diagnostics;
11 using System.Globalization;
12 using System.ComponentModel;
14 namespace System.Xml.Xsl.Runtime {
17 /// Merges several doc-order-distinct sequences into a single doc-order-distinct sequence.
19 [EditorBrowsable(EditorBrowsableState.Never)]
20 public struct DodSequenceMerge {
21 private IList<XPathNavigator> firstSequence;
22 private List<IEnumerator<XPathNavigator>> sequencesToMerge;
23 private int nodeCount;
24 private XmlQueryRuntime runtime;
27 /// Initialize this instance of DodSequenceMerge.
29 public void Create(XmlQueryRuntime runtime) {
30 this.firstSequence = null;
31 this.sequencesToMerge = null;
33 this.runtime = runtime;
37 /// Add a new sequence to the list of sequences to merge.
39 public void AddSequence(IList<XPathNavigator> sequence) {
40 // Ignore empty sequences
41 if (sequence.Count == 0)
44 if (this.firstSequence == null) {
45 this.firstSequence = sequence;
48 if (this.sequencesToMerge == null) {
49 this.sequencesToMerge = new List<IEnumerator<XPathNavigator>>();
50 MoveAndInsertSequence(this.firstSequence.GetEnumerator());
51 this.nodeCount = this.firstSequence.Count;
54 MoveAndInsertSequence(sequence.GetEnumerator());
55 this.nodeCount += sequence.Count;
60 /// Return the fully merged sequence.
62 public IList<XPathNavigator> MergeSequences() {
63 XmlQueryNodeSequence newSequence;
65 // Zero sequences to merge
66 if (this.firstSequence == null)
67 return XmlQueryNodeSequence.Empty;
69 // One sequence to merge
70 if (this.sequencesToMerge == null || this.sequencesToMerge.Count <= 1)
71 return this.firstSequence;
73 // Two or more sequences to merge
74 newSequence = new XmlQueryNodeSequence(this.nodeCount);
76 while (this.sequencesToMerge.Count != 1) {
77 // Save last item in list in temp variable, and remove it from list
78 IEnumerator<XPathNavigator> sequence = this.sequencesToMerge[this.sequencesToMerge.Count - 1];
79 this.sequencesToMerge.RemoveAt(this.sequencesToMerge.Count - 1);
81 // Add current node to merged sequence
82 newSequence.Add(sequence.Current);
84 // Now move to the next node, and re-insert it into the list in reverse document order
85 MoveAndInsertSequence(sequence);
88 // Add nodes in remaining sequence to end of list
89 Debug.Assert(this.sequencesToMerge.Count == 1, "While loop should terminate when count == 1");
91 newSequence.Add(this.sequencesToMerge[0].Current);
93 while (this.sequencesToMerge[0].MoveNext());
99 /// Move to the next item in the sequence. If there is no next item, then do not
100 /// insert the sequence. Otherwise, call InsertSequence.
102 private void MoveAndInsertSequence(IEnumerator<XPathNavigator> sequence) {
103 if (sequence.MoveNext())
104 InsertSequence(sequence);
108 /// Insert the specified sequence into the list of sequences to be merged.
109 /// Insert it in reverse document order with respect to the current nodes in other sequences.
111 private void InsertSequence(IEnumerator<XPathNavigator> sequence) {
112 for (int i = this.sequencesToMerge.Count - 1; i >= 0; i--) {
113 int cmp = this.runtime.ComparePosition(sequence.Current, this.sequencesToMerge[i].Current);
116 // Insert after current item
117 this.sequencesToMerge.Insert(i + 1, sequence);
121 // Found duplicate, so skip the duplicate
122 if (!sequence.MoveNext()) {
123 // No more nodes, so don't insert anything
127 // Next node must be after current node in document order, so don't need to reset loop
131 // Insert at beginning of list
132 this.sequencesToMerge.Insert(0, sequence);