1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.collections4.iterators;
18
19 import java.util.Collection;
20 import java.util.Iterator;
21 import java.util.NoSuchElementException;
22 import java.util.Objects;
23
24 import org.apache.commons.collections4.ResettableIterator;
25
26 /**
27 * An Iterator that restarts when it reaches the end.
28 * <p>
29 * The iterator will loop continuously around the provided elements, unless
30 * there are no elements in the collection to begin with, or all the elements
31 * have been {@link #remove removed}.
32 * </p>
33 * <p>
34 * Concurrent modifications are not directly supported, and for most collection
35 * implementations will throw a ConcurrentModificationException.
36 * </p>
37 *
38 * @param <E> the type of elements returned by this iterator.
39 * @since 3.0
40 */
41 public class LoopingIterator<E> implements ResettableIterator<E> {
42
43 /** The collection to base the iterator on */
44 private final Collection<? extends E> collection;
45 /** The current iterator */
46 private Iterator<? extends E> iterator;
47
48 /**
49 * Constructor that wraps a collection.
50 * <p>
51 * There is no way to reset an Iterator instance without recreating it from
52 * the original source, so the Collection must be passed in.
53 * </p>
54 *
55 * @param collection the collection to wrap
56 * @throws NullPointerException if the collection is null
57 */
58 public LoopingIterator(final Collection<? extends E> collection) {
59 this.collection = Objects.requireNonNull(collection, "collection");
60 reset();
61 }
62
63 /**
64 * Has the iterator any more elements.
65 * <p>
66 * Returns false only if the collection originally had zero elements, or
67 * all the elements have been {@link #remove removed}.
68 * </p>
69 *
70 * @return {@code true} if there are more elements
71 */
72 @Override
73 public boolean hasNext() {
74 return !collection.isEmpty();
75 }
76
77 /**
78 * Returns the next object in the collection.
79 * <p>
80 * If at the end of the collection, return the first element.
81 * </p>
82 *
83 * @return the next object
84 * @throws NoSuchElementException if there are no elements
85 * at all. Use {@link #hasNext} to avoid this error.
86 */
87 @Override
88 public E next() {
89 if (collection.isEmpty()) {
90 throw new NoSuchElementException("There are no elements for this iterator to loop on");
91 }
92 if (!iterator.hasNext()) {
93 reset();
94 }
95 return iterator.next();
96 }
97
98 /**
99 * Removes the previously retrieved item from the underlying collection.
100 * <p>
101 * This feature is only supported if the underlying collection's
102 * {@link Collection#iterator()} method returns an implementation
103 * that supports it.
104 * </p>
105 * <p>
106 * This method can only be called after at least one {@link #next} method call.
107 * After a removal, the remove method may not be called again until another
108 * next has been performed. If the {@link #reset} is called, then remove may
109 * not be called until {@link #next} is called again.
110 * </p>
111 */
112 @Override
113 public void remove() {
114 iterator.remove();
115 }
116
117 /**
118 * Resets the iterator back to the start of the collection.
119 */
120 @Override
121 public void reset() {
122 iterator = collection.iterator();
123 }
124
125 /**
126 * Gets the size of the collection underlying the iterator.
127 *
128 * @return the current collection size
129 */
130 public int size() {
131 return collection.size();
132 }
133
134 }