1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel.Dispatcher
7 using System.Xml.XPath;
10 /// This structure contains the criteria used to select a set of nodes from a context node
12 /// Selectors select nodes with particular a relationship to a context node. Candidate nodes are first identified by
13 /// traversing away from the context node along an axis of traversal. The attribute axis, for example, identifies all
14 /// attributes of a given context node as candidates for selection.
16 /// The candidate nodeset identified by axis traversal is then refined by applying node tests.
17 /// A nodeType test constructs a new nodeset by selecting only those nodes of a given type from source candidate set
18 /// A qname test further refines this nodeset by selecting only those that match a given qname
20 class NodeSelectCriteria
22 protected QueryAxis axis;
23 protected NodeQName qname;
24 protected NodeQNameType qnameType;
25 protected QueryNodeType type;
27 internal NodeSelectCriteria(QueryAxisType axis, NodeQName qname, QueryNodeType nodeType)
29 this.axis = QueryDataModel.GetAxis(axis);
31 this.qnameType = qname.GetQNameType();
35 internal QueryAxis Axis
43 internal bool IsCompressable
47 // PERF, Microsoft, weaken guard?
48 return QueryAxisType.Self == this.axis.Type || QueryAxisType.Child == this.axis.Type;
49 //return ((QueryAxisType.Self == this.axis.Type) || ((this.axis.Type != QueryAxisType.DescendantOrSelf || this.axis.Type != QueryAxisType.Descendant)&& 0 != ((QueryNodeType.Element | QueryNodeType.Root) & this.type)));
53 internal NodeQName QName
61 internal QueryNodeType Type
70 internal static NodeSelectCriteria Create(QueryAxisType axis, NodeQName qname, QueryNodeType nodeType)
72 return new NodeSelectCriteria(axis, qname, nodeType);
75 public bool Equals(NodeSelectCriteria criteria)
77 return (this.axis.Type == criteria.axis.Type && this.type == criteria.type && this.qname.Equals(criteria.qname));
80 internal bool Match(SeekableXPathNavigator node)
82 return (this.MatchType(node) && this.MatchQName(node));
85 internal bool MatchType(SeekableXPathNavigator node)
87 QueryNodeType nodeType;
88 switch (node.NodeType)
93 case XPathNodeType.Root:
94 nodeType = QueryNodeType.Root;
97 case XPathNodeType.Attribute:
98 nodeType = QueryNodeType.Attribute;
101 case XPathNodeType.Element:
102 nodeType = QueryNodeType.Element;
105 case XPathNodeType.Comment:
106 nodeType = QueryNodeType.Comment;
109 case XPathNodeType.Text:
110 case XPathNodeType.Whitespace:
111 case XPathNodeType.SignificantWhitespace:
112 nodeType = QueryNodeType.Text;
115 case XPathNodeType.ProcessingInstruction:
116 nodeType = QueryNodeType.Processing;
120 return (nodeType == (this.type & nodeType));
123 internal bool MatchQName(SeekableXPathNavigator node)
125 // Is this a standard qname test.. with known names and namespaces
126 switch (this.qnameType & NodeQNameType.Standard)
131 case NodeQNameType.Name:
132 // Selection criteria did not specify a namespace. Then, if the node supplies a namespace, we know
133 // that the criteria cannot possibly match
134 return (0 == node.NamespaceURI.Length && this.qname.EqualsName(node.LocalName));
136 case NodeQNameType.Standard:
137 string str = node.LocalName;
138 if (this.qname.name.Length == str.Length && this.qname.name == str)
140 str = node.NamespaceURI;
141 return (this.qname.ns.Length == str.Length && this.qname.ns == str);
146 if (NodeQNameType.Empty == this.qnameType)
152 switch (this.qnameType & NodeQNameType.Wildcard)
157 case NodeQNameType.NameWildcard:
158 return this.qname.EqualsNamespace(node.NamespaceURI);
160 case NodeQNameType.Wildcard:
167 internal void Select(SeekableXPathNavigator contextNode, NodeSequence destSequence)
172 if (QueryAxisType.Self == this.axis.Type)
174 if (this.MatchType(contextNode) && this.MatchQName(contextNode))
176 destSequence.Add(contextNode);
179 else if (QueryAxisType.Descendant == this.axis.Type)
181 SelectDescendants(contextNode, destSequence);
183 else if (QueryAxisType.DescendantOrSelf == this.axis.Type)
185 destSequence.Add(contextNode);
186 SelectDescendants(contextNode, destSequence);
188 else if (QueryAxisType.Child == this.axis.Type)
190 // Select children of arbitrary type off the context node
191 if (contextNode.MoveToFirstChild())
195 // Select the node if its type and qname matches
196 if (this.MatchType(contextNode) && this.MatchQName(contextNode))
198 destSequence.Add(contextNode);
201 while (contextNode.MoveToNext());
205 else if (QueryAxisType.Attribute == this.axis.Type)
207 if (contextNode.MoveToFirstAttribute())
211 // Select the node if its type and qname matches
212 if (this.MatchType(contextNode) && this.MatchQName(contextNode))
214 destSequence.Add(contextNode);
215 // you can't have multiple instances of an attibute with the same qname
216 // Stop once one was found
217 // UNLESS WE HAVE A WILDCARD OFCOURSE!
218 if (0 == (this.qnameType & NodeQNameType.Wildcard))
224 while (contextNode.MoveToNextAttribute());
230 throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.Unexpected));
234 case QueryNodeType.Attribute:
235 // Select attributes off the context Node
236 if (contextNode.MoveToFirstAttribute())
240 if (this.MatchQName(contextNode))
242 destSequence.Add(contextNode);
243 // you can't have multiple instances of an attibute with the same qname
244 // Stop once one was found
245 // UNLESS WE HAVE A WILDCARD OFCOURSE!
246 if (0 == (this.qnameType & NodeQNameType.Wildcard))
252 while (contextNode.MoveToNextAttribute());
256 case QueryNodeType.ChildNodes:
257 if (QueryAxisType.Descendant == this.axis.Type)
259 // Select descendants of arbitrary type off the context node
260 SelectDescendants(contextNode, destSequence);
264 // Select children of arbitrary type off the context node
265 if (contextNode.MoveToFirstChild())
269 // Select the node if its type and qname matches
270 if (this.MatchType(contextNode) && this.MatchQName(contextNode))
272 destSequence.Add(contextNode);
275 while (contextNode.MoveToNext());
280 case QueryNodeType.Element:
281 if (QueryAxisType.Descendant == this.axis.Type)
283 // Select descendants of arbitrary type off the context node
284 SelectDescendants(contextNode, destSequence);
286 else if (QueryAxisType.DescendantOrSelf == this.axis.Type)
288 destSequence.Add(contextNode);
289 SelectDescendants(contextNode, destSequence);
291 else if (contextNode.MoveToFirstChild())
295 // Children could have non element nodes in line
296 // Select the node if it is an element and the qname matches
297 if (XPathNodeType.Element == contextNode.NodeType && this.MatchQName(contextNode))
299 destSequence.Add(contextNode);
302 while (contextNode.MoveToNext());
306 case QueryNodeType.Root:
307 contextNode.MoveToRoot();
308 destSequence.Add(contextNode);
311 case QueryNodeType.Text:
312 // Select child text nodes
313 if (contextNode.MoveToFirstChild())
317 // Select the node if its type matches
318 // Can't just do a comparison to XPathNodeType.Text since whitespace nodes
320 if (this.MatchType(contextNode))
322 destSequence.Add(contextNode);
325 while (contextNode.MoveToNext());
331 internal Opcode Select(SeekableXPathNavigator contextNode, NodeSequence destSequence, SelectOpcode next)
333 Opcode returnOpcode = next.Next;
338 if (QueryAxisType.Self == this.axis.Type)
340 if (this.MatchType(contextNode) && this.MatchQName(contextNode))
342 long position = contextNode.CurrentPosition;
343 returnOpcode = next.Eval(destSequence, contextNode);
344 contextNode.CurrentPosition = position;
349 throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.Unexpected));
354 case QueryNodeType.ChildNodes:
355 // Select children of arbitrary type off the context node
356 if (contextNode.MoveToFirstChild())
360 // Select the node if its type and qname matches
361 if (this.MatchType(contextNode) && this.MatchQName(contextNode))
363 destSequence.Add(contextNode);
366 while (contextNode.MoveToNext());
370 case QueryNodeType.Element:
371 // Select child elements
372 if (contextNode.MoveToFirstChild())
376 // Children could have non element nodes in line
377 // Select the node if it is an element and the qname matches
378 if (XPathNodeType.Element == contextNode.NodeType && this.MatchQName(contextNode))
380 long position = contextNode.CurrentPosition;
381 returnOpcode = next.Eval(destSequence, contextNode);
382 contextNode.CurrentPosition = position;
384 } while (contextNode.MoveToNext());
389 case QueryNodeType.Root:
390 contextNode.MoveToRoot();
391 returnOpcode = next.Eval(destSequence, contextNode);
398 void SelectDescendants(SeekableXPathNavigator contextNode, NodeSequence destSequence)
401 if (!contextNode.MoveToFirstChild())
407 // Don't need type check. All child nodes allowed.
408 if (this.MatchQName(contextNode))
410 destSequence.Add(contextNode);
413 if (contextNode.MoveToFirstChild())
417 else if (contextNode.MoveToNext())
425 contextNode.MoveToParent();
428 if (contextNode.MoveToNext())
438 public override string ToString()
440 return string.Format("{0}, {1}:{2}", this.type, this.qname.ns, this.qname.name);
446 // General purpose selector
447 // Pops all parameters from the value stack
448 internal class SelectOpcode : Opcode
450 protected NodeSelectCriteria criteria;
452 internal SelectOpcode(NodeSelectCriteria criteria)
453 : this(OpcodeID.Select, criteria)
457 internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria)
458 : this(id, criteria, OpcodeFlags.None)
462 internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria, OpcodeFlags flags)
465 this.criteria = criteria;
466 this.flags |= (flags | OpcodeFlags.Select);
467 if (criteria.IsCompressable && (0 == (this.flags & OpcodeFlags.InitialSelect)))
469 this.flags |= OpcodeFlags.CompressableSelect;
473 internal NodeSelectCriteria Criteria
477 return this.criteria;
481 internal override bool Equals(Opcode op)
485 return this.criteria.Equals(((SelectOpcode)op).criteria);
491 internal override Opcode Eval(ProcessingContext context)
493 StackFrame topFrame = context.TopSequenceArg;
494 SeekableXPathNavigator node = null;
495 Value[] sequences = context.Sequences;
497 for (int i = topFrame.basePtr; i <= topFrame.endPtr; ++i)
499 // Each NodeSequence will generate a new one, but only if the source FilterSequence isn't empty
500 // If the source FilterSequence is empty, release it and replace it with an empty sequence
501 NodeSequence sourceSeq = sequences[i].Sequence;
502 int sourceSeqCount = sourceSeq.Count;
503 if (sourceSeqCount == 0)
505 context.ReplaceSequenceAt(i, NodeSequence.Empty);
506 context.ReleaseSequence(sourceSeq);
510 NodeSequenceItem[] items = sourceSeq.Items;
511 if (sourceSeq.CanReuse(context))
513 node = items[0].GetNavigator();
515 sourceSeq.StartNodeset();
517 this.criteria.Select(node, sourceSeq);
519 sourceSeq.StopNodeset();
523 NodeSequence newSeq = null;
524 for (int item = 0; item < sourceSeqCount; ++item)
526 node = items[item].GetNavigator();
527 Fx.Assert(null != node, "");
530 newSeq = context.CreateSequence();
532 newSeq.StartNodeset();
533 this.criteria.Select(node, newSeq);
534 newSeq.StopNodeset();
536 context.ReplaceSequenceAt(i, (null != newSeq) ? newSeq : NodeSequence.Empty);
537 context.ReleaseSequence(sourceSeq);
545 internal override Opcode Eval(NodeSequence sequence, SeekableXPathNavigator node)
547 if (this.next == null || 0 == (this.next.Flags & OpcodeFlags.CompressableSelect))
549 // The next opcode is not a compressible select. Complete the select operation and return the next opcode
550 sequence.StartNodeset();
551 this.criteria.Select(node, sequence);
552 sequence.StopNodeset();
556 return this.criteria.Select(node, sequence, (SelectOpcode)this.next);
560 public override string ToString()
562 return string.Format("{0} {1}", base.ToString(), this.criteria.ToString());
567 internal class InitialSelectOpcode : SelectOpcode
569 internal InitialSelectOpcode(NodeSelectCriteria criteria)
570 : base(OpcodeID.InitialSelect, criteria, OpcodeFlags.InitialSelect)
574 internal override Opcode Eval(ProcessingContext context)
576 StackFrame topFrame = context.TopSequenceArg;
577 Value[] sequences = context.Sequences;
579 bool wasInUse = context.SequenceStackInUse;
580 context.PushSequenceFrame();
581 for (int i = topFrame.basePtr; i <= topFrame.endPtr; ++i)
583 NodeSequence sourceSeq = sequences[i].Sequence;
584 int count = sourceSeq.Count;
588 // Since there are no nodes in the sequence, we will track this sequence also
589 // using an empty sequence
591 context.PushSequence(NodeSequence.Empty);
595 NodeSequenceItem[] items = sourceSeq.Items;
596 for (int item = 0; item < sourceSeq.Count; ++item)
598 SeekableXPathNavigator node = items[item].GetNavigator();
599 Fx.Assert(null != node, "");
601 NodeSequence newSeq = context.CreateSequence();
602 newSeq.StartNodeset();
604 this.criteria.Select(node, newSeq);
606 newSeq.StopNodeset();
608 context.PushSequence(newSeq);
616 internal class SelectRootOpcode : Opcode
618 internal SelectRootOpcode()
619 : base(OpcodeID.SelectRoot)
623 internal override Opcode Eval(ProcessingContext context)
625 // The query processor object also serves as the query document root
626 int iterationCount = context.IterationCount;
627 Opcode returnOpcode = this.next;
629 // A root is always an initial step
630 context.PushSequenceFrame();
631 NodeSequence seq = context.CreateSequence();
632 if (this.next != null && 0 != (this.next.Flags & OpcodeFlags.CompressableSelect))
634 SeekableXPathNavigator node = context.Processor.ContextNode;
636 returnOpcode = this.next.Eval(seq, node);
637 while (returnOpcode != null && 0 != (returnOpcode.Flags & OpcodeFlags.CompressableSelect))
639 returnOpcode = returnOpcode.Next;
644 // Roots do not have any qnames..
646 SeekableXPathNavigator node = context.Processor.ContextNode;
654 context.ReleaseSequence(seq);
655 seq = NodeSequence.Empty;
658 for (int i = 0; i < iterationCount; ++i)
660 context.PushSequence(seq);
662 if (iterationCount > 1)
663 seq.refCount += iterationCount - 1;