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 */ 017package org.apache.commons.collections4.iterators; 018 019import java.util.List; 020import java.util.ListIterator; 021import java.util.NoSuchElementException; 022 023import org.apache.commons.collections4.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 3.2 038 * @version $Id: LoopingListIterator.html 972421 2015-11-14 20:00:04Z tn $ 039 */ 040public class LoopingListIterator<E> implements ResettableListIterator<E> { 041 042 /** The list to base the iterator on */ 043 private final List<E> list; 044 /** The current list iterator */ 045 private ListIterator<E> iterator; 046 047 /** 048 * Constructor that wraps a list. 049 * <p> 050 * There is no way to reset a ListIterator instance without 051 * recreating it from the original source, so the List must be 052 * passed in and a reference to it held. 053 * 054 * @param list the list to wrap 055 * @throws NullPointerException if the list it null 056 */ 057 public LoopingListIterator(final List<E> list) { 058 if (list == null) { 059 throw new NullPointerException("The list must not be null"); 060 } 061 this.list = list; 062 _reset(); 063 } 064 065 /** 066 * Returns whether this iterator has any more elements. 067 * <p> 068 * Returns false only if the list originally had zero elements, or 069 * all elements have been {@link #remove removed}. 070 * 071 * @return <code>true</code> if there are more elements 072 */ 073 public boolean hasNext() { 074 return !list.isEmpty(); 075 } 076 077 /** 078 * Returns the next object in the list. 079 * <p> 080 * If at the end of the list, returns the first element. 081 * 082 * @return the object after the last element returned 083 * @throws NoSuchElementException if there are no elements in the list 084 */ 085 public E next() { 086 if (list.isEmpty()) { 087 throw new NoSuchElementException( 088 "There are no elements for this iterator to loop on"); 089 } 090 if (iterator.hasNext() == false) { 091 reset(); 092 } 093 return iterator.next(); 094 } 095 096 /** 097 * Returns the index of the element that would be returned by a 098 * subsequent call to {@link #next}. 099 * <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 _reset(); 239 } 240 241 private 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}