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 * @version $Id: ListIteratorWrapper.html 972421 2015-11-14 20:00:04Z tn $ 047 */ 048public class ListIteratorWrapper<E> implements ResettableListIterator<E> { 049 050 /** Message used when set or add are called. */ 051 private static final String UNSUPPORTED_OPERATION_MESSAGE = 052 "ListIteratorWrapper does not support optional operations of ListIterator."; 053 054 /** Message used when set or add are called. */ 055 private static final String CANNOT_REMOVE_MESSAGE = "Cannot remove element at index {0}."; 056 057 /** The underlying iterator being decorated. */ 058 private final Iterator<? extends E> iterator; 059 /** The list being used to cache the iterator. */ 060 private final List<E> list = new ArrayList<E>(); 061 062 /** The current index of this iterator. */ 063 private int currentIndex = 0; 064 /** The current index of the wrapped iterator. */ 065 private int wrappedIteratorIndex = 0; 066 /** recall whether the wrapped iterator's "cursor" is in such a state as to allow remove() to be called */ 067 private boolean removeState; 068 069 // Constructor 070 //------------------------------------------------------------------------- 071 /** 072 * Constructs a new <code>ListIteratorWrapper</code> that will wrap 073 * the given iterator. 074 * 075 * @param iterator the iterator to wrap 076 * @throws NullPointerException if the iterator is null 077 */ 078 public ListIteratorWrapper(final Iterator<? extends E> iterator) { 079 super(); 080 if (iterator == null) { 081 throw new NullPointerException("Iterator must not be null"); 082 } 083 this.iterator = iterator; 084 } 085 086 // ListIterator interface 087 //------------------------------------------------------------------------- 088 /** 089 * Throws {@link UnsupportedOperationException} 090 * unless the underlying <code>Iterator</code> is a <code>ListIterator</code>. 091 * 092 * @param obj the object to add 093 * @throws UnsupportedOperationException if the underlying iterator is not of 094 * type {@link ListIterator} 095 */ 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 public boolean hasNext() { 112 if (currentIndex == wrappedIteratorIndex || iterator instanceof ListIterator) { 113 return iterator.hasNext(); 114 } 115 return true; 116 } 117 118 /** 119 * Returns true if there are previous elements in the iterator. 120 * 121 * @return true if there are previous elements 122 */ 123 public boolean hasPrevious() { 124 if (iterator instanceof ListIterator) { 125 final ListIterator<?> li = (ListIterator<?>) iterator; 126 return li.hasPrevious(); 127 } 128 return currentIndex > 0; 129 } 130 131 /** 132 * Returns the next element from the iterator. 133 * 134 * @return the next element from the iterator 135 * @throws NoSuchElementException if there are no more elements 136 */ 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 public int nextIndex() { 161 if (iterator instanceof ListIterator) { 162 final ListIterator<?> li = (ListIterator<?>) iterator; 163 return li.nextIndex(); 164 } 165 return currentIndex; 166 } 167 168 /** 169 * Returns the the previous element. 170 * 171 * @return the previous element 172 * @throws NoSuchElementException if there are no previous elements 173 */ 174 public E previous() throws NoSuchElementException { 175 if (iterator instanceof ListIterator) { 176 @SuppressWarnings("unchecked") 177 final ListIterator<E> li = (ListIterator<E>) iterator; 178 return li.previous(); 179 } 180 181 if (currentIndex == 0) { 182 throw new NoSuchElementException(); 183 } 184 removeState = wrappedIteratorIndex == currentIndex; 185 return list.get(--currentIndex); 186 } 187 188 /** 189 * Returns the index of the previous element. 190 * 191 * @return the index of the previous element 192 */ 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 * Throws {@link UnsupportedOperationException} if {@link #previous()} has ever been called. 203 * 204 * @throws UnsupportedOperationException always 205 */ 206 public void remove() throws UnsupportedOperationException { 207 if (iterator instanceof ListIterator) { 208 iterator.remove(); 209 return; 210 } 211 int removeIndex = currentIndex; 212 if (currentIndex == wrappedIteratorIndex) { 213 --removeIndex; 214 } 215 if (!removeState || wrappedIteratorIndex - currentIndex > 1) { 216 throw new IllegalStateException(MessageFormat.format(CANNOT_REMOVE_MESSAGE, Integer.valueOf(removeIndex))); 217 } 218 iterator.remove(); 219 list.remove(removeIndex); 220 currentIndex = removeIndex; 221 wrappedIteratorIndex--; 222 removeState = false; 223 } 224 225 /** 226 * Throws {@link UnsupportedOperationException} 227 * unless the underlying <code>Iterator</code> is a <code>ListIterator</code>. 228 * 229 * @param obj the object to set 230 * @throws UnsupportedOperationException if the underlying iterator is not of 231 * type {@link ListIterator} 232 */ 233 public void set(final E obj) throws UnsupportedOperationException { 234 if (iterator instanceof ListIterator) { 235 @SuppressWarnings("unchecked") 236 final ListIterator<E> li = (ListIterator<E>) iterator; 237 li.set(obj); 238 return; 239 } 240 throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE); 241 } 242 243 // ResettableIterator interface 244 //------------------------------------------------------------------------- 245 /** 246 * Resets this iterator back to the position at which the iterator 247 * was created. 248 * 249 * @since 3.2 250 */ 251 public void reset() { 252 if (iterator instanceof ListIterator) { 253 final ListIterator<?> li = (ListIterator<?>) iterator; 254 while (li.previousIndex() >= 0) { 255 li.previous(); 256 } 257 return; 258 } 259 currentIndex = 0; 260 } 261 262}