1 /*
2 Copyright 2015 Atlassian
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 package io.atlassian.fugue;
18
19 import java.util.Collection;
20 import java.util.Iterator;
21 import java.util.NoSuchElementException;
22
23 import static java.util.Objects.requireNonNull;
24
25 /**
26 * Utility class for constructing iterables
27 *
28 * @since 3.0
29 */
30 class Iterators {
31
32 /**
33 * Adds all the elements of the iterator to the collectionToModify
34 *
35 * @param collectionToModify collection to add element to, must not be null
36 * @param iterator source of elements to add, must not be null
37 * @param <A> element type
38 * @return true if any of the elements from iterator were not also in
39 * collectionToModify
40 *
41 * @since 3.0
42 */
43 static <A> boolean addAll(final Collection<A> collectionToModify, final Iterator<? extends A> iterator) {
44 requireNonNull(collectionToModify);
45 requireNonNull(iterator);
46 boolean wasModified = false;
47 while (iterator.hasNext()) {
48 wasModified |= collectionToModify.add(iterator.next());
49 }
50 return wasModified;
51 }
52
53 /**
54 * Wrap an iterator to add support for the peek operation. Do not maintain a
55 * reference to the iterator passed in as a parameter. Iterators are mutable
56 * by nature and this function makes no promises about how or when the input
57 * iterator will be mutated.
58 *
59 * @param iterator iterator that may not support peek, must not be null
60 * @param <A> element type
61 * @return an iterator for which {@code peek} will return Iterator#next
62 * without removing the value from the iterator
63 *
64 * @since 3.0
65 */
66 static <A> Iterators.Peeking<A> peekingIterator(final Iterator<? extends A> iterator) {
67 if (iterator instanceof PeekingImpl) {
68 // Safe to cast <? extends T> to <T> because PeekingImpl only uses T
69 // covariantly (and cannot be subclassed to add non-covariant uses).
70 @SuppressWarnings("unchecked")
71 final PeekingImpl<A> peeking = (PeekingImpl<A>) iterator;
72 return peeking;
73 }
74 return new PeekingImpl<>(iterator);
75 }
76
77 /**
78 * Implementation of Iterators.Peeking that avoids peeking unless necessary.
79 */
80 private static class PeekingImpl<A> implements Iterators.Peeking<A> {
81
82 private final java.util.Iterator<? extends A> iterator;
83 private boolean hasPeeked;
84 private A peekedElement;
85
86 public PeekingImpl(final Iterator<? extends A> iterator) {
87 this.iterator = requireNonNull(iterator);
88 }
89
90 @Override public boolean hasNext() {
91 return hasPeeked || iterator.hasNext();
92 }
93
94 @Override public A next() {
95 if (!hasPeeked) {
96 return iterator.next();
97 }
98 final A result = peekedElement;
99 hasPeeked = false;
100 peekedElement = null;
101 return result;
102 }
103
104 @Override public void remove() {
105 if (hasPeeked) {
106 throw new IllegalStateException("Cannot remove an element after peeking");
107 }
108 iterator.remove();
109 }
110
111 @Override public A peek() {
112 if (!hasPeeked) {
113 peekedElement = iterator.next();
114 hasPeeked = true;
115 }
116 return peekedElement;
117 }
118 }
119
120 /**
121 * Iterator that returns a single element
122 *
123 * @param a element to return
124 * @param <A> element type
125 * @return iterator returning only a
126 *
127 * @since 3.0
128 */
129 static <A> Iterator<A> singletonIterator(final A a) {
130 return new Iterator<A>() {
131 boolean done = false;
132
133 @Override public boolean hasNext() {
134 return !done;
135 }
136
137 @Override public A next() {
138 if (done) {
139 throw new UnsupportedOperationException("Attempted to call next on empty iterator");
140 } else {
141 done = true;
142 return a;
143 }
144 }
145
146 @Override public void remove() {
147 throw new UnsupportedOperationException("Cannot call remove on this iterator");
148 }
149 };
150 }
151
152 /**
153 * Iterator with no values inside
154 *
155 * @param <A> element type
156 * @return empty iterator
157 * @since 3.0
158 */
159 @SuppressWarnings("unchecked") public static <A> Iterator<A> emptyIterator() {
160 return (Iterator<A>) EmptyIterator.INSTANCE;
161 }
162
163 private enum EmptyIterator implements Iterator<Object> {
164 INSTANCE;
165
166 @Override public boolean hasNext() {
167 return false;
168 }
169
170 @Override public Object next() {
171 throw new NoSuchElementException("Attempted to call next on empty iterator");
172 }
173
174 @Override public void remove() {
175 throw new UnsupportedOperationException("Cannot call remove on this iterator");
176 }
177 }
178
179 //
180 // Implementation classes
181 //
182
183 /**
184 * Marker interface for use in constructing iterators
185 *
186 * @since 3.0
187 */
188 interface Peek<A> {
189 /**
190 * Look at but do not modify the "next" thing.
191 */
192 A peek();
193 }
194
195 /**
196 * Iterator that can examine next without removing it
197 *
198 * @since 3.0
199 * @param <A> element type
200 */
201 interface Peeking<A> extends Peek<A>, Iterator<A> {}
202
203 /**
204 * A template implementation of the {@code Iterator} interface, so clients can
205 * more easily implement Iterator for some patterns of iteration.
206 * <P>
207 * An example is an iterator that skips over null elements in a backing
208 * iterator. This could be implemented as:
209 *
210 * <pre>
211 * {@code
212 *
213 * public static Iterator<String> filterNulls(final Iterator<String> in) {
214 * return new AbstractIterator<String>() {
215 * protected String computeNext() {
216 * while (in.hasNext()) {
217 * String s = in.next();
218 * if (s != null) {
219 * return s;
220 * }
221 * }
222 * return endOfData();
223 * }
224 * };
225 * }}
226 * </pre>
227 *
228 * <P>
229 * This class supports iterators that include null elements.
230 *
231 * <P>
232 * This class is a re-implentation of the Guava AbstractIterator class.
233 *
234 * @since 3.0
235 */
236 static abstract class Abstract<A> extends Unmodifiable<A> {
237 private State state = State.NotReady;
238
239 /** Constructor for use by subclasses. */
240 protected Abstract() {}
241
242 private enum State {
243 Ready, NotReady, Complete, Failed
244 }
245
246 private A next;
247
248 /**
249 * The next element.
250 * <P>
251 * <b>Note:</b> the implementation must call {@link #endOfData()} when there
252 * are no elements left in the iteration. Failure to do so could result in
253 * an infinite loop.
254 */
255 protected abstract A computeNext();
256
257 /**
258 * Implementations of {@link #computeNext} <b>must</b> invoke this method
259 * when there are no elements left in the iteration.
260 *
261 * @return {@code null}; a convenience so your {@code computeNext}
262 * implementation can use the simple statement {@code return endOfData();}
263 */
264 protected final A endOfData() {
265 state = State.Complete;
266 return null;
267 }
268
269 @Override public final boolean hasNext() {
270 switch (state) {
271 case Failed:
272 throw new IllegalStateException("Failed iterator");
273 case Ready:
274 return true;
275 case Complete:
276 return false;
277 default:
278 return tryToComputeNext();
279 }
280 }
281
282 private boolean tryToComputeNext() {
283 try {
284 next = computeNext();
285 if (state != State.Complete) {
286 state = State.Ready;
287 return true;
288 }
289 return false;
290 } catch (RuntimeException | Error e) {
291 state = State.Failed;
292 throw e;
293 }
294 }
295
296 @Override public final A next() {
297 if (!hasNext())
298 throw new NoSuchElementException();
299 try {
300 return next;
301 } finally {
302 next = null;
303 state = State.NotReady;
304 }
305 }
306 }
307
308 /**
309 * Iterator where {@link #remove} is unsupported.
310 */
311 static abstract class Unmodifiable<E> implements Iterator<E> {
312 protected Unmodifiable() {}
313
314 @Deprecated @Override public final void remove() {
315 throw new UnsupportedOperationException();
316 }
317 }
318
319 }