    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *     http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.jxpath.ri.axes;
    018
    019    import java.util.ArrayList;
    020    import java.util.Collections;
    021    import java.util.List;
    022
    023    import org.apache.commons.jxpath.JXPathException;
    024    import org.apache.commons.jxpath.ri.Compiler;
    025    import org.apache.commons.jxpath.ri.EvalContext;
    026    import org.apache.commons.jxpath.ri.InfoSetUtil;
    027    import org.apache.commons.jxpath.ri.QName;
    028    import org.apache.commons.jxpath.ri.compiler.Expression;
    029    import org.apache.commons.jxpath.ri.compiler.NameAttributeTest;
    030    import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
    031    import org.apache.commons.jxpath.ri.compiler.NodeTest;
    032    import org.apache.commons.jxpath.ri.compiler.Step;
    033    import org.apache.commons.jxpath.ri.model.NodeIterator;
    034    import org.apache.commons.jxpath.ri.model.NodePointer;
    035    import org.apache.commons.jxpath.ri.model.beans.LangAttributePointer;
    036    import org.apache.commons.jxpath.ri.model.beans.NullElementPointer;
    037    import org.apache.commons.jxpath.ri.model.beans.NullPropertyPointer;
    038    import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
    039    import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
    040
    041    /**
    042     * An evaluation mechanism for simple XPaths, which
    043     * is much faster than the usual process. It is only used for
    044     * xpaths which have no context-dependent parts, consist entirely of
    045     * <code>child::name</code> and <code>self::node()</code> steps with
    046     * predicates that either integer or have the form <code>[@name = ...]</code>.
    047     *
    048     * @author Dmitri Plotnikov
    049     * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
    050     */
    051    public class SimplePathInterpreter {
    052
    053        // Because of the complexity caused by the variety of situations
    054        // that need to be addressed by this class, we attempt to break up
    055        // the class into individual methods addressing those situations
    056        // individually.  The names of the methods are supposed to
    057        // give brief descriptions of those situations.
    058
    059        private static final QName QNAME_NAME = new QName(null, "name");
    060        private static final int PERFECT_MATCH = 1000;
    061
    062        // Uncomment this variable and the PATH = ... lines in
    063        // the two following methods in order to be able to print the
    064        // currently evaluated path for debugging of this class
    065    //    private static String PATH;       // Debugging
    066
    067        /**
    068         * Interpret a simple path that starts with the given root and
    069         * follows the given steps. All steps must have the axis "child::"
    070         * and a name test.  They can also optionally have predicates
    071         * of type [@name=expression] or simply [expression] interpreted
    072         * as an index.
    073         * @param context evaluation context
    074         * @param root root pointer
    075         * @param steps path steps
    076         * @return NodePointer
    077         */
    078        public static NodePointer interpretSimpleLocationPath(
    079                EvalContext context, NodePointer root, Step[] steps) {
    080    //        PATH = createNullPointer(context, root, steps, 0).toString();  // Dbg
    081            NodePointer pointer = doStep(context, root, steps, 0);
    082    //        return valuePointer(pointer);
    083            return pointer;
    084        }
    085
    086        /**
    087         * Interpret the steps of a simple expression path that
    088         * starts with the given root, which is the result of evaluation
    089         * of the root expression of the expression path, applies the
    090         * given predicates to it and then follows the given steps.
    091         * All steps must have the axis "child::" or "attribute::"
    092         * and a name test.  They can also optionally have predicates
    093         * of type [@name=...] or simply [...] interpreted as an index.
    094         * @param context evaluation context
    095         * @param root root pointer
    096         * @param predicates predicates corresponding to <code>steps</code>
    097         * @param steps path steps
    098         * @return NodePointer
    099         */
    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    }