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.util.Collection;
020import java.util.Iterator;
021import java.util.NoSuchElementException;
022import java.util.Objects;
023
024import org.apache.commons.collections4.ResettableIterator;
025
026/**
027 * An Iterator that restarts when it reaches the end.
028 * <p>
029 * The iterator will loop continuously around the provided elements, unless
030 * there are no elements in the collection to begin with, or all the elements
031 * have been {@link #remove removed}.
032 * </p>
033 * <p>
034 * Concurrent modifications are not directly supported, and for most collection
035 * implementations will throw a ConcurrentModificationException.
036 * </p>
037 *
038 * @param <E> The type of elements returned by this iterator.
039 * @since 3.0
040 */
041public class LoopingIterator<E> implements ResettableIterator<E> {
042
043    /** The collection to base the iterator on */
044    private final Collection<? extends E> collection;
045
046    /** The current iterator */
047    private Iterator<? extends E> iterator;
048
049    /**
050     * Constructor that wraps a collection.
051     * <p>
052     * There is no way to reset an Iterator instance without recreating it from
053     * the original source, so the Collection must be passed in.
054     * </p>
055     *
056     * @param collection  The collection to wrap
057     * @throws NullPointerException if the collection is null
058     */
059    public LoopingIterator(final Collection<? extends E> collection) {
060        this.collection = Objects.requireNonNull(collection, "collection");
061        reset();
062    }
063
064    /**
065     * Has the iterator any more elements.
066     * <p>
067     * Returns false only if the collection originally had zero elements, or
068     * all the elements have been {@link #remove removed}.
069     * </p>
070     *
071     * @return {@code true} if there are more elements
072     */
073    @Override
074    public boolean hasNext() {
075        return !collection.isEmpty();
076    }
077
078    /**
079     * Returns the next object in the collection.
080     * <p>
081     * If at the end of the collection, return the first element.
082     * </p>
083     *
084     * @return The next object
085     * @throws NoSuchElementException if there are no elements
086     *         at all.  Use {@link #hasNext} to avoid this error.
087     */
088    @Override
089    public E next() {
090        if (collection.isEmpty()) {
091            throw new NoSuchElementException("There are no elements for this iterator to loop on");
092        }
093        if (!iterator.hasNext()) {
094            reset();
095        }
096        return iterator.next();
097    }
098
099    /**
100     * Removes the previously retrieved item from the underlying collection.
101     * <p>
102     * This feature is only supported if the underlying collection's
103     * {@link Collection#iterator()} method returns an implementation
104     * that supports it.
105     * </p>
106     * <p>
107     * This method can only be called after at least one {@link #next} method call.
108     * After a removal, the remove method may not be called again until another
109     * next has been performed. If the {@link #reset} is called, then remove may
110     * not be called until {@link #next} is called again.
111     * </p>
112     */
113    @Override
114    public void remove() {
115        iterator.remove();
116    }
117
118    /**
119     * Resets the iterator back to the start of the collection.
120     */
121    @Override
122    public void reset() {
123        iterator = collection.iterator();
124    }
125
126    /**
127     * Gets the size of the collection underlying the iterator.
128     *
129     * @return The current collection size
130     */
131    public int size() {
132        return collection.size();
133    }
134
135}