001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.iterators;
018
019import java.util.ArrayDeque;
020import java.util.Deque;
021import java.util.Iterator;
022import java.util.Objects;
023
024/**
025 * Decorates an iterator to support pushback of elements.
026 * <p>
027 * The decorator stores the pushed back elements in a LIFO manner: the last element
028 * that has been pushed back, will be returned as the next element in a call to {@link #next()}.
029 * </p>
030 * <p>
031 * The decorator does not support the removal operation. Any call to {@link #remove()} will
032 * result in an {@link UnsupportedOperationException}.
033 * </p>
034 *
035 * @param <E> the type of elements returned by this iterator.
036 * @since 4.0
037 */
038public class PushbackIterator<E> implements Iterator<E> {
039
040    /**
041     * Decorates the specified iterator to support one-element lookahead.
042     * <p>
043     * If the iterator is already a {@link PushbackIterator} it is returned directly.
044     * </p>
045     *
046     * @param <E>  the element type
047     * @param iterator  the iterator to decorate
048     * @return a new peeking iterator
049     * @throws NullPointerException if the iterator is null
050     */
051    public static <E> PushbackIterator<E> pushbackIterator(final Iterator<? extends E> iterator) {
052        Objects.requireNonNull(iterator, "iterator");
053        if (iterator instanceof PushbackIterator<?>) {
054            @SuppressWarnings("unchecked") // safe cast
055            final PushbackIterator<E> it = (PushbackIterator<E>) iterator;
056            return it;
057        }
058        return new PushbackIterator<>(iterator);
059    }
060
061    /** The iterator being decorated. */
062    private final Iterator<? extends E> iterator;
063
064    /** The LIFO queue containing the pushed back items. */
065    private final Deque<E> items = new ArrayDeque<>();
066
067    /**
068     * Constructs a new instance.
069     *
070     * @param iterator  the iterator to decorate
071     */
072    public PushbackIterator(final Iterator<? extends E> iterator) {
073        this.iterator = iterator;
074    }
075
076    @Override
077    public boolean hasNext() {
078        return !items.isEmpty() || iterator.hasNext();
079    }
080
081    @Override
082    public E next() {
083        return !items.isEmpty() ? items.pop() : iterator.next();
084    }
085
086    /**
087     * Push back the given element to the iterator.
088     * <p>
089     * Calling {@link #next()} immediately afterwards will return exactly this element.
090     * </p>
091     *
092     * @param item  the element to push back to the iterator
093     */
094    public void pushback(final E item) {
095        items.push(item);
096    }
097
098    /**
099     * 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}