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