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.collections.iterators;
18  
19  import java.util.List;
20  import java.util.ListIterator;
21  import java.util.NoSuchElementException;
22  
23  import org.apache.commons.collections.ResettableListIterator;
24  
25  /**
26   * A ListIterator that restarts when it reaches the end or when it
27   * reaches the beginning.
28   * <p>
29   * The iterator will loop continuously around the provided list,
30   * unless there are no elements in the collection to begin with, or
31   * all of the elements have been {@link #remove removed}.
32   * <p>
33   * Concurrent modifications are not directly supported, and for most
34   * collection implementations will throw a
35   * ConcurrentModificationException.
36   *
37   * @since 3.2
38   * @version $Id: LoopingListIterator.java 1429905 2013-01-07 17:15:14Z ggregory $
39   */
40  public class LoopingListIterator<E> implements ResettableListIterator<E> {
41  
42      /** The list to base the iterator on */
43      private final List<E> list;
44      /** The current list iterator */
45      private ListIterator<E> iterator;
46  
47      /**
48       * Constructor that wraps a list.
49       * <p>
50       * There is no way to reset a ListIterator instance without
51       * recreating it from the original source, so the List must be
52       * passed in and a reference to it held.
53       *
54       * @param list the list to wrap
55       * @throws NullPointerException if the list it null
56       */
57      public LoopingListIterator(final List<E> list) {
58          if (list == null) {
59              throw new NullPointerException("The list must not be null");
60          }
61          this.list = list;
62          reset();
63      }
64  
65      /**
66       * Returns whether this iterator has any more elements.
67       * <p>
68       * Returns false only if the list originally had zero elements, or
69       * all elements have been {@link #remove removed}.
70       *
71       * @return <code>true</code> if there are more elements
72       */
73      public boolean hasNext() {
74          return !list.isEmpty();
75      }
76  
77      /**
78       * Returns the next object in the list.
79       * <p>
80       * If at the end of the list, returns the first element.
81       *
82       * @return the object after the last element returned
83       * @throws NoSuchElementException if there are no elements in the list
84       */
85      public E next() {
86          if (list.isEmpty()) {
87              throw new NoSuchElementException(
88                  "There are no elements for this iterator to loop on");
89          }
90          if (iterator.hasNext() == false) {
91              reset();
92          }
93          return iterator.next();
94      }
95  
96      /**
97       * Returns the index of the element that would be returned by a
98       * subsequent call to {@link #next}.
99       * <p>
100      * As would be expected, if the iterator is at the physical end of
101      * the underlying list, 0 is returned, signifying the beginning of
102      * the list.
103      *
104      * @return the index of the element that would be returned if next() were called
105      * @throws NoSuchElementException if there are no elements in the list
106      */
107     public int nextIndex() {
108         if (list.isEmpty()) {
109             throw new NoSuchElementException(
110                 "There are no elements for this iterator to loop on");
111         }
112         if (iterator.hasNext() == false) {
113             return 0;
114         }
115         return iterator.nextIndex();
116     }
117 
118     /**
119      * Returns whether this iterator has any more previous elements.
120      * <p>
121      * Returns false only if the list originally had zero elements, or
122      * all elements have been {@link #remove removed}.
123      *
124      * @return <code>true</code> if there are more elements
125      */
126     public boolean hasPrevious() {
127         return !list.isEmpty();
128     }
129 
130     /**
131      * Returns the previous object in the list.
132      * <p>
133      * If at the beginning of the list, return the last element. Note
134      * that in this case, traversal to find that element takes linear time.
135      *
136      * @return the object before the last element returned
137      * @throws NoSuchElementException if there are no elements in the list
138      */
139     public E previous() {
140         if (list.isEmpty()) {
141             throw new NoSuchElementException(
142                 "There are no elements for this iterator to loop on");
143         }
144         if (iterator.hasPrevious() == false) {
145             E result = null;
146             while (iterator.hasNext()) {
147                 result = iterator.next();
148             }
149             iterator.previous();
150             return result;
151         }
152         return iterator.previous();
153     }
154 
155     /**
156      * Returns the index of the element that would be returned by a
157      * subsequent call to {@link #previous}.
158      * <p>
159      * As would be expected, if at the iterator is at the physical
160      * beginning of the underlying list, the list's size minus one is
161      * returned, signifying the end of the list.
162      *
163      * @return the index of the element that would be returned if previous() were called
164      * @throws NoSuchElementException if there are no elements in the list
165      */
166     public int previousIndex() {
167         if (list.isEmpty()) {
168             throw new NoSuchElementException(
169                 "There are no elements for this iterator to loop on");
170         }
171         if (iterator.hasPrevious() == false) {
172             return list.size() - 1;
173         }
174         return iterator.previousIndex();
175     }
176 
177     /**
178      * Removes the previously retrieved item from the underlying list.
179      * <p>
180      * This feature is only supported if the underlying list's
181      * {@link List#iterator iterator} method returns an implementation
182      * that supports it.
183      * <p>
184      * This method can only be called after at least one {@link #next}
185      * or {@link #previous} method call. After a removal, the remove
186      * method may not be called again until another {@link #next} or
187      * {@link #previous} has been performed. If the {@link #reset} is
188      * called, then remove may not be called until {@link #next} or
189      * {@link #previous} is called again.
190      *
191      * @throws UnsupportedOperationException if the remove method is
192      * not supported by the iterator implementation of the underlying
193      * list
194      */
195     public void remove() {
196         iterator.remove();
197     }
198 
199     /**
200      * Inserts the specified element into the underlying list.
201      * <p>
202      * The element is inserted before the next element that would be
203      * returned by {@link #next}, if any, and after the next element
204      * that would be returned by {@link #previous}, if any.
205      * <p>
206      * This feature is only supported if the underlying list's
207      * {@link List#listIterator} method returns an implementation
208      * that supports it.
209      *
210      * @param obj  the element to insert
211      * @throws UnsupportedOperationException if the add method is not
212      *  supported by the iterator implementation of the underlying list
213      */
214     public void add(final E obj) {
215         iterator.add(obj);
216     }
217 
218     /**
219      * Replaces the last element that was returned by {@link #next} or
220      * {@link #previous}.
221      * <p>
222      * This feature is only supported if the underlying list's
223      * {@link List#listIterator} method returns an implementation
224      * that supports it.
225      *
226      * @param obj  the element with which to replace the last element returned
227      * @throws UnsupportedOperationException if the set method is not
228      *  supported by the iterator implementation of the underlying list
229      */
230     public void set(final E obj) {
231         iterator.set(obj);
232     }
233 
234     /**
235      * Resets the iterator back to the start of the list.
236      */
237     public void reset() {
238         iterator = list.listIterator();
239     }
240 
241     /**
242      * Gets the size of the list underlying the iterator.
243      *
244      * @return the current list size
245      */
246     public int size() {
247         return list.size();
248     }
249 
250 }