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 */
017package org.apache.commons.jxpath.ri.axes;
018
019import java.util.Stack;
020
021import org.apache.commons.jxpath.Pointer;
022import org.apache.commons.jxpath.ri.Compiler;
023import org.apache.commons.jxpath.ri.EvalContext;
024import org.apache.commons.jxpath.ri.compiler.NodeTest;
025import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
026import org.apache.commons.jxpath.ri.model.NodeIterator;
027import 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 22:10:38 +0200 (Mo, 23 Jun 2008) $
035 */
036public 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}