ListIteratorWrapper.java

  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. import java.text.MessageFormat;
  19. import java.util.ArrayList;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. import java.util.ListIterator;
  23. import java.util.NoSuchElementException;
  24. import java.util.Objects;

  25. import org.apache.commons.collections4.ResettableListIterator;

  26. /**
  27.  * Converts an {@link Iterator} into a {@link ResettableListIterator}.
  28.  * For plain {@code Iterator}s this is accomplished by caching the returned
  29.  * elements.  This class can also be used to simply add
  30.  * {@link org.apache.commons.collections4.ResettableIterator ResettableIterator}
  31.  * functionality to a given {@link ListIterator}.
  32.  * <p>
  33.  * The {@code ListIterator} interface has additional useful methods
  34.  * for navigation - {@code previous()} and the index methods.
  35.  * This class allows a regular {@code Iterator} to behave as a
  36.  * {@code ListIterator}. It achieves this by building a list internally
  37.  * of as the underlying iterator is traversed.
  38.  * </p>
  39.  * <p>
  40.  * The optional operations of {@code ListIterator} are not supported for plain {@code Iterator}s.
  41.  * </p>
  42.  * <p>
  43.  * This class implements ResettableListIterator from Commons Collections 3.2.
  44.  * </p>
  45.  *
  46.  * @param <E> the type of elements in this iterator.
  47.  * @since 2.1
  48.  */
  49. public class ListIteratorWrapper<E> implements ResettableListIterator<E> {

  50.     /** Message used when set or add are called. */
  51.     private static final String UNSUPPORTED_OPERATION_MESSAGE =
  52.         "ListIteratorWrapper does not support optional operations of ListIterator.";

  53.     /** Message used when set or add are called. */
  54.     private static final String CANNOT_REMOVE_MESSAGE = "Cannot remove element at index {0}.";

  55.     /** The underlying iterator being decorated. */
  56.     private final Iterator<? extends E> iterator;
  57.     /** The list being used to cache the iterator. */
  58.     private final List<E> list = new ArrayList<>();

  59.     /** The current index of this iterator. */
  60.     private int currentIndex;
  61.     /** The current index of the wrapped iterator. */
  62.     private int wrappedIteratorIndex;
  63.     /** Recall whether the wrapped iterator's "cursor" is in such a state as to allow remove() to be called */
  64.     private boolean removeState;

  65.     /**
  66.      * Constructs a new {@code ListIteratorWrapper} that will wrap
  67.      * the given iterator.
  68.      *
  69.      * @param iterator  the iterator to wrap
  70.      * @throws NullPointerException if the iterator is null
  71.      */
  72.     public ListIteratorWrapper(final Iterator<? extends E> iterator) {
  73.         this.iterator = Objects.requireNonNull(iterator, "iterator");
  74.     }

  75.     /**
  76.      * Throws {@link UnsupportedOperationException}
  77.      * unless the underlying {@code Iterator} is a {@code ListIterator}.
  78.      *
  79.      * @param obj  the object to add
  80.      * @throws UnsupportedOperationException if the underlying iterator is not of
  81.      * type {@link ListIterator}
  82.      */
  83.     @Override
  84.     public void add(final E obj) throws UnsupportedOperationException {
  85.         if (iterator instanceof ListIterator) {
  86.             @SuppressWarnings("unchecked")
  87.             final ListIterator<E> li = (ListIterator<E>) iterator;
  88.             li.add(obj);
  89.             return;
  90.         }
  91.         throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE);
  92.     }

  93.     /**
  94.      * Returns true if there are more elements in the iterator.
  95.      *
  96.      * @return true if there are more elements
  97.      */
  98.     @Override
  99.     public boolean hasNext() {
  100.         if (currentIndex == wrappedIteratorIndex || iterator instanceof ListIterator) {
  101.             return iterator.hasNext();
  102.         }
  103.         return true;
  104.     }

  105.     /**
  106.      * Returns true if there are previous elements in the iterator.
  107.      *
  108.      * @return true if there are previous elements
  109.      */
  110.     @Override
  111.     public boolean hasPrevious() {
  112.         if (iterator instanceof ListIterator) {
  113.             final ListIterator<?> li = (ListIterator<?>) iterator;
  114.             return li.hasPrevious();
  115.         }
  116.         return currentIndex > 0;
  117.     }

  118.     /**
  119.      * Returns the next element from the iterator.
  120.      *
  121.      * @return the next element from the iterator
  122.      * @throws NoSuchElementException if there are no more elements
  123.      */
  124.     @Override
  125.     public E next() throws NoSuchElementException {
  126.         if (iterator instanceof ListIterator) {
  127.             return iterator.next();
  128.         }

  129.         if (currentIndex < wrappedIteratorIndex) {
  130.             ++currentIndex;
  131.             return list.get(currentIndex - 1);
  132.         }

  133.         final E retval = iterator.next();
  134.         list.add(retval);
  135.         ++currentIndex;
  136.         ++wrappedIteratorIndex;
  137.         removeState = true;
  138.         return retval;
  139.     }

  140.     /**
  141.      * Returns the index of the next element.
  142.      *
  143.      * @return the index of the next element
  144.      */
  145.     @Override
  146.     public int nextIndex() {
  147.         if (iterator instanceof ListIterator) {
  148.             final ListIterator<?> li = (ListIterator<?>) iterator;
  149.             return li.nextIndex();
  150.         }
  151.         return currentIndex;
  152.     }

  153.     /**
  154.      * Returns the previous element.
  155.      *
  156.      * @return the previous element
  157.      * @throws NoSuchElementException  if there are no previous elements
  158.      */
  159.     @Override
  160.     public E previous() throws NoSuchElementException {
  161.         if (iterator instanceof ListIterator) {
  162.             @SuppressWarnings("unchecked")
  163.             final ListIterator<E> li = (ListIterator<E>) iterator;
  164.             return li.previous();
  165.         }

  166.         if (currentIndex == 0) {
  167.             throw new NoSuchElementException();
  168.         }
  169.         removeState = wrappedIteratorIndex == currentIndex;
  170.         return list.get(--currentIndex);
  171.     }

  172.     /**
  173.      * Returns the index of the previous element.
  174.      *
  175.      * @return  the index of the previous element
  176.      */
  177.     @Override
  178.     public int previousIndex() {
  179.         if (iterator instanceof ListIterator) {
  180.             final ListIterator<?> li = (ListIterator<?>) iterator;
  181.             return li.previousIndex();
  182.         }
  183.         return currentIndex - 1;
  184.     }

  185.     /**
  186.      * Removes the last element that was returned by {@link #next()} or {@link #previous()} from the underlying collection.
  187.      * 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.
  188.      *
  189.      * @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}
  190.      */
  191.     @Override
  192.     public void remove() throws IllegalStateException {
  193.         if (iterator instanceof ListIterator) {
  194.             iterator.remove();
  195.             return;
  196.         }
  197.         int removeIndex = currentIndex;
  198.         if (currentIndex == wrappedIteratorIndex) {
  199.             --removeIndex;
  200.         }
  201.         if (!removeState || wrappedIteratorIndex - currentIndex > 1) {
  202.             throw new IllegalStateException(MessageFormat.format(CANNOT_REMOVE_MESSAGE, Integer.valueOf(removeIndex)));
  203.         }
  204.         iterator.remove();
  205.         list.remove(removeIndex);
  206.         currentIndex = removeIndex;
  207.         wrappedIteratorIndex--;
  208.         removeState = false;
  209.     }

  210.     /**
  211.      * Resets this iterator back to the position at which the iterator
  212.      * was created.
  213.      *
  214.      * @since 3.2
  215.      */
  216.     @Override
  217.     public void reset()  {
  218.         if (iterator instanceof ListIterator) {
  219.             final ListIterator<?> li = (ListIterator<?>) iterator;
  220.             while (li.previousIndex() >= 0) {
  221.                 li.previous();
  222.             }
  223.             return;
  224.         }
  225.         currentIndex = 0;
  226.     }

  227.     /**
  228.      * Throws {@link UnsupportedOperationException}
  229.      * unless the underlying {@code Iterator} is a {@code ListIterator}.
  230.      *
  231.      * @param obj  the object to set
  232.      * @throws UnsupportedOperationException if the underlying iterator is not of
  233.      * type {@link ListIterator}
  234.      */
  235.     @Override
  236.     public void set(final E obj) throws UnsupportedOperationException {
  237.         if (iterator instanceof ListIterator) {
  238.             @SuppressWarnings("unchecked")
  239.             final ListIterator<E> li = (ListIterator<E>) iterator;
  240.             li.set(obj);
  241.             return;
  242.         }
  243.         throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE);
  244.     }

  245. }