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 *      https://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.collections4.iterators;
018
019import java.lang.reflect.Array;
020import java.util.Iterator;
021import java.util.NoSuchElementException;
022
023import org.apache.commons.collections4.ResettableIterator;
024
025/**
026 * Implements an {@link Iterator Iterator} over any array.
027 * <p>
028 * The array can be either an array of object or of primitives. If you know
029 * that you have an object array, the
030 * {@link ObjectArrayIterator ObjectArrayIterator}
031 * class is a better choice, as it will perform better.
032 * </p>
033 * <p>
034 * The iterator implements a {@link #reset} method, allowing the reset of
035 * the iterator back to the start if required.
036 * </p>
037 *
038 * @param <E> The type of elements returned by this iterator.
039 * @since 1.0
040 */
041public class ArrayIterator<E> implements ResettableIterator<E> {
042
043    /** The array to iterate over */
044    final Object array;
045
046    /** The start index to loop from */
047    final int startIndex;
048
049    /** The end index to loop to */
050    final int endIndex;
051
052    /** The current iterator index */
053    int index;
054
055    /**
056     * Constructs an ArrayIterator that will iterate over the values in the
057     * specified array.
058     *
059     * @param array The array to iterate over.
060     * @throws IllegalArgumentException if {@code array} is not an array.
061     * @throws NullPointerException if {@code array} is {@code null}
062     */
063    public ArrayIterator(final Object array) {
064        this(array, 0);
065    }
066
067    /**
068     * Constructs an ArrayIterator that will iterate over the values in the
069     * specified array from a specific start index.
070     *
071     * @param array  The array to iterate over.
072     * @param startIndex  The index to start iterating at.
073     * @throws IllegalArgumentException if {@code array} is not an array.
074     * @throws NullPointerException if {@code array} is {@code null}
075     * @throws IndexOutOfBoundsException if the index is invalid
076     */
077    public ArrayIterator(final Object array, final int startIndex) {
078        this(array, startIndex, Array.getLength(array));
079    }
080
081    /**
082     * Constructs an ArrayIterator that will iterate over a range of values
083     * in the specified array.
084     *
085     * @param array  The array to iterate over.
086     * @param startIndex  The index to start iterating at.
087     * @param endIndex  The index to finish iterating at.
088     * @throws IllegalArgumentException if {@code array} is not an array.
089     * @throws NullPointerException if {@code array} is {@code null}
090     * @throws IndexOutOfBoundsException if either index is invalid
091     */
092    public ArrayIterator(final Object array, final int startIndex, final int endIndex) {
093        this.array = array;
094        this.startIndex = startIndex;
095        this.endIndex = endIndex;
096        this.index = startIndex;
097
098        final int len = Array.getLength(array);
099        checkBound(startIndex, len, "start");
100        checkBound(endIndex, len, "end");
101        if (endIndex < startIndex) {
102            throw new IllegalArgumentException("End index must not be less than start index.");
103        }
104    }
105
106    /**
107     * Checks whether the index is valid or not.
108     *
109     * @param bound  The index to check
110     * @param len  The length of the array
111     * @param type  The index type (for error messages)
112     * @throws IndexOutOfBoundsException if the index is invalid
113     */
114    protected void checkBound(final int bound, final int len, final String type) {
115        if (bound > len) {
116            throw new ArrayIndexOutOfBoundsException("Attempt to make an ArrayIterator that " + type + "s beyond the end of the array. ");
117        }
118        if (bound < 0) {
119            throw new ArrayIndexOutOfBoundsException("Attempt to make an ArrayIterator that " + type + "s before the start of the array. ");
120        }
121    }
122
123    /**
124     * Gets the array that this iterator is iterating over.
125     *
126     * @return The array this iterator iterates over.
127     */
128    public Object getArray() {
129        return array;
130    }
131
132    /**
133     * Gets the end index to loop to.
134     *
135     * @return The end index
136     * @since 4.0
137     */
138    public int getEndIndex() {
139        return endIndex;
140    }
141
142    /**
143     * Gets the start index to loop from.
144     *
145     * @return The start index
146     * @since 4.0
147     */
148    public int getStartIndex() {
149        return startIndex;
150    }
151
152    /**
153     * Returns true if there are more elements to return from the array.
154     *
155     * @return true if there is a next element to return
156     */
157    @Override
158    public boolean hasNext() {
159        return index < endIndex;
160    }
161
162    /**
163     * Returns the next element in the array.
164     *
165     * @return The next element in the array
166     * @throws NoSuchElementException if all the elements in the array
167     *  have already been returned
168     */
169    @Override
170    @SuppressWarnings("unchecked")
171    public E next() {
172        if (!hasNext()) {
173            throw new NoSuchElementException();
174        }
175        return (E) Array.get(array, index++);
176    }
177
178    /**
179     * Always throws {@link UnsupportedOperationException}.
180     *
181     * @throws UnsupportedOperationException Always thrown.
182     */
183    @Override
184    public void remove() {
185        throw new UnsupportedOperationException("remove() method is not supported");
186    }
187
188    /**
189     * Resets the iterator back to the start index.
190     */
191    @Override
192    public void reset() {
193        index = startIndex;
194    }
195
196}