QueryModel.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.ServiceModel.Dispatcher
  5. {
  6. using System.Globalization;
  7. using System.Runtime;
  8. using System.Xml.XPath;
  9. internal enum AxisDirection : byte
  10. {
  11. Forward,
  12. Reverse
  13. }
  14. internal enum QueryNodeType : byte
  15. {
  16. Any = 0x00,
  17. Root = 0x01,
  18. Attribute = 0x02,
  19. Element = 0x04,
  20. Text = 0x08,
  21. Comment = 0x10,
  22. Processing = 0x20,
  23. Namespace = 0x40,
  24. Multiple = 0x80,
  25. ChildNodes = (QueryNodeType.Multiple | QueryNodeType.Element | QueryNodeType.Comment | QueryNodeType.Text | QueryNodeType.Processing),
  26. Ancestor = (QueryNodeType.Multiple | QueryNodeType.Element | QueryNodeType.Root),
  27. All = (QueryNodeType.Multiple | QueryNodeType.Element | QueryNodeType.Attribute | QueryNodeType.Namespace | QueryNodeType.Root | QueryNodeType.Comment | QueryNodeType.Text | QueryNodeType.Processing)
  28. }
  29. internal enum QueryAxisType : byte
  30. {
  31. None = 0,
  32. Ancestor = 1,
  33. AncestorOrSelf = 2,
  34. Attribute = 3,
  35. Child = 4,
  36. Descendant = 5,
  37. DescendantOrSelf = 6,
  38. Following = 7,
  39. FollowingSibling = 8,
  40. Namespace = 9,
  41. Parent = 10,
  42. Preceding = 11,
  43. PrecedingSibling = 12,
  44. Self = 13
  45. }
  46. // 4 bytes - each element is a byte
  47. internal struct QueryAxis
  48. {
  49. AxisDirection direction;
  50. QueryNodeType principalNode;
  51. QueryAxisType type;
  52. QueryNodeType validNodeTypes;
  53. internal QueryAxis(QueryAxisType type, AxisDirection direction, QueryNodeType principalNode, QueryNodeType validNodeTypes)
  54. {
  55. this.direction = direction;
  56. this.principalNode = principalNode;
  57. this.type = type;
  58. this.validNodeTypes = validNodeTypes;
  59. }
  60. #if NO
  61. internal AxisDirection Direction
  62. {
  63. get
  64. {
  65. return this.direction;
  66. }
  67. }
  68. #endif
  69. internal QueryNodeType PrincipalNodeType
  70. {
  71. get
  72. {
  73. return this.principalNode;
  74. }
  75. }
  76. internal QueryAxisType Type
  77. {
  78. get
  79. {
  80. return this.type;
  81. }
  82. }
  83. internal QueryNodeType ValidNodeTypes
  84. {
  85. get
  86. {
  87. return this.validNodeTypes;
  88. }
  89. }
  90. internal bool IsSupported()
  91. {
  92. switch (this.type)
  93. {
  94. default:
  95. return false;
  96. case QueryAxisType.DescendantOrSelf:
  97. case QueryAxisType.Descendant:
  98. case QueryAxisType.Attribute:
  99. case QueryAxisType.Child:
  100. case QueryAxisType.Self:
  101. break;
  102. }
  103. return true;
  104. }
  105. }
  106. /// <summary>
  107. /// Information about a qname
  108. /// </summary>
  109. internal enum NodeQNameType : byte
  110. {
  111. // QName has neither name nor namespace. Entirely empty
  112. Empty = 0x00,
  113. // QName has a regular name
  114. Name = 0x01,
  115. // QName has regular namespace
  116. Namespace = 0x02,
  117. // QName has both name and namespace
  118. Standard = NodeQNameType.Name | NodeQNameType.Namespace,
  119. // QName has a wildcard name
  120. NameWildcard = 0x04,
  121. // QName has a wildcard namespace
  122. NamespaceWildcard = 0x08,
  123. // QName is entirely wildcard
  124. Wildcard = NodeQNameType.NameWildcard | NodeQNameType.NamespaceWildcard
  125. }
  126. /// <summary>
  127. /// We'll use our own class to store QNames instead of XmlQualifiedName because:
  128. /// 1. Our is a struct. No allocations required. We have to dynamically create QNames in several places and
  129. /// and don't want to do allocations
  130. /// 2. Our equality tests are frequently faster. XmlQualifiedName implements .Equal with the assumption that
  131. /// strings are atomized using a shared name table, which in the case of arbitrary object graphs, they almost
  132. /// never will be.
  133. /// </summary>
  134. internal struct NodeQName
  135. {
  136. internal static NodeQName Empty = new NodeQName(string.Empty, string.Empty);
  137. internal string name;
  138. internal string ns;
  139. internal NodeQName(string name)
  140. : this(name, string.Empty)
  141. {
  142. }
  143. internal NodeQName(string name, string ns)
  144. {
  145. this.name = (null == name) ? string.Empty : name;
  146. this.ns = (null == ns) ? string.Empty : ns;
  147. }
  148. #if NO
  149. internal NodeQName(string name, string ns, string defaultNS)
  150. {
  151. Fx.Assert(null != defaultNS, "");
  152. this.name = (null == name) ? string.Empty : name;
  153. this.ns = (null == ns) ? defaultNS : ns;
  154. }
  155. internal NodeQName(XmlQualifiedName qname)
  156. {
  157. this.name = qname.Name;
  158. this.ns = qname.Namespace;
  159. }
  160. internal bool HasWildcard
  161. {
  162. get
  163. {
  164. return (this.IsNameWildcard || this.IsNamespaceWildcard);
  165. }
  166. }
  167. #endif
  168. internal bool IsEmpty
  169. {
  170. get
  171. {
  172. return (this.name.Length == 0 && this.ns.Length == 0);
  173. }
  174. }
  175. internal bool IsNameDefined
  176. {
  177. get
  178. {
  179. return (this.name.Length > 0);
  180. }
  181. }
  182. internal bool IsNameWildcard
  183. {
  184. get
  185. {
  186. return object.ReferenceEquals(this.name, QueryDataModel.Wildcard);
  187. }
  188. }
  189. internal bool IsNamespaceDefined
  190. {
  191. get
  192. {
  193. return (this.ns.Length > 0);
  194. }
  195. }
  196. internal bool IsNamespaceWildcard
  197. {
  198. get
  199. {
  200. return object.ReferenceEquals(this.ns, QueryDataModel.Wildcard);
  201. }
  202. }
  203. internal string Name
  204. {
  205. get
  206. {
  207. return this.name;
  208. }
  209. #if NO
  210. set
  211. {
  212. Fx.Assert(null != value, "");
  213. this.name = value;
  214. }
  215. #endif
  216. }
  217. internal string Namespace
  218. {
  219. get
  220. {
  221. return this.ns;
  222. }
  223. #if NO
  224. set
  225. {
  226. Fx.Assert(null != value, "");
  227. this.ns = value;
  228. }
  229. #endif
  230. }
  231. /// <summary>
  232. /// If this qname's strings are == to the constants defined in NodeQName, replace the strings with the
  233. /// constants
  234. /// </summary>
  235. #if NO
  236. internal bool Atomize()
  237. {
  238. return false;
  239. }
  240. #endif
  241. internal bool EqualsName(string name)
  242. {
  243. return (name == this.name);
  244. }
  245. #if NO
  246. internal bool Equals(string name)
  247. {
  248. return this.EqualsName(name);
  249. }
  250. internal bool Equals(string name, string ns)
  251. {
  252. return ( (name.Length == this.name.Length && name == this.name) && (ns.Length == this.ns.Length && ns == this.ns));
  253. }
  254. #endif
  255. internal bool Equals(NodeQName qname)
  256. {
  257. return ((qname.name.Length == this.name.Length && qname.name == this.name) && (qname.ns.Length == this.ns.Length && qname.ns == this.ns));
  258. }
  259. #if NO
  260. internal bool Equals(SeekableXPathNavigator navigator)
  261. {
  262. string str = navigator.LocalName;
  263. if (this.name.Length == str.Length && this.name == str)
  264. {
  265. str = navigator.NamespaceURI;
  266. return (this.ns.Length == str.Length && this.ns == str);
  267. }
  268. return false;
  269. }
  270. #endif
  271. internal bool EqualsNamespace(string ns)
  272. {
  273. return (ns == this.ns);
  274. }
  275. #if NO
  276. internal bool EqualsReference(NodeQName qname)
  277. {
  278. return (object.ReferenceEquals(qname.name, this.name) && object.ReferenceEquals(qname.ns, this.ns));
  279. }
  280. internal string QName()
  281. {
  282. return this.ns + ':' + this.name;
  283. }
  284. #endif
  285. /// <summary>
  286. /// Return this qname's type - whether the name is defined, whether the name is a wildcard etc
  287. /// </summary>
  288. internal NodeQNameType GetQNameType()
  289. {
  290. NodeQNameType type = NodeQNameType.Empty;
  291. if (this.IsNameDefined)
  292. {
  293. if (this.IsNameWildcard)
  294. {
  295. type |= NodeQNameType.NameWildcard;
  296. }
  297. else
  298. {
  299. type |= NodeQNameType.Name;
  300. }
  301. }
  302. if (this.IsNamespaceDefined)
  303. {
  304. if (this.IsNamespaceWildcard)
  305. {
  306. type |= NodeQNameType.NamespaceWildcard;
  307. }
  308. else
  309. {
  310. type |= NodeQNameType.Namespace;
  311. }
  312. }
  313. return type;
  314. }
  315. }
  316. internal static class QueryDataModel
  317. {
  318. internal static QueryAxis[] axes;
  319. internal static string Wildcard = "*";
  320. static QueryDataModel()
  321. {
  322. // Init axes table
  323. QueryDataModel.axes = new QueryAxis[] {
  324. new QueryAxis(QueryAxisType.None, AxisDirection.Forward, QueryNodeType.Any, QueryNodeType.Any),
  325. new QueryAxis(QueryAxisType.Ancestor, AxisDirection.Reverse, QueryNodeType.Element, QueryNodeType.Ancestor),
  326. new QueryAxis(QueryAxisType.AncestorOrSelf, AxisDirection.Reverse, QueryNodeType.Element, QueryNodeType.All),
  327. new QueryAxis(QueryAxisType.Attribute, AxisDirection.Forward, QueryNodeType.Attribute, QueryNodeType.Attribute),
  328. new QueryAxis(QueryAxisType.Child, AxisDirection.Forward, QueryNodeType.Element, QueryNodeType.ChildNodes),
  329. new QueryAxis(QueryAxisType.Descendant, AxisDirection.Forward, QueryNodeType.Element, QueryNodeType.ChildNodes),
  330. new QueryAxis(QueryAxisType.DescendantOrSelf, AxisDirection.Forward, QueryNodeType.Element, QueryNodeType.All),
  331. new QueryAxis(QueryAxisType.Following, AxisDirection.Forward, QueryNodeType.Element, QueryNodeType.ChildNodes),
  332. new QueryAxis(QueryAxisType.FollowingSibling, AxisDirection.Forward, QueryNodeType.Element, QueryNodeType.ChildNodes),
  333. new QueryAxis(QueryAxisType.Namespace, AxisDirection.Forward, QueryNodeType.Namespace, QueryNodeType.Namespace),
  334. new QueryAxis(QueryAxisType.Parent, AxisDirection.Reverse, QueryNodeType.Element, QueryNodeType.Ancestor),
  335. new QueryAxis(QueryAxisType.Preceding, AxisDirection.Reverse, QueryNodeType.Element, QueryNodeType.ChildNodes),
  336. new QueryAxis(QueryAxisType.PrecedingSibling, AxisDirection.Reverse, QueryNodeType.Element, QueryNodeType.All),
  337. new QueryAxis(QueryAxisType.Self, AxisDirection.Forward, QueryNodeType.Element, QueryNodeType.All),
  338. };
  339. }
  340. /// <summary>
  341. /// XPath does not interpret namespace declarations as attributes
  342. /// Any attributes that not qualified by the XmlNamespaces namespaces is therefore kosher
  343. /// </summary>
  344. internal static bool IsAttribute(string ns)
  345. {
  346. return (0 != string.CompareOrdinal("http://www.w3.org/2000/xmlns/", ns));
  347. }
  348. #if NO
  349. internal static bool IsDigit(char ch)
  350. {
  351. return char.IsDigit(ch);
  352. }
  353. internal static bool IsLetter(char ch)
  354. {
  355. return char.IsLetter(ch);
  356. }
  357. internal static bool IsLetterOrDigit(char ch)
  358. {
  359. return char.IsLetterOrDigit(ch);
  360. }
  361. internal static bool IsWhitespace(char ch)
  362. {
  363. return char.IsWhiteSpace(ch);
  364. }
  365. #endif
  366. internal static QueryAxis GetAxis(QueryAxisType type)
  367. {
  368. return QueryDataModel.axes[(int)type];
  369. }
  370. #if NO
  371. internal static QueryNodeType GetNodeType(XPathNodeType type)
  372. {
  373. QueryNodeType nodeType;
  374. switch (type)
  375. {
  376. default:
  377. nodeType = QueryNodeType.Any;
  378. break;
  379. case XPathNodeType.Root:
  380. nodeType = QueryNodeType.Root;
  381. break;
  382. case XPathNodeType.Attribute:
  383. nodeType = QueryNodeType.Attribute;
  384. break;
  385. case XPathNodeType.Element:
  386. nodeType = QueryNodeType.Element;
  387. break;
  388. case XPathNodeType.Comment:
  389. nodeType = QueryNodeType.Comment;
  390. break;
  391. case XPathNodeType.Text:
  392. case XPathNodeType.Whitespace:
  393. case XPathNodeType.SignificantWhitespace:
  394. nodeType = QueryNodeType.Text;
  395. break;
  396. case XPathNodeType.ProcessingInstruction:
  397. nodeType = QueryNodeType.Processing;
  398. break;
  399. }
  400. return nodeType;
  401. }
  402. internal static XPathNodeType GetXPathNodeType(QueryNodeType type)
  403. {
  404. XPathNodeType nodeType = XPathNodeType.All;
  405. switch(type)
  406. {
  407. default:
  408. break;
  409. case QueryNodeType.Attribute:
  410. nodeType = XPathNodeType.Attribute;
  411. break;
  412. case QueryNodeType.Root:
  413. nodeType = XPathNodeType.Root;
  414. break;
  415. case QueryNodeType.Namespace:
  416. nodeType = XPathNodeType.Namespace;
  417. break;
  418. case QueryNodeType.Element:
  419. nodeType = XPathNodeType.Element;
  420. break;
  421. case QueryNodeType.Comment:
  422. nodeType = XPathNodeType.Comment;
  423. break;
  424. case QueryNodeType.Text:
  425. nodeType = XPathNodeType.Text;
  426. break;
  427. case QueryNodeType.Processing:
  428. nodeType = XPathNodeType.ProcessingInstruction;
  429. break;
  430. }
  431. return nodeType;
  432. }
  433. // Is it possible to select nodes matching the given criteria from nodes of the given type
  434. internal static bool IsSelectPossible(QueryNodeType nodeType, NodeSelectCriteria desc)
  435. {
  436. if (0 != (nodeType & QueryNodeType.Attribute))
  437. {
  438. switch(desc.Axis.Type)
  439. {
  440. default:
  441. return false;
  442. // Navigation is possible from attributes on these axes
  443. case QueryAxisType.Self:
  444. case QueryAxisType.Ancestor:
  445. case QueryAxisType.AncestorOrSelf:
  446. case QueryAxisType.Parent:
  447. return true;
  448. }
  449. }
  450. else if (0 != (nodeType & QueryNodeType.Root))
  451. {
  452. if (AxisDirection.Reverse == desc.Axis.Direction)
  453. {
  454. return false;
  455. }
  456. switch(desc.Axis.Type)
  457. {
  458. default:
  459. return true;
  460. // Navigation is possible from attributes on these axes
  461. case QueryAxisType.Attribute:
  462. case QueryAxisType.Namespace:
  463. return false;
  464. }
  465. }
  466. return true;
  467. }
  468. #endif
  469. }
  470. internal static class QueryValueModel
  471. {
  472. /*
  473. Conversions
  474. The following EXACTLY follow the XPath 1.0 spec. Some conversions may seem ----/inefficient, but
  475. we prefer to adhere to the spec and shall leave them be unless performance becomes an issue.
  476. */
  477. internal static bool Boolean(string val)
  478. {
  479. Fx.Assert(null != val, "");
  480. return (val.Length > 0);
  481. }
  482. internal static bool Boolean(double dblVal)
  483. {
  484. return (dblVal != 0 && !double.IsNaN(dblVal));
  485. }
  486. internal static bool Boolean(NodeSequence sequence)
  487. {
  488. Fx.Assert(null != sequence, "");
  489. return sequence.IsNotEmpty;
  490. }
  491. internal static bool Boolean(XPathNodeIterator iterator)
  492. {
  493. Fx.Assert(null != iterator, "");
  494. return (iterator.Count > 0);
  495. }
  496. internal static double Double(bool val)
  497. {
  498. return (val ? 1 : 0);
  499. }
  500. internal static double Double(string val)
  501. {
  502. // XPath does not convert numbers the same way .NET does. A string preceeded by + is actually converted
  503. // to NAN! Go figure.. Anyway, we have to do this manually.
  504. val = val.TrimStart();
  505. if (val.Length > 0 && val[0] != '+')
  506. {
  507. double dblVal;
  508. if (double.TryParse(val,
  509. NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowTrailingWhite,
  510. NumberFormatInfo.InvariantInfo,
  511. out dblVal))
  512. {
  513. return dblVal;
  514. }
  515. }
  516. return double.NaN;
  517. }
  518. internal static double Double(NodeSequence sequence)
  519. {
  520. Fx.Assert(null != sequence, "");
  521. return QueryValueModel.Double(sequence.StringValue());
  522. }
  523. internal static double Double(XPathNodeIterator iterator)
  524. {
  525. Fx.Assert(null != iterator, "");
  526. return QueryValueModel.Double(QueryValueModel.String(iterator));
  527. }
  528. #if NO
  529. internal static string String(object val)
  530. {
  531. return val.ToString();
  532. }
  533. #endif
  534. internal static string String(bool val)
  535. {
  536. return val ? "true" : "false"; // XPath requires all lower case. bool.ToString() returns 'False' and 'True'
  537. }
  538. internal static string String(double val)
  539. {
  540. return val.ToString(CultureInfo.InvariantCulture);
  541. }
  542. internal static string String(NodeSequence sequence)
  543. {
  544. Fx.Assert(null != sequence, "");
  545. return sequence.StringValue();
  546. }
  547. internal static string String(XPathNodeIterator iterator)
  548. {
  549. Fx.Assert(null != iterator, "");
  550. if (iterator.Count == 0)
  551. {
  552. return string.Empty;
  553. }
  554. else if (iterator.CurrentPosition == 0)
  555. {
  556. iterator.MoveNext();
  557. return iterator.Current.Value;
  558. }
  559. else if (iterator.CurrentPosition == 1)
  560. {
  561. return iterator.Current.Value;
  562. }
  563. else
  564. {
  565. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  566. new InvalidOperationException(SR.GetString(SR.QueryCantGetStringForMovedIterator)));
  567. }
  568. }
  569. // OPTIMIZE Comparisons in general!!
  570. internal static bool Compare(bool x, bool y, RelationOperator op)
  571. {
  572. switch (op)
  573. {
  574. default:
  575. return QueryValueModel.Compare(QueryValueModel.Double(x), QueryValueModel.Double(y), op);
  576. case RelationOperator.Eq:
  577. return (x == y);
  578. case RelationOperator.Ne:
  579. return (x != y);
  580. }
  581. }
  582. internal static bool Compare(bool x, double y, RelationOperator op)
  583. {
  584. switch (op)
  585. {
  586. default:
  587. return QueryValueModel.Compare(QueryValueModel.Double(x), y, op);
  588. case RelationOperator.Eq:
  589. return (x == QueryValueModel.Boolean(y));
  590. case RelationOperator.Ne:
  591. return (x != QueryValueModel.Boolean(y));
  592. }
  593. }
  594. internal static bool Compare(bool x, string y, RelationOperator op)
  595. {
  596. Fx.Assert(null != y, "");
  597. switch (op)
  598. {
  599. default:
  600. return QueryValueModel.Compare(QueryValueModel.Double(x), QueryValueModel.Double(y), op);
  601. case RelationOperator.Eq:
  602. return (x == QueryValueModel.Boolean(y));
  603. case RelationOperator.Ne:
  604. return (x != QueryValueModel.Boolean(y));
  605. }
  606. }
  607. internal static bool Compare(bool x, NodeSequence y, RelationOperator op)
  608. {
  609. Fx.Assert(null != y, "");
  610. return QueryValueModel.Compare(x, QueryValueModel.Boolean(y), op);
  611. }
  612. internal static bool Compare(double x, bool y, RelationOperator op)
  613. {
  614. switch (op)
  615. {
  616. default:
  617. return QueryValueModel.Compare(x, QueryValueModel.Double(y), op);
  618. case RelationOperator.Eq:
  619. return (QueryValueModel.Boolean(x) == y);
  620. case RelationOperator.Ne:
  621. return (QueryValueModel.Boolean(x) != y);
  622. }
  623. }
  624. internal static bool Compare(double x, double y, RelationOperator op)
  625. {
  626. switch (op)
  627. {
  628. default:
  629. throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch));
  630. case RelationOperator.Eq:
  631. return (x == y);
  632. case RelationOperator.Ge:
  633. return (x >= y);
  634. case RelationOperator.Gt:
  635. return (x > y);
  636. case RelationOperator.Le:
  637. return (x <= y);
  638. case RelationOperator.Lt:
  639. return (x < y);
  640. case RelationOperator.Ne:
  641. return (x != y);
  642. }
  643. }
  644. internal static bool Compare(double x, string y, RelationOperator op)
  645. {
  646. Fx.Assert(null != y, "");
  647. return QueryValueModel.Compare(x, QueryValueModel.Double(y), op);
  648. }
  649. internal static bool Compare(double x, NodeSequence y, RelationOperator op)
  650. {
  651. Fx.Assert(null != y, "");
  652. switch (op)
  653. {
  654. default:
  655. return y.Compare(x, op);
  656. case RelationOperator.Ge:
  657. return y.Compare(x, RelationOperator.Le);
  658. case RelationOperator.Gt:
  659. return y.Compare(x, RelationOperator.Lt);
  660. case RelationOperator.Le:
  661. return y.Compare(x, RelationOperator.Ge);
  662. case RelationOperator.Lt:
  663. return y.Compare(x, RelationOperator.Gt);
  664. }
  665. }
  666. internal static bool Compare(string x, bool y, RelationOperator op)
  667. {
  668. Fx.Assert(null != x, "");
  669. switch (op)
  670. {
  671. default:
  672. return QueryValueModel.Compare(QueryValueModel.Double(x), QueryValueModel.Double(y), op);
  673. case RelationOperator.Eq:
  674. return (y == QueryValueModel.Boolean(x));
  675. case RelationOperator.Ne:
  676. return (y != QueryValueModel.Boolean(x));
  677. }
  678. }
  679. internal static bool Compare(string x, double y, RelationOperator op)
  680. {
  681. Fx.Assert(null != x, "");
  682. return QueryValueModel.Compare(QueryValueModel.Double(x), y, op);
  683. }
  684. internal static bool Compare(string x, string y, RelationOperator op)
  685. {
  686. Fx.Assert(null != x && null != y, "");
  687. switch (op)
  688. {
  689. default:
  690. Fx.Assert("Invalid RelationOperator");
  691. break;
  692. case RelationOperator.Eq:
  693. return QueryValueModel.Equals(x, y);
  694. case RelationOperator.Ge:
  695. case RelationOperator.Gt:
  696. case RelationOperator.Le:
  697. case RelationOperator.Lt:
  698. return QueryValueModel.Compare(QueryValueModel.Double(x), QueryValueModel.Double(y), op);
  699. case RelationOperator.Ne:
  700. return (x.Length != y.Length || 0 != string.CompareOrdinal(x, y));
  701. }
  702. return false;
  703. }
  704. internal static bool Compare(string x, NodeSequence y, RelationOperator op)
  705. {
  706. Fx.Assert(null != y, "");
  707. switch (op)
  708. {
  709. default:
  710. return y.Compare(x, op);
  711. case RelationOperator.Ge:
  712. return y.Compare(x, RelationOperator.Le);
  713. case RelationOperator.Gt:
  714. return y.Compare(x, RelationOperator.Lt);
  715. case RelationOperator.Le:
  716. return y.Compare(x, RelationOperator.Ge);
  717. case RelationOperator.Lt:
  718. return y.Compare(x, RelationOperator.Gt);
  719. }
  720. }
  721. internal static bool Compare(NodeSequence x, bool y, RelationOperator op)
  722. {
  723. Fx.Assert(null != x, "");
  724. return QueryValueModel.Compare(QueryValueModel.Boolean(x), y, op);
  725. }
  726. internal static bool Compare(NodeSequence x, double y, RelationOperator op)
  727. {
  728. Fx.Assert(null != x, "");
  729. return x.Compare(y, op);
  730. }
  731. internal static bool Compare(NodeSequence x, string y, RelationOperator op)
  732. {
  733. Fx.Assert(null != x, "");
  734. return x.Compare(y, op);
  735. }
  736. internal static bool Compare(NodeSequence x, NodeSequence y, RelationOperator op)
  737. {
  738. Fx.Assert(null != x, "");
  739. return x.Compare(y, op);
  740. }
  741. internal static bool CompileTimeCompare(object x, object y, RelationOperator op)
  742. {
  743. Fx.Assert(null != x && null != y, "");
  744. if (x is string)
  745. {
  746. if (y is double)
  747. {
  748. return QueryValueModel.Compare((string)x, (double)y, op);
  749. }
  750. else if (y is string)
  751. {
  752. return QueryValueModel.Compare((string)x, (string)y, op);
  753. }
  754. }
  755. else if (x is double)
  756. {
  757. if (y is double)
  758. {
  759. return QueryValueModel.Compare((double)x, (double)y, op);
  760. }
  761. else if (y is string)
  762. {
  763. return QueryValueModel.Compare((double)x, (string)y, op);
  764. }
  765. }
  766. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.InvalidComparison));
  767. }
  768. internal static bool Equals(bool x, string y)
  769. {
  770. return (x == QueryValueModel.Boolean(y));
  771. }
  772. internal static bool Equals(double x, string y)
  773. {
  774. return (x == QueryValueModel.Double(y));
  775. }
  776. internal static bool Equals(string x, string y)
  777. {
  778. return (x.Length == y.Length && x == y);
  779. }
  780. internal static bool Equals(NodeSequence x, string y)
  781. {
  782. return x.Equals(y);
  783. }
  784. internal static bool Equals(bool x, double y)
  785. {
  786. return (x == QueryValueModel.Boolean(y));
  787. }
  788. internal static bool Equals(double x, double y)
  789. {
  790. return (x == y);
  791. }
  792. internal static bool Equals(NodeSequence x, double y)
  793. {
  794. return x.Equals(y);
  795. }
  796. internal static double Round(double val)
  797. {
  798. // Math.Round does bankers rounding, which is IEEE 754, section 4.
  799. // If a is halfway between two whole numbers, one of which by definition is even and the other odd, then
  800. // the even number is returned. Thus Round(3.5) == Round(4.5) == 4.0
  801. // XPath has different rules.. which is Math.Floor(a + 0.5)... with two exceptions (see below)
  802. // The round function returns the number that is closest to the argument and that is an integer.
  803. // If there are two such numbers, then the one that is closest to positive infinity is returned.
  804. // If the argument is NaN, then NaN is returned.
  805. // If the argument is positive infinity, then positive infinity is returned.
  806. // If the argument is negative infinity, then negative infinity is returned.
  807. // If the argument is positive zero, then positive zero is returned.
  808. // If the argument is negative zero, then negative zero is returned.
  809. // If the argument is less than zero, but greater than or equal to -0.5, then negative zero is returned.
  810. // For these last two cases, the result of calling the round function is not the same as the result of
  811. // adding 0.5 and then calling the floor function.
  812. // Note: .NET has no positive or negative zero... so we give up and use Math.Round...
  813. // For all other cases, we use Floor to Round...
  814. return (-0.5 <= val && val <= 0.0) ? Math.Round(val) : Math.Floor(val + 0.5);
  815. }
  816. #if NO
  817. internal static XPathResultType ResultType(ValueDataType dataType)
  818. {
  819. switch (dataType)
  820. {
  821. default:
  822. break;
  823. case ValueDataType.Boolean:
  824. return XPathResultType.Boolean;
  825. case ValueDataType.Double:
  826. return XPathResultType.Number;
  827. case ValueDataType.Sequence:
  828. return XPathResultType.NodeSet;
  829. case ValueDataType.String:
  830. return XPathResultType.String;
  831. }
  832. return XPathResultType.Any;
  833. }
  834. #endif
  835. }
  836. }