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