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}