View Javadoc
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 }