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