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