View Javadoc

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