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 * https://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
46 /** The current iterator */
47 private Iterator<? extends E> iterator;
48
49 /**
50 * Constructor that wraps a collection.
51 * <p>
52 * There is no way to reset an Iterator instance without recreating it from
53 * the original source, so the Collection must be passed in.
54 * </p>
55 *
56 * @param collection The collection to wrap
57 * @throws NullPointerException if the collection is null
58 */
59 public LoopingIterator(final Collection<? extends E> collection) {
60 this.collection = Objects.requireNonNull(collection, "collection");
61 reset();
62 }
63
64 /**
65 * Has the iterator any more elements.
66 * <p>
67 * Returns false only if the collection originally had zero elements, or
68 * all the elements have been {@link #remove removed}.
69 * </p>
70 *
71 * @return {@code true} if there are more elements
72 */
73 @Override
74 public boolean hasNext() {
75 return !collection.isEmpty();
76 }
77
78 /**
79 * Returns the next object in the collection.
80 * <p>
81 * If at the end of the collection, return the first element.
82 * </p>
83 *
84 * @return The next object
85 * @throws NoSuchElementException if there are no elements
86 * at all. Use {@link #hasNext} to avoid this error.
87 */
88 @Override
89 public E next() {
90 if (collection.isEmpty()) {
91 throw new NoSuchElementException("There are no elements for this iterator to loop on");
92 }
93 if (!iterator.hasNext()) {
94 reset();
95 }
96 return iterator.next();
97 }
98
99 /**
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 }