View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jxpath.ri.axes;
19  
20  import java.util.Stack;
21  
22  import org.apache.commons.jxpath.ri.EvalContext;
23  import org.apache.commons.jxpath.ri.compiler.NodeTest;
24  import org.apache.commons.jxpath.ri.model.NodeIterator;
25  import org.apache.commons.jxpath.ri.model.NodePointer;
26  
27  /**
28   * EvalContext that walks the "preceding::" and "following::" axes.
29   */
30  public class PrecedingOrFollowingContext extends EvalContext {
31  
32      private final NodeTest nodeTest;
33      private boolean setStarted;
34      private Stack<NodeIterator> stack;
35      private NodePointer currentNodePointer;
36      private NodePointer currentRootLocation;
37      private final boolean reverse;
38  
39      /**
40       * Constructs a new PrecedingOrFollowingContext.
41       *
42       * @param parentContext parent context
43       * @param nodeTest      test
44       * @param reverse       whether to iterate in reverse order
45       */
46      public PrecedingOrFollowingContext(final EvalContext parentContext, final NodeTest nodeTest, final boolean reverse) {
47          super(parentContext);
48          this.nodeTest = nodeTest;
49          this.reverse = reverse;
50      }
51  
52      @Override
53      public NodePointer getCurrentNodePointer() {
54          return currentNodePointer;
55      }
56  
57      @Override
58      public int getDocumentOrder() {
59          return reverse ? -1 : 1;
60      }
61  
62      @Override
63      public boolean nextNode() {
64          if (!setStarted) {
65              setStarted = true;
66              if (stack == null) {
67                  stack = new Stack<>();
68              } else {
69                  stack.clear();
70              }
71              currentRootLocation = parentContext.getCurrentNodePointer();
72              final NodePointer parent = currentRootLocation.getParent();
73              if (parent != null) {
74                  // TBD: check type
75                  stack.push(parent.childIterator(null, reverse, currentRootLocation));
76              }
77          }
78          while (true) {
79              if (stack.isEmpty()) {
80                  currentRootLocation = currentRootLocation.getParent();
81                  if (currentRootLocation == null || currentRootLocation.isRoot()) {
82                      break;
83                  }
84                  final NodePointer parent = currentRootLocation.getParent();
85                  if (parent != null) {
86                      stack.push(parent.childIterator(null, reverse, currentRootLocation));
87                  }
88              }
89              while (!stack.isEmpty()) {
90                  if (!reverse) {
91                      final NodeIterator it = stack.peek();
92                      if (it.setPosition(it.getPosition() + 1)) {
93                          currentNodePointer = it.getNodePointer();
94                          if (!currentNodePointer.isLeaf()) {
95                              stack.push(currentNodePointer.childIterator(null, reverse, null));
96                          }
97                          if (currentNodePointer.testNode(nodeTest)) {
98                              super.setPosition(getCurrentPosition() + 1);
99                              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 }