1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel.Dispatcher
7 using System.Collections;
8 using System.Collections.Generic;
10 using System.Threading;
12 using System.Xml.XPath;
15 /// A navigator is a cursor over the nodes in a DOM, where each node is assigned a unique position.
16 /// A node is a (navigator, position) pair.
18 internal struct QueryNode
20 SeekableXPathNavigator node;
24 /// Create a query node from the given navigator and its current position
26 /// <param name="node"></param>
27 internal QueryNode(SeekableXPathNavigator node)
30 this.nodePosition = node.CurrentPosition;
34 /// Initialize using the given (node, position) pair
37 internal QueryNode(SeekableXPathNavigator node, long nodePosition)
40 this.nodePosition = nodePosition;
43 internal string LocalName
47 return this.node.GetLocalName(this.nodePosition);
52 /// Return the node's name
58 return this.node.GetName(this.nodePosition);
62 /// Return the node's namespace
64 internal string Namespace
68 return this.node.GetNamespace(this.nodePosition);
72 /// Return this query node's underlying Node
74 internal SeekableXPathNavigator Node
84 internal long Position
89 return this.nodePosition;
96 internal QueryNodeType Type
100 return QueryDataModel.GetNodeType(this.node.GetNodeType(this.nodePosition));
105 /// This node's string value
107 internal string Value
111 return this.node.GetValue(this.nodePosition);
116 /// Raw xpath node type
118 internal XPathNodeType XPathNodeType
122 return this.node.GetNodeType(this.nodePosition);
127 /// Move this node's navigator to its position
129 /// <returns></returns>
130 internal SeekableXPathNavigator MoveTo()
132 this.node.CurrentPosition = this.nodePosition;
137 internal enum NodeSequenceItemFlags : byte
143 // PERF, [....], Remove when generic sort works
144 // Used to sort in document order
146 internal class NodeSequenceItemObjectComparer : IComparer
148 internal NodeSequenceItemObjectComparer()
152 public int Compare(object obj1, object obj2)
154 NodeSequenceItem item1 = (NodeSequenceItem)obj1;
155 NodeSequenceItem item2 = (NodeSequenceItem)obj2;
157 XmlNodeOrder order = item1.Node.Node.ComparePosition(item1.Node.Position, item2.Node.Position);
161 case XmlNodeOrder.Before:
165 case XmlNodeOrder.Same:
169 case XmlNodeOrder.After:
173 case XmlNodeOrder.Unknown:
175 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XPathException(SR.GetString(SR.QueryNotSortable)), TraceEventType.Critical);
182 // Used to sort in document order
183 internal class NodeSequenceItemComparer : IComparer<NodeSequenceItem>
185 internal NodeSequenceItemComparer()
189 public int Compare(NodeSequenceItem item1, NodeSequenceItem item2)
191 XmlNodeOrder order = item1.Node.Node.ComparePosition(item1.Node.Position, item2.Node.Position);
195 case XmlNodeOrder.Before:
199 case XmlNodeOrder.Same:
203 case XmlNodeOrder.After:
207 case XmlNodeOrder.Unknown:
209 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XPathException(SR.GetString(SR.QueryNotSortable)), TraceEventType.Critical);
215 public bool Equals(NodeSequenceItem item1, NodeSequenceItem item2)
217 return Compare(item1, item2) == 0;
220 public int GetHashCode(NodeSequenceItem item)
222 return item.GetHashCode();
226 // Used to sort in document order
227 internal class QueryNodeComparer : IComparer<QueryNode>
229 public QueryNodeComparer()
233 public int Compare(QueryNode item1, QueryNode item2)
235 XmlNodeOrder order = item1.Node.ComparePosition(item1.Position, item2.Position);
239 case XmlNodeOrder.Before:
243 case XmlNodeOrder.Same:
247 case XmlNodeOrder.After:
251 case XmlNodeOrder.Unknown:
253 throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new XPathException(SR.GetString(SR.QueryNotSortable)));
259 public bool Equals(QueryNode item1, QueryNode item2)
261 return Compare(item1, item2) == 0;
264 public int GetHashCode(QueryNode item)
266 return item.GetHashCode();
270 internal struct NodeSequenceItem
272 NodeSequenceItemFlags flags;
277 internal NodeSequenceItemFlags Flags
293 return (0 != (NodeSequenceItemFlags.NodesetLast & this.flags));
299 this.flags |= NodeSequenceItemFlags.NodesetLast;
303 this.flags &= ~(NodeSequenceItemFlags.NodesetLast);
308 internal string LocalName
312 return this.node.LocalName;
320 return this.node.Name;
324 internal string Namespace
328 return this.node.Namespace;
332 internal QueryNode Node
346 internal int Position
350 return this.position;
355 this.position = value;
372 internal bool Compare(double dblVal, RelationOperator op)
374 return QueryValueModel.Compare(this.NumberValue(), dblVal, op);
377 internal bool Compare(string strVal, RelationOperator op)
379 return QueryValueModel.Compare(this.StringValue(), strVal, op);
382 internal bool Compare(ref NodeSequenceItem item, RelationOperator op)
384 return QueryValueModel.Compare(this.StringValue(), item.StringValue(), op);
387 internal bool Equals(string literal)
389 return QueryValueModel.Equals(this.StringValue(), literal);
392 internal bool Equals(double literal)
394 return (this.NumberValue() == literal);
397 internal SeekableXPathNavigator GetNavigator()
399 return this.node.MoveTo();
402 internal long GetNavigatorPosition()
404 return this.node.Position;
407 internal double NumberValue()
409 return QueryValueModel.Double(this.StringValue());
412 internal void Set(SeekableXPathNavigator node, int position, int size)
414 Fx.Assert(position > 0, "");
415 Fx.Assert(null != node, "");
417 this.node = new QueryNode(node);
418 this.position = position;
420 this.flags = NodeSequenceItemFlags.None;
423 internal void Set(QueryNode node, int position, int size)
425 Fx.Assert(position > 0, "");
428 this.position = position;
430 this.flags = NodeSequenceItemFlags.None;
433 internal void Set(ref NodeSequenceItem item, int position, int size)
435 Fx.Assert(position > 0, "");
437 this.node = item.node;
438 this.position = position;
440 this.flags = item.flags;
443 internal void SetPositionAndSize(int position, int size)
445 this.position = position;
447 this.flags &= ~NodeSequenceItemFlags.NodesetLast;
450 internal void SetSizeAndLast()
453 this.flags |= NodeSequenceItemFlags.NodesetLast;
456 // This is not optimized right now
457 // We may want to CACHE string values once they are computed
458 internal string StringValue()
460 return this.node.Value;
464 internal class NodeSequence
467 // debugging aid. Because C# references do not have displayble numeric values, hard to deduce the
468 // graph structure to see what opcode is connected to what
469 static long nextUniqueId = 0;
470 internal long uniqueID;
473 internal static NodeSequence Empty = new NodeSequence(0);
474 NodeSequenceItem[] items;
476 ProcessingContext ownerContext;
478 internal int refCount;
481 static readonly QueryNodeComparer staticQueryNodeComparerInstance = new QueryNodeComparer();
483 internal NodeSequence()
488 internal NodeSequence(int capacity)
489 : this(capacity, null)
493 internal NodeSequence(int capacity, ProcessingContext ownerContext)
495 this.items = new NodeSequenceItem[capacity];
496 this.ownerContext = ownerContext;
498 this.uniqueID = Interlocked.Increment(ref NodeSequence.nextUniqueId);
503 internal NodeSequence(int capacity, ProcessingContext ownerContext, XPathNodeIterator iter)
504 : this(capacity, ownerContext)
506 while(iter.MoveNext())
508 SeekableXPathNavigator nav = iter.Current as SeekableXPathNavigator;
515 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryProcessingException(QueryProcessingError.Unexpected, SR.GetString(SR.QueryMustBeSeekable)), TraceEventType.Critical);
529 Fx.Assert(value >= 0 && value <= this.count, "");
535 internal NodeSequenceItem this[int index]
539 return this.items[index];
543 internal NodeSequenceItem[] Items
551 internal bool IsNotEmpty
555 return (this.count > 0);
559 internal string LocalName
565 return this.items[0].LocalName;
578 return this.items[0].Name;
585 internal string Namespace
591 return this.items[0].Namespace;
598 internal NodeSequence Next
610 internal ProcessingContext OwnerContext
614 return this.ownerContext;
618 this.ownerContext = value;
622 internal int NodesetStartAt
626 return -this.sizePosition;
630 internal void Add(XPathNodeIterator iter)
632 while (iter.MoveNext())
634 SeekableXPathNavigator nav = iter.Current as SeekableXPathNavigator;
641 throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.Unexpected, SR.GetString(SR.QueryMustBeSeekable)));
646 internal void Add(SeekableXPathNavigator node)
648 Fx.Assert(this.items.Length > 0, "");
649 if (this.count == this.items.Length)
651 this.Grow(this.items.Length * 2);
654 this.items[this.count++].Set(node, this.position, this.sizePosition);
657 internal void Add(QueryNode node)
659 Fx.Assert(this.items.Length > 0, "");
660 if (this.count == this.items.Length)
662 this.Grow(this.items.Length * 2);
666 this.items[this.count++].Set(node, this.position, this.sizePosition);
669 internal void Add(ref NodeSequenceItem item)
671 Fx.Assert(this.items.Length > 0, "");
672 if (this.count == this.items.Length)
674 this.Grow(this.items.Length * 2);
678 this.items[this.count++].Set(ref item, this.position, this.sizePosition);
681 internal void AddCopy(ref NodeSequenceItem item, int size)
683 Fx.Assert(this.items.Length > 0, "");
684 if (this.count == this.items.Length)
686 this.Grow(this.items.Length * 2);
689 this.items[this.count] = item;
690 this.items[this.count++].Size = size;
693 internal void AddCopy(ref NodeSequenceItem item)
695 Fx.Assert(this.items.Length > 0, "");
696 if (this.count == this.items.Length)
698 this.Grow(this.items.Length * 2);
701 this.items[this.count++] = item;
704 internal void Add(NodeSequence seq)
706 int newCount = this.count + seq.count;
707 if (newCount > this.items.Length)
709 // We are going to need room. Grow the array
710 int growTo = this.items.Length * 2;
711 this.Grow(newCount > growTo ? newCount : growTo);
713 Array.Copy(seq.items, 0, this.items, this.count, seq.count);
714 this.count += seq.count;
717 internal bool CanReuse(ProcessingContext context)
719 return (this.count == 1 && this.ownerContext == context && this.refCount == 1);
722 internal void Clear()
727 internal void Reset(NodeSequence nextSeq)
734 internal bool Compare(double val, RelationOperator op)
736 for (int i = 0; i < this.count; ++i)
738 if (this.items[i].Compare(val, op))
746 internal bool Compare(string val, RelationOperator op)
748 Fx.Assert(null != val, "");
749 for (int i = 0; i < this.count; ++i)
751 if (this.items[i].Compare(val, op))
759 internal bool Compare(ref NodeSequenceItem item, RelationOperator op)
761 for (int i = 0; i < this.count; ++i)
763 if (this.items[i].Compare(ref item, op))
771 internal bool Compare(NodeSequence sequence, RelationOperator op)
773 Fx.Assert(null != sequence, "");
774 for (int i = 0; i < sequence.count; ++i)
776 if (this.Compare(ref sequence.items[i], op))
784 void EnsureCapacity()
786 if (this.count == this.items.Length)
788 this.Grow(this.items.Length * 2);
792 void EnsureCapacity(int capacity)
794 if (capacity > this.items.Length)
796 int newSize = this.items.Length * 2;
797 this.Grow(newSize > capacity ? newSize : capacity);
801 internal bool Equals(string val)
803 Fx.Assert(null != val, "");
804 for (int i = 0; i < this.count; ++i)
806 if (this.items[i].Equals(val))
814 internal bool Equals(double val)
816 for (int i = 0; i < this.count; ++i)
818 if (this.items[i].Equals(val))
826 internal static int GetContextSize(NodeSequence sequence, int itemIndex)
828 Fx.Assert(null != sequence, "");
829 int size = sequence.items[itemIndex].Size;
832 return sequence.items[-size].Size;
837 void Grow(int newSize)
839 NodeSequenceItem[] newItems = new NodeSequenceItem[newSize];
840 if (this.items != null)
842 Array.Copy(this.items, newItems, this.items.Length);
844 this.items = newItems;
848 /// Merge all nodesets in this sequence... turning it into a sequence with a single nodeset
849 /// This is done by simply renumbering all positions.. and clearing the nodeset flag
851 internal void Merge()
856 internal void Merge(bool renumber)
869 // Assumes list is flat and sorted
870 internal void RemoveDuplicates()
878 for(int next = 1; next < this.count; ++next)
880 if(Comparer.Compare(this.items[last], this.items[next]) != 0)
885 this.items[last] = this.items[next];
890 this.count = last + 1;
899 for (int i = 0; i < this.count; ++i)
901 this.items[i].SetPositionAndSize(i + 1, this.count);
903 this.items[this.count - 1].Flags |= NodeSequenceItemFlags.NodesetLast;
907 internal void SortNodes()
911 // PERF, [....], make this work
912 //Array.Sort<NodeSequenceItem>(this.items, 0, this.count, NodeSequence.Comparer);
913 Array.Sort(this.items, 0, this.count, NodeSequence.ObjectComparer);
918 internal void StartNodeset()
921 this.sizePosition = -this.count;
924 internal void StopNodeset()
926 switch (this.position)
929 int sizePos = -this.sizePosition;
930 this.items[sizePos].Size = this.position;
931 this.items[sizePos + this.position - 1].Last = true;
938 this.items[-this.sizePosition].SetSizeAndLast();
943 internal string StringValue()
947 return this.items[0].StringValue();
955 /// 1. Add both sequences of items to a newly created sequence
956 /// 2. Sort the items based on document position
957 /// 3. Renumber positions in this new unionized sequence
959 internal NodeSequence Union(ProcessingContext context, NodeSequence otherSeq)
961 NodeSequence seq = context.CreateSequence();
963 SortedBuffer<QueryNode, QueryNodeComparer> buff = new SortedBuffer<QueryNode, QueryNodeComparer>(staticQueryNodeComparerInstance);
964 for (int i = 0; i < this.count; ++i)
965 buff.Add(this.items[i].Node);
967 for (int i = 0; i < otherSeq.count; ++i)
968 buff.Add(otherSeq.items[i].Node);
970 for (int i = 0; i < buff.Count; ++i)
977 // PERF, [....], I think we can do the merge ourselves and avoid the sort.
978 // Need to verify that the sequences are always in document order.
979 for(int i = 0; i < this.count; ++i)
981 seq.AddCopy(ref this.items[i]);
984 for(int i = 0; i < otherSeq.count; ++i)
986 seq.AddCopy(ref otherSeq.items[i]);
990 seq.RemoveDuplicates();
996 #region IQueryBufferPool Members
1006 if (this.count == 0)
1010 else if (this.count < this.items.Length)
1012 NodeSequenceItem[] newItems = new NodeSequenceItem[this.count];
1013 Array.Copy(this.items, newItems, this.count);
1014 this.items = newItems;
1021 internal class NodeSequenceIterator : XPathNodeIterator
1027 NodeSequenceIterator data;
1029 SeekableXPathNavigator nav; // the navigator that will be used by this iterator
1031 internal NodeSequenceIterator(NodeSequence seq)
1038 internal NodeSequenceIterator(NodeSequenceIterator iter)
1040 this.data = iter.data;
1041 this.index = iter.index;
1044 public override int Count
1048 return this.data.seq.Count;
1052 public override XPathNavigator Current
1056 if (this.index == 0)
1058 #pragma warning suppress 56503 // [....], postponing the public change
1059 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryProcessingException(QueryProcessingError.Unexpected, SR.GetString(SR.QueryContextNotSupportedInSequences)));
1062 if (this.index > this.data.seq.Count)
1064 #pragma warning suppress 56503 // [....], postponing the public change
1065 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.QueryAfterNodes)));
1068 // From MSDN - the public contract of .Current
1069 // You can use the properties of the XPathNavigator to return information on the current node.
1070 // However, the XPathNavigator cannot be used to move away from the selected node set.
1071 // Doing so could invalidate the state of the navigator. Alternatively, you can clone the XPathNavigator.
1072 // The cloned XPathNavigator can then be moved away from the selected node set. This is an application level decision.
1073 // Providing this functionality may effect the performance of the XPath query.
1075 // Return the navigator as is - where it is positioned. If the user moved the navigator, then the user is
1076 // hosed. We will make no guarantees - and are not required to. Doing so would force cloning, which is expensive.
1078 // NOTE: .Current can get called repeatedly, so its activity should be relative CHEAP.
1079 // No cloning, copying etc. All that work should be done in MoveNext()
1084 public override int CurrentPosition
1092 internal void Clear()
1094 this.data.seq = null;
1098 public override XPathNodeIterator Clone()
1100 return new NodeSequenceIterator(this);
1103 public override IEnumerator GetEnumerator()
1105 return new NodeSequenceEnumerator(this);
1108 public override bool MoveNext()
1110 if (null == this.data.seq)
1112 // User is trying to use an iterator that is out of scope.
1113 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.QueryIteratorOutOfScope)));
1116 if (this.index < this.data.seq.Count)
1118 if (null == this.nav)
1120 // We haven't aquired the navigator we will use for this iterator yet.
1121 this.nav = (SeekableXPathNavigator)this.data.seq[this.index].GetNavigator().Clone();
1125 this.nav.CurrentPosition = this.data.seq[this.index].GetNavigatorPosition();
1143 internal class NodeSequenceEnumerator : IEnumerator
1145 NodeSequenceIterator iter;
1147 internal NodeSequenceEnumerator(NodeSequenceIterator iter)
1149 this.iter = new NodeSequenceIterator(iter);
1153 public object Current
1157 if (this.iter.CurrentPosition == 0)
1159 #pragma warning suppress 56503 // [....], postponing the public change
1160 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.QueryBeforeNodes)));
1163 if (this.iter.CurrentPosition > this.iter.Count)
1165 #pragma warning suppress 56503 // [....], postponing the public change
1166 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.QueryAfterNodes)));
1169 return this.iter.Current;
1173 public bool MoveNext()
1175 return iter.MoveNext();
1184 internal class SafeNodeSequenceIterator : NodeSequenceIterator, IDisposable
1186 ProcessingContext context;
1190 public SafeNodeSequenceIterator(NodeSequence seq, ProcessingContext context)
1193 this.context = context;
1195 Interlocked.Increment(ref this.seq.refCount);
1196 this.context.Processor.AddRef();
1199 public override XPathNodeIterator Clone()
1201 return new SafeNodeSequenceIterator(this.seq, this.context);
1204 public void Dispose()
1206 if (Interlocked.CompareExchange(ref this.disposed, 1, 0) == 0)
1208 QueryProcessor processor = this.context.Processor;
1209 this.context.ReleaseSequence(this.seq);
1210 this.context.Processor.Matcher.ReleaseProcessor(processor);
1215 internal struct NodesetIterator
1219 NodeSequence sequence;
1220 NodeSequenceItem[] items;
1222 internal NodesetIterator(NodeSequence sequence)
1224 Fx.Assert(null != sequence, "");
1225 this.sequence = sequence;
1226 this.items = sequence.Items;
1228 this.indexStart = -1;
1239 internal bool NextItem()
1241 if (-1 == this.index)
1243 this.index = this.indexStart;
1247 if (this.items[this.index].Last)
1256 internal bool NextNodeset()
1258 this.indexStart = this.index + 1;
1260 return (this.indexStart < this.sequence.Count);
1264 internal struct NodeSequenceBuilder
1266 ProcessingContext context;
1267 NodeSequence sequence;
1269 internal NodeSequenceBuilder(ProcessingContext context, NodeSequence sequence)
1271 this.context = context;
1272 this.sequence = sequence;
1275 internal NodeSequenceBuilder(ProcessingContext context)
1276 : this(context, null)
1280 internal NodeSequenceBuilder(NodeSequence sequence)
1281 : this(sequence.OwnerContext, sequence)
1285 internal NodeSequence Sequence
1289 return (null != this.sequence) ? this.sequence : NodeSequence.Empty;
1293 this.sequence = value;
1297 internal void Add(ref NodeSequenceItem item)
1299 if (null == this.sequence)
1301 this.sequence = this.context.CreateSequence();
1302 this.sequence.StartNodeset();
1305 this.sequence.Add(ref item);
1308 internal void EndNodeset()
1310 if (null != this.sequence)
1312 this.sequence.StopNodeset();
1316 internal void StartNodeset()
1318 if (null != this.sequence)
1320 this.sequence.StartNodeset();