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