View Javadoc
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.ArrayDeque;
20  import java.util.Deque;
21  import java.util.Iterator;
22  import java.util.Objects;
23  
24  /**
25   * Decorates an iterator to support pushback of elements.
26   * <p>
27   * The decorator stores the pushed back elements in a LIFO manner: the last element
28   * that has been pushed back, will be returned as the next element in a call to {@link #next()}.
29   * </p>
30   * <p>
31   * The decorator does not support the removal operation. Any call to {@link #remove()} will
32   * result in an {@link UnsupportedOperationException}.
33   * </p>
34   *
35   * @param <E> the type of elements returned by this iterator.
36   * @since 4.0
37   */
38  public class PushbackIterator<E> implements Iterator<E> {
39  
40      /**
41       * Decorates the specified iterator to support one-element lookahead.
42       * <p>
43       * If the iterator is already a {@link PushbackIterator} it is returned directly.
44       * </p>
45       *
46       * @param <E>  the element type
47       * @param iterator  the iterator to decorate
48       * @return a new peeking iterator
49       * @throws NullPointerException if the iterator is null
50       */
51      public static <E> PushbackIterator<E> pushbackIterator(final Iterator<? extends E> iterator) {
52          Objects.requireNonNull(iterator, "iterator");
53          if (iterator instanceof PushbackIterator<?>) {
54              @SuppressWarnings("unchecked") // safe cast
55              final PushbackIterator<E> it = (PushbackIterator<E>) iterator;
56              return it;
57          }
58          return new PushbackIterator<>(iterator);
59      }
60  
61      /** The iterator being decorated. */
62      private final Iterator<? extends E> iterator;
63  
64      /** The LIFO queue containing the pushed back items. */
65      private final Deque<E> items = new ArrayDeque<>();
66  
67      /**
68       * Constructs a new instance.
69       *
70       * @param iterator  the iterator to decorate
71       */
72      public PushbackIterator(final Iterator<? extends E> iterator) {
73          this.iterator = iterator;
74      }
75  
76      @Override
77      public boolean hasNext() {
78          return !items.isEmpty() || iterator.hasNext();
79      }
80  
81      @Override
82      public E next() {
83          return !items.isEmpty() ? items.pop() : iterator.next();
84      }
85  
86      /**
87       * Push back the given element to the iterator.
88       * <p>
89       * Calling {@link #next()} immediately afterwards will return exactly this element.
90       * </p>
91       *
92       * @param item  the element to push back to the iterator
93       */
94      public void pushback(final E item) {
95          items.push(item);
96      }
97  
98      /**
99       * This iterator will always throw an {@link UnsupportedOperationException}.
100      *
101      * @throws UnsupportedOperationException always
102      */
103     @Override
104     public void remove() {
105         throw new UnsupportedOperationException();
106     }
107 
108 }