    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.Stack;
    020
    021    import org.apache.commons.jxpath.Pointer;
    022    import org.apache.commons.jxpath.ri.Compiler;
    023    import org.apache.commons.jxpath.ri.EvalContext;
    024    import org.apache.commons.jxpath.ri.compiler.NodeTest;
    025    import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
    026    import org.apache.commons.jxpath.ri.model.NodeIterator;
    027    import org.apache.commons.jxpath.ri.model.NodePointer;
    028
    029    /**
    030     * An EvalContext that walks the "descendant::" and "descendant-or-self::"
    031     * axes.
    032     *
    033     * @author Dmitri Plotnikov
    034     * @version $Revision: 670727 $ $Date: 2008-06-23 15:10:38 -0500 (Mon, 23 Jun 2008) $
    035     */
    036    public class DescendantContext extends EvalContext {
    037        private NodeTest nodeTest;
    038        private boolean setStarted = false;
    039        private Stack stack = null;
    040        private NodePointer currentNodePointer = null;
    041        private boolean includeSelf;
    042        private static final NodeTest ELEMENT_NODE_TEST =
    043                new NodeTypeTest(Compiler.NODE_TYPE_NODE);
    044
    045        /**
    046         * Create a new DescendantContext.
    047         * @param parentContext parent context
    048         * @param includeSelf whether to include this node
    049         * @param nodeTest test
    050         */
    051        public DescendantContext(EvalContext parentContext, boolean includeSelf,
    052                NodeTest nodeTest) {
    053            super(parentContext);
    054            this.includeSelf = includeSelf;
    055            this.nodeTest = nodeTest;
    056        }
    057
    058        public boolean isChildOrderingRequired() {
    059            return true;
    060        }
    061
    062        public NodePointer getCurrentNodePointer() {
    063            if (position == 0 && !setPosition(1)) {
    064                return null;
    065            }
    066            return currentNodePointer;
    067        }
    068
    069        public void reset() {
    070            super.reset();
    071            setStarted = false;
    072        }
    073
    074        public boolean setPosition(int position) {
    075            if (position < this.position) {
    076                reset();
    077            }
    078
    079            while (this.position < position) {
    080                if (!nextNode()) {
    081                    return false;
    082                }
    083            }
    084            return true;
    085        }
    086
    087        public boolean nextNode() {
    088            if (!setStarted) {
    089                setStarted = true;
    090                if (stack == null) {
    091                    stack = new Stack();
    092                }
    093                else {
    094                    stack.clear();
    095                }
    096                currentNodePointer = parentContext.getCurrentNodePointer();
    097                if (currentNodePointer != null) {
    098                    if (!currentNodePointer.isLeaf()) {
    099                        stack.push(
    100                            currentNodePointer.childIterator(
    101                                ELEMENT_NODE_TEST,
    102                                false,
    103                                null));
    104                    }
    105                    if (includeSelf && currentNodePointer.testNode(nodeTest)) {
    106                        position++;
    107                        return true;
    108                    }
    109                }
    110            }
    111
    112            while (!stack.isEmpty()) {
    113                NodeIterator it = (NodeIterator) stack.peek();
    114                if (it.setPosition(it.getPosition() + 1)) {
    115                    currentNodePointer = it.getNodePointer();
    116                    if (!isRecursive()) {
    117                        if (!currentNodePointer.isLeaf()) {
    118                            stack.push(
    119                                currentNodePointer.childIterator(
    120                                    ELEMENT_NODE_TEST,
    121                                    false,
    122                                    null));
    123                        }
    124                        if (currentNodePointer.testNode(nodeTest)) {
    125                            position++;
    126                            return true;
    127                        }
    128                    }
    129                }
    130                else {
    131                    // We get here only if the name test failed
    132                    // and the iterator ended
    133                    stack.pop();
    134                }
    135            }
    136            return false;
    137        }
    138
    139        /**
    140         * Checks if we are reentering a bean we have already seen and if so
    141         * returns true to prevent infinite recursion.
    142         * @return boolean
    143         */
    144        private boolean isRecursive() {
    145            Object node = currentNodePointer.getNode();
    146            for (int i = stack.size() - 1; --i >= 0;) {
    147                NodeIterator it = (NodeIterator) stack.get(i);
    148                Pointer pointer = it.getNodePointer();
    149                if (pointer != null && pointer.getNode() == node) {
    150                    return true;
    151                }
    152            }
    153            return false;
    154        }
    155    }