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 */ 017package org.apache.commons.jxpath.ri.axes; 018 019import java.util.Stack; 020 021import org.apache.commons.jxpath.Pointer; 022import org.apache.commons.jxpath.ri.Compiler; 023import org.apache.commons.jxpath.ri.EvalContext; 024import org.apache.commons.jxpath.ri.compiler.NodeTest; 025import org.apache.commons.jxpath.ri.compiler.NodeTypeTest; 026import org.apache.commons.jxpath.ri.model.NodeIterator; 027import 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 22:10:38 +0200 (Mo, 23 Jun 2008) $ 035 */ 036public 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}