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 * https://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.text.MessageFormat; 020import java.util.ArrayList; 021import java.util.Iterator; 022import java.util.List; 023import java.util.ListIterator; 024import java.util.NoSuchElementException; 025import java.util.Objects; 026 027import org.apache.commons.collections4.ResettableIterator; 028import org.apache.commons.collections4.ResettableListIterator; 029 030/** 031 * Converts an {@link Iterator} into a {@link ResettableListIterator}. 032 * For plain {@code Iterator}s this is accomplished by caching the returned 033 * elements. This class can also be used to simply add 034 * {@link ResettableIterator ResettableIterator} 035 * functionality to a given {@link ListIterator}. 036 * <p> 037 * The {@code ListIterator} interface has additional useful methods 038 * for navigation - {@code previous()} and the index methods. 039 * This class allows a regular {@code Iterator} to behave as a 040 * {@code ListIterator}. It achieves this by building a list internally 041 * of as the underlying iterator is traversed. 042 * </p> 043 * <p> 044 * The optional operations of {@code ListIterator} are not supported for plain {@code Iterator}s. 045 * </p> 046 * <p> 047 * This class implements ResettableListIterator from Commons Collections 3.2. 048 * </p> 049 * 050 * @param <E> The type of elements in this iterator. 051 * @since 2.1 052 */ 053public class ListIteratorWrapper<E> implements ResettableListIterator<E> { 054 055 /** Message used when set or add are called. */ 056 private static final String UNSUPPORTED_OPERATION_MESSAGE = 057 "ListIteratorWrapper does not support optional operations of ListIterator."; 058 059 /** Message used when set or add are called. */ 060 private static final String CANNOT_REMOVE_MESSAGE = "Cannot remove element at index {0}."; 061 062 /** The underlying iterator being decorated. */ 063 private final Iterator<? extends E> iterator; 064 065 /** The list being used to cache the iterator. */ 066 private final List<E> list = new ArrayList<>(); 067 068 /** The current index of this iterator. */ 069 private int currentIndex; 070 071 /** The current index of the wrapped iterator. */ 072 private int wrappedIteratorIndex; 073 074 /** Recall whether the wrapped iterator's "cursor" is in such a state as to allow remove() to be called */ 075 private boolean removeState; 076 077 /** 078 * Constructs a new {@code ListIteratorWrapper} that will wrap 079 * the given iterator. 080 * 081 * @param iterator The iterator to wrap 082 * @throws NullPointerException if the iterator is null 083 */ 084 public ListIteratorWrapper(final Iterator<? extends E> iterator) { 085 this.iterator = Objects.requireNonNull(iterator, "iterator"); 086 } 087 088 /** 089 * Throws {@link UnsupportedOperationException} 090 * unless the underlying {@code Iterator} is a {@code ListIterator}. 091 * 092 * @param obj The object to add 093 * @throws UnsupportedOperationException if the underlying iterator is not of 094 * type {@link ListIterator} 095 */ 096 @Override 097 public void add(final E obj) throws UnsupportedOperationException { 098 if (iterator instanceof ListIterator) { 099 @SuppressWarnings("unchecked") 100 final ListIterator<E> li = (ListIterator<E>) iterator; 101 li.add(obj); 102 return; 103 } 104 throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE); 105 } 106 107 /** 108 * Returns true if there are more elements in the iterator. 109 * 110 * @return true if there are more elements 111 */ 112 @Override 113 public boolean hasNext() { 114 if (currentIndex == wrappedIteratorIndex || iterator instanceof ListIterator) { 115 return iterator.hasNext(); 116 } 117 return true; 118 } 119 120 /** 121 * Returns true if there are previous elements in the iterator. 122 * 123 * @return true if there are previous elements 124 */ 125 @Override 126 public boolean hasPrevious() { 127 if (iterator instanceof ListIterator) { 128 final ListIterator<?> li = (ListIterator<?>) iterator; 129 return li.hasPrevious(); 130 } 131 return currentIndex > 0; 132 } 133 134 /** 135 * Returns the next element from the iterator. 136 * 137 * @return The next element from the iterator 138 * @throws NoSuchElementException if there are no more elements 139 */ 140 @Override 141 public E next() throws NoSuchElementException { 142 if (iterator instanceof ListIterator) { 143 return iterator.next(); 144 } 145 146 if (currentIndex < wrappedIteratorIndex) { 147 ++currentIndex; 148 return list.get(currentIndex - 1); 149 } 150 151 final E retval = iterator.next(); 152 list.add(retval); 153 ++currentIndex; 154 ++wrappedIteratorIndex; 155 removeState = true; 156 return retval; 157 } 158 159 /** 160 * Returns the index of the next element. 161 * 162 * @return The index of the next element 163 */ 164 @Override 165 public int nextIndex() { 166 if (iterator instanceof ListIterator) { 167 final ListIterator<?> li = (ListIterator<?>) iterator; 168 return li.nextIndex(); 169 } 170 return currentIndex; 171 } 172 173 /** 174 * Returns the previous element. 175 * 176 * @return The previous element 177 * @throws NoSuchElementException if there are no previous elements 178 */ 179 @Override 180 public E previous() throws NoSuchElementException { 181 if (iterator instanceof ListIterator) { 182 @SuppressWarnings("unchecked") 183 final ListIterator<E> li = (ListIterator<E>) iterator; 184 return li.previous(); 185 } 186 187 if (currentIndex == 0) { 188 throw new NoSuchElementException(); 189 } 190 removeState = wrappedIteratorIndex == currentIndex; 191 return list.get(--currentIndex); 192 } 193 194 /** 195 * Returns the index of the previous element. 196 * 197 * @return the index of the previous element 198 */ 199 @Override 200 public int previousIndex() { 201 if (iterator instanceof ListIterator) { 202 final ListIterator<?> li = (ListIterator<?>) iterator; 203 return li.previousIndex(); 204 } 205 return currentIndex - 1; 206 } 207 208 /** 209 * Removes the last element that was returned by {@link #next()} or {@link #previous()} from the underlying collection. 210 * 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. 211 * 212 * @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} 213 */ 214 @Override 215 public void remove() throws IllegalStateException { 216 if (iterator instanceof ListIterator) { 217 iterator.remove(); 218 return; 219 } 220 int removeIndex = currentIndex; 221 if (currentIndex == wrappedIteratorIndex) { 222 --removeIndex; 223 } 224 if (!removeState || wrappedIteratorIndex - currentIndex > 1) { 225 throw new IllegalStateException(MessageFormat.format(CANNOT_REMOVE_MESSAGE, Integer.valueOf(removeIndex))); 226 } 227 iterator.remove(); 228 list.remove(removeIndex); 229 currentIndex = removeIndex; 230 wrappedIteratorIndex--; 231 removeState = false; 232 } 233 234 /** 235 * Resets this iterator back to the position at which the iterator 236 * was created. 237 * 238 * @since 3.2 239 */ 240 @Override 241 public void reset() { 242 if (iterator instanceof ListIterator) { 243 final ListIterator<?> li = (ListIterator<?>) iterator; 244 while (li.previousIndex() >= 0) { 245 li.previous(); 246 } 247 return; 248 } 249 currentIndex = 0; 250 } 251 252 /** 253 * Throws {@link UnsupportedOperationException} 254 * unless the underlying {@code Iterator} is a {@code ListIterator}. 255 * 256 * @param obj The object to set 257 * @throws UnsupportedOperationException if the underlying iterator is not of 258 * type {@link ListIterator} 259 */ 260 @Override 261 public void set(final E obj) throws UnsupportedOperationException { 262 if (iterator instanceof ListIterator) { 263 @SuppressWarnings("unchecked") 264 final ListIterator<E> li = (ListIterator<E>) iterator; 265 li.set(obj); 266 return; 267 } 268 throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE); 269 } 270 271}