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 }