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.ri.EvalContext; 023import org.apache.commons.jxpath.ri.compiler.NodeTest; 024import org.apache.commons.jxpath.ri.model.NodeIterator; 025import org.apache.commons.jxpath.ri.model.NodePointer; 026 027/** 028 * EvalContext that walks the "preceding::" and "following::" axes. 029 */ 030public class PrecedingOrFollowingContext extends EvalContext { 031 032 private final NodeTest nodeTest; 033 private boolean setStarted; 034 private Stack<NodeIterator> stack; 035 private NodePointer currentNodePointer; 036 private NodePointer currentRootLocation; 037 private final boolean reverse; 038 039 /** 040 * Constructs a new PrecedingOrFollowingContext. 041 * 042 * @param parentContext parent context 043 * @param nodeTest test 044 * @param reverse whether to iterate in reverse order 045 */ 046 public PrecedingOrFollowingContext(final EvalContext parentContext, final NodeTest nodeTest, final boolean reverse) { 047 super(parentContext); 048 this.nodeTest = nodeTest; 049 this.reverse = reverse; 050 } 051 052 @Override 053 public NodePointer getCurrentNodePointer() { 054 return currentNodePointer; 055 } 056 057 @Override 058 public int getDocumentOrder() { 059 return reverse ? -1 : 1; 060 } 061 062 @Override 063 public boolean nextNode() { 064 if (!setStarted) { 065 setStarted = true; 066 if (stack == null) { 067 stack = new Stack<>(); 068 } else { 069 stack.clear(); 070 } 071 currentRootLocation = parentContext.getCurrentNodePointer(); 072 final NodePointer parent = currentRootLocation.getParent(); 073 if (parent != null) { 074 // TBD: check type 075 stack.push(parent.childIterator(null, reverse, currentRootLocation)); 076 } 077 } 078 while (true) { 079 if (stack.isEmpty()) { 080 currentRootLocation = currentRootLocation.getParent(); 081 if (currentRootLocation == null || currentRootLocation.isRoot()) { 082 break; 083 } 084 final NodePointer parent = currentRootLocation.getParent(); 085 if (parent != null) { 086 stack.push(parent.childIterator(null, reverse, currentRootLocation)); 087 } 088 } 089 while (!stack.isEmpty()) { 090 if (!reverse) { 091 final NodeIterator it = stack.peek(); 092 if (it.setPosition(it.getPosition() + 1)) { 093 currentNodePointer = it.getNodePointer(); 094 if (!currentNodePointer.isLeaf()) { 095 stack.push(currentNodePointer.childIterator(null, reverse, null)); 096 } 097 if (currentNodePointer.testNode(nodeTest)) { 098 super.setPosition(getCurrentPosition() + 1); 099 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}