    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.text.MessageFormat;
    020import java.util.ArrayList;
    021import java.util.Iterator;
    022import java.util.List;
    023import java.util.ListIterator;
    024import java.util.NoSuchElementException;
    025import java.util.Objects;
    026
    027import org.apache.commons.collections4.ResettableListIterator;
    028
    029/**
    030 * Converts an {@link Iterator} into a {@link ResettableListIterator}.
    031 * For plain {@code Iterator}s this is accomplished by caching the returned
    032 * elements.  This class can also be used to simply add
    033 * {@link org.apache.commons.collections4.ResettableIterator ResettableIterator}
    034 * functionality to a given {@link ListIterator}.
    035 * <p>
    036 * The {@code ListIterator} interface has additional useful methods
    037 * for navigation - {@code previous()} and the index methods.
    038 * This class allows a regular {@code Iterator} to behave as a
    039 * {@code ListIterator}. It achieves this by building a list internally
    040 * of as the underlying iterator is traversed.
    041 * </p>
    042 * <p>
    043 * The optional operations of {@code ListIterator} are not supported for plain {@code Iterator}s.
    044 * </p>
    045 * <p>
    046 * This class implements ResettableListIterator from Commons Collections 3.2.
    047 * </p>
    048 *
    049 * @param <E> the type of elements in this iterator.
    050 * @since 2.1
    051 */
    052public class ListIteratorWrapper<E> implements ResettableListIterator<E> {
    053
    054    /** Message used when set or add are called. */
    055    private static final String UNSUPPORTED_OPERATION_MESSAGE =
    056        "ListIteratorWrapper does not support optional operations of ListIterator.";
    057
    058    /** Message used when set or add are called. */
    059    private static final String CANNOT_REMOVE_MESSAGE = "Cannot remove element at index {0}.";
    060
    061    /** The underlying iterator being decorated. */
    062    private final Iterator<? extends E> iterator;
    063    /** The list being used to cache the iterator. */
    064    private final List<E> list = new ArrayList<>();
    065
    066    /** The current index of this iterator. */
    067    private int currentIndex;
    068    /** The current index of the wrapped iterator. */
    069    private int wrappedIteratorIndex;
    070    /** Recall whether the wrapped iterator's "cursor" is in such a state as to allow remove() to be called */
    071    private boolean removeState;
    072
    073    /**
    074     * Constructs a new {@code ListIteratorWrapper} that will wrap
    075     * the given iterator.
    076     *
    077     * @param iterator  the iterator to wrap
    078     * @throws NullPointerException if the iterator is null
    079     */
    080    public ListIteratorWrapper(final Iterator<? extends E> iterator) {
    081        this.iterator = Objects.requireNonNull(iterator, "iterator");
    082    }
    083
    084    /**
    085     * Throws {@link UnsupportedOperationException}
    086     * unless the underlying {@code Iterator} is a {@code ListIterator}.
    087     *
    088     * @param obj  the object to add
    089     * @throws UnsupportedOperationException if the underlying iterator is not of
    090     * type {@link ListIterator}
    091     */
    092    @Override
    093    public void add(final E obj) throws UnsupportedOperationException {
    094        if (iterator instanceof ListIterator) {
    095            @SuppressWarnings("unchecked")
    096            final ListIterator<E> li = (ListIterator<E>) iterator;
    097            li.add(obj);
    098            return;
    099        }
    100        throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE);
    101    }
    102
    103    /**
    104     * Returns true if there are more elements in the iterator.
    105     *
    106     * @return true if there are more elements
    107     */
    108    @Override
    109    public boolean hasNext() {
    110        if (currentIndex == wrappedIteratorIndex || iterator instanceof ListIterator) {
    111            return iterator.hasNext();
    112        }
    113        return true;
    114    }
    115
    116    /**
    117     * Returns true if there are previous elements in the iterator.
    118     *
    119     * @return true if there are previous elements
    120     */
    121    @Override
    122    public boolean hasPrevious() {
    123        if (iterator instanceof ListIterator) {
    124            final ListIterator<?> li = (ListIterator<?>) iterator;
    125            return li.hasPrevious();
    126        }
    127        return currentIndex > 0;
    128    }
    129
    130    /**
    131     * Returns the next element from the iterator.
    132     *
    133     * @return the next element from the iterator
    134     * @throws NoSuchElementException if there are no more elements
    135     */
    136    @Override
    137    public E next() throws NoSuchElementException {
    138        if (iterator instanceof ListIterator) {
    139            return iterator.next();
    140        }
    141
    142        if (currentIndex < wrappedIteratorIndex) {
    143            ++currentIndex;
    144            return list.get(currentIndex - 1);
    145        }
    146
    147        final E retval = iterator.next();
    148        list.add(retval);
    149        ++currentIndex;
    150        ++wrappedIteratorIndex;
    151        removeState = true;
    152        return retval;
    153    }
    154
    155    /**
    156     * Returns the index of the next element.
    157     *
    158     * @return the index of the next element
    159     */
    160    @Override
    161    public int nextIndex() {
    162        if (iterator instanceof ListIterator) {
    163            final ListIterator<?> li = (ListIterator<?>) iterator;
    164            return li.nextIndex();
    165        }
    166        return currentIndex;
    167    }
    168
    169    /**
    170     * Returns the previous element.
    171     *
    172     * @return the previous element
    173     * @throws NoSuchElementException  if there are no previous elements
    174     */
    175    @Override
    176    public E previous() throws NoSuchElementException {
    177        if (iterator instanceof ListIterator) {
    178            @SuppressWarnings("unchecked")
    179            final ListIterator<E> li = (ListIterator<E>) iterator;
    180            return li.previous();
    181        }
    182
    183        if (currentIndex == 0) {
    184            throw new NoSuchElementException();
    185        }
    186        removeState = wrappedIteratorIndex == currentIndex;
    187        return list.get(--currentIndex);
    188    }
    189
    190    /**
    191     * Returns the index of the previous element.
    192     *
    193     * @return  the index of the previous element
    194     */
    195    @Override
    196    public int previousIndex() {
    197        if (iterator instanceof ListIterator) {
    198            final ListIterator<?> li = (ListIterator<?>) iterator;
    199            return li.previousIndex();
    200        }
    201        return currentIndex - 1;
    202    }
    203
    204    /**
    205     * Removes the last element that was returned by {@link #next()} or {@link #previous()} from the underlying collection.
    206     * This call can only be made once per call to {@code next} or {@code previous} and only if {@link #add(Object)} was not called in between.
    207     *
    208     * @throws IllegalStateException if {@code next} or {@code previous} have not been called before, or if {@code remove} or {@code add} have been called after the last call to {@code next} or {@code previous}
    209     */
    210    @Override
    211    public void remove() throws IllegalStateException {
    212        if (iterator instanceof ListIterator) {
    213            iterator.remove();
    214            return;
    215        }
    216        int removeIndex = currentIndex;
    217        if (currentIndex == wrappedIteratorIndex) {
    218            --removeIndex;
    219        }
    220        if (!removeState || wrappedIteratorIndex - currentIndex > 1) {
    221            throw new IllegalStateException(MessageFormat.format(CANNOT_REMOVE_MESSAGE, Integer.valueOf(removeIndex)));
    222        }
    223        iterator.remove();
    224        list.remove(removeIndex);
    225        currentIndex = removeIndex;
    226        wrappedIteratorIndex--;
    227        removeState = false;
    228    }
    229
    230    /**
    231     * Resets this iterator back to the position at which the iterator
    232     * was created.
    233     *
    234     * @since 3.2
    235     */
    236    @Override
    237    public void reset()  {
    238        if (iterator instanceof ListIterator) {
    239            final ListIterator<?> li = (ListIterator<?>) iterator;
    240            while (li.previousIndex() >= 0) {
    241                li.previous();
    242            }
    243            return;
    244        }
    245        currentIndex = 0;
    246    }
    247
    248    /**
    249     * Throws {@link UnsupportedOperationException}
    250     * unless the underlying {@code Iterator} is a {@code ListIterator}.
    251     *
    252     * @param obj  the object to set
    253     * @throws UnsupportedOperationException if the underlying iterator is not of
    254     * type {@link ListIterator}
    255     */
    256    @Override
    257    public void set(final E obj) throws UnsupportedOperationException {
    258        if (iterator instanceof ListIterator) {
    259            @SuppressWarnings("unchecked")
    260            final ListIterator<E> li = (ListIterator<E>) iterator;
    261            li.set(obj);
    262            return;
    263        }
    264        throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE);
    265    }
    266
    267}