QueryMatcher.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.ServiceModel.Dispatcher
  5. {
  6. using System;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using System.Collections.ObjectModel;
  10. using System.Runtime;
  11. using System.ServiceModel.Channels;
  12. using System.ServiceModel.Diagnostics;
  13. using System.Xml;
  14. using System.Xml.XPath;
  15. using System.Xml.Xsl;
  16. internal enum QueryCompilerFlags
  17. {
  18. None = 0x00000000,
  19. InverseQuery = 0x00000001
  20. }
  21. internal struct FilterResult
  22. {
  23. QueryProcessor processor;
  24. bool result;
  25. internal FilterResult(QueryProcessor processor)
  26. {
  27. this.processor = processor;
  28. this.result = this.processor.Result;
  29. }
  30. internal FilterResult(bool result)
  31. {
  32. this.processor = null;
  33. this.result = result;
  34. }
  35. #if NO
  36. internal ICollection<MessageFilter> Matches
  37. {
  38. get
  39. {
  40. return this.processor.ResultSet;
  41. }
  42. }
  43. #endif
  44. internal QueryProcessor Processor
  45. {
  46. get
  47. {
  48. return this.processor;
  49. }
  50. }
  51. internal bool Result
  52. {
  53. get
  54. {
  55. return this.result;
  56. }
  57. }
  58. internal MessageFilter GetSingleMatch()
  59. {
  60. Collection<MessageFilter> matches = processor.MatchList;
  61. MessageFilter match;
  62. switch (matches.Count)
  63. {
  64. default:
  65. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.GetString(SR.FilterMultipleMatches), null, matches));
  66. case 0:
  67. match = null;
  68. break;
  69. case 1:
  70. match = matches[0];
  71. break;
  72. }
  73. return match;
  74. }
  75. }
  76. // XPathResult.GetResultAsString and XPathResult.GetResultAsBoolean,
  77. // drive knowledge of TResult into the engine.
  78. internal class QueryResult<TResult> : IEnumerable<KeyValuePair<MessageQuery, TResult>>
  79. {
  80. bool evalBody;
  81. QueryMatcher matcher;
  82. Message message;
  83. internal QueryResult(QueryMatcher matcher, Message message, bool evalBody)
  84. {
  85. this.matcher = matcher;
  86. this.message = message;
  87. this.evalBody = evalBody;
  88. }
  89. public TResult GetSingleResult()
  90. {
  91. QueryProcessor processor = this.matcher.CreateProcessor();
  92. XPathResult result;
  93. try
  94. {
  95. processor.Eval(this.matcher.RootOpcode, this.message, this.evalBody);
  96. }
  97. catch (XPathNavigatorException e)
  98. {
  99. throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
  100. }
  101. catch (NavigatorInvalidBodyAccessException e)
  102. {
  103. throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
  104. }
  105. finally
  106. {
  107. if (this.evalBody)
  108. {
  109. this.message.Close();
  110. }
  111. result = processor.QueryResult;
  112. this.matcher.ReleaseProcessor(processor);
  113. }
  114. if (typeof(TResult) == typeof(XPathResult) || typeof(TResult) == typeof(object))
  115. {
  116. return (TResult)(object)result;
  117. }
  118. else if (typeof(TResult) == typeof(string))
  119. {
  120. return (TResult)(object)result.GetResultAsString();
  121. }
  122. else if (typeof(TResult) == typeof(bool))
  123. {
  124. return (TResult)(object)result.GetResultAsBoolean();
  125. }
  126. else
  127. {
  128. throw Fx.AssertAndThrowFatal("unsupported type");
  129. }
  130. }
  131. public IEnumerator<KeyValuePair<MessageQuery, TResult>> GetEnumerator()
  132. {
  133. QueryProcessor processor = this.matcher.CreateProcessor();
  134. Collection<KeyValuePair<MessageQuery, XPathResult>> results =
  135. new Collection<KeyValuePair<MessageQuery, XPathResult>>();
  136. processor.ResultSet = results;
  137. try
  138. {
  139. processor.Eval(this.matcher.RootOpcode, this.message, this.evalBody);
  140. if (typeof(TResult) == typeof(XPathResult))
  141. {
  142. return (IEnumerator<KeyValuePair<MessageQuery, TResult>>)(object)results.GetEnumerator();
  143. }
  144. else if (typeof(TResult) == typeof(string) ||
  145. typeof(TResult) == typeof(bool) ||
  146. typeof(TResult) == typeof(object))
  147. {
  148. Collection<KeyValuePair<MessageQuery, TResult>> typedResults =
  149. new Collection<KeyValuePair<MessageQuery, TResult>>();
  150. foreach (var result in results)
  151. {
  152. if (typeof(TResult) == typeof(string))
  153. {
  154. typedResults.Add(
  155. new KeyValuePair<MessageQuery, TResult>(
  156. result.Key, (TResult)(object)result.Value.GetResultAsString()));
  157. }
  158. else if (typeof(TResult) == typeof(bool))
  159. {
  160. typedResults.Add(
  161. new KeyValuePair<MessageQuery, TResult>(
  162. result.Key, (TResult)(object)result.Value.GetResultAsBoolean()));
  163. }
  164. else
  165. {
  166. typedResults.Add(new KeyValuePair<MessageQuery, TResult>(
  167. result.Key, (TResult)(object)result.Value));
  168. }
  169. }
  170. return (IEnumerator<KeyValuePair<MessageQuery, TResult>>)typedResults.GetEnumerator();
  171. }
  172. else
  173. {
  174. throw Fx.AssertAndThrowFatal("unsupported type");
  175. }
  176. }
  177. catch (XPathNavigatorException e)
  178. {
  179. throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
  180. }
  181. catch (NavigatorInvalidBodyAccessException e)
  182. {
  183. throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
  184. }
  185. finally
  186. {
  187. if (this.evalBody)
  188. {
  189. this.message.Close();
  190. }
  191. this.matcher.ReleaseProcessor(processor);
  192. }
  193. }
  194. IEnumerator IEnumerable.GetEnumerator()
  195. {
  196. return this.GetEnumerator();
  197. }
  198. }
  199. /// <summary>
  200. ///
  201. /// </summary>
  202. internal abstract class QueryMatcher
  203. {
  204. static IFunctionLibrary[] defaultFunctionLibs; // The set of function libraries that our XPath compiler will link to
  205. static XPathNavigator fxCompiler; // fx compiler
  206. protected int maxNodes; // Maximum # of nodes that we will process while performing any individual match
  207. protected Opcode query; // root opcode - this is where query evaluation starts
  208. protected int subExprVars; // the number of subexpr node sequences the processing context must hold
  209. // Processor Pool
  210. protected WeakReference processorPool;
  211. internal class QueryProcessorPool
  212. {
  213. QueryProcessor processor;
  214. internal QueryProcessorPool()
  215. {
  216. }
  217. internal QueryProcessor Pop()
  218. {
  219. QueryProcessor p = this.processor;
  220. if (null != p)
  221. {
  222. this.processor = (QueryProcessor)p.next;
  223. p.next = null;
  224. return p;
  225. }
  226. return null;
  227. }
  228. internal void Push(QueryProcessor p)
  229. {
  230. p.next = this.processor;
  231. this.processor = p;
  232. }
  233. }
  234. static QueryMatcher()
  235. {
  236. QueryMatcher.defaultFunctionLibs = new IFunctionLibrary[] { new XPathFunctionLibrary() };
  237. // For some incomprehensible reason, the Framework XPath compiler requires an instance of an XPath navigator
  238. // to compile an xpath. This compiler uses a dummy xml document to create a navigator
  239. XmlDocument doc = new XmlDocument();
  240. doc.LoadXml("<a/>");
  241. QueryMatcher.fxCompiler = doc.CreateNavigator();
  242. }
  243. internal QueryMatcher()
  244. {
  245. this.maxNodes = int.MaxValue;
  246. this.query = null;
  247. this.processorPool = new WeakReference(null);
  248. this.subExprVars = 0;
  249. }
  250. #if NO
  251. internal QueryMatcher(QueryMatcher matcher)
  252. {
  253. this.processorPool = new WeakReference(null);
  254. this.maxNodes = matcher.maxNodes;
  255. this.query = matcher.query;
  256. this.subExprVars = matcher.subExprVars;
  257. }
  258. #endif
  259. internal bool IsCompiled
  260. {
  261. get
  262. {
  263. return (null != this.query);
  264. }
  265. }
  266. internal int NodeQuota
  267. {
  268. get
  269. {
  270. return this.maxNodes;
  271. }
  272. set
  273. {
  274. Fx.Assert(value > 0, "");
  275. this.maxNodes = value;
  276. }
  277. }
  278. internal Opcode RootOpcode
  279. {
  280. get
  281. {
  282. return this.query;
  283. }
  284. }
  285. internal int SubExprVarCount
  286. {
  287. get
  288. {
  289. return this.subExprVars;
  290. }
  291. }
  292. /// <summary>
  293. /// Compile the given filter to run on an external (fx) xpath engine
  294. /// </summary>
  295. internal static OpcodeBlock CompileForExternalEngine(string expression, XmlNamespaceManager namespaces, object item, bool match)
  296. {
  297. // Compile...
  298. XPathExpression xpathExpr = QueryMatcher.fxCompiler.Compile(expression);
  299. // Fx will bind prefixes and functions here.
  300. if (namespaces != null)
  301. {
  302. // There's a bug in System.Xml.XPath. If we pass an XsltContext to SetContext it won't throw if there's
  303. // an undefined prefix.
  304. if (namespaces is XsltContext)
  305. {
  306. // Lex the xpath to find all prefixes used
  307. XPathLexer lexer = new XPathLexer(expression, false);
  308. while (lexer.MoveNext())
  309. {
  310. string prefix = lexer.Token.Prefix;
  311. if (prefix.Length > 0 && namespaces.LookupNamespace(prefix) == null)
  312. {
  313. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XsltException(SR.GetString(SR.FilterUndefinedPrefix, prefix)));
  314. }
  315. }
  316. }
  317. xpathExpr.SetContext(namespaces);
  318. }
  319. //
  320. // FORCE the function to COMPILE - they won't bind namespaces unless we check the return type
  321. //
  322. if (XPathResultType.Error == xpathExpr.ReturnType)
  323. {
  324. // This should never be reached. The above property should throw if there's an error
  325. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XPathException(SR.GetString(SR.FilterCouldNotCompile, expression)));
  326. }
  327. OpcodeBlock codeBlock = new OpcodeBlock();
  328. SingleFxEngineResultOpcode op;
  329. if (!match)
  330. {
  331. op = new QuerySingleFxEngineResultOpcode();
  332. }
  333. else
  334. {
  335. op = new MatchSingleFxEngineResultOpcode();
  336. }
  337. op.XPath = xpathExpr;
  338. op.Item = item;
  339. codeBlock.Append(op);
  340. return codeBlock;
  341. }
  342. /// <summary>
  343. /// Compile the given filter for evaluation using the internal engine.
  344. /// </summary>
  345. /// <param name="flags">Caller customizes optimizations via the flags parameter</param>
  346. /// <param name="returnType">Every xpath expression has a return type</param>
  347. /// <returns>The opcode block we execute to evaluate</returns>
  348. internal static OpcodeBlock CompileForInternalEngine(XPathMessageFilter filter, QueryCompilerFlags flags, IFunctionLibrary[] functionLibs, out ValueDataType returnType)
  349. {
  350. return QueryMatcher.CompileForInternalEngine(filter.XPath.Trim(), filter.namespaces, flags, functionLibs, out returnType);
  351. }
  352. internal static OpcodeBlock CompileForInternalEngine(string xpath, XmlNamespaceManager nsManager, QueryCompilerFlags flags, IFunctionLibrary[] functionLibs, out ValueDataType returnType)
  353. {
  354. OpcodeBlock codeBlock;
  355. returnType = ValueDataType.None;
  356. if (0 == xpath.Length)
  357. {
  358. // 0 length XPaths always match
  359. codeBlock = new OpcodeBlock();
  360. codeBlock.Append(new PushBooleanOpcode(true)); // Always match by pushing true on the eval stack
  361. }
  362. else
  363. {
  364. // Try to parse the xpath. Bind to default function libraries
  365. // The parser returns an expression tree
  366. XPathParser parser = new XPathParser(xpath, nsManager, functionLibs);
  367. XPathExpr parseTree = parser.Parse();
  368. if (null == parseTree)
  369. {
  370. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.CouldNotParseExpression));
  371. }
  372. returnType = parseTree.ReturnType;
  373. // Compile the expression tree
  374. XPathCompiler compiler = new XPathCompiler(flags);
  375. codeBlock = compiler.Compile(parseTree);
  376. }
  377. return codeBlock;
  378. }
  379. internal static OpcodeBlock CompileForInternalEngine(string xpath, XmlNamespaceManager ns, QueryCompilerFlags flags, out ValueDataType returnType)
  380. {
  381. return QueryMatcher.CompileForInternalEngine(xpath, ns, flags, QueryMatcher.defaultFunctionLibs, out returnType);
  382. }
  383. internal SeekableXPathNavigator CreateMessageNavigator(Message message, bool matchBody)
  384. {
  385. SeekableXPathNavigator nav = message.GetNavigator(matchBody, this.maxNodes);
  386. // Position the navigator at the root element
  387. // This allows a caller to run relative XPaths on message
  388. nav.MoveToRoot();
  389. return nav;
  390. }
  391. /// <summary>
  392. /// Checks the context pool for a generic navigator first. If none is available, creates a new one
  393. /// </summary>
  394. internal SeekableXPathNavigator CreateSeekableNavigator(XPathNavigator navigator)
  395. {
  396. return new GenericSeekableNavigator(navigator);
  397. }
  398. internal SeekableXPathNavigator CreateSafeNavigator(SeekableXPathNavigator navigator)
  399. {
  400. INodeCounter counter = navigator as INodeCounter;
  401. if (counter != null)
  402. {
  403. counter.CounterMarker = this.maxNodes;
  404. counter.MaxCounter = this.maxNodes;
  405. }
  406. else
  407. {
  408. navigator = new SafeSeekableNavigator(navigator, this.maxNodes);
  409. }
  410. return navigator;
  411. }
  412. /// <summary>
  413. /// Checks the context pool for a processor first. If none is available, creates a new one
  414. /// </summary>
  415. internal QueryProcessor CreateProcessor()
  416. {
  417. QueryProcessor p = null;
  418. lock (this.processorPool)
  419. {
  420. QueryProcessorPool pool = this.processorPool.Target as QueryProcessorPool;
  421. if (null != pool)
  422. {
  423. p = pool.Pop();
  424. }
  425. }
  426. if (null != p)
  427. {
  428. p.ClearProcessor();
  429. }
  430. else
  431. {
  432. p = new QueryProcessor(this);
  433. }
  434. p.AddRef();
  435. return p;
  436. }
  437. internal FilterResult Match(MessageBuffer messageBuffer, ICollection<MessageFilter> matches)
  438. {
  439. Message message = messageBuffer.CreateMessage();
  440. FilterResult result;
  441. try
  442. {
  443. result = this.Match(message, true, matches);
  444. }
  445. finally
  446. {
  447. message.Close();
  448. }
  449. return result;
  450. }
  451. internal FilterResult Match(Message message, bool matchBody, ICollection<MessageFilter> matches)
  452. {
  453. QueryProcessor processor = this.CreateProcessor();
  454. processor.MatchSet = matches;
  455. processor.EnsureFilterCollection();
  456. try
  457. {
  458. processor.Eval(this.query, message, matchBody);
  459. }
  460. catch (XPathNavigatorException e)
  461. {
  462. throw TraceUtility.ThrowHelperError(e.Process(this.query), message);
  463. }
  464. catch (NavigatorInvalidBodyAccessException e)
  465. {
  466. throw TraceUtility.ThrowHelperError(e.Process(this.query), message);
  467. }
  468. return new FilterResult(processor);
  469. }
  470. internal QueryResult<TResult> Evaluate<TResult>(MessageBuffer messageBuffer)
  471. {
  472. Message message = messageBuffer.CreateMessage();
  473. return this.Evaluate<TResult>(message, true);
  474. }
  475. internal QueryResult<TResult> Evaluate<TResult>(Message message, bool matchBody)
  476. {
  477. return new QueryResult<TResult>(this, message, matchBody);
  478. }
  479. /// <summary>
  480. /// Execute matches over the given seekable navigator. If the navigator is not safe, wrap it with one that is
  481. /// </summary>
  482. internal FilterResult Match(SeekableXPathNavigator navigator, ICollection<MessageFilter> matches)
  483. {
  484. // If the matcher places restrictions on the # of nodes we will inspect, and the navigator passed does
  485. // not do any nodecounting itself, we must make that navigator safe by wrapping it
  486. if (this.maxNodes < int.MaxValue)
  487. {
  488. navigator = this.CreateSafeNavigator(navigator);
  489. }
  490. QueryProcessor processor = this.CreateProcessor();
  491. processor.MatchSet = matches;
  492. processor.EnsureFilterCollection();
  493. try
  494. {
  495. processor.Eval(this.query, navigator);
  496. }
  497. catch (XPathNavigatorException e)
  498. {
  499. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
  500. }
  501. catch (NavigatorInvalidBodyAccessException e)
  502. {
  503. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
  504. }
  505. return new FilterResult(processor);
  506. }
  507. /// <summary>
  508. /// Execute matches over the given navigator by wrapping it with a Seekable Navigator
  509. /// </summary>
  510. internal FilterResult Match(XPathNavigator navigator, ICollection<MessageFilter> matches)
  511. {
  512. SeekableXPathNavigator nav = this.CreateSeekableNavigator(navigator);
  513. return this.Match(nav, matches);
  514. }
  515. /// <summary>
  516. /// Release the given processor and place it back in the context pool
  517. /// </summary>
  518. internal void ReleaseProcessor(QueryProcessor processor)
  519. {
  520. if (!processor.ReleaseRef())
  521. {
  522. return;
  523. }
  524. lock (this.processorPool)
  525. {
  526. QueryProcessorPool pool = this.processorPool.Target as QueryProcessorPool;
  527. if (null == pool)
  528. {
  529. pool = new QueryProcessorPool();
  530. this.processorPool.Target = pool;
  531. }
  532. pool.Push(processor);
  533. }
  534. }
  535. internal void ReleaseResult(FilterResult result)
  536. {
  537. if (null != result.Processor)
  538. {
  539. result.Processor.MatchSet = null;
  540. this.ReleaseProcessor(result.Processor);
  541. }
  542. }
  543. /// <summary>
  544. /// Trim all pool
  545. /// </summary>
  546. internal virtual void Trim()
  547. {
  548. if (this.query != null)
  549. {
  550. this.query.Trim();
  551. }
  552. }
  553. }
  554. internal enum XPathFilterFlags
  555. {
  556. None = 0x00,
  557. AlwaysMatch = 0x01, // filter always matches
  558. IsFxFilter = 0x02, // filter is matched using the framework engine
  559. }
  560. /// <summary>
  561. /// A matcher used to evalute single XPath expressions
  562. /// </summary>
  563. internal class XPathQueryMatcher : QueryMatcher
  564. {
  565. XPathFilterFlags flags;
  566. bool match;
  567. static PushBooleanOpcode matchAlwaysFilter; // used for compiling xpaths that always match - i.e. xpath.Length == 0
  568. static OpcodeBlock rootFilter; // used for compiling "/"
  569. static XPathQueryMatcher()
  570. {
  571. XPathQueryMatcher.matchAlwaysFilter = new PushBooleanOpcode(true); //dummy
  572. ValueDataType returnType;
  573. XPathQueryMatcher.rootFilter = QueryMatcher.CompileForInternalEngine("/", null, QueryCompilerFlags.None, out returnType);
  574. XPathQueryMatcher.rootFilter.Append(new MatchResultOpcode());
  575. }
  576. internal XPathQueryMatcher(bool match)
  577. : base()
  578. {
  579. this.flags = XPathFilterFlags.None;
  580. this.match = match;
  581. }
  582. #if NO
  583. internal XPathFilterMatcher(XPathFilterMatcher matcher)
  584. : base(matcher)
  585. {
  586. this.flags = matcher.flags;
  587. }
  588. #endif
  589. internal bool IsAlwaysMatch
  590. {
  591. get
  592. {
  593. return (0 != (this.flags & XPathFilterFlags.AlwaysMatch));
  594. }
  595. }
  596. internal bool IsFxFilter
  597. {
  598. get
  599. {
  600. return (0 != (this.flags & XPathFilterFlags.IsFxFilter));
  601. }
  602. }
  603. /// <summary>
  604. /// If the xpath is an empty string, there is nothing to compile and the filter always matches
  605. /// If not, try to compile the filter for execution within the filter engine's own query processor
  606. /// If that query processor cannot accept the filter (it doesn't fall within the class of xpaths it can handle),
  607. /// then revert to the fall-back solution - the slower Fx engine
  608. /// </summary>
  609. internal void Compile(string expression, XmlNamespaceManager namespaces)
  610. {
  611. if (null == this.query)
  612. {
  613. // Try to compile for the internal engine first
  614. try
  615. {
  616. this.CompileForInternal(expression, namespaces);
  617. }
  618. catch (QueryCompileException)
  619. {
  620. }
  621. if (null == this.query)
  622. {
  623. // Try for an external engine that might work..
  624. this.CompileForExternal(expression, namespaces);
  625. }
  626. }
  627. }
  628. /// <summary>
  629. /// Compile this xpath to run on an external (fx) xpath engine
  630. /// </summary>
  631. internal void CompileForExternal(string xpath, XmlNamespaceManager names)
  632. {
  633. Opcode op = QueryMatcher.CompileForExternalEngine(xpath, names, null, this.match).First;
  634. this.query = op;
  635. this.flags |= XPathFilterFlags.IsFxFilter;
  636. }
  637. /// <summary>
  638. /// Compile for the internal engine with default flags
  639. /// By defalt, we compile an xpath to run stand alone, with standard optimizations
  640. /// </summary>
  641. internal void CompileForInternal(string xpath, XmlNamespaceManager names)
  642. {
  643. this.query = null;
  644. xpath = xpath.Trim();
  645. if (0 == xpath.Length)
  646. {
  647. // Empty xpaths always match. Same for xpaths that refer to the root only
  648. // We will evaluate such filters with minimal overhead. However, we
  649. // don't want a null value for this.query, so we stick a dummy value in there
  650. this.query = XPathQueryMatcher.matchAlwaysFilter;
  651. this.flags |= (XPathFilterFlags.AlwaysMatch);
  652. }
  653. else if (1 == xpath.Length && '/' == xpath[0])
  654. {
  655. this.query = XPathQueryMatcher.rootFilter.First;
  656. this.flags |= (XPathFilterFlags.AlwaysMatch);
  657. }
  658. else
  659. {
  660. ValueDataType returnType;
  661. OpcodeBlock codeBlock = QueryMatcher.CompileForInternalEngine(xpath, names, QueryCompilerFlags.None, out returnType);
  662. // Inject a final opcode that will place the query result on the query context
  663. // This query is now ready for execution STAND ALONE
  664. if (this.match)
  665. {
  666. codeBlock.Append(new MatchResultOpcode());
  667. }
  668. else
  669. {
  670. codeBlock.Append(new QueryResultOpcode());
  671. }
  672. this.query = codeBlock.First;
  673. }
  674. this.flags &= ~XPathFilterFlags.IsFxFilter;
  675. }
  676. internal FilterResult Match(MessageBuffer messageBuffer)
  677. {
  678. Message message = messageBuffer.CreateMessage();
  679. FilterResult result;
  680. try
  681. {
  682. result = this.Match(message, true);
  683. }
  684. finally
  685. {
  686. message.Close();
  687. }
  688. return result;
  689. }
  690. internal FilterResult Match(Message message, bool matchBody)
  691. {
  692. if (this.IsAlwaysMatch)
  693. {
  694. // No need to do any expensive query evaluation if we know that the query will always match
  695. return new FilterResult(true);
  696. }
  697. return base.Match(message, matchBody, null);
  698. }
  699. internal FilterResult Match(SeekableXPathNavigator navigator)
  700. {
  701. if (this.IsAlwaysMatch)
  702. {
  703. // No need to do any expensive query evaluation if we know that the query will always match
  704. return new FilterResult(true);
  705. }
  706. // Is it a filter that we will evaluate using the framework engine?
  707. // We can evaluate that without having to allocate a query processor
  708. if (this.IsFxFilter)
  709. {
  710. return new FilterResult(this.MatchFx(navigator));
  711. }
  712. return base.Match(navigator, null);
  713. }
  714. internal FilterResult Match(XPathNavigator navigator)
  715. {
  716. Fx.Assert(null != this.query, "");
  717. if (this.IsAlwaysMatch)
  718. {
  719. return new FilterResult(true);
  720. }
  721. // Is it a filter that we will evaluate using the framework engine?
  722. // We can evaluate that without having to allocate a query processor
  723. if (this.IsFxFilter)
  724. {
  725. return new FilterResult(this.MatchFx(navigator));
  726. }
  727. return base.Match(navigator, null);
  728. }
  729. /// <summary>
  730. /// Evaluates the filter over infosets surfaced via the given navigator by using the Fx engine
  731. /// We assume that the filter was pre-compiled using the framework engine
  732. /// </summary>
  733. internal bool MatchFx(XPathNavigator navigator)
  734. {
  735. INodeCounter counter = navigator as INodeCounter;
  736. if (counter == null)
  737. {
  738. navigator = new SafeSeekableNavigator(new GenericSeekableNavigator(navigator), this.NodeQuota);
  739. }
  740. else
  741. {
  742. counter.CounterMarker = this.NodeQuota;
  743. counter.MaxCounter = this.NodeQuota;
  744. }
  745. Fx.Assert(null != this.query && OpcodeID.MatchSingleFx == this.query.ID, "");
  746. try
  747. {
  748. return ((MatchSingleFxEngineResultOpcode)this.query).Match(navigator);
  749. }
  750. catch (XPathNavigatorException e)
  751. {
  752. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
  753. }
  754. catch (NavigatorInvalidBodyAccessException e)
  755. {
  756. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
  757. }
  758. }
  759. }
  760. internal class InverseQueryMatcher : QueryMatcher
  761. {
  762. SubExprEliminator elim;
  763. Dictionary<object, Opcode> lastLookup;
  764. bool match;
  765. internal InverseQueryMatcher(bool match)
  766. : base()
  767. {
  768. this.elim = new SubExprEliminator();
  769. this.lastLookup = new Dictionary<object, Opcode>();
  770. this.match = match;
  771. }
  772. internal void Add(string expression, XmlNamespaceManager names, object item, bool forceExternal)
  773. {
  774. Fx.Assert(null != item, "");
  775. // Compile the new filter
  776. bool compiled = false;
  777. OpcodeBlock codeBlock = new OpcodeBlock();
  778. codeBlock.Append(new NoOpOpcode(OpcodeID.QueryTree));
  779. if (!forceExternal)
  780. {
  781. try
  782. {
  783. ValueDataType returnType = ValueDataType.None;
  784. // Try to compile and merge the compiled query into the query tree
  785. codeBlock.Append(QueryMatcher.CompileForInternalEngine(expression, names, QueryCompilerFlags.InverseQuery, out returnType));
  786. MultipleResultOpcode opcode;
  787. if (!this.match)
  788. {
  789. opcode = new QueryMultipleResultOpcode();
  790. }
  791. else
  792. {
  793. opcode = new MatchMultipleResultOpcode();
  794. }
  795. opcode.AddItem(item);
  796. codeBlock.Append(opcode);
  797. compiled = true;
  798. // Perform SubExpression Elimination
  799. codeBlock = new OpcodeBlock(this.elim.Add(item, codeBlock.First));
  800. this.subExprVars = this.elim.VariableCount;
  801. }
  802. catch (QueryCompileException)
  803. {
  804. // If the filter couldn't be compiled, we drop down to the framework engine
  805. }
  806. }
  807. if (!compiled)
  808. {
  809. codeBlock.Append(QueryMatcher.CompileForExternalEngine(expression, names, item, this.match));
  810. }
  811. // Merge the compiled query into the query tree
  812. QueryTreeBuilder builder = new QueryTreeBuilder();
  813. this.query = builder.Build(this.query, codeBlock);
  814. // To de-merge this filter from the tree, we'll have to walk backwards up the tree... so we
  815. // have to remember the last opcode that is executed on behalf of this filter
  816. this.lastLookup[item] = builder.LastOpcode;
  817. }
  818. internal void Clear()
  819. {
  820. foreach (object item in this.lastLookup.Keys)
  821. {
  822. this.Remove(this.lastLookup[item], item);
  823. this.elim.Remove(item);
  824. }
  825. this.subExprVars = this.elim.VariableCount;
  826. this.lastLookup.Clear();
  827. }
  828. internal void Remove(object item)
  829. {
  830. Fx.Assert(this.lastLookup.ContainsKey(item), "");
  831. this.Remove(this.lastLookup[item], item);
  832. this.lastLookup.Remove(item);
  833. // Remove filter from subexpr eliminator
  834. this.elim.Remove(item);
  835. this.subExprVars = this.elim.VariableCount;
  836. }
  837. void Remove(Opcode opcode, object item)
  838. {
  839. MultipleResultOpcode multiOpcode = opcode as MultipleResultOpcode;
  840. if (multiOpcode != null)
  841. {
  842. multiOpcode.RemoveItem(item);
  843. }
  844. else
  845. {
  846. opcode.Remove();
  847. }
  848. }
  849. internal override void Trim()
  850. {
  851. base.Trim();
  852. elim.Trim();
  853. }
  854. }
  855. }