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