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.Pointer;
23  import org.apache.commons.jxpath.ri.Compiler;
24  import org.apache.commons.jxpath.ri.EvalContext;
25  import org.apache.commons.jxpath.ri.compiler.NodeTest;
26  import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
27  import org.apache.commons.jxpath.ri.model.NodeIterator;
28  import org.apache.commons.jxpath.ri.model.NodePointer;
29  
30  /**
31   * An EvalContext that walks the "descendant::" and "descendant-or-self::" axes.
32   */
33  public class DescendantContext extends EvalContext {
34  
35      private static final NodeTest ELEMENT_NODE_TEST = new NodeTypeTest(Compiler.NODE_TYPE_NODE);
36      private final NodeTest nodeTest;
37      private boolean setStarted;
38      private Stack<NodeIterator> stack;
39      private NodePointer currentNodePointer;
40      private final boolean includeSelf;
41  
42      /**
43       * Constructs a new DescendantContext.
44       *
45       * @param parentContext parent context
46       * @param includeSelf   whether to include this node
47       * @param nodeTest      test
48       */
49      public DescendantContext(final EvalContext parentContext, final boolean includeSelf, final NodeTest nodeTest) {
50          super(parentContext);
51          this.includeSelf = includeSelf;
52          this.nodeTest = nodeTest;
53      }
54  
55      @Override
56      public NodePointer getCurrentNodePointer() {
57          if (position == 0 && !setPosition(1)) {
58              return null;
59          }
60          return currentNodePointer;
61      }
62  
63      @Override
64      public boolean isChildOrderingRequired() {
65          return true;
66      }
67  
68      /**
69       * Checks if we are reentering a bean we have already seen and if so returns true to prevent infinite recursion.
70       *
71       * @return boolean
72       */
73      private boolean isRecursive() {
74          final Object node = currentNodePointer.getNode();
75          for (int i = stack.size() - 1; --i >= 0;) {
76              final NodeIterator it = stack.get(i);
77              final Pointer pointer = it.getNodePointer();
78              if (pointer != null && pointer.getNode() == node) {
79                  return true;
80              }
81          }
82          return false;
83      }
84  
85      @Override
86      public boolean nextNode() {
87          if (!setStarted) {
88              setStarted = true;
89              if (stack == null) {
90                  stack = new Stack<>();
91              } else {
92                  stack.clear();
93              }
94              currentNodePointer = parentContext.getCurrentNodePointer();
95              if (currentNodePointer != null) {
96                  if (!currentNodePointer.isLeaf()) {
97                      stack.push(currentNodePointer.childIterator(ELEMENT_NODE_TEST, false, null));
98                  }
99                  if (includeSelf && currentNodePointer.testNode(nodeTest)) {
100                     position++;
101                     return true;
102                 }
103             }
104         }
105         while (!stack.isEmpty()) {
106             final NodeIterator it = stack.peek();
107             if (it.setPosition(it.getPosition() + 1)) {
108                 currentNodePointer = it.getNodePointer();
109                 if (!isRecursive()) {
110                     if (!currentNodePointer.isLeaf()) {
111                         stack.push(currentNodePointer.childIterator(ELEMENT_NODE_TEST, false, null));
112                     }
113                     if (currentNodePointer.testNode(nodeTest)) {
114                         position++;
115                         return true;
116                     }
117                 }
118             } else {
119                 // We get here only if the name test failed
120                 // and the iterator ended
121                 stack.pop();
122             }
123         }
124         return false;
125     }
126 
127     @Override
128     public void reset() {
129         super.reset();
130         setStarted = false;
131     }
132 
133     @Override
134     public boolean setPosition(final int position) {
135         if (position < this.position) {
136             reset();
137         }
138         while (this.position < position) {
139             if (!nextNode()) {
140                 return false;
141             }
142         }
143         return true;
144     }
145 }