View Javadoc
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  
19  import java.text.MessageFormat;
20  import java.util.ArrayList;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.ListIterator;
24  import java.util.NoSuchElementException;
25  import java.util.Objects;
26  
27  import org.apache.commons.collections4.ResettableListIterator;
28  
29  /**
30   * Converts an {@link Iterator} into a {@link ResettableListIterator}.
31   * For plain {@code Iterator}s this is accomplished by caching the returned
32   * elements.  This class can also be used to simply add
33   * {@link org.apache.commons.collections4.ResettableIterator ResettableIterator}
34   * functionality to a given {@link ListIterator}.
35   * <p>
36   * The {@code ListIterator} interface has additional useful methods
37   * for navigation - {@code previous()} and the index methods.
38   * This class allows a regular {@code Iterator} to behave as a
39   * {@code ListIterator}. It achieves this by building a list internally
40   * of as the underlying iterator is traversed.
41   * <p>
42   * The optional operations of {@code ListIterator} are not supported for plain {@code Iterator}s.
43   * <p>
44   * This class implements ResettableListIterator from Commons Collections 3.2.
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  
51      /** Message used when set or add are called. */
52      private static final String UNSUPPORTED_OPERATION_MESSAGE =
53          "ListIteratorWrapper does not support optional operations of ListIterator.";
54  
55      /** Message used when set or add are called. */
56      private static final String CANNOT_REMOVE_MESSAGE = "Cannot remove element at index {0}.";
57  
58      /** The underlying iterator being decorated. */
59      private final Iterator<? extends E> iterator;
60      /** The list being used to cache the iterator. */
61      private final List<E> list = new ArrayList<>();
62  
63      /** The current index of this iterator. */
64      private int currentIndex;
65      /** The current index of the wrapped iterator. */
66      private int wrappedIteratorIndex;
67      /** Recall whether the wrapped iterator's "cursor" is in such a state as to allow remove() to be called */
68      private boolean removeState;
69  
70      /**
71       * Constructs a new {@code ListIteratorWrapper} that will wrap
72       * the given iterator.
73       *
74       * @param iterator  the iterator to wrap
75       * @throws NullPointerException if the iterator is null
76       */
77      public ListIteratorWrapper(final Iterator<? extends E> iterator) {
78          this.iterator = Objects.requireNonNull(iterator, "iterator");
79      }
80  
81      /**
82       * Throws {@link UnsupportedOperationException}
83       * unless the underlying {@code Iterator} is a {@code ListIterator}.
84       *
85       * @param obj  the object to add
86       * @throws UnsupportedOperationException if the underlying iterator is not of
87       * type {@link ListIterator}
88       */
89      @Override
90      public void add(final E obj) throws UnsupportedOperationException {
91          if (iterator instanceof ListIterator) {
92              @SuppressWarnings("unchecked")
93              final ListIterator<E> li = (ListIterator<E>) iterator;
94              li.add(obj);
95              return;
96          }
97          throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE);
98      }
99  
100     /**
101      * Returns true if there are more elements in the iterator.
102      *
103      * @return true if there are more elements
104      */
105     @Override
106     public boolean hasNext() {
107         if (currentIndex == wrappedIteratorIndex || iterator instanceof ListIterator) {
108             return iterator.hasNext();
109         }
110         return true;
111     }
112 
113     /**
114      * Returns true if there are previous elements in the iterator.
115      *
116      * @return true if there are previous elements
117      */
118     @Override
119     public boolean hasPrevious() {
120         if (iterator instanceof ListIterator) {
121             final ListIterator<?> li = (ListIterator<?>) iterator;
122             return li.hasPrevious();
123         }
124         return currentIndex > 0;
125     }
126 
127     /**
128      * Returns the next element from the iterator.
129      *
130      * @return the next element from the iterator
131      * @throws NoSuchElementException if there are no more elements
132      */
133     @Override
134     public E next() throws NoSuchElementException {
135         if (iterator instanceof ListIterator) {
136             return iterator.next();
137         }
138 
139         if (currentIndex < wrappedIteratorIndex) {
140             ++currentIndex;
141             return list.get(currentIndex - 1);
142         }
143 
144         final E retval = iterator.next();
145         list.add(retval);
146         ++currentIndex;
147         ++wrappedIteratorIndex;
148         removeState = true;
149         return retval;
150     }
151 
152     /**
153      * Returns the index of the next element.
154      *
155      * @return the index of the next element
156      */
157     @Override
158     public int nextIndex() {
159         if (iterator instanceof ListIterator) {
160             final ListIterator<?> li = (ListIterator<?>) iterator;
161             return li.nextIndex();
162         }
163         return currentIndex;
164     }
165 
166     /**
167      * Returns the previous element.
168      *
169      * @return the previous element
170      * @throws NoSuchElementException  if there are no previous elements
171      */
172     @Override
173     public E previous() throws NoSuchElementException {
174         if (iterator instanceof ListIterator) {
175             @SuppressWarnings("unchecked")
176             final ListIterator<E> li = (ListIterator<E>) iterator;
177             return li.previous();
178         }
179 
180         if (currentIndex == 0) {
181             throw new NoSuchElementException();
182         }
183         removeState = wrappedIteratorIndex == currentIndex;
184         return list.get(--currentIndex);
185     }
186 
187     /**
188      * Returns the index of the previous element.
189      *
190      * @return  the index of the previous element
191      */
192     @Override
193     public int previousIndex() {
194         if (iterator instanceof ListIterator) {
195             final ListIterator<?> li = (ListIterator<?>) iterator;
196             return li.previousIndex();
197         }
198         return currentIndex - 1;
199     }
200 
201     /**
202      * Removes the last element that was returned by {@link #next()} or {@link #previous()} from the underlying collection.
203      * 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.
204      *
205      * @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}
206      */
207     @Override
208     public void remove() throws IllegalStateException {
209         if (iterator instanceof ListIterator) {
210             iterator.remove();
211             return;
212         }
213         int removeIndex = currentIndex;
214         if (currentIndex == wrappedIteratorIndex) {
215             --removeIndex;
216         }
217         if (!removeState || wrappedIteratorIndex - currentIndex > 1) {
218             throw new IllegalStateException(MessageFormat.format(CANNOT_REMOVE_MESSAGE, Integer.valueOf(removeIndex)));
219         }
220         iterator.remove();
221         list.remove(removeIndex);
222         currentIndex = removeIndex;
223         wrappedIteratorIndex--;
224         removeState = false;
225     }
226 
227     /**
228      * Resets this iterator back to the position at which the iterator
229      * was created.
230      *
231      * @since 3.2
232      */
233     @Override
234     public void reset()  {
235         if (iterator instanceof ListIterator) {
236             final ListIterator<?> li = (ListIterator<?>) iterator;
237             while (li.previousIndex() >= 0) {
238                 li.previous();
239             }
240             return;
241         }
242         currentIndex = 0;
243     }
244 
245     /**
246      * Throws {@link UnsupportedOperationException}
247      * unless the underlying {@code Iterator} is a {@code ListIterator}.
248      *
249      * @param obj  the object to set
250      * @throws UnsupportedOperationException if the underlying iterator is not of
251      * type {@link ListIterator}
252      */
253     @Override
254     public void set(final E obj) throws UnsupportedOperationException {
255         if (iterator instanceof ListIterator) {
256             @SuppressWarnings("unchecked")
257             final ListIterator<E> li = (ListIterator<E>) iterator;
258             li.set(obj);
259             return;
260         }
261         throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE);
262     }
263 
264 }