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.ri.EvalContext;
023import org.apache.commons.jxpath.ri.compiler.NodeTest;
024import org.apache.commons.jxpath.ri.model.NodeIterator;
025import org.apache.commons.jxpath.ri.model.NodePointer;
026
027/**
028 * EvalContext that walks the "preceding::" and "following::" axes.
029 */
030public class PrecedingOrFollowingContext extends EvalContext {
031
032    private final NodeTest nodeTest;
033    private boolean setStarted;
034    private Stack<NodeIterator> stack;
035    private NodePointer currentNodePointer;
036    private NodePointer currentRootLocation;
037    private final boolean reverse;
038
039    /**
040     * Constructs a new PrecedingOrFollowingContext.
041     *
042     * @param parentContext parent context
043     * @param nodeTest      test
044     * @param reverse       whether to iterate in reverse order
045     */
046    public PrecedingOrFollowingContext(final EvalContext parentContext, final NodeTest nodeTest, final boolean reverse) {
047        super(parentContext);
048        this.nodeTest = nodeTest;
049        this.reverse = reverse;
050    }
051
052    @Override
053    public NodePointer getCurrentNodePointer() {
054        return currentNodePointer;
055    }
056
057    @Override
058    public int getDocumentOrder() {
059        return reverse ? -1 : 1;
060    }
061
062    @Override
063    public boolean nextNode() {
064        if (!setStarted) {
065            setStarted = true;
066            if (stack == null) {
067                stack = new Stack<>();
068            } else {
069                stack.clear();
070            }
071            currentRootLocation = parentContext.getCurrentNodePointer();
072            final NodePointer parent = currentRootLocation.getParent();
073            if (parent != null) {
074                // TBD: check type
075                stack.push(parent.childIterator(null, reverse, currentRootLocation));
076            }
077        }
078        while (true) {
079            if (stack.isEmpty()) {
080                currentRootLocation = currentRootLocation.getParent();
081                if (currentRootLocation == null || currentRootLocation.isRoot()) {
082                    break;
083                }
084                final NodePointer parent = currentRootLocation.getParent();
085                if (parent != null) {
086                    stack.push(parent.childIterator(null, reverse, currentRootLocation));
087                }
088            }
089            while (!stack.isEmpty()) {
090                if (!reverse) {
091                    final NodeIterator it = stack.peek();
092                    if (it.setPosition(it.getPosition() + 1)) {
093                        currentNodePointer = it.getNodePointer();
094                        if (!currentNodePointer.isLeaf()) {
095                            stack.push(currentNodePointer.childIterator(null, reverse, null));
096                        }
097                        if (currentNodePointer.testNode(nodeTest)) {
098                            super.setPosition(getCurrentPosition() + 1);
099                            return true;
100                        }
101                    } else {
102                        // We get here only if the name test failed
103                        // and the iterator ended
104                        stack.pop();
105                    }
106                } else {
107                    NodeIterator it = stack.peek();
108                    if (it.setPosition(it.getPosition() + 1)) {
109                        currentNodePointer = it.getNodePointer();
110                        if (!currentNodePointer.isLeaf()) {
111                            stack.push(currentNodePointer.childIterator(null, reverse, null));
112                        } else if (currentNodePointer.testNode(nodeTest)) {
113                            super.setPosition(getCurrentPosition() + 1);
114                            return true;
115                        }
116                    } else {
117                        stack.pop();
118                        if (!stack.isEmpty()) {
119                            it = stack.peek();
120                            currentNodePointer = it.getNodePointer();
121                            if (currentNodePointer.testNode(nodeTest)) {
122                                super.setPosition(getCurrentPosition() + 1);
123                                return true;
124                            }
125                        }
126                    }
127                }
128            }
129        }
130        return false;
131    }
132
133    @Override
134    public void reset() {
135        super.reset();
136        setStarted = false;
137    }
138
139    @Override
140    public boolean setPosition(final int position) {
141        if (position < this.position) {
142            reset();
143        }
144        while (this.position < position) {
145            if (!nextNode()) {
146                return false;
147            }
148        }
149        return true;
150    }
151}