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 */
037public class LoopingIterator<E> implements ResettableIterator<E> {
038
039    /** The collection to base the iterator on */
040    private final Collection<? extends E> collection;
041    /** The current iterator */
042    private Iterator<? extends E> iterator;
043
044    /**
045     * Constructor that wraps a collection.
046     * <p>
047     * There is no way to reset an Iterator instance without recreating it from
048     * the original source, so the Collection must be passed in.
049     *
050     * @param coll  the collection to wrap
051     * @throws NullPointerException if the collection is null
052     */
053    public LoopingIterator(final Collection<? extends E> coll) {
054        if (coll == null) {
055            throw new NullPointerException("The collection must not be null");
056        }
057        collection = coll;
058        reset();
059    }
060
061    /**
062     * Has the iterator any more elements.
063     * <p>
064     * Returns false only if the collection originally had zero elements, or
065     * all the elements have been {@link #remove removed}.
066     *
067     * @return <code>true</code> if there are more elements
068     */
069    @Override
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    @Override
084    public E next() {
085        if (collection.size() == 0) {
086            throw new NoSuchElementException("There are no elements for this iterator to loop on");
087        }
088        if (iterator.hasNext() == false) {
089            reset();
090        }
091        return iterator.next();
092    }
093
094    /**
095     * Removes the previously retrieved item from the underlying collection.
096     * <p>
097     * This feature is only supported if the underlying collection's
098     * {@link Collection#iterator iterator} method returns an implementation
099     * that supports it.
100     * <p>
101     * This method can only be called after at least one {@link #next} method call.
102     * After a removal, the remove method may not be called again until another
103     * next has been performed. If the {@link #reset} is called, then remove may
104     * not be called until {@link #next} is called again.
105     */
106    @Override
107    public void remove() {
108        iterator.remove();
109    }
110
111    /**
112     * Resets the iterator back to the start of the collection.
113     */
114    @Override
115    public void reset() {
116        iterator = collection.iterator();
117    }
118
119    /**
120     * Gets the size of the collection underlying the iterator.
121     *
122     * @return the current collection size
123     */
124    public int size() {
125        return collection.size();
126    }
127
128}