View Javadoc

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