    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}