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 org.apache.commons.jxpath.Pointer; 021import org.apache.commons.jxpath.ri.EvalContext; 022import org.apache.commons.jxpath.ri.compiler.NodeTest; 023import org.apache.commons.jxpath.ri.model.NodeIterator; 024import org.apache.commons.jxpath.ri.model.NodePointer; 025 026/** 027 * EvalContext that can walk the "child::", "following-sibling::" and "preceding-sibling::" axes. 028 */ 029public class ChildContext extends EvalContext { 030 031 private final NodeTest nodeTest; 032 private final boolean startFromParentLocation; 033 private final boolean reverse; 034 private NodeIterator iterator; 035 036 /** 037 * Constructs a new ChildContext. 038 * 039 * @param parentContext parent EvalContext 040 * @param nodeTest NodeTest 041 * @param startFromParentLocation whether to start from parent location 042 * @param reverse whether to iterate in reverse 043 */ 044 public ChildContext(final EvalContext parentContext, final NodeTest nodeTest, final boolean startFromParentLocation, final boolean reverse) { 045 super(parentContext); 046 this.nodeTest = nodeTest; 047 this.startFromParentLocation = startFromParentLocation; 048 this.reverse = reverse; 049 } 050 051 @Override 052 public NodePointer getCurrentNodePointer() { 053 if (position == 0 && !setPosition(1)) { 054 return null; 055 } 056 return iterator == null ? null : iterator.getNodePointer(); 057 } 058 059 /** 060 * This method is called on the last context on the path when only one value is needed. Note that this will return the whole property, even if it is a 061 * collection. It will not extract the first element of the collection. For example, "books" will return the collection of books rather than the first book 062 * from that collection. 063 * 064 * @return Pointer 065 */ 066 @Override 067 public Pointer getSingleNodePointer() { 068 if (position == 0) { 069 while (nextSet()) { 070 prepare(); 071 if (iterator == null) { 072 return null; 073 } 074 // See if there is a property there, singular or collection 075 final NodePointer pointer = iterator.getNodePointer(); 076 if (pointer != null) { 077 return pointer; 078 } 079 } 080 return null; 081 } 082 return getCurrentNodePointer(); 083 } 084 085 @Override 086 public boolean nextNode() { 087 return setPosition(getCurrentPosition() + 1); 088 } 089 090 /** 091 * Allocates a PropertyIterator. 092 */ 093 private void prepare() { 094 final NodePointer parent = parentContext.getCurrentNodePointer(); 095 if (parent == null) { 096 return; 097 } 098 final NodePointer useParent = startFromParentLocation ? parent.getParent() : parent; 099 iterator = useParent == null ? null : useParent.childIterator(nodeTest, reverse, startFromParentLocation ? parent : null); 100 } 101 102 @Override 103 public void reset() { 104 super.reset(); 105 iterator = null; 106 } 107 108 @Override 109 public boolean setPosition(final int position) { 110 final int oldPosition = getCurrentPosition(); 111 super.setPosition(position); 112 if (oldPosition == 0) { 113 prepare(); 114 } 115 return iterator != null && iterator.setPosition(position); 116 } 117}