QuerySelectOp.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.ServiceModel.Dispatcher
  5. {
  6. using System.Runtime;
  7. using System.Xml.XPath;
  8. /// <summary>
  9. /// This structure contains the criteria used to select a set of nodes from a context node
  10. ///
  11. /// Selectors select nodes with particular a relationship to a context node. Candidate nodes are first identified by
  12. /// traversing away from the context node along an axis of traversal. The attribute axis, for example, identifies all
  13. /// attributes of a given context node as candidates for selection.
  14. ///
  15. /// The candidate nodeset identified by axis traversal is then refined by applying node tests.
  16. /// A nodeType test constructs a new nodeset by selecting only those nodes of a given type from source candidate set
  17. /// A qname test further refines this nodeset by selecting only those that match a given qname
  18. /// </summary>
  19. class NodeSelectCriteria
  20. {
  21. protected QueryAxis axis;
  22. protected NodeQName qname;
  23. protected NodeQNameType qnameType;
  24. protected QueryNodeType type;
  25. internal NodeSelectCriteria(QueryAxisType axis, NodeQName qname, QueryNodeType nodeType)
  26. {
  27. this.axis = QueryDataModel.GetAxis(axis);
  28. this.qname = qname;
  29. this.qnameType = qname.GetQNameType();
  30. this.type = nodeType;
  31. }
  32. internal QueryAxis Axis
  33. {
  34. get
  35. {
  36. return this.axis;
  37. }
  38. }
  39. internal bool IsCompressable
  40. {
  41. get
  42. {
  43. // PERF, [....], weaken guard?
  44. return QueryAxisType.Self == this.axis.Type || QueryAxisType.Child == this.axis.Type;
  45. //return ((QueryAxisType.Self == this.axis.Type) || ((this.axis.Type != QueryAxisType.DescendantOrSelf || this.axis.Type != QueryAxisType.Descendant)&& 0 != ((QueryNodeType.Element | QueryNodeType.Root) & this.type)));
  46. }
  47. }
  48. internal NodeQName QName
  49. {
  50. get
  51. {
  52. return this.qname;
  53. }
  54. }
  55. internal QueryNodeType Type
  56. {
  57. get
  58. {
  59. return this.type;
  60. }
  61. }
  62. #if NO
  63. internal static NodeSelectCriteria Create(QueryAxisType axis, NodeQName qname, QueryNodeType nodeType)
  64. {
  65. return new NodeSelectCriteria(axis, qname, nodeType);
  66. }
  67. #endif
  68. public bool Equals(NodeSelectCriteria criteria)
  69. {
  70. return (this.axis.Type == criteria.axis.Type && this.type == criteria.type && this.qname.Equals(criteria.qname));
  71. }
  72. #if NO
  73. internal bool Match(SeekableXPathNavigator node)
  74. {
  75. return (this.MatchType(node) && this.MatchQName(node));
  76. }
  77. #endif
  78. internal bool MatchType(SeekableXPathNavigator node)
  79. {
  80. QueryNodeType nodeType;
  81. switch (node.NodeType)
  82. {
  83. default:
  84. return false;
  85. case XPathNodeType.Root:
  86. nodeType = QueryNodeType.Root;
  87. break;
  88. case XPathNodeType.Attribute:
  89. nodeType = QueryNodeType.Attribute;
  90. break;
  91. case XPathNodeType.Element:
  92. nodeType = QueryNodeType.Element;
  93. break;
  94. case XPathNodeType.Comment:
  95. nodeType = QueryNodeType.Comment;
  96. break;
  97. case XPathNodeType.Text:
  98. case XPathNodeType.Whitespace:
  99. case XPathNodeType.SignificantWhitespace:
  100. nodeType = QueryNodeType.Text;
  101. break;
  102. case XPathNodeType.ProcessingInstruction:
  103. nodeType = QueryNodeType.Processing;
  104. break;
  105. }
  106. return (nodeType == (this.type & nodeType));
  107. }
  108. internal bool MatchQName(SeekableXPathNavigator node)
  109. {
  110. // Is this a standard qname test.. with known names and namespaces
  111. switch (this.qnameType & NodeQNameType.Standard)
  112. {
  113. default:
  114. break;
  115. case NodeQNameType.Name:
  116. // Selection criteria did not specify a namespace. Then, if the node supplies a namespace, we know
  117. // that the criteria cannot possibly match
  118. return (0 == node.NamespaceURI.Length && this.qname.EqualsName(node.LocalName));
  119. case NodeQNameType.Standard:
  120. string str = node.LocalName;
  121. if (this.qname.name.Length == str.Length && this.qname.name == str)
  122. {
  123. str = node.NamespaceURI;
  124. return (this.qname.ns.Length == str.Length && this.qname.ns == str);
  125. }
  126. return false;
  127. }
  128. if (NodeQNameType.Empty == this.qnameType)
  129. {
  130. return true;
  131. }
  132. // Maybe a wildcard
  133. switch (this.qnameType & NodeQNameType.Wildcard)
  134. {
  135. default:
  136. break;
  137. case NodeQNameType.NameWildcard:
  138. return this.qname.EqualsNamespace(node.NamespaceURI);
  139. case NodeQNameType.Wildcard:
  140. return true;
  141. }
  142. return false;
  143. }
  144. internal void Select(SeekableXPathNavigator contextNode, NodeSequence destSequence)
  145. {
  146. switch (this.type)
  147. {
  148. default:
  149. if (QueryAxisType.Self == this.axis.Type)
  150. {
  151. if (this.MatchType(contextNode) && this.MatchQName(contextNode))
  152. {
  153. destSequence.Add(contextNode);
  154. }
  155. }
  156. else if (QueryAxisType.Descendant == this.axis.Type)
  157. {
  158. SelectDescendants(contextNode, destSequence);
  159. }
  160. else if (QueryAxisType.DescendantOrSelf == this.axis.Type)
  161. {
  162. destSequence.Add(contextNode);
  163. SelectDescendants(contextNode, destSequence);
  164. }
  165. else if (QueryAxisType.Child == this.axis.Type)
  166. {
  167. // Select children of arbitrary type off the context node
  168. if (contextNode.MoveToFirstChild())
  169. {
  170. do
  171. {
  172. // Select the node if its type and qname matches
  173. if (this.MatchType(contextNode) && this.MatchQName(contextNode))
  174. {
  175. destSequence.Add(contextNode);
  176. }
  177. }
  178. while (contextNode.MoveToNext());
  179. }
  180. }
  181. else if (QueryAxisType.Attribute == this.axis.Type)
  182. {
  183. if (contextNode.MoveToFirstAttribute())
  184. {
  185. do
  186. {
  187. // Select the node if its type and qname matches
  188. if (this.MatchType(contextNode) && this.MatchQName(contextNode))
  189. {
  190. destSequence.Add(contextNode);
  191. // you can't have multiple instances of an attibute with the same qname
  192. // Stop once one was found
  193. // UNLESS WE HAVE A WILDCARD OFCOURSE!
  194. if (0 == (this.qnameType & NodeQNameType.Wildcard))
  195. {
  196. break;
  197. }
  198. }
  199. }
  200. while (contextNode.MoveToNextAttribute());
  201. }
  202. }
  203. else
  204. {
  205. throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.Unexpected));
  206. }
  207. break;
  208. case QueryNodeType.Attribute:
  209. // Select attributes off the context Node
  210. if (contextNode.MoveToFirstAttribute())
  211. {
  212. do
  213. {
  214. if (this.MatchQName(contextNode))
  215. {
  216. destSequence.Add(contextNode);
  217. // you can't have multiple instances of an attibute with the same qname
  218. // Stop once one was found
  219. // UNLESS WE HAVE A WILDCARD OFCOURSE!
  220. if (0 == (this.qnameType & NodeQNameType.Wildcard))
  221. {
  222. break;
  223. }
  224. }
  225. }
  226. while (contextNode.MoveToNextAttribute());
  227. }
  228. break;
  229. case QueryNodeType.ChildNodes:
  230. if (QueryAxisType.Descendant == this.axis.Type)
  231. {
  232. // Select descendants of arbitrary type off the context node
  233. SelectDescendants(contextNode, destSequence);
  234. }
  235. else
  236. {
  237. // Select children of arbitrary type off the context node
  238. if (contextNode.MoveToFirstChild())
  239. {
  240. do
  241. {
  242. // Select the node if its type and qname matches
  243. if (this.MatchType(contextNode) && this.MatchQName(contextNode))
  244. {
  245. destSequence.Add(contextNode);
  246. }
  247. }
  248. while (contextNode.MoveToNext());
  249. }
  250. }
  251. break;
  252. case QueryNodeType.Element:
  253. if (QueryAxisType.Descendant == this.axis.Type)
  254. {
  255. // Select descendants of arbitrary type off the context node
  256. SelectDescendants(contextNode, destSequence);
  257. }
  258. else if (QueryAxisType.DescendantOrSelf == this.axis.Type)
  259. {
  260. destSequence.Add(contextNode);
  261. SelectDescendants(contextNode, destSequence);
  262. }
  263. else if (contextNode.MoveToFirstChild())
  264. {
  265. do
  266. {
  267. // Children could have non element nodes in line
  268. // Select the node if it is an element and the qname matches
  269. if (XPathNodeType.Element == contextNode.NodeType && this.MatchQName(contextNode))
  270. {
  271. destSequence.Add(contextNode);
  272. }
  273. }
  274. while (contextNode.MoveToNext());
  275. }
  276. break;
  277. case QueryNodeType.Root:
  278. contextNode.MoveToRoot();
  279. destSequence.Add(contextNode);
  280. break;
  281. case QueryNodeType.Text:
  282. // Select child text nodes
  283. if (contextNode.MoveToFirstChild())
  284. {
  285. do
  286. {
  287. // Select the node if its type matches
  288. // Can't just do a comparison to XPathNodeType.Text since whitespace nodes
  289. // count as text
  290. if (this.MatchType(contextNode))
  291. {
  292. destSequence.Add(contextNode);
  293. }
  294. }
  295. while (contextNode.MoveToNext());
  296. }
  297. break;
  298. }
  299. }
  300. internal Opcode Select(SeekableXPathNavigator contextNode, NodeSequence destSequence, SelectOpcode next)
  301. {
  302. Opcode returnOpcode = next.Next;
  303. switch (this.type)
  304. {
  305. default:
  306. if (QueryAxisType.Self == this.axis.Type)
  307. {
  308. if (this.MatchType(contextNode) && this.MatchQName(contextNode))
  309. {
  310. long position = contextNode.CurrentPosition;
  311. returnOpcode = next.Eval(destSequence, contextNode);
  312. contextNode.CurrentPosition = position;
  313. }
  314. }
  315. else
  316. {
  317. throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.Unexpected));
  318. }
  319. break;
  320. case QueryNodeType.ChildNodes:
  321. // Select children of arbitrary type off the context node
  322. if (contextNode.MoveToFirstChild())
  323. {
  324. do
  325. {
  326. // Select the node if its type and qname matches
  327. if (this.MatchType(contextNode) && this.MatchQName(contextNode))
  328. {
  329. destSequence.Add(contextNode);
  330. }
  331. }
  332. while (contextNode.MoveToNext());
  333. }
  334. break;
  335. case QueryNodeType.Element:
  336. // Select child elements
  337. if (contextNode.MoveToFirstChild())
  338. {
  339. do
  340. {
  341. // Children could have non element nodes in line
  342. // Select the node if it is an element and the qname matches
  343. if (XPathNodeType.Element == contextNode.NodeType && this.MatchQName(contextNode))
  344. {
  345. long position = contextNode.CurrentPosition;
  346. returnOpcode = next.Eval(destSequence, contextNode);
  347. contextNode.CurrentPosition = position;
  348. }
  349. } while (contextNode.MoveToNext());
  350. }
  351. break;
  352. case QueryNodeType.Root:
  353. contextNode.MoveToRoot();
  354. returnOpcode = next.Eval(destSequence, contextNode);
  355. break;
  356. }
  357. return returnOpcode;
  358. }
  359. void SelectDescendants(SeekableXPathNavigator contextNode, NodeSequence destSequence)
  360. {
  361. int level = 1;
  362. if (!contextNode.MoveToFirstChild())
  363. {
  364. return;
  365. }
  366. while (level > 0)
  367. {
  368. // Don't need type check. All child nodes allowed.
  369. if (this.MatchQName(contextNode))
  370. {
  371. destSequence.Add(contextNode);
  372. }
  373. if (contextNode.MoveToFirstChild())
  374. {
  375. ++level;
  376. }
  377. else if (contextNode.MoveToNext())
  378. {
  379. }
  380. else
  381. {
  382. while (level > 0)
  383. {
  384. contextNode.MoveToParent();
  385. --level;
  386. if (contextNode.MoveToNext())
  387. {
  388. break;
  389. }
  390. }
  391. }
  392. }
  393. }
  394. #if DEBUG_FILTER
  395. public override string ToString()
  396. {
  397. return string.Format("{0}, {1}:{2}", this.type, this.qname.ns, this.qname.name);
  398. }
  399. #endif
  400. }
  401. // General purpose selector
  402. // Pops all parameters from the value stack
  403. internal class SelectOpcode : Opcode
  404. {
  405. protected NodeSelectCriteria criteria;
  406. internal SelectOpcode(NodeSelectCriteria criteria)
  407. : this(OpcodeID.Select, criteria)
  408. {
  409. }
  410. internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria)
  411. : this(id, criteria, OpcodeFlags.None)
  412. {
  413. }
  414. internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria, OpcodeFlags flags)
  415. : base(id)
  416. {
  417. this.criteria = criteria;
  418. this.flags |= (flags | OpcodeFlags.Select);
  419. if (criteria.IsCompressable && (0 == (this.flags & OpcodeFlags.InitialSelect)))
  420. {
  421. this.flags |= OpcodeFlags.CompressableSelect;
  422. }
  423. }
  424. internal NodeSelectCriteria Criteria
  425. {
  426. get
  427. {
  428. return this.criteria;
  429. }
  430. }
  431. internal override bool Equals(Opcode op)
  432. {
  433. if (base.Equals(op))
  434. {
  435. return this.criteria.Equals(((SelectOpcode)op).criteria);
  436. }
  437. return false;
  438. }
  439. internal override Opcode Eval(ProcessingContext context)
  440. {
  441. StackFrame topFrame = context.TopSequenceArg;
  442. SeekableXPathNavigator node = null;
  443. Value[] sequences = context.Sequences;
  444. for (int i = topFrame.basePtr; i <= topFrame.endPtr; ++i)
  445. {
  446. // Each NodeSequence will generate a new one, but only if the source FilterSequence isn't empty
  447. // If the source FilterSequence is empty, release it and replace it with an empty sequence
  448. NodeSequence sourceSeq = sequences[i].Sequence;
  449. int sourceSeqCount = sourceSeq.Count;
  450. if (sourceSeqCount == 0)
  451. {
  452. context.ReplaceSequenceAt(i, NodeSequence.Empty);
  453. context.ReleaseSequence(sourceSeq);
  454. }
  455. else
  456. {
  457. NodeSequenceItem[] items = sourceSeq.Items;
  458. if (sourceSeq.CanReuse(context))
  459. {
  460. node = items[0].GetNavigator();
  461. sourceSeq.Clear();
  462. sourceSeq.StartNodeset();
  463. this.criteria.Select(node, sourceSeq);
  464. sourceSeq.StopNodeset();
  465. }
  466. else
  467. {
  468. NodeSequence newSeq = null;
  469. for (int item = 0; item < sourceSeqCount; ++item)
  470. {
  471. node = items[item].GetNavigator();
  472. Fx.Assert(null != node, "");
  473. if (null == newSeq)
  474. {
  475. newSeq = context.CreateSequence();
  476. }
  477. newSeq.StartNodeset();
  478. this.criteria.Select(node, newSeq);
  479. newSeq.StopNodeset();
  480. }
  481. context.ReplaceSequenceAt(i, (null != newSeq) ? newSeq : NodeSequence.Empty);
  482. context.ReleaseSequence(sourceSeq);
  483. }
  484. }
  485. }
  486. return this.next;
  487. }
  488. internal override Opcode Eval(NodeSequence sequence, SeekableXPathNavigator node)
  489. {
  490. if (this.next == null || 0 == (this.next.Flags & OpcodeFlags.CompressableSelect))
  491. {
  492. // The next opcode is not a compressible select. Complete the select operation and return the next opcode
  493. sequence.StartNodeset();
  494. this.criteria.Select(node, sequence);
  495. sequence.StopNodeset();
  496. return this.next;
  497. }
  498. return this.criteria.Select(node, sequence, (SelectOpcode)this.next);
  499. }
  500. #if DEBUG_FILTER
  501. public override string ToString()
  502. {
  503. return string.Format("{0} {1}", base.ToString(), this.criteria.ToString());
  504. }
  505. #endif
  506. }
  507. internal class InitialSelectOpcode : SelectOpcode
  508. {
  509. internal InitialSelectOpcode(NodeSelectCriteria criteria)
  510. : base(OpcodeID.InitialSelect, criteria, OpcodeFlags.InitialSelect)
  511. {
  512. }
  513. internal override Opcode Eval(ProcessingContext context)
  514. {
  515. StackFrame topFrame = context.TopSequenceArg;
  516. Value[] sequences = context.Sequences;
  517. bool wasInUse = context.SequenceStackInUse;
  518. context.PushSequenceFrame();
  519. for (int i = topFrame.basePtr; i <= topFrame.endPtr; ++i)
  520. {
  521. NodeSequence sourceSeq = sequences[i].Sequence;
  522. int count = sourceSeq.Count;
  523. if (count == 0)
  524. {
  525. // Empty sequence.
  526. // Since there are no nodes in the sequence, we will track this sequence also
  527. // using an empty sequence
  528. if (!wasInUse)
  529. context.PushSequence(NodeSequence.Empty);
  530. }
  531. else
  532. {
  533. NodeSequenceItem[] items = sourceSeq.Items;
  534. for (int item = 0; item < sourceSeq.Count; ++item)
  535. {
  536. SeekableXPathNavigator node = items[item].GetNavigator();
  537. Fx.Assert(null != node, "");
  538. NodeSequence newSeq = context.CreateSequence();
  539. newSeq.StartNodeset();
  540. this.criteria.Select(node, newSeq);
  541. newSeq.StopNodeset();
  542. context.PushSequence(newSeq);
  543. }
  544. }
  545. }
  546. return this.next;
  547. }
  548. }
  549. internal class SelectRootOpcode : Opcode
  550. {
  551. internal SelectRootOpcode()
  552. : base(OpcodeID.SelectRoot)
  553. {
  554. }
  555. internal override Opcode Eval(ProcessingContext context)
  556. {
  557. // The query processor object also serves as the query document root
  558. int iterationCount = context.IterationCount;
  559. Opcode returnOpcode = this.next;
  560. // A root is always an initial step
  561. context.PushSequenceFrame();
  562. NodeSequence seq = context.CreateSequence();
  563. if (this.next != null && 0 != (this.next.Flags & OpcodeFlags.CompressableSelect))
  564. {
  565. SeekableXPathNavigator node = context.Processor.ContextNode;
  566. node.MoveToRoot();
  567. returnOpcode = this.next.Eval(seq, node);
  568. while (returnOpcode != null && 0 != (returnOpcode.Flags & OpcodeFlags.CompressableSelect))
  569. {
  570. returnOpcode = returnOpcode.Next;
  571. }
  572. }
  573. else
  574. {
  575. // Roots do not have any qnames..
  576. seq.StartNodeset();
  577. SeekableXPathNavigator node = context.Processor.ContextNode;
  578. node.MoveToRoot();
  579. seq.Add(node);
  580. seq.StopNodeset();
  581. }
  582. if (seq.Count == 0)
  583. {
  584. context.ReleaseSequence(seq);
  585. seq = NodeSequence.Empty;
  586. }
  587. for (int i = 0; i < iterationCount; ++i)
  588. {
  589. context.PushSequence(seq);
  590. }
  591. if (iterationCount > 1)
  592. seq.refCount += iterationCount - 1;
  593. return returnOpcode;
  594. }
  595. }
  596. }