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