1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.jxpath.ri.axes;
  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.List;
  20. import org.apache.commons.jxpath.JXPathException;
  21. import org.apache.commons.jxpath.ri.Compiler;
  22. import org.apache.commons.jxpath.ri.EvalContext;
  23. import org.apache.commons.jxpath.ri.InfoSetUtil;
  24. import org.apache.commons.jxpath.ri.QName;
  25. import org.apache.commons.jxpath.ri.compiler.Expression;
  26. import org.apache.commons.jxpath.ri.compiler.NameAttributeTest;
  27. import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
  28. import org.apache.commons.jxpath.ri.compiler.NodeTest;
  29. import org.apache.commons.jxpath.ri.compiler.Step;
  30. import org.apache.commons.jxpath.ri.model.NodeIterator;
  31. import org.apache.commons.jxpath.ri.model.NodePointer;
  32. import org.apache.commons.jxpath.ri.model.beans.LangAttributePointer;
  33. import org.apache.commons.jxpath.ri.model.beans.NullElementPointer;
  34. import org.apache.commons.jxpath.ri.model.beans.NullPropertyPointer;
  35. import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
  36. import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
  37. /**
  38. * An evaluation mechanism for simple XPaths, which
  39. * is much faster than the usual process. It is only used for
  40. * xpaths which have no context-dependent parts, consist entirely of
  41. * <code>child::name</code> and <code>self::node()</code> steps with
  42. * predicates that either integer or have the form <code>[@name = ...]</code>.
  43. *
  44. * @author Dmitri Plotnikov
  45. * @version $Revision: 1.10 $ $Date: 2002/04/24 04:05:40 $
  46. */
  47. public class SimplePathInterpreter {
  48. // Because of the complexity caused by the variety of situations
  49. // that need to be addressed by this class, we attempt to break up
  50. // the class into individual methods addressing those situations
  51. // individually. The names of the methods are supposed to
  52. // give brief descriptions of those situations.
  53. private static final QName QNAME_NAME = new QName(null, "name");
  54. private static final int PERFECT_MATCH = 1000;
  55. // Uncomment this variable and the PATH = ... lines in
  56. // the two following methods in order to be able to print the
  57. // currently evaluated path for debugging of this class
  58. // private static String PATH; // Debugging
  59. /**
  60. * Interpret a simple path that starts with the given root and
  61. * follows the given steps. All steps must have the axis "child::"
  62. * and a name test. They can also optionally have predicates
  63. * of type [@name=expression] or simply [expression] interpreted
  64. * as an index.
  65. */
  66. public static NodePointer interpretSimpleLocationPath(
  67. EvalContext context, NodePointer root, Step steps[])
  68. {
  69. // PATH = createNullPointer(context, root, steps, 0).toString(); // Dbg
  70. NodePointer pointer = doStep(context, root, steps, 0);
  71. // return valuePointer(pointer);
  72. return pointer;
  73. }
  74. /**
  75. * Interpret the steps of a simple expression path that
  76. * starts with the given root, which is the result of evaluation
  77. * of the root expression of the expression path, applies the
  78. * given predicates to it and then follows the given steps.
  79. * All steps must have the axis "child::" or "attribute::"
  80. * and a name test. They can also optionally have predicates
  81. * of type [@name=...] or simply [...] interpreted as an index.
  82. */
  83. public static NodePointer interpretSimpleExpressionPath(
  84. EvalContext context, NodePointer root,
  85. Expression predicates[], Step[] steps)
  86. {
  87. // PATH = createNullPointerForPredicates(context, root,
  88. // steps, -1, predicates, 0).toString(); // Debugging
  89. NodePointer pointer =
  90. doPredicate(context, root, steps, -1, predicates, 0);
  91. // return valuePointer(pointer);
  92. return pointer;
  93. }
  94. /**
  95. * Recursive evaluation of a path. The general plan is:
  96. * Look at the current step,
  97. * find nodes that match it,
  98. * iterate over those nodes and
  99. * for each of them call doStep again for subsequent steps.
  100. */
  101. private static NodePointer doStep(
  102. EvalContext context, NodePointer parent,
  103. Step steps[], int currentStep)
  104. {
  105. if (parent == null) {
  106. return null;
  107. }
  108. if (currentStep == steps.length) {
  109. // We have reached the end of the list of steps
  110. return parent;
  111. }
  112. // Open all containers
  113. parent = valuePointer(parent);
  114. Step step = steps[currentStep];
  115. Expression predicates[] = step.getPredicates();
  116. // Divide and conquer: the process is broken out into
  117. // four major use cases.
  118. // 1. Current step has no predicates and
  119. // the root is a property owner (e.g. bean or map)
  120. // 2. Current step has predicates and
  121. // the root is a property owner (e.g. bean or map)
  122. // 3. Current step has no predicates and
  123. // the root is an InfoSet standard node (e.g. DOM Node)
  124. // 4. Current step has predicates and
  125. // the root is an InfoSet standard node (e.g. DOM Node)
  126. if (parent instanceof PropertyOwnerPointer) {
  127. if (predicates == null || predicates.length == 0) {
  128. return doStepNoPredicatesPropertyOwner(
  129. context,
  130. (PropertyOwnerPointer) parent,
  131. steps,
  132. currentStep);
  133. }
  134. else {
  135. return doStepPredicatesPropertyOwner(
  136. context,
  137. (PropertyOwnerPointer) parent,
  138. steps,
  139. currentStep);
  140. }
  141. }
  142. else {
  143. if (predicates == null || predicates.length == 0) {
  144. return doStepNoPredicatesStandard(
  145. context,
  146. parent,
  147. steps,
  148. currentStep);
  149. }
  150. else {
  151. return doStepPredicatesStandard(
  152. context,
  153. parent,
  154. steps,
  155. currentStep);
  156. }
  157. }
  158. }
  159. /**
  160. * We have a step that starts with a property owner (bean, map, etc) and has
  161. * no predicates. The name test of the step may map to a scalar property
  162. * or to a collection. If it is a collection, we should apply the tail of
  163. * the path to each element until we find a match. If we don't find
  164. * a perfect match, we should return the "best quality" pointer, which
  165. * has the longest chain of steps mapping to existing nodes and the shortes
  166. * tail of Null* pointers.
  167. */
  168. private static NodePointer doStepNoPredicatesPropertyOwner(
  169. EvalContext context, PropertyOwnerPointer parentPointer,
  170. Step[] steps, int currentStep)
  171. {
  172. Step step = steps[currentStep];
  173. NodePointer childPointer =
  174. createChildPointerForStep(parentPointer, step);
  175. if (!childPointer.isActual()) {
  176. // The property does not exist - create a null pointer.
  177. return createNullPointer(
  178. context,
  179. parentPointer,
  180. steps,
  181. currentStep);
  182. }
  183. else if (currentStep == steps.length - 1) {
  184. // If this is the last step - we are done, we found it
  185. return childPointer;
  186. }
  187. else if (childPointer.isCollection()) {
  188. // Iterate over all values and
  189. // execute remaining steps for each node,
  190. // looking for the best quality match
  191. int bestQuality = 0;
  192. childPointer = (NodePointer) childPointer.clone();
  193. NodePointer bestMatch = null;
  194. int count = childPointer.getLength();
  195. for (int i = 0; i < count; i++) {
  196. childPointer.setIndex(i);
  197. NodePointer pointer =
  198. doStep(context, childPointer, steps, currentStep + 1);
  199. int quality = computeQuality(pointer);
  200. if (quality == PERFECT_MATCH) {
  201. return pointer;
  202. }
  203. else if (quality > bestQuality) {
  204. bestQuality = quality;
  205. bestMatch = (NodePointer) pointer.clone();
  206. }
  207. }
  208. if (bestMatch != null) {
  209. return bestMatch;
  210. }
  211. // This step did not find anything - return a null pointer
  212. return createNullPointer(context, childPointer, steps, currentStep);
  213. }
  214. else {
  215. // Evaluate subsequent steps
  216. return doStep(context, childPointer, steps, currentStep + 1);
  217. }
  218. }
  219. /**
  220. * A path that starts with a standard InfoSet node (e.g. DOM Node) and
  221. * has no predicates. Get a child iterator and apply the tail of
  222. * the path to each element until we find a match. If we don't find
  223. * a perfect match, we should return the "best quality" pointer, which
  224. * has the longest chain of steps mapping to existing nodes and the shortes
  225. * tail of Null* pointers.
  226. */
  227. private static NodePointer doStepNoPredicatesStandard(
  228. EvalContext context, NodePointer parentPointer,
  229. Step[] steps, int currentStep)
  230. {
  231. Step step = steps[currentStep];
  232. if (step.getAxis() == Compiler.AXIS_SELF) {
  233. return doStep(context, parentPointer, steps, currentStep + 1);
  234. }
  235. int bestQuality = 0;
  236. NodePointer bestMatch = null;
  237. NodeIterator it = getNodeIterator(context, parentPointer, step);
  238. if (it != null) {
  239. for (int i = 1; it.setPosition(i); i++) {
  240. NodePointer childPointer = it.getNodePointer();
  241. if (steps.length == currentStep + 1) {
  242. // If this is the last step - we are done, we found it
  243. return childPointer;
  244. }
  245. NodePointer pointer = doStep(
  246. context, childPointer, steps, currentStep + 1);
  247. int quality = computeQuality(pointer);
  248. if (quality == PERFECT_MATCH) {
  249. return pointer;
  250. }
  251. else if (quality > bestQuality) {
  252. bestQuality = quality;
  253. bestMatch = (NodePointer) pointer.clone();
  254. }
  255. }
  256. }
  257. if (bestMatch != null) {
  258. return bestMatch;
  259. }
  260. return createNullPointer(
  261. context, parentPointer, steps, currentStep);
  262. }
  263. /**
  264. * A path that starts with a property owner. The method evaluates
  265. * the first predicate in a special way and then forwards to
  266. * a general predicate processing method.
  267. */
  268. private static NodePointer doStepPredicatesPropertyOwner(
  269. EvalContext context, PropertyOwnerPointer parentPointer,
  270. Step[] steps, int currentStep)
  271. {
  272. Step step = steps[currentStep];
  273. Expression predicates[] = step.getPredicates();
  274. NodePointer childPointer =
  275. createChildPointerForStep(parentPointer, step);
  276. if (!childPointer.isActual()) {
  277. // Property does not exist - return a null pointer
  278. return createNullPointer(
  279. context,
  280. parentPointer,
  281. steps,
  282. currentStep);
  283. }
  284. // Evaluate predicates
  285. return doPredicate(
  286. context,
  287. childPointer,
  288. steps,
  289. currentStep,
  290. predicates,
  291. 0);
  292. }
  293. private static NodePointer createChildPointerForStep(
  294. PropertyOwnerPointer parentPointer, Step step)
  295. {
  296. int axis = step.getAxis();
  297. if (axis == Compiler.AXIS_CHILD || axis == Compiler.AXIS_ATTRIBUTE) {
  298. NodePointer childPointer;
  299. QName name = ((NodeNameTest) step.getNodeTest()).getNodeName();
  300. if (axis == Compiler.AXIS_ATTRIBUTE && isLangAttribute(name)) {
  301. childPointer = new LangAttributePointer(parentPointer);
  302. }
  303. else {
  304. childPointer = parentPointer.getPropertyPointer();
  305. ((PropertyPointer) childPointer).setPropertyName(
  306. name.toString());
  307. childPointer.setAttribute(axis == Compiler.AXIS_ATTRIBUTE);
  308. }
  309. return childPointer;
  310. }
  311. else {
  312. return parentPointer;
  313. }
  314. }
  315. /**
  316. * A path that starts with a standard InfoSet node, e.g. a DOM Node.
  317. * The method evaluates the first predicate in a special way and
  318. * then forwards to a general predicate processing method.
  319. */
  320. private static NodePointer doStepPredicatesStandard(
  321. EvalContext context, NodePointer parent,
  322. Step[] steps, int currentStep)
  323. {
  324. Step step = steps[currentStep];
  325. Expression predicates[] = step.getPredicates();
  326. int axis = step.getAxis();
  327. if (axis == Compiler.AXIS_SELF) {
  328. return doPredicate(
  329. context,
  330. parent,
  331. steps,
  332. currentStep,
  333. predicates,
  334. 0);
  335. }
  336. Expression predicate = predicates[0];
  337. // Optimize for a single predicate to avoid building a list
  338. // and to allow the direct access to the index'th element
  339. // in the case of a simple subscript predecate
  340. // It is a very common use case, so it deserves individual
  341. // attention
  342. if (predicates.length == 1) {
  343. NodeIterator it = getNodeIterator(context, parent, step);
  344. NodePointer pointer = null;
  345. if (it != null) {
  346. if (predicate instanceof NameAttributeTest) { // [@name = key]
  347. String key = keyFromPredicate(context, predicate);
  348. for (int i = 1; it.setPosition(i); i++) {
  349. NodePointer ptr = it.getNodePointer();
  350. if (isNameAttributeEqual(ptr, key)) {
  351. pointer = ptr;
  352. break;
  353. }
  354. }
  355. }
  356. else {
  357. int index = indexFromPredicate(context, predicate);
  358. if (it.setPosition(index + 1)) {
  359. pointer = it.getNodePointer();
  360. }
  361. }
  362. }
  363. if (pointer != null) {
  364. return doStep(context, pointer, steps, currentStep + 1);
  365. }
  366. }
  367. else {
  368. NodeIterator it = getNodeIterator(context, parent, step);
  369. if (it != null) {
  370. List list = new ArrayList();
  371. for (int i = 1; it.setPosition(i); i++) {
  372. list.add(it.getNodePointer());
  373. }
  374. NodePointer pointer =
  375. doPredicatesStandard(
  376. context,
  377. list,
  378. steps,
  379. currentStep,
  380. predicates,
  381. 0);
  382. if (pointer != null) {
  383. return pointer;
  384. }
  385. }
  386. }
  387. return createNullPointer(context, parent, steps, currentStep);
  388. }
  389. /**
  390. * Evaluates predicates and proceeds with the subsequent steps
  391. * of the path.
  392. */
  393. private static NodePointer doPredicate(
  394. EvalContext context, NodePointer parent,
  395. Step[] steps, int currentStep,
  396. Expression predicates[], int currentPredicate)
  397. {
  398. if (currentPredicate == predicates.length) {
  399. return doStep(context, parent, steps, currentStep + 1);
  400. }
  401. Expression predicate = predicates[currentPredicate];
  402. if (predicate instanceof NameAttributeTest) { // [@name = key1]
  403. return doPredicateName(
  404. context,
  405. parent,
  406. steps,
  407. currentStep,
  408. predicates,
  409. currentPredicate);
  410. }
  411. else { // [index]
  412. return doPredicateIndex(
  413. context,
  414. parent,
  415. steps,
  416. currentStep,
  417. predicates,
  418. currentPredicate);
  419. }
  420. }
  421. private static NodePointer doPredicateName(
  422. EvalContext context, NodePointer parent,
  423. Step[] steps, int currentStep,
  424. Expression[] predicates, int currentPredicate)
  425. {
  426. Expression predicate = predicates[currentPredicate];
  427. String key = keyFromPredicate(context, predicate);
  428. NodePointer child = valuePointer(parent);
  429. if (child instanceof PropertyOwnerPointer) {
  430. PropertyPointer pointer =
  431. ((PropertyOwnerPointer) child).getPropertyPointer();
  432. pointer.setPropertyName(key);
  433. if (pointer.isActual()) {
  434. return doPredicate(
  435. context,
  436. pointer,
  437. steps,
  438. currentStep,
  439. predicates,
  440. currentPredicate + 1);
  441. }
  442. }
  443. else if (child.isCollection()) {
  444. // For each node in the collection, perform the following:
  445. // if the node is a property owner, apply this predicate to it;
  446. // if the node is a collection, apply this predicate to each elem.;
  447. // if the node is not a prop owner or a collection,
  448. // see if it has the attribute "name" with the right value,
  449. // if so - proceed to the next predicate
  450. NodePointer bestMatch = null;
  451. int bestQuality = 0;
  452. child = (NodePointer) child.clone();
  453. int count = child.getLength();
  454. for (int i = 0; i < count; i++) {
  455. child.setIndex(i);
  456. NodePointer valuePointer = valuePointer(child);
  457. NodePointer pointer;
  458. if ((valuePointer instanceof PropertyOwnerPointer)
  459. || valuePointer.isCollection()) {
  460. pointer =
  461. doPredicateName(
  462. context,
  463. valuePointer,
  464. steps,
  465. currentStep,
  466. predicates,
  467. currentPredicate);
  468. }
  469. else if (isNameAttributeEqual(valuePointer, key)) {
  470. pointer =
  471. doPredicate(
  472. context,
  473. valuePointer,
  474. steps,
  475. currentStep,
  476. predicates,
  477. currentPredicate + 1);
  478. }
  479. else {
  480. pointer = null;
  481. }
  482. if (pointer != null) {
  483. int quality = computeQuality(pointer);
  484. if (quality == PERFECT_MATCH) {
  485. return pointer;
  486. }
  487. if (quality > bestQuality) {
  488. bestMatch = (NodePointer) pointer.clone();
  489. bestQuality = quality;
  490. }
  491. }
  492. }
  493. if (bestMatch != null) {
  494. return bestMatch;
  495. }
  496. }
  497. else {
  498. // If the node is a standard InfoSet node (e.g. DOM Node),
  499. // employ doPredicates_standard, which will iterate through
  500. // the node's children and apply all predicates
  501. NodePointer found =
  502. doPredicatesStandard(
  503. context,
  504. Collections.singletonList(child),
  505. steps,
  506. currentStep,
  507. predicates,
  508. currentPredicate);
  509. if (found != null) {
  510. return found;
  511. }
  512. }
  513. // If nothing worked - return a null pointer
  514. return createNullPointerForPredicates(
  515. context,
  516. child,
  517. steps,
  518. currentStep,
  519. predicates,
  520. currentPredicate);
  521. }
  522. /**
  523. * Called exclusively for standard InfoSet nodes, e.g. DOM nodes
  524. * to evaluate predicate sequences like [@name=...][@name=...][index].
  525. */
  526. private static NodePointer doPredicatesStandard(
  527. EvalContext context, List parents,
  528. Step[] steps, int currentStep,
  529. Expression predicates[], int currentPredicate)
  530. {
  531. if (parents.size() == 0) {
  532. return null;
  533. }
  534. // If all predicates have been processed, take the first
  535. // element from the list of results and proceed to the
  536. // remaining steps with that element.
  537. if (currentPredicate == predicates.length) {
  538. NodePointer pointer = (NodePointer) parents.get(0);
  539. return doStep(context, pointer, steps, currentStep + 1);
  540. }
  541. Expression predicate = predicates[currentPredicate];
  542. if (predicate instanceof NameAttributeTest) {
  543. String key = keyFromPredicate(context, predicate);
  544. List newList = new ArrayList();
  545. for (int i = 0; i < parents.size(); i++) {
  546. NodePointer pointer = (NodePointer) parents.get(i);
  547. if (isNameAttributeEqual(pointer, key)) {
  548. newList.add(pointer);
  549. }
  550. }
  551. if (newList.size() == 0) {
  552. return null;
  553. }
  554. return doPredicatesStandard(
  555. context,
  556. newList,
  557. steps,
  558. currentStep,
  559. predicates,
  560. currentPredicate + 1);
  561. }
  562. else {
  563. // For a subscript, simply take the corresponding
  564. // element from the list of results and
  565. // proceed to the remaining predicates with that element
  566. int index = indexFromPredicate(context, predicate);
  567. if (index < 0 || index >= parents.size()) {
  568. return null;
  569. }
  570. NodePointer ptr = (NodePointer) parents.get(index);
  571. return doPredicate(
  572. context,
  573. ptr,
  574. steps,
  575. currentStep,
  576. predicates,
  577. currentPredicate + 1);
  578. }
  579. }
  580. /**
  581. * Evaluate a subscript predicate: see if the node is a collection and
  582. * if the index is inside the collection
  583. */
  584. private static NodePointer doPredicateIndex(
  585. EvalContext context, NodePointer parent,
  586. Step[] steps, int currentStep,
  587. Expression[] predicates, int currentPredicate)
  588. {
  589. Expression predicate = predicates[currentPredicate];
  590. int index = indexFromPredicate(context, predicate);
  591. NodePointer pointer = parent;
  592. if (isCollectionElement(pointer, index)) {
  593. pointer = (NodePointer) pointer.clone();
  594. pointer.setIndex(index);
  595. return doPredicate(
  596. context,
  597. pointer,
  598. steps,
  599. currentStep,
  600. predicates,
  601. currentPredicate + 1);
  602. }
  603. return createNullPointerForPredicates(
  604. context,
  605. parent,
  606. steps,
  607. currentStep,
  608. predicates,
  609. currentPredicate);
  610. }
  611. /**
  612. * Extract an integer from a subscript predicate. The returned index
  613. * starts with 0, even though the subscript starts with 1.
  614. */
  615. private static int indexFromPredicate(
  616. EvalContext context,
  617. Expression predicate)
  618. {
  619. Object value = predicate.computeValue(context);
  620. if (value instanceof EvalContext) {
  621. value = ((EvalContext) value).getSingleNodePointer();
  622. }
  623. if (value instanceof NodePointer) {
  624. value = ((NodePointer) value).getValue();
  625. }
  626. if (value == null) {
  627. throw new JXPathException("Predicate value is null");
  628. }
  629. if (value instanceof Number) {
  630. return (int) (InfoSetUtil.doubleValue(value) + 0.5) - 1;
  631. }
  632. else if (InfoSetUtil.booleanValue(value)) {
  633. return 0;
  634. }
  635. return -1;
  636. }
  637. /**
  638. * Extracts the string value of the expression from a predicate like
  639. * [@name=expression].
  640. */
  641. private static String keyFromPredicate(EvalContext context,
  642. Expression predicate)
  643. {
  644. Expression expr =
  645. ((NameAttributeTest) predicate).getNameTestExpression();
  646. return InfoSetUtil.stringValue(expr.computeValue(context));
  647. }
  648. /**
  649. * For a pointer that matches an actual node, returns 0.
  650. * For a pointer that does not match an actual node, but whose
  651. * parent pointer does returns -1, etc.
  652. */
  653. private static int computeQuality(NodePointer pointer) {
  654. int quality = PERFECT_MATCH;
  655. while (pointer != null && !pointer.isActual()) {
  656. quality--;
  657. pointer = pointer.getImmediateParentPointer();
  658. }
  659. return quality;
  660. }
  661. /**
  662. * Returns true if the pointer has an attribute called "name" and
  663. * its value is equal to the supplied string.
  664. */
  665. private static boolean isNameAttributeEqual(
  666. NodePointer pointer,
  667. String name)
  668. {
  669. NodeIterator it = pointer.attributeIterator(QNAME_NAME);
  670. return it != null
  671. && it.setPosition(1)
  672. && name.equals(it.getNodePointer().getValue());
  673. }
  674. /**
  675. * Returns true if the pointer is a collection and the index is
  676. * withing the bounds of the collection.
  677. */
  678. private static boolean isCollectionElement(
  679. NodePointer pointer,
  680. int index)
  681. {
  682. return pointer.isActual()
  683. && (index == 0
  684. || (pointer.isCollection()
  685. && index >= 0
  686. && index < pointer.getLength()));
  687. }
  688. /**
  689. * For an intermediate pointer (e.g. PropertyPointer, ContainerPointer)
  690. * returns a pointer for the contained value.
  691. */
  692. private static NodePointer valuePointer(NodePointer pointer) {
  693. return pointer == null ? null : pointer.getValuePointer();
  694. }
  695. /**
  696. * Creates a "null pointer" that
  697. * a) represents the requested path and
  698. * b) can be used for creation of missing nodes in the path.
  699. */
  700. public static NodePointer createNullPointer(
  701. EvalContext context, NodePointer parent, Step[] steps,
  702. int currentStep)
  703. {
  704. if (currentStep == steps.length) {
  705. return parent;
  706. }
  707. parent = valuePointer(parent);
  708. Step step = steps[currentStep];
  709. int axis = step.getAxis();
  710. if (axis == Compiler.AXIS_CHILD || axis == Compiler.AXIS_ATTRIBUTE) {
  711. NullPropertyPointer pointer = new NullPropertyPointer(parent);
  712. QName name = ((NodeNameTest) step.getNodeTest()).getNodeName();
  713. pointer.setPropertyName(name.toString());
  714. pointer.setAttribute(axis == Compiler.AXIS_ATTRIBUTE);
  715. parent = pointer;
  716. }
  717. // else { it is self::node() }
  718. Expression predicates[] = step.getPredicates();
  719. return createNullPointerForPredicates(
  720. context,
  721. parent,
  722. steps,
  723. currentStep,
  724. predicates,
  725. 0);
  726. }
  727. /**
  728. * Creates a "null pointer" that starts with predicates.
  729. */
  730. private static NodePointer createNullPointerForPredicates(
  731. EvalContext context, NodePointer parent,
  732. Step[] steps, int currentStep,
  733. Expression predicates[], int currentPredicate)
  734. {
  735. for (int i = currentPredicate; i < predicates.length; i++) {
  736. Expression predicate = predicates[i];
  737. if (predicate instanceof NameAttributeTest) {
  738. String key = keyFromPredicate(context, predicate);
  739. parent = valuePointer(parent);
  740. NullPropertyPointer pointer = new NullPropertyPointer(parent);
  741. pointer.setNameAttributeValue(key);
  742. parent = pointer;
  743. }
  744. else {
  745. int index = indexFromPredicate(context, predicate);
  746. if (parent instanceof NullPropertyPointer) {
  747. parent.setIndex(index);
  748. }
  749. else {
  750. parent = new NullElementPointer(parent, index);
  751. }
  752. }
  753. }
  754. // Proceed with the remaining steps
  755. return createNullPointer(
  756. context, parent, steps, currentStep + 1);
  757. }
  758. private static NodeIterator getNodeIterator(
  759. EvalContext context,
  760. NodePointer pointer,
  761. Step step)
  762. {
  763. if (step.getAxis() == Compiler.AXIS_CHILD) {
  764. NodeTest nodeTest = step.getNodeTest();
  765. QName qname = ((NodeNameTest) nodeTest).getNodeName();
  766. String prefix = qname.getPrefix();
  767. if (prefix != null) {
  768. String namespaceURI = context.getJXPathContext()
  769. .getNamespaceURI(prefix);
  770. nodeTest = new NodeNameTest(qname, namespaceURI);
  771. }
  772. return pointer.childIterator(nodeTest, false, null);
  773. }
  774. else { // Compiler.AXIS_ATTRIBUTE
  775. if (!(step.getNodeTest() instanceof NodeNameTest)) {
  776. throw new UnsupportedOperationException(
  777. "Not supported node test for attributes: "
  778. + step.getNodeTest());
  779. }
  780. return pointer.attributeIterator(
  781. ((NodeNameTest) step.getNodeTest()).getNodeName());
  782. }
  783. }
  784. private static boolean isLangAttribute(QName name) {
  785. return name.getPrefix() != null
  786. && name.getPrefix().equals("xml")
  787. && name.getName().equals("lang");
  788. }
  789. }