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 * The decorator does not support the removal operation. Any call to {@link #remove()} will
031 * result in an {@link UnsupportedOperationException}.
032 *
033 * @param <E> the type of elements returned by this iterator.
034 * @since 4.0
035 */
036public class PushbackIterator<E> implements Iterator<E> {
037
038    /**
039     * Decorates the specified iterator to support one-element lookahead.
040     * <p>
041     * If the iterator is already a {@link PushbackIterator} it is returned directly.
042     *
043     * @param <E>  the element type
044     * @param iterator  the iterator to decorate
045     * @return a new peeking iterator
046     * @throws NullPointerException if the iterator is null
047     */
048    public static <E> PushbackIterator<E> pushbackIterator(final Iterator<? extends E> iterator) {
049        Objects.requireNonNull(iterator, "iterator");
050        if (iterator instanceof PushbackIterator<?>) {
051            @SuppressWarnings("unchecked") // safe cast
052            final PushbackIterator<E> it = (PushbackIterator<E>) iterator;
053            return it;
054        }
055        return new PushbackIterator<>(iterator);
056    }
057
058    /** The iterator being decorated. */
059    private final Iterator<? extends E> iterator;
060
061    /** The LIFO queue containing the pushed back items. */
062    private final Deque<E> items = new ArrayDeque<>();
063
064    /**
065     * Constructs a new instance.
066     *
067     * @param iterator  the iterator to decorate
068     */
069    public PushbackIterator(final Iterator<? extends E> iterator) {
070        this.iterator = iterator;
071    }
072
073    @Override
074    public boolean hasNext() {
075        return !items.isEmpty() || iterator.hasNext();
076    }
077
078    @Override
079    public E next() {
080        return !items.isEmpty() ? items.pop() : iterator.next();
081    }
082
083    /**
084     * Push back the given element to the iterator.
085     * <p>
086     * Calling {@link #next()} immediately afterwards will return exactly this element.
087     *
088     * @param item  the element to push back to the iterator
089     */
090    public void pushback(final E item) {
091        items.push(item);
092    }
093
094    /**
095     * This iterator will always throw an {@link UnsupportedOperationException}.
096     *
097     * @throws UnsupportedOperationException always
098     */
099    @Override
100    public void remove() {
101        throw new UnsupportedOperationException();
102    }
103
104}