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.Pointer;
022 import org.apache.commons.jxpath.ri.Compiler;
023 import org.apache.commons.jxpath.ri.EvalContext;
024 import org.apache.commons.jxpath.ri.compiler.NodeTest;
025 import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
026 import org.apache.commons.jxpath.ri.model.NodeIterator;
027 import org.apache.commons.jxpath.ri.model.NodePointer;
028
029 /**
030 * An EvalContext that walks the "descendant::" and "descendant-or-self::"
031 * axes.
032 *
033 * @author Dmitri Plotnikov
034 * @version $Revision: 670727 $ $Date: 2008-06-23 15:10:38 -0500 (Mon, 23 Jun 2008) $
035 */
036 public class DescendantContext extends EvalContext {
037 private NodeTest nodeTest;
038 private boolean setStarted = false;
039 private Stack stack = null;
040 private NodePointer currentNodePointer = null;
041 private boolean includeSelf;
042 private static final NodeTest ELEMENT_NODE_TEST =
043 new NodeTypeTest(Compiler.NODE_TYPE_NODE);
044
045 /**
046 * Create a new DescendantContext.
047 * @param parentContext parent context
048 * @param includeSelf whether to include this node
049 * @param nodeTest test
050 */
051 public DescendantContext(EvalContext parentContext, boolean includeSelf,
052 NodeTest nodeTest) {
053 super(parentContext);
054 this.includeSelf = includeSelf;
055 this.nodeTest = nodeTest;
056 }
057
058 public boolean isChildOrderingRequired() {
059 return true;
060 }
061
062 public NodePointer getCurrentNodePointer() {
063 if (position == 0 && !setPosition(1)) {
064 return null;
065 }
066 return currentNodePointer;
067 }
068
069 public void reset() {
070 super.reset();
071 setStarted = false;
072 }
073
074 public boolean setPosition(int position) {
075 if (position < this.position) {
076 reset();
077 }
078
079 while (this.position < position) {
080 if (!nextNode()) {
081 return false;
082 }
083 }
084 return true;
085 }
086
087 public boolean nextNode() {
088 if (!setStarted) {
089 setStarted = true;
090 if (stack == null) {
091 stack = new Stack();
092 }
093 else {
094 stack.clear();
095 }
096 currentNodePointer = parentContext.getCurrentNodePointer();
097 if (currentNodePointer != null) {
098 if (!currentNodePointer.isLeaf()) {
099 stack.push(
100 currentNodePointer.childIterator(
101 ELEMENT_NODE_TEST,
102 false,
103 null));
104 }
105 if (includeSelf && currentNodePointer.testNode(nodeTest)) {
106 position++;
107 return true;
108 }
109 }
110 }
111
112 while (!stack.isEmpty()) {
113 NodeIterator it = (NodeIterator) stack.peek();
114 if (it.setPosition(it.getPosition() + 1)) {
115 currentNodePointer = it.getNodePointer();
116 if (!isRecursive()) {
117 if (!currentNodePointer.isLeaf()) {
118 stack.push(
119 currentNodePointer.childIterator(
120 ELEMENT_NODE_TEST,
121 false,
122 null));
123 }
124 if (currentNodePointer.testNode(nodeTest)) {
125 position++;
126 return true;
127 }
128 }
129 }
130 else {
131 // We get here only if the name test failed
132 // and the iterator ended
133 stack.pop();
134 }
135 }
136 return false;
137 }
138
139 /**
140 * Checks if we are reentering a bean we have already seen and if so
141 * returns true to prevent infinite recursion.
142 * @return boolean
143 */
144 private boolean isRecursive() {
145 Object node = currentNodePointer.getNode();
146 for (int i = stack.size() - 1; --i >= 0;) {
147 NodeIterator it = (NodeIterator) stack.get(i);
148 Pointer pointer = it.getNodePointer();
149 if (pointer != null && pointer.getNode() == node) {
150 return true;
151 }
152 }
153 return false;
154 }
155 }