PageRenderTime 48ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/saxonB/net/sf/saxon/om/Navigator.java

https://bitbucket.org/dmwelch/phdxnat_pipeline
Java | 1283 lines | 816 code | 144 blank | 323 comment | 265 complexity | 9fd81306f0c08cef4d53bc0044998e1c MD5 | raw file
  1. package net.sf.saxon.om;
  2. import net.sf.saxon.Controller;
  3. import net.sf.saxon.event.Receiver;
  4. import net.sf.saxon.expr.Expression;
  5. import net.sf.saxon.expr.XPathContext;
  6. import net.sf.saxon.pattern.*;
  7. import net.sf.saxon.tinytree.TinyNodeImpl;
  8. import net.sf.saxon.trans.XPathException;
  9. import net.sf.saxon.type.Type;
  10. import net.sf.saxon.value.Whitespace;
  11. import java.net.URI;
  12. import java.net.URISyntaxException;
  13. import java.util.ArrayList;
  14. import java.util.List;
  15. /**
  16. * The Navigator class provides helper classes for navigating a tree, irrespective
  17. * of its implementation
  18. *
  19. * @author Michael H. Kay
  20. */
  21. public final class Navigator {
  22. // Class is never instantiated
  23. private Navigator() {
  24. }
  25. /**
  26. * Get the string value of an attribute of a given element, given the URI and
  27. * local part of the attribute name.
  28. * @param element the element on which the required attribute appears
  29. * @param uri The namespace URI of the attribute name. The null URI is represented as an empty string.
  30. * @param localName The local part of the attribute name.
  31. * @return the attribute value, or null if the attribute is not present
  32. * @since 9.0
  33. */
  34. public static String getAttributeValue(NodeInfo element, String uri, String localName) {
  35. int fingerprint = element.getNamePool().allocate("", uri, localName);
  36. return element.getAttributeValue(fingerprint);
  37. }
  38. /**
  39. * Helper method to construct a NodeTest for use with the {@link NodeInfo#iterateAxis} method
  40. * @param pool the NamePool. The relevant NamePool can be obtained by calling the method
  41. * {@link NodeInfo#getNamePool}.
  42. * @param nodeKind The kind of node required, for example {@link Type#ELEMENT} or {@link Type#ATTRIBUTE}.
  43. * To select nodes of any kind, use {@link Type#NODE}.
  44. * @param uri The namespace URI of the nodes to be selected. Supply null to selects nodes from any
  45. * namespace or none. Supply "" to select nodes that are not in a namespace.
  46. * @param localName The local name of the nodes to be selected. Supply null to select nodes irrespective
  47. * of their local name.
  48. * @return a NodeTest that matches the requested nodes
  49. * @since 9.0
  50. */
  51. public static NodeTest makeNodeTest(NamePool pool, int nodeKind, String uri, String localName) {
  52. if (uri == null && localName == null) {
  53. return NodeKindTest.makeNodeKindTest(nodeKind);
  54. } else if (uri == null) {
  55. return new LocalNameTest(pool, nodeKind, localName);
  56. } else if (localName == null) {
  57. return new NamespaceTest(pool, nodeKind, uri);
  58. } else {
  59. int fp = pool.allocate("", uri, localName);
  60. return new NameTest(nodeKind, fp, pool);
  61. }
  62. }
  63. /**
  64. * Helper method to get the base URI of an element or processing instruction node
  65. * @param node the node whose base URI is required
  66. * @return the base URI of the node
  67. * @since 8.7
  68. */
  69. public static String getBaseURI(NodeInfo node) {
  70. String xmlBase = node.getAttributeValue(StandardNames.XML_BASE);
  71. if (xmlBase != null) {
  72. URI baseURI;
  73. try {
  74. baseURI = new URI(xmlBase);
  75. if (!baseURI.isAbsolute()) {
  76. NodeInfo parent = node.getParent();
  77. if (parent == null) {
  78. // We have a parentless element with a relative xml:base attribute.
  79. // See for example test XQTS fn-base-uri-10 and base-uri-27
  80. URI base = new URI(node.getSystemId());
  81. URI resolved = (xmlBase.length()==0 ? base : base.resolve(baseURI));
  82. return resolved.toString();
  83. }
  84. String startSystemId = node.getSystemId();
  85. String parentSystemId = parent.getSystemId();
  86. URI base = new URI(startSystemId.equals(parentSystemId) ? parent.getBaseURI() : startSystemId);
  87. baseURI = (xmlBase.length()==0 ? base : base.resolve(baseURI));
  88. }
  89. } catch (URISyntaxException e) {
  90. // xml:base is an invalid URI. Just return it as is: the operation that needs the base URI
  91. // will probably fail as a result. \
  92. return xmlBase;
  93. }
  94. return baseURI.toString();
  95. }
  96. String startSystemId = node.getSystemId();
  97. NodeInfo parent = node.getParent();
  98. if (parent == null) {
  99. return startSystemId;
  100. }
  101. String parentSystemId = parent.getSystemId();
  102. if (startSystemId.equals(parentSystemId)) {
  103. return parent.getBaseURI();
  104. } else {
  105. return startSystemId;
  106. }
  107. }
  108. /**
  109. * Get an absolute XPath expression that identifies a given node within its document
  110. *
  111. * @param node the node whose path is required. If null is supplied,
  112. * an empty string is returned - this fact is used in making a recursive call
  113. * for a parentless node.
  114. * @return a path expression that can be used to retrieve the node
  115. */
  116. public static String getPath(NodeInfo node) {
  117. if (node == null) {
  118. return "";
  119. }
  120. String pre;
  121. NodeInfo parent = node.getParent();
  122. // System.err.println("node = " + node + " parent = " + parent);
  123. switch (node.getNodeKind()) {
  124. case Type.DOCUMENT:
  125. return "/";
  126. case Type.ELEMENT:
  127. if (parent == null) {
  128. return node.getDisplayName();
  129. } else {
  130. pre = getPath(parent);
  131. if (pre.equals("/")) {
  132. return '/' + node.getDisplayName();
  133. } else {
  134. return pre + '/' + node.getDisplayName() + '[' + getNumberSimple(node) + ']';
  135. }
  136. }
  137. case Type.ATTRIBUTE:
  138. return getPath(parent) + "/@" + node.getDisplayName();
  139. case Type.TEXT:
  140. pre = getPath(parent);
  141. return (pre.equals("/") ? "" : pre) +
  142. "/text()[" + getNumberSimple(node) + ']';
  143. case Type.COMMENT:
  144. pre = getPath(parent);
  145. return (pre.equals("/") ? "" : pre) +
  146. "/comment()[" + getNumberSimple(node) + ']';
  147. case Type.PROCESSING_INSTRUCTION:
  148. pre = getPath(parent);
  149. return (pre.equals("/") ? "" : pre) +
  150. "/processing-instruction()[" + getNumberSimple(node) + ']';
  151. case Type.NAMESPACE:
  152. String test = node.getLocalPart();
  153. if (test.length() == 0) {
  154. // default namespace: need a node-test that selects unnamed nodes only
  155. test = "*[not(local-name()]";
  156. }
  157. return getPath(parent) + "/namespace::" + test;
  158. default:
  159. return "";
  160. }
  161. }
  162. /**
  163. * Get simple node number. This is defined as one plus the number of previous siblings of the
  164. * same node type and name. It is not accessible directly in XSL.
  165. *
  166. * @param node The node whose number is required
  167. * @param context Used for remembering previous result, for
  168. * performance
  169. * @return the node number, as defined above
  170. * @throws XPathException if any error occurs
  171. */
  172. public static int getNumberSimple(NodeInfo node, XPathContext context) throws XPathException {
  173. //checkNumberable(node);
  174. int fingerprint = node.getFingerprint();
  175. NodeTest same;
  176. if (fingerprint == -1) {
  177. same = NodeKindTest.makeNodeKindTest(node.getNodeKind());
  178. } else {
  179. same = new NameTest(node);
  180. }
  181. SequenceIterator preceding = node.iterateAxis(Axis.PRECEDING_SIBLING, same);
  182. int i = 1;
  183. while (true) {
  184. NodeInfo prev = (NodeInfo)preceding.next();
  185. if (prev == null) {
  186. break;
  187. }
  188. Controller controller = context.getController();
  189. int memo = controller.getRememberedNumber(prev);
  190. if (memo > 0) {
  191. memo += i;
  192. controller.setRememberedNumber(node, memo);
  193. return memo;
  194. }
  195. i++;
  196. }
  197. context.getController().setRememberedNumber(node, i);
  198. return i;
  199. }
  200. /**
  201. * Get simple node number. This is defined as one plus the number of previous siblings of the
  202. * same node type and name. It is not accessible directly in XSL. This version doesn't require
  203. * the controller, and therefore doesn't remember previous results. It is used only by getPath().
  204. *
  205. * @param node the node whose number is required
  206. * @return the node number, as defined above
  207. */
  208. private static int getNumberSimple(NodeInfo node) {
  209. int fingerprint = node.getFingerprint();
  210. NodeTest same;
  211. if (fingerprint == -1) {
  212. same = NodeKindTest.makeNodeKindTest(node.getNodeKind());
  213. } else {
  214. same = new NameTest(node);
  215. }
  216. AxisIterator preceding = node.iterateAxis(Axis.PRECEDING_SIBLING, same);
  217. int i = 1;
  218. while (preceding.next() != null) {
  219. i++;
  220. }
  221. return i;
  222. }
  223. /**
  224. * Get node number (level="single"). If the current node matches the supplied pattern, the returned
  225. * number is one plus the number of previous siblings that match the pattern. Otherwise,
  226. * return the element number of the nearest ancestor that matches the supplied pattern.
  227. *
  228. * @param node the current node, the one whose node number is required
  229. * @param count Pattern that identifies which nodes should be
  230. * counted. Default (null) is the element name if the current node is
  231. * an element, or "node()" otherwise.
  232. * @param from Pattern that specifies where counting starts from.
  233. * Default (null) is the root node. (This parameter does not seem
  234. * useful but is included for the sake of XSLT conformance.)
  235. * @param context the dynamic context of the transformation, used if
  236. * the patterns reference context values (e.g. variables)
  237. * @return the node number established as follows: go to the nearest
  238. * ancestor-or-self that matches the 'count' pattern and that is a
  239. * descendant of the nearest ancestor that matches the 'from' pattern.
  240. * Return one plus the nunber of preceding siblings of that ancestor
  241. * that match the 'count' pattern. If there is no such ancestor,
  242. * return 0.
  243. * @throws XPathException when any error occurs in processing
  244. */
  245. public static int getNumberSingle(NodeInfo node, Pattern count,
  246. Pattern from, XPathContext context) throws XPathException {
  247. if (count == null && from == null) {
  248. return getNumberSimple(node, context);
  249. }
  250. boolean knownToMatch = false;
  251. if (count == null) {
  252. if (node.getFingerprint() == -1) { // unnamed node
  253. count = new NodeTestPattern(NodeKindTest.makeNodeKindTest(node.getNodeKind()));
  254. } else {
  255. count = new NodeTestPattern(new NameTest(node));
  256. }
  257. knownToMatch = true;
  258. }
  259. NodeInfo target = node;
  260. while (!(knownToMatch || count.matches(target, context))) {
  261. target = target.getParent();
  262. if (target == null) {
  263. return 0;
  264. }
  265. if (from != null && from.matches(target, context)) {
  266. return 0;
  267. }
  268. }
  269. // we've found the ancestor to count from
  270. SequenceIterator preceding =
  271. target.iterateAxis(Axis.PRECEDING_SIBLING, count.getNodeTest());
  272. // pass the filter condition down to the axis enumeration where possible
  273. boolean alreadyChecked = (count instanceof NodeTestPattern);
  274. int i = 1;
  275. while (true) {
  276. NodeInfo p = (NodeInfo)preceding.next();
  277. if (p == null) {
  278. return i;
  279. }
  280. if (alreadyChecked || count.matches(p, context)) {
  281. i++;
  282. }
  283. }
  284. }
  285. /**
  286. * Get node number (level="any").
  287. * Return one plus the number of previous nodes in the
  288. * document that match the supplied pattern
  289. *
  290. * @param inst Identifies the xsl:number expression; this is relevant
  291. * when the function is memoised to support repeated use of the same
  292. * instruction to number multiple nodes
  293. * @param node The node being numbered
  294. * @param count Pattern that identifies which nodes should be
  295. * counted. Default (null) is the element name if the current node is
  296. * an element, or "node()" otherwise.
  297. * @param from Pattern that specifies where counting starts from.
  298. * Default (null) is the root node. Only nodes after the first (most
  299. * recent) node that matches the 'from' pattern are counted.
  300. * @param context The dynamic context for the transformation
  301. * @param hasVariablesInPatterns if the count or from patterns
  302. * contain variables, then it's not safe to get the answer by adding
  303. * one to the number of the most recent node that matches
  304. * @return one plus the number of nodes that precede the current node,
  305. * that match the count pattern, and that follow the first node that
  306. * matches the from pattern if specified.
  307. * @throws net.sf.saxon.trans.XPathException
  308. *
  309. */
  310. public static int getNumberAny(Expression inst, NodeInfo node, Pattern count,
  311. Pattern from, XPathContext context, boolean hasVariablesInPatterns) throws XPathException {
  312. NodeInfo memoNode = null;
  313. int memoNumber = 0;
  314. Controller controller = context.getController();
  315. boolean memoise = (!hasVariablesInPatterns) && from==null;
  316. if (memoise) {
  317. Object[] memo = (Object[])controller.getUserData(inst, "xsl:number");
  318. if (memo != null) {
  319. memoNode = (NodeInfo)memo[0];
  320. memoNumber = ((Integer)memo[1]).intValue();
  321. }
  322. }
  323. int num = 0;
  324. if (count == null) {
  325. if (node.getFingerprint() == -1) { // unnamed node
  326. count = new NodeTestPattern(NodeKindTest.makeNodeKindTest(node.getNodeKind()));
  327. } else {
  328. count = new NodeTestPattern(new NameTest(node));
  329. }
  330. num = 1;
  331. } else if (count.matches(node, context)) {
  332. num = 1;
  333. }
  334. // We use a special axis invented for the purpose: the union of the preceding and
  335. // ancestor axes, but in reverse document order
  336. // Pass part of the filtering down to the axis iterator if possible
  337. NodeTest filter;
  338. if (from == null) {
  339. filter = count.getNodeTest();
  340. } else if (from.getNodeKind() == Type.ELEMENT && count.getNodeKind() == Type.ELEMENT) {
  341. filter = NodeKindTest.ELEMENT;
  342. } else {
  343. filter = AnyNodeTest.getInstance();
  344. }
  345. SequenceIterator preceding =
  346. node.iterateAxis(Axis.PRECEDING_OR_ANCESTOR, filter);
  347. boolean found = false;
  348. while (true) {
  349. NodeInfo prev = (NodeInfo)preceding.next();
  350. if (prev == null) {
  351. break;
  352. }
  353. if (count.matches(prev, context)) {
  354. if (num == 1 && memoNode != null && prev.isSameNodeInfo(memoNode)) {
  355. num = memoNumber + 1;
  356. found = true;
  357. break;
  358. }
  359. num++;
  360. }
  361. if (from != null && from.matches(prev, context)) {
  362. found = true;
  363. break;
  364. }
  365. }
  366. if (!found && from != null) {
  367. // we've got to the end without matching the from pattern - result is ()
  368. return 0;
  369. }
  370. if (memoise) {
  371. Object[] memo = new Object[2];
  372. memo[0] = node;
  373. memo[1] = new Integer(num);
  374. controller.setUserData(inst, "xsl:number", memo);
  375. }
  376. return num;
  377. }
  378. /**
  379. * Get node number (level="multiple").
  380. * Return a vector giving the hierarchic position of this node. See the XSLT spec for details.
  381. *
  382. * @param node The node to be numbered
  383. * @param count Pattern that identifies which nodes (ancestors and
  384. * their previous siblings) should be counted. Default (null) is the
  385. * element name if the current node is an element, or "node()"
  386. * otherwise.
  387. * @param from Pattern that specifies where counting starts from.
  388. * Default (null) is the root node. Only nodes below the first (most
  389. * recent) node that matches the 'from' pattern are counted.
  390. * @param context The dynamic context for the transformation
  391. * @return a vector containing for each ancestor-or-self that matches the
  392. * count pattern and that is below the nearest node that matches the
  393. * from pattern, an Integer which is one greater than the number of
  394. * previous siblings that match the count pattern.
  395. * @throws XPathException
  396. */
  397. public static List getNumberMulti(NodeInfo node, Pattern count,
  398. Pattern from, XPathContext context) throws XPathException {
  399. //checkNumberable(node);
  400. ArrayList v = new ArrayList(5);
  401. if (count == null) {
  402. if (node.getFingerprint() == -1) { // unnamed node
  403. count = new NodeTestPattern(NodeKindTest.makeNodeKindTest(node.getNodeKind()));
  404. } else {
  405. count = new NodeTestPattern(new NameTest(node));
  406. }
  407. }
  408. NodeInfo curr = node;
  409. while (true) {
  410. if (count.matches(curr, context)) {
  411. int num = getNumberSingle(curr, count, null, context);
  412. v.add(0, new Long(num));
  413. }
  414. curr = curr.getParent();
  415. if (curr == null) {
  416. break;
  417. }
  418. if (from != null && from.matches(curr, context)) {
  419. break;
  420. }
  421. }
  422. return v;
  423. }
  424. /**
  425. * Generic (model-independent) implementation of deep copy algorithm for nodes.
  426. * This is available for use by any node implementations that choose to use it.
  427. *
  428. * @param node The node to be copied
  429. * @param out The receiver to which events will be sent
  430. * @param namePool Namepool holding the name codes (used only to resolve namespace
  431. * codes)
  432. * @param whichNamespaces Indicates which namespace nodes for an element should
  433. * be copied. Values are {@link NodeInfo#NO_NAMESPACES},
  434. * {@link NodeInfo#LOCAL_NAMESPACES}, {@link NodeInfo#ALL_NAMESPACES}
  435. * @param copyAnnotations Indicates whether type annotations should be copied
  436. * @param locationId The location of the instruction invoking the copy
  437. * @throws XPathException on any failure reported by the Receiver
  438. */
  439. public static void copy(NodeInfo node,
  440. Receiver out,
  441. NamePool namePool,
  442. int whichNamespaces,
  443. boolean copyAnnotations,
  444. int locationId) throws XPathException {
  445. switch (node.getNodeKind()) {
  446. case Type.DOCUMENT:
  447. {
  448. out.startDocument(0);
  449. AxisIterator children0 = node.iterateAxis(Axis.CHILD, AnyNodeTest.getInstance());
  450. while (true) {
  451. NodeInfo child = (NodeInfo)children0.next();
  452. if (child == null) {
  453. break;
  454. }
  455. child.copy(out, whichNamespaces, copyAnnotations, locationId);
  456. }
  457. out.endDocument();
  458. break;
  459. }
  460. case Type.ELEMENT:
  461. {
  462. out.startElement(node.getNameCode(),
  463. copyAnnotations ? node.getTypeAnnotation() : StandardNames.XS_UNTYPED,
  464. 0, 0);
  465. // output the namespaces
  466. switch (whichNamespaces) {
  467. case NodeInfo.NO_NAMESPACES:
  468. break;
  469. case NodeInfo.LOCAL_NAMESPACES:
  470. int[] localNamespaces = node.getDeclaredNamespaces(null);
  471. for (int i=0; i<localNamespaces.length; i++) {
  472. int ns = localNamespaces[i];
  473. if (ns == -1) {
  474. break;
  475. }
  476. out.namespace(ns, 0);
  477. }
  478. break;
  479. case NodeInfo.ALL_NAMESPACES:
  480. NamespaceCodeIterator.sendNamespaces(node, out);
  481. break;
  482. }
  483. // output the attributes
  484. AxisIterator attributes = node.iterateAxis(Axis.ATTRIBUTE, AnyNodeTest.getInstance());
  485. while (true) {
  486. NodeInfo att = (NodeInfo)attributes.next();
  487. if (att == null) {
  488. break;
  489. }
  490. att.copy(out, whichNamespaces, copyAnnotations, locationId);
  491. }
  492. // notify the start of content
  493. out.startContent();
  494. // output the children
  495. AxisIterator children = node.iterateAxis(Axis.CHILD, AnyNodeTest.getInstance());
  496. while (true) {
  497. NodeInfo child = (NodeInfo)children.next();
  498. if (child == null) {
  499. break;
  500. }
  501. child.copy(out, whichNamespaces, copyAnnotations, locationId);
  502. }
  503. // finally the end tag
  504. out.endElement();
  505. return;
  506. }
  507. case Type.ATTRIBUTE:
  508. {
  509. out.attribute(node.getNameCode(),
  510. copyAnnotations ? node.getTypeAnnotation() : StandardNames.XS_UNTYPED_ATOMIC,
  511. node.getStringValueCS(), 0, 0);
  512. return;
  513. }
  514. case Type.TEXT:
  515. {
  516. CharSequence val = node.getStringValueCS();
  517. if (val.length() != 0) {
  518. out.characters(val, 0, 0);
  519. }
  520. return;
  521. }
  522. case Type.COMMENT:
  523. {
  524. out.comment(node.getStringValueCS(), 0, 0);
  525. return;
  526. }
  527. case Type.PROCESSING_INSTRUCTION:
  528. {
  529. out.processingInstruction(node.getLocalPart(), node.getStringValueCS(), 0, 0);
  530. return;
  531. }
  532. case Type.NAMESPACE:
  533. {
  534. out.namespace(namePool.allocateNamespaceCode(node.getLocalPart(), node.getStringValue()), 0);
  535. return;
  536. }
  537. default:
  538. }
  539. }
  540. /**
  541. * Generic (model-independent) method to determine the relative position of two
  542. * node in document order. The nodes must be in the same tree.
  543. *
  544. * @param first The first node
  545. * @param second The second node, whose position is to be compared with the first node
  546. * @return -1 if this node precedes the other node, +1 if it follows the other
  547. * node, or 0 if they are the same node. (In this case, isSameNode() will always
  548. * return true, and the two nodes will produce the same result for generateId())
  549. */
  550. public static int compareOrder(SiblingCountingNode first, SiblingCountingNode second) {
  551. // are they the same node?
  552. if (first.isSameNodeInfo(second)) {
  553. return 0;
  554. }
  555. NodeInfo firstParent = first.getParent();
  556. if (firstParent == null) {
  557. // first node is the root
  558. return -1;
  559. }
  560. NodeInfo secondParent = second.getParent();
  561. if (secondParent == null) {
  562. // second node is the root
  563. return +1;
  564. }
  565. // do they have the same parent (common case)?
  566. if (firstParent.isSameNodeInfo(secondParent)) {
  567. int cat1 = nodeCategories[first.getNodeKind()];
  568. int cat2 = nodeCategories[second.getNodeKind()];
  569. if (cat1 == cat2) {
  570. return first.getSiblingPosition() - second.getSiblingPosition();
  571. } else {
  572. return cat1 - cat2;
  573. }
  574. }
  575. // find the depths of both nodes in the tree
  576. int depth1 = 0;
  577. int depth2 = 0;
  578. NodeInfo p1 = first;
  579. NodeInfo p2 = second;
  580. while (p1 != null) {
  581. depth1++;
  582. p1 = p1.getParent();
  583. }
  584. while (p2 != null) {
  585. depth2++;
  586. p2 = p2.getParent();
  587. }
  588. // move up one branch of the tree so we have two nodes on the same level
  589. p1 = first;
  590. while (depth1 > depth2) {
  591. p1 = p1.getParent();
  592. if (p1.isSameNodeInfo(second)) {
  593. return +1;
  594. }
  595. depth1--;
  596. }
  597. p2 = second;
  598. while (depth2 > depth1) {
  599. p2 = p2.getParent();
  600. if (p2.isSameNodeInfo(first)) {
  601. return -1;
  602. }
  603. depth2--;
  604. }
  605. // now move up both branches in sync until we find a common parent
  606. while (true) {
  607. NodeInfo par1 = p1.getParent();
  608. NodeInfo par2 = p2.getParent();
  609. if (par1 == null || par2 == null) {
  610. throw new NullPointerException("Node order comparison - internal error");
  611. }
  612. if (par1.isSameNodeInfo(par2)) {
  613. if (p1.getNodeKind() == Type.ATTRIBUTE && p2.getNodeKind() != Type.ATTRIBUTE) {
  614. return -1; // attributes first
  615. }
  616. if (p1.getNodeKind() != Type.ATTRIBUTE && p2.getNodeKind() == Type.ATTRIBUTE) {
  617. return +1; // attributes first
  618. }
  619. return ((SiblingCountingNode)p1).getSiblingPosition() -
  620. ((SiblingCountingNode)p2).getSiblingPosition();
  621. }
  622. p1 = par1;
  623. p2 = par2;
  624. }
  625. }
  626. /**
  627. * Classify node kinds into categories for sorting into document order:
  628. * 0 = document, 1 = namespace, 2 = attribute, 3 = (element, text, comment, pi)
  629. */
  630. private static int[] nodeCategories = {
  631. -1, //0 = not used
  632. 3, //1 = element
  633. 2, //2 = attribute
  634. 3, //3 = text
  635. -1, -1, -1, //4,5,6 = not used
  636. 3, //7 = processing-instruction
  637. 3, //8 = comment
  638. 0, //9 = document
  639. -1, -1, -1, //10,11,12 = not used
  640. 1 //13 = namespace
  641. };
  642. /**
  643. * Get a character string that uniquely identifies this node and that collates nodes
  644. * into document order
  645. * @param node the node whose unique identifier is reuqired
  646. * @param sb a buffer to which the unique identifier will be appended
  647. * @param addDocNr true if a unique document number is to be included in the information
  648. */
  649. public static void appendSequentialKey(SiblingCountingNode node, FastStringBuffer sb, boolean addDocNr) {
  650. if (addDocNr) {
  651. sb.append('w');
  652. sb.append(Integer.toString(node.getDocumentNumber()));
  653. }
  654. if (node.getNodeKind() != Type.DOCUMENT) {
  655. NodeInfo parent = node.getParent();
  656. if (parent != null) {
  657. appendSequentialKey(((SiblingCountingNode)parent), sb, false);
  658. }
  659. }
  660. sb.append(alphaKey(node.getSiblingPosition()));
  661. }
  662. /**
  663. * Construct an alphabetic key from an positive integer; the key collates in the same sequence
  664. * as the integer
  665. *
  666. * @param value The positive integer key value (negative values are treated as zero).
  667. * @return the alphabetic key value
  668. */
  669. public static String alphaKey(int value) {
  670. if (value < 1) {
  671. return "a";
  672. }
  673. if (value < 10) {
  674. return "b" + value;
  675. }
  676. if (value < 100) {
  677. return "c" + value;
  678. }
  679. if (value < 1000) {
  680. return "d" + value;
  681. }
  682. if (value < 10000) {
  683. return "e" + value;
  684. }
  685. if (value < 100000) {
  686. return "f" + value;
  687. }
  688. if (value < 1000000) {
  689. return "g" + value;
  690. }
  691. if (value < 10000000) {
  692. return "h" + value;
  693. }
  694. if (value < 100000000) {
  695. return "i" + value;
  696. }
  697. if (value < 1000000000) {
  698. return "j" + value;
  699. }
  700. return "k" + value;
  701. }
  702. /**
  703. * Determine if a string is all-whitespace
  704. *
  705. * @param content the string to be tested
  706. * @return true if the supplied string contains no non-whitespace
  707. * characters
  708. * @deprecated since Saxon 8.5: use {@link Whitespace#isWhite}
  709. */
  710. public static boolean isWhite(CharSequence content) {
  711. return Whitespace.isWhite(content);
  712. }
  713. /**
  714. * Test if one node is an ancestor-or-self of another
  715. *
  716. * @param a the putative ancestor-or-self node
  717. * @param d the putative descendant node
  718. * @return true if a is an ancestor-or-self of d
  719. */
  720. public static boolean isAncestorOrSelf(NodeInfo a, NodeInfo d) {
  721. // Fast path for the TinyTree implementation
  722. if (a instanceof TinyNodeImpl) {
  723. if (d instanceof TinyNodeImpl) {
  724. return ((TinyNodeImpl)a).isAncestorOrSelf((TinyNodeImpl)d);
  725. } else if (d.getNodeKind() == Type.NAMESPACE) {
  726. // fall through
  727. } else {
  728. return false;
  729. }
  730. }
  731. // Generic implementation
  732. NodeInfo p = d;
  733. while (p != null) {
  734. if (a.isSameNodeInfo(p)) {
  735. return true;
  736. }
  737. p = p.getParent();
  738. }
  739. return false;
  740. }
  741. ///////////////////////////////////////////////////////////////////////////////
  742. // Helper classes to support axis iteration
  743. ///////////////////////////////////////////////////////////////////////////////
  744. /**
  745. * Create an iterator over a singleton node, if it exists and matches a nodetest;
  746. * otherwise return an empty iterator
  747. * @param node the singleton node, or null if the node does not exist
  748. * @param nodeTest the test to be applied
  749. * @return an iterator over the node if it exists and matches the test.
  750. */
  751. public static AxisIterator filteredSingleton(NodeInfo node, NodeTest nodeTest) {
  752. if (node != null && nodeTest.matches(node)) {
  753. return SingleNodeIterator.makeIterator(node);
  754. } else {
  755. return EmptyIterator.getInstance();
  756. }
  757. }
  758. /**
  759. * AxisFilter is an iterator that applies a NodeTest filter to
  760. * the nodes returned by an underlying AxisIterator.
  761. */
  762. public static class AxisFilter extends AxisIteratorImpl {
  763. private AxisIterator base;
  764. private NodeTest nodeTest;
  765. //private int last = -1;
  766. /**
  767. * S
  768. * Construct a AxisFilter
  769. *
  770. * @param base the underlying iterator that returns all the nodes on
  771. * a required axis. This must not be an atomizing iterator!
  772. * @param test a NodeTest that is applied to each node returned by the
  773. * underlying AxisIterator; only those nodes that pass the NodeTest are
  774. * returned by the AxisFilter
  775. */
  776. public AxisFilter(AxisIterator base, NodeTest test) {
  777. this.base = base;
  778. nodeTest = test;
  779. position = 0;
  780. }
  781. public Item next() {
  782. while (true) {
  783. current = (NodeInfo)base.next();
  784. if (current == null) {
  785. position = -1;
  786. return null;
  787. }
  788. if (nodeTest.matches(current)) {
  789. position++;
  790. return current;
  791. }
  792. }
  793. }
  794. public SequenceIterator getAnother() {
  795. return new AxisFilter((AxisIterator)base.getAnother(), nodeTest);
  796. }
  797. }
  798. /**
  799. * BaseEnumeration is an abstract implementation of an AxisIterator, it
  800. * simplifies the implementation of the underlying AxisIterator by requiring
  801. * it to provide only two methods: advance(), and getAnother().
  802. * <p/>
  803. * NOTA BENE: BaseEnumeration does not maintain the value of the position variable.
  804. * It must therefore either (a) be wrapped in an AxisFilter, which does maintain
  805. * position, or (b) be subclassed by a class that maintains position itself.
  806. */
  807. public static abstract class BaseEnumeration extends AxisIteratorImpl {
  808. public final Item next() {
  809. advance();
  810. return current;
  811. }
  812. /**
  813. * The advance() method must be provided in each concrete implementation.
  814. * It must leave the variable current set to the next node to be returned in the
  815. * iteration, or to null if there are no more nodes to be returned.
  816. */
  817. public abstract void advance();
  818. public abstract SequenceIterator getAnother();
  819. }
  820. /**
  821. * General-purpose implementation of the ancestor and ancestor-or-self axes
  822. */
  823. public static final class AncestorEnumeration extends BaseEnumeration {
  824. private boolean includeSelf;
  825. private boolean atStart;
  826. private NodeInfo start;
  827. /**
  828. * Create an iterator over the ancestor or ancestor-or-self axis
  829. * @param start the initial context node
  830. * @param includeSelf true if the "self" node is to be included
  831. */
  832. public AncestorEnumeration(NodeInfo start, boolean includeSelf) {
  833. this.start = start;
  834. this.includeSelf = includeSelf;
  835. current = start;
  836. atStart = true;
  837. }
  838. public void advance() {
  839. if (atStart) {
  840. atStart = false;
  841. if (includeSelf) {
  842. return;
  843. }
  844. }
  845. current = (current == null ? null : current.getParent());
  846. }
  847. public SequenceIterator getAnother() {
  848. return new AncestorEnumeration(start, includeSelf);
  849. }
  850. } // end of class AncestorEnumeration
  851. /**
  852. * General-purpose implementation of the descendant and descendant-or-self axes,
  853. * in terms of the child axis.
  854. * But it also has the option to return the descendants in reverse document order;
  855. * this is used when evaluating the preceding axis. Note that the includeSelf option
  856. * should not be used when scanning in reverse order, as the self node will always be
  857. * returned first.
  858. */
  859. public static final class DescendantEnumeration extends BaseEnumeration {
  860. private AxisIterator children = null;
  861. private AxisIterator descendants = null;
  862. private NodeInfo start;
  863. private boolean includeSelf;
  864. private boolean forwards;
  865. private boolean atEnd = false;
  866. /**
  867. * Create an iterator over the descendant or descendant-or-self axis
  868. * @param start the initial context node
  869. * @param includeSelf true if the "self" node is to be included
  870. * @param forwards true for a forwards iteration, false for reverse order
  871. */
  872. public DescendantEnumeration(NodeInfo start,
  873. boolean includeSelf, boolean forwards) {
  874. this.start = start;
  875. this.includeSelf = includeSelf;
  876. this.forwards = forwards;
  877. }
  878. public void advance() {
  879. if (descendants != null) {
  880. NodeInfo nextd = (NodeInfo)descendants.next();
  881. if (nextd != null) {
  882. current = nextd;
  883. return;
  884. } else {
  885. descendants = null;
  886. }
  887. }
  888. if (children != null) {
  889. NodeInfo n = (NodeInfo)children.next();
  890. if (n != null) {
  891. if (n.hasChildNodes()) {
  892. if (forwards) {
  893. descendants = new DescendantEnumeration(n, false, forwards);
  894. current = n;
  895. } else {
  896. descendants = new DescendantEnumeration(n, true, forwards);
  897. advance();
  898. }
  899. } else {
  900. current = n;
  901. }
  902. } else {
  903. if (forwards || !includeSelf) {
  904. current = null;
  905. } else {
  906. atEnd = true;
  907. children = null;
  908. current = start;
  909. }
  910. }
  911. } else if (atEnd) {
  912. // we're just finishing a backwards scan
  913. current = null;
  914. } else {
  915. // we're just starting...
  916. if (start.hasChildNodes()) {
  917. //children = new NodeWrapper.ChildEnumeration(start, true, forwards);
  918. children = start.iterateAxis(Axis.CHILD);
  919. if (!forwards) {
  920. if (children instanceof net.sf.saxon.expr.ReversibleIterator) {
  921. children = (AxisIterator)((net.sf.saxon.expr.ReversibleIterator)children).getReverseIterator();
  922. } else {
  923. try {
  924. List list = new ArrayList(20);
  925. SequenceIterator forwards = start.iterateAxis(Axis.CHILD);
  926. while (true) {
  927. Item n = forwards.next();
  928. if (n == null) {
  929. break;
  930. }
  931. list.add(n);
  932. }
  933. NodeInfo[] nodes = new NodeInfo[list.size()];
  934. nodes = (NodeInfo[])list.toArray(nodes);
  935. children = new ReverseNodeArrayIterator(nodes, 0, nodes.length);
  936. } catch (XPathException e) {
  937. throw new AssertionError("Internal error in Navigator#descendantEnumeration: " + e.getMessage());
  938. // shouldn't happen.
  939. }
  940. }
  941. }
  942. } else {
  943. children = EmptyIterator.getInstance();
  944. }
  945. if (forwards && includeSelf) {
  946. current = start;
  947. } else {
  948. advance();
  949. }
  950. }
  951. }
  952. public SequenceIterator getAnother() {
  953. return new DescendantEnumeration(start, includeSelf, forwards);
  954. }
  955. } // end of class DescendantEnumeration
  956. /**
  957. * General purpose implementation of the following axis, in terms of the
  958. * ancestor, child, and following-sibling axes
  959. */
  960. public static final class FollowingEnumeration extends BaseEnumeration {
  961. private NodeInfo start;
  962. private AxisIterator ancestorEnum = null;
  963. private AxisIterator siblingEnum = null;
  964. private AxisIterator descendEnum = null;
  965. /**
  966. * Create an iterator over the "following" axis
  967. * @param start the initial context node
  968. */
  969. public FollowingEnumeration(NodeInfo start) {
  970. this.start = start;
  971. ancestorEnum = new AncestorEnumeration(start, false);
  972. switch (start.getNodeKind()) {
  973. case Type.ELEMENT:
  974. case Type.TEXT:
  975. case Type.COMMENT:
  976. case Type.PROCESSING_INSTRUCTION:
  977. //siblingEnum = new NodeWrapper.ChildEnumeration(start, false, true);
  978. // gets following siblings
  979. siblingEnum = start.iterateAxis(Axis.FOLLOWING_SIBLING);
  980. break;
  981. case Type.ATTRIBUTE:
  982. case Type.NAMESPACE:
  983. //siblingEnum = new NodeWrapper.ChildEnumeration((NodeWrapper)start.getParent(), true, true);
  984. // gets children of the attribute's parent node
  985. NodeInfo parent = start.getParent();
  986. if (parent == null) {
  987. siblingEnum = EmptyIterator.getInstance();
  988. } else {
  989. siblingEnum = parent.iterateAxis(Axis.CHILD);
  990. }
  991. break;
  992. default:
  993. siblingEnum = EmptyIterator.getInstance();
  994. }
  995. //advance();
  996. }
  997. public void advance() {
  998. if (descendEnum != null) {
  999. NodeInfo nextd = (NodeInfo)descendEnum.next();
  1000. if (nextd != null) {
  1001. current = nextd;
  1002. return;
  1003. } else {
  1004. descendEnum = null;
  1005. }
  1006. }
  1007. if (siblingEnum != null) {
  1008. NodeInfo nexts = (NodeInfo)siblingEnum.next();
  1009. if (nexts != null) {
  1010. current = nexts;
  1011. NodeInfo n = current;
  1012. if (n.hasChildNodes()) {
  1013. descendEnum = new DescendantEnumeration(n, false, true);
  1014. } else {
  1015. descendEnum = null;
  1016. }
  1017. return;
  1018. } else {
  1019. descendEnum = null;
  1020. siblingEnum = null;
  1021. }
  1022. }
  1023. NodeInfo nexta = (NodeInfo)ancestorEnum.next();
  1024. if (nexta != null) {
  1025. current = nexta;
  1026. NodeInfo n = current;
  1027. if (n.getNodeKind() == Type.DOCUMENT) {
  1028. siblingEnum = EmptyIterator.getInstance();
  1029. } else {
  1030. //siblingEnum = new NodeWrapper.ChildEnumeration(next, false, true);
  1031. siblingEnum = n.iterateAxis(Axis.FOLLOWING_SIBLING);
  1032. }
  1033. advance();
  1034. } else {
  1035. current = null;
  1036. }
  1037. }
  1038. public SequenceIterator getAnother() {
  1039. return new FollowingEnumeration(start);
  1040. }
  1041. } // end of class FollowingEnumeration
  1042. /**
  1043. * Helper method to iterate over the preceding axis, or Saxon's internal
  1044. * preceding-or-ancestor axis, by making use of the ancestor, descendant, and
  1045. * preceding-sibling axes.
  1046. */
  1047. public static final class PrecedingEnumeration extends BaseEnumeration {
  1048. private NodeInfo start;
  1049. private AxisIterator ancestorEnum = null;
  1050. private AxisIterator siblingEnum = null;
  1051. private AxisIterator descendEnum = null;
  1052. private boolean includeAncestors;
  1053. /**
  1054. * Create an iterator for the preceding or "preceding-or-ancestor" axis (the latter being
  1055. * used internall to support xsl:number)
  1056. * @param start the initial context node
  1057. * @param includeAncestors true if ancestors of the initial context node are to be included
  1058. * in the result
  1059. */
  1060. public PrecedingEnumeration(NodeInfo start, boolean includeAncestors) {
  1061. this.start = start;
  1062. this.includeAncestors = includeAncestors;
  1063. ancestorEnum = new AncestorEnumeration(start, false);
  1064. switch (start.getNodeKind()) {
  1065. case Type.ELEMENT:
  1066. case Type.TEXT:
  1067. case Type.COMMENT:
  1068. case Type.PROCESSING_INSTRUCTION:
  1069. // get preceding-sibling enumeration
  1070. siblingEnum = start.iterateAxis(Axis.PRECEDING_SIBLING);
  1071. break;
  1072. default:
  1073. siblingEnum = EmptyIterator.getInstance();
  1074. }
  1075. }
  1076. public void advance() {
  1077. if (descendEnum != null) {
  1078. NodeInfo nextd = (NodeInfo)descendEnum.next();
  1079. if (nextd != null) {
  1080. current = nextd;
  1081. return;
  1082. } else {
  1083. descendEnum = null;
  1084. }
  1085. }
  1086. if (siblingEnum != null) {
  1087. NodeInfo nexts = (NodeInfo)siblingEnum.next();
  1088. if (nexts != null) {
  1089. if (nexts.hasChildNodes()) {
  1090. descendEnum = new DescendantEnumeration(nexts, true, false);
  1091. advance();
  1092. } else {
  1093. descendEnum = null;
  1094. current = nexts;
  1095. }
  1096. return;
  1097. } else {
  1098. descendEnum = null;
  1099. siblingEnum = null;
  1100. }
  1101. }
  1102. NodeInfo nexta = (NodeInfo)ancestorEnum.next();
  1103. if (nexta != null) {
  1104. current = nexta;
  1105. NodeInfo n = current;
  1106. if (n.getNodeKind() == Type.DOCUMENT) {
  1107. siblingEnum = EmptyIterator.getInstance();
  1108. } else {
  1109. siblingEnum = n.iterateAxis(Axis.PRECEDING_SIBLING);
  1110. }
  1111. if (!includeAncestors) {
  1112. advance();
  1113. }
  1114. } else {
  1115. current = null;
  1116. }
  1117. }
  1118. public SequenceIterator getAnother() {
  1119. return new PrecedingEnumeration(start, includeAncestors);
  1120. }
  1121. } // end of class PrecedingEnumeration
  1122. }
  1123. //
  1124. // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
  1125. // you may not use this file except in compliance with the License. You may obtain a copy of the
  1126. // License at http://www.mozilla.org/MPL/
  1127. //
  1128. // Software distributed under the License is distributed on an "AS IS" basis,
  1129. // WITHOUT WARRANTY OF ANY KIND, either express or implied.
  1130. // See the License for the specific language governing rights and limitations under the License.
  1131. //
  1132. // The Original Code is: all this file.
  1133. //
  1134. // The Initial Developer of the Original Code is Michael H. Kay.
  1135. //
  1136. // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
  1137. //
  1138. // Contributor(s): none.
  1139. //