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