001: /*
002: * Copyright 2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.collections.iterators;
017:
018: import java.util.List;
019: import java.util.ListIterator;
020: import java.util.NoSuchElementException;
021:
022: import org.apache.commons.collections.ResettableListIterator;
023:
024: /**
025: * A ListIterator that restarts when it reaches the end or when it
026: * reaches the beginning.
027: * <p>
028: * The iterator will loop continuously around the provided list,
029: * unless there are no elements in the collection to begin with, or
030: * all of the elements have been {@link #remove removed}.
031: * <p>
032: * Concurrent modifications are not directly supported, and for most
033: * collection implementations will throw a
034: * ConcurrentModificationException.
035: *
036: * @since Commons Collections 3.2
037: * @version $Revision: 405920 $ $Date: 2006-05-12 23:48:04 +0100 (Fri, 12 May 2006) $
038: *
039: * @author Eric Crampton <ccesc@eonomine.com>
040: */
041: public class LoopingListIterator implements ResettableListIterator {
042:
043: /** The list to base the iterator on */
044: private List list;
045: /** The current list iterator */
046: private ListIterator iterator;
047:
048: /**
049: * Constructor that wraps a list.
050: * <p>
051: * There is no way to reset a ListIterator instance without
052: * recreating it from the original source, so the List must be
053: * passed in and a reference to it held.
054: *
055: * @param list the list to wrap
056: * @throws NullPointerException if the list it null
057: */
058: public LoopingListIterator(List list) {
059: if (list == null) {
060: throw new NullPointerException("The list must not be null");
061: }
062: this .list = list;
063: reset();
064: }
065:
066: /**
067: * Returns whether this iterator has any more elements.
068: * <p>
069: * Returns false only if the list originally had zero elements, or
070: * all elements have been {@link #remove removed}.
071: *
072: * @return <code>true</code> if there are more elements
073: */
074: public boolean hasNext() {
075: return !list.isEmpty();
076: }
077:
078: /**
079: * Returns the next object in the list.
080: * <p>
081: * If at the end of the list, returns the first element.
082: *
083: * @return the object after the last element returned
084: * @throws NoSuchElementException if there are no elements in the list
085: */
086: public Object next() {
087: if (list.isEmpty()) {
088: throw new NoSuchElementException(
089: "There are no elements for this iterator to loop on");
090: }
091: if (iterator.hasNext() == false) {
092: reset();
093: }
094: return iterator.next();
095: }
096:
097: /**
098: * Returns the index of the element that would be returned by a
099: * subsequent call to {@link #next}.
100: * <p>
101: * As would be expected, if the iterator is at the physical end of
102: * the underlying list, 0 is returned, signifying the beginning of
103: * the list.
104: *
105: * @return the index of the element that would be returned if next() were called
106: * @throws NoSuchElementException if there are no elements in the list
107: */
108: public int nextIndex() {
109: if (list.isEmpty()) {
110: throw new NoSuchElementException(
111: "There are no elements for this iterator to loop on");
112: }
113: if (iterator.hasNext() == false) {
114: return 0;
115: } else {
116: return iterator.nextIndex();
117: }
118: }
119:
120: /**
121: * Returns whether this iterator has any more previous elements.
122: * <p>
123: * Returns false only if the list originally had zero elements, or
124: * all elements have been {@link #remove removed}.
125: *
126: * @return <code>true</code> if there are more elements
127: */
128: public boolean hasPrevious() {
129: return !list.isEmpty();
130: }
131:
132: /**
133: * Returns the previous object in the list.
134: * <p>
135: * If at the beginning of the list, return the last element. Note
136: * that in this case, traversal to find that element takes linear time.
137: *
138: * @return the object before the last element returned
139: * @throws NoSuchElementException if there are no elements in the list
140: */
141: public Object previous() {
142: if (list.isEmpty()) {
143: throw new NoSuchElementException(
144: "There are no elements for this iterator to loop on");
145: }
146: if (iterator.hasPrevious() == false) {
147: Object result = null;
148: while (iterator.hasNext()) {
149: result = iterator.next();
150: }
151: iterator.previous();
152: return result;
153: } else {
154: return iterator.previous();
155: }
156: }
157:
158: /**
159: * Returns the index of the element that would be returned by a
160: * subsequent call to {@link #previous}.
161: * <p>
162: * As would be expected, if at the iterator is at the physical
163: * beginning of the underlying list, the list's size minus one is
164: * returned, signifying the end of the list.
165: *
166: * @return the index of the element that would be returned if previous() were called
167: * @throws NoSuchElementException if there are no elements in the list
168: */
169: public int previousIndex() {
170: if (list.isEmpty()) {
171: throw new NoSuchElementException(
172: "There are no elements for this iterator to loop on");
173: }
174: if (iterator.hasPrevious() == false) {
175: return list.size() - 1;
176: } else {
177: return iterator.previousIndex();
178: }
179: }
180:
181: /**
182: * Removes the previously retrieved item from the underlying list.
183: * <p>
184: * This feature is only supported if the underlying list's
185: * {@link List#iterator iterator} method returns an implementation
186: * that supports it.
187: * <p>
188: * This method can only be called after at least one {@link #next}
189: * or {@link #previous} method call. After a removal, the remove
190: * method may not be called again until another {@link #next} or
191: * {@link #previous} has been performed. If the {@link #reset} is
192: * called, then remove may not be called until {@link #next} or
193: * {@link #previous} is called again.
194: *
195: * @throws UnsupportedOperationException if the remove method is
196: * not supported by the iterator implementation of the underlying
197: * list
198: */
199: public void remove() {
200: iterator.remove();
201: }
202:
203: /**
204: * Inserts the specified element into the underlying list.
205: * <p>
206: * The element is inserted before the next element that would be
207: * returned by {@link #next}, if any, and after the next element
208: * that would be returned by {@link #previous}, if any.
209: * <p>
210: * This feature is only supported if the underlying list's
211: * {@link List#listIterator} method returns an implementation
212: * that supports it.
213: *
214: * @param obj the element to insert
215: * @throws UnsupportedOperationException if the add method is not
216: * supported by the iterator implementation of the underlying list
217: */
218: public void add(Object obj) {
219: iterator.add(obj);
220: }
221:
222: /**
223: * Replaces the last element that was returned by {@link #next} or
224: * {@link #previous}.
225: * <p>
226: * This feature is only supported if the underlying list's
227: * {@link List#listIterator} method returns an implementation
228: * that supports it.
229: *
230: * @param obj the element with which to replace the last element returned
231: * @throws UnsupportedOperationException if the set method is not
232: * supported by the iterator implementation of the underlying list
233: */
234: public void set(Object obj) {
235: iterator.set(obj);
236: }
237:
238: /**
239: * Resets the iterator back to the start of the list.
240: */
241: public void reset() {
242: iterator = list.listIterator();
243: }
244:
245: /**
246: * Gets the size of the list underlying the iterator.
247: *
248: * @return the current list size
249: */
250: public int size() {
251: return list.size();
252: }
253:
254: }
|