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.Pointer; 023import org.apache.commons.jxpath.ri.Compiler; 024import org.apache.commons.jxpath.ri.EvalContext; 025import org.apache.commons.jxpath.ri.compiler.NodeTest; 026import org.apache.commons.jxpath.ri.compiler.NodeTypeTest; 027import org.apache.commons.jxpath.ri.model.NodeIterator; 028import org.apache.commons.jxpath.ri.model.NodePointer; 029 030/** 031 * An EvalContext that walks the "descendant::" and "descendant-or-self::" axes. 032 */ 033public class DescendantContext extends EvalContext { 034 035 private static final NodeTest ELEMENT_NODE_TEST = new NodeTypeTest(Compiler.NODE_TYPE_NODE); 036 private final NodeTest nodeTest; 037 private boolean setStarted; 038 private Stack<NodeIterator> stack; 039 private NodePointer currentNodePointer; 040 private final boolean includeSelf; 041 042 /** 043 * Constructs a new DescendantContext. 044 * 045 * @param parentContext parent context 046 * @param includeSelf whether to include this node 047 * @param nodeTest test 048 */ 049 public DescendantContext(final EvalContext parentContext, final boolean includeSelf, final NodeTest nodeTest) { 050 super(parentContext); 051 this.includeSelf = includeSelf; 052 this.nodeTest = nodeTest; 053 } 054 055 @Override 056 public NodePointer getCurrentNodePointer() { 057 if (position == 0 && !setPosition(1)) { 058 return null; 059 } 060 return currentNodePointer; 061 } 062 063 @Override 064 public boolean isChildOrderingRequired() { 065 return true; 066 } 067 068 /** 069 * Checks if we are reentering a bean we have already seen and if so returns true to prevent infinite recursion. 070 * 071 * @return boolean 072 */ 073 private boolean isRecursive() { 074 final Object node = currentNodePointer.getNode(); 075 for (int i = stack.size() - 1; --i >= 0;) { 076 final NodeIterator it = stack.get(i); 077 final Pointer pointer = it.getNodePointer(); 078 if (pointer != null && pointer.getNode() == node) { 079 return true; 080 } 081 } 082 return false; 083 } 084 085 @Override 086 public boolean nextNode() { 087 if (!setStarted) { 088 setStarted = true; 089 if (stack == null) { 090 stack = new Stack<>(); 091 } else { 092 stack.clear(); 093 } 094 currentNodePointer = parentContext.getCurrentNodePointer(); 095 if (currentNodePointer != null) { 096 if (!currentNodePointer.isLeaf()) { 097 stack.push(currentNodePointer.childIterator(ELEMENT_NODE_TEST, false, null)); 098 } 099 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}