trie_node_factory.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import * as DomUtil from '../common/dom_util.js';
  2. import * as XpathUtil from '../common/xpath_util.js';
  3. import { Grammar } from '../rule_engine/grammar.js';
  4. import * as MathCompoundStore from '../rule_engine/math_compound_store.js';
  5. import { AbstractTrieNode } from './abstract_trie_node.js';
  6. import { StaticTrieNode } from './abstract_trie_node.js';
  7. import { TrieNodeKind } from './trie_node.js';
  8. export function getNode(kind, constraint, context) {
  9. switch (kind) {
  10. case TrieNodeKind.ROOT:
  11. return new RootTrieNode();
  12. case TrieNodeKind.DYNAMIC:
  13. return new DynamicTrieNode(constraint);
  14. case TrieNodeKind.QUERY:
  15. return new QueryTrieNode(constraint, context);
  16. case TrieNodeKind.BOOLEAN:
  17. return new BooleanTrieNode(constraint, context);
  18. default:
  19. return null;
  20. }
  21. }
  22. class RootTrieNode extends AbstractTrieNode {
  23. constructor() {
  24. super('', () => true);
  25. this.kind = TrieNodeKind.ROOT;
  26. }
  27. }
  28. class DynamicTrieNode extends AbstractTrieNode {
  29. constructor(constraint) {
  30. super(constraint, (axis) => axis === constraint);
  31. this.kind = TrieNodeKind.DYNAMIC;
  32. }
  33. }
  34. const comparator = {
  35. '=': (x, y) => x === y,
  36. '!=': (x, y) => x !== y,
  37. '<': (x, y) => x < y,
  38. '>': (x, y) => x > y,
  39. '<=': (x, y) => x <= y,
  40. '>=': (x, y) => x >= y
  41. };
  42. function constraintTest(constraint) {
  43. if (constraint.match(/^self::\*$/)) {
  44. return (_node) => true;
  45. }
  46. if (constraint.match(/^self::\w+$/)) {
  47. const tag = constraint.slice(6).toUpperCase();
  48. return (node) => node.tagName && DomUtil.tagName(node) === tag;
  49. }
  50. if (constraint.match(/^self::\w+:\w+$/)) {
  51. const inter = constraint.split(':');
  52. const namespace = XpathUtil.resolveNameSpace(inter[2]);
  53. if (!namespace) {
  54. return null;
  55. }
  56. const tag = inter[3].toUpperCase();
  57. return (node) => node.localName &&
  58. node.localName.toUpperCase() === tag &&
  59. node.namespaceURI === namespace;
  60. }
  61. if (constraint.match(/^@\w+$/)) {
  62. const attr = constraint.slice(1);
  63. return (node) => node.hasAttribute && node.hasAttribute(attr);
  64. }
  65. if (constraint.match(/^@\w+="[\w\d ]+"$/)) {
  66. const split = constraint.split('=');
  67. const attr = split[0].slice(1);
  68. const value = split[1].slice(1, -1);
  69. return (node) => node.hasAttribute &&
  70. node.hasAttribute(attr) &&
  71. node.getAttribute(attr) === value;
  72. }
  73. if (constraint.match(/^@\w+!="[\w\d ]+"$/)) {
  74. const split = constraint.split('!=');
  75. const attr = split[0].slice(1);
  76. const value = split[1].slice(1, -1);
  77. return (node) => !node.hasAttribute ||
  78. !node.hasAttribute(attr) ||
  79. node.getAttribute(attr) !== value;
  80. }
  81. if (constraint.match(/^contains\(\s*@grammar\s*,\s*"[\w\d ]+"\s*\)$/)) {
  82. const split = constraint.split('"');
  83. const value = split[1];
  84. return (_node) => !!Grammar.getInstance().getParameter(value);
  85. }
  86. if (constraint.match(/^not\(\s*contains\(\s*@grammar\s*,\s*"[\w\d ]+"\s*\)\s*\)$/)) {
  87. const split = constraint.split('"');
  88. const value = split[1];
  89. return (_node) => !Grammar.getInstance().getParameter(value);
  90. }
  91. if (constraint.match(/^name\(\.\.\/\.\.\)="\w+"$/)) {
  92. const split = constraint.split('"');
  93. const tag = split[1].toUpperCase();
  94. return (node) => {
  95. var _a, _b;
  96. return ((_b = (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.tagName) &&
  97. DomUtil.tagName(node.parentNode.parentNode) === tag;
  98. };
  99. }
  100. if (constraint.match(/^count\(preceding-sibling::\*\)=\d+$/)) {
  101. const split = constraint.split('=');
  102. const num = parseInt(split[1], 10);
  103. return (node) => { var _a; return ((_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.childNodes[num]) === node; };
  104. }
  105. if (constraint.match(/^.+\[@category!?=".+"\]$/)) {
  106. let [, query, equality, category] = constraint.match(/^(.+)\[@category(!?=)"(.+)"\]$/);
  107. const unit = category.match(/^unit:(.+)$/);
  108. let add = '';
  109. if (unit) {
  110. category = unit[1];
  111. add = ':unit';
  112. }
  113. return (node) => {
  114. const xpath = XpathUtil.evalXPath(query, node)[0];
  115. if (xpath) {
  116. const result = MathCompoundStore.lookupCategory(xpath.textContent + add);
  117. return equality === '=' ? result === category : result !== category;
  118. }
  119. return false;
  120. };
  121. }
  122. if (constraint.match(/^string-length\(.+\)\W+\d+/)) {
  123. const [, select, comp, count] = constraint.match(/^string-length\((.+)\)(\W+)(\d+)/);
  124. const func = comparator[comp] || comparator['='];
  125. const numb = parseInt(count, 10);
  126. return (node) => {
  127. const xpath = XpathUtil.evalXPath(select, node)[0];
  128. if (!xpath) {
  129. return false;
  130. }
  131. return func(Array.from(xpath.textContent).length, numb);
  132. };
  133. }
  134. return null;
  135. }
  136. class QueryTrieNode extends StaticTrieNode {
  137. constructor(constraint, context) {
  138. super(constraint, constraintTest(constraint));
  139. this.context = context;
  140. this.kind = TrieNodeKind.QUERY;
  141. }
  142. applyTest(object) {
  143. return this.test
  144. ? this.test(object)
  145. : this.context.applyQuery(object, this.constraint) === object;
  146. }
  147. }
  148. class BooleanTrieNode extends StaticTrieNode {
  149. constructor(constraint, context) {
  150. super(constraint, constraintTest(constraint));
  151. this.context = context;
  152. this.kind = TrieNodeKind.BOOLEAN;
  153. }
  154. applyTest(object) {
  155. return this.test
  156. ? this.test(object)
  157. : this.context.applyConstraint(object, this.constraint);
  158. }
  159. }