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