    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}