    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.model.beans;
    019
    020import java.util.ArrayList;
    021import java.util.List;
    022
    023import org.apache.commons.jxpath.JXPathException;
    024import org.apache.commons.jxpath.ri.model.NodeIterator;
    025import org.apache.commons.jxpath.ri.model.NodePointer;
    026
    027/**
    028 * Combines node iterators of all elements of a collection into one aggregate node iterator.
    029 */
    030public abstract class CollectionNodeIterator implements NodeIterator {
    031
    032    private final CollectionPointer pointer;
    033    private final boolean reverse;
    034    private final NodePointer startWith;
    035    private int position;
    036    private List<NodePointer> collection;
    037
    038    /**
    039     * Constructs a new CollectionNodeIterator.
    040     *
    041     * @param pointer   collection pointer
    042     * @param reverse   iteration order
    043     * @param startWith starting pointer
    044     */
    045    protected CollectionNodeIterator(final CollectionPointer pointer, final boolean reverse, final NodePointer startWith) {
    046        this.pointer = pointer;
    047        this.reverse = reverse;
    048        this.startWith = startWith;
    049    }
    050
    051    /**
    052     * Implemented by subclasses to produce child/attribute node iterators.
    053     *
    054     * @param elementPointer owning pointer
    055     * @return NodeIterator
    056     */
    057    protected abstract NodeIterator getElementNodeIterator(NodePointer elementPointer);
    058
    059    @Override
    060    public NodePointer getNodePointer() {
    061        if (position == 0) {
    062            return null;
    063        }
    064        return collection.get(position - 1);
    065    }
    066
    067    @Override
    068    public int getPosition() {
    069        return position;
    070    }
    071
    072    /**
    073     * Prepare...
    074     */
    075    private void prepare() {
    076        collection = new ArrayList<>();
    077        final NodePointer ptr = (NodePointer) pointer.clone();
    078        final int length = ptr.getLength();
    079        for (int i = 0; i < length; i++) {
    080            ptr.setIndex(i);
    081            final NodePointer elementPointer = ptr.getValuePointer();
    082            final NodeIterator iter = getElementNodeIterator(elementPointer);
    083            for (int j = 1; iter.setPosition(j); j++) {
    084                final NodePointer childPointer = iter.getNodePointer();
    085                if (reverse) {
    086                    collection.add(0, childPointer);
    087                } else {
    088                    collection.add(childPointer);
    089                }
    090            }
    091        }
    092        if (startWith != null) {
    093            final int index = collection.indexOf(startWith);
    094            if (index == -1) {
    095                throw new JXPathException("Invalid starting pointer for iterator: " + startWith);
    096            }
    097            while (collection.size() > index) {
    098                if (!reverse) {
    099                    collection.remove(collection.size() - 1);
    100                } else {
    101                    collection.remove(0);
    102                }
    103            }
    104        }
    105    }
    106
    107    @Override
    108    public boolean setPosition(final int position) {
    109        if (collection == null) {
    110            prepare();
    111        }
    112        if (position < 1 || position > collection.size()) {
    113            return false;
    114        }
    115        this.position = position;
    116        return true;
    117    }
    118}