1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.collections4.iterators;
18
19 import java.util.ListIterator;
20 import java.util.NoSuchElementException;
21
22 import org.apache.commons.collections4.Predicate;
23
24 /**
25 * Decorates another {@link ListIterator} using a predicate to filter elements.
26 * <p>
27 * This iterator decorates the underlying iterator, only allowing through
28 * those elements that match the specified {@link Predicate Predicate}.
29 * </p>
30 *
31 * @param <E> the type of elements returned by this iterator.
32 * @since 2.0
33 */
34 public class FilterListIterator<E> implements ListIterator<E> {
35
36 /** The iterator being used */
37 private ListIterator<? extends E> iterator;
38
39 /** The predicate being used */
40 private Predicate<? super E> predicate;
41
42 /**
43 * The value of the next (matching) object, when
44 * {@link #nextObjectSet} is true.
45 */
46 private E nextObject;
47
48 /**
49 * Whether or not the {@link #nextObject} has been set
50 * (possibly to {@code null}).
51 */
52 private boolean nextObjectSet;
53
54 /**
55 * The value of the previous (matching) object, when
56 * {@link #previousObjectSet} is true.
57 */
58 private E previousObject;
59
60 /**
61 * Whether or not the {@link #previousObject} has been set
62 * (possibly to {@code null}).
63 */
64 private boolean previousObjectSet;
65
66 /**
67 * The index of the element that would be returned by {@link #next}.
68 */
69 private int nextIndex;
70
71 /**
72 * Constructs a new {@code FilterListIterator} that will not function
73 * until {@link #setListIterator(ListIterator) setListIterator}
74 * and {@link #setPredicate(Predicate) setPredicate} are invoked.
75 */
76 public FilterListIterator() {
77 }
78
79 /**
80 * Constructs a new {@code FilterListIterator} that will not
81 * function until {@link #setPredicate(Predicate) setPredicate} is invoked.
82 *
83 * @param iterator the iterator to use
84 */
85 public FilterListIterator(final ListIterator<? extends E> iterator) {
86 this.iterator = iterator;
87 }
88
89 /**
90 * Constructs a new {@code FilterListIterator}.
91 *
92 * @param iterator the iterator to use
93 * @param predicate the predicate to use
94 */
95 public FilterListIterator(final ListIterator<? extends E> iterator, final Predicate<? super E> predicate) {
96 this.iterator = iterator;
97 this.predicate = predicate;
98 }
99
100 /**
101 * Constructs a new {@code FilterListIterator} that will not function
102 * until {@link #setListIterator(ListIterator) setListIterator} is invoked.
103 *
104 * @param predicate the predicate to use.
105 */
106 public FilterListIterator(final Predicate<? super E> predicate) {
107 this.predicate = predicate;
108 }
109
110 /**
111 * Not supported.
112 * @param o the element to insert
113 */
114 @Override
115 public void add(final E o) {
116 throw new UnsupportedOperationException("FilterListIterator.add(Object) is not supported.");
117 }
118
119 private void clearNextObject() {
120 nextObject = null;
121 nextObjectSet = false;
122 }
123
124 private void clearPreviousObject() {
125 previousObject = null;
126 previousObjectSet = false;
127 }
128
129 /**
130 * Gets the iterator this iterator is using.
131 *
132 * @return the iterator.
133 */
134 public ListIterator<? extends E> getListIterator() {
135 return iterator;
136 }
137
138 /**
139 * Gets the predicate this iterator is using.
140 *
141 * @return the predicate.
142 */
143 public Predicate<? super E> getPredicate() {
144 return predicate;
145 }
146
147 @Override
148 public boolean hasNext() {
149 return nextObjectSet || setNextObject();
150 }
151
152 @Override
153 public boolean hasPrevious() {
154 return previousObjectSet || setPreviousObject();
155 }
156
157 @Override
158 public E next() {
159 if (!nextObjectSet && !setNextObject()) {
160 throw new NoSuchElementException();
161 }
162 nextIndex++;
163 final E temp = nextObject;
164 clearNextObject();
165 return temp;
166 }
167
168 @Override
169 public int nextIndex() {
170 return nextIndex;
171 }
172
173 @Override
174 public E previous() {
175 if (!previousObjectSet && !setPreviousObject()) {
176 throw new NoSuchElementException();
177 }
178 nextIndex--;
179 final E temp = previousObject;
180 clearPreviousObject();
181 return temp;
182 }
183
184 @Override
185 public int previousIndex() {
186 return nextIndex - 1;
187 }
188
189 /** Not supported. */
190 @Override
191 public void remove() {
192 throw new UnsupportedOperationException("FilterListIterator.remove() is not supported.");
193 }
194
195 /**
196 * Not supported.
197 * @param ignored the element with which to replace the last element returned by
198 * {@code next} or {@code previous}
199 */
200 @Override
201 public void set(final E ignored) {
202 throw new UnsupportedOperationException("FilterListIterator.set(Object) is not supported.");
203 }
204
205 /**
206 * Sets the iterator for this iterator to use.
207 * If iteration has started, this effectively resets the iterator.
208 *
209 * @param iterator the iterator to use
210 */
211 public void setListIterator(final ListIterator<? extends E> iterator) {
212 this.iterator = iterator;
213 }
214
215 private boolean setNextObject() {
216 // if previousObjectSet,
217 // then we've walked back one step in the
218 // underlying list (due to a hasPrevious() call)
219 // so skip ahead one matching object
220 if (previousObjectSet) {
221 clearPreviousObject();
222 if (!setNextObject()) {
223 return false;
224 }
225 clearNextObject();
226 }
227
228 if (iterator == null) {
229 return false;
230 }
231 while (iterator.hasNext()) {
232 final E object = iterator.next();
233 if (predicate.test(object)) {
234 nextObject = object;
235 nextObjectSet = true;
236 return true;
237 }
238 }
239 return false;
240 }
241
242 /**
243 * Sets the predicate this the iterator to use.
244 *
245 * @param predicate the transformer to use
246 */
247 public void setPredicate(final Predicate<? super E> predicate) {
248 this.predicate = predicate;
249 }
250
251 private boolean setPreviousObject() {
252 // if nextObjectSet,
253 // then we've walked back one step in the
254 // underlying list (due to a hasNext() call)
255 // so skip ahead one matching object
256 if (nextObjectSet) {
257 clearNextObject();
258 if (!setPreviousObject()) {
259 return false;
260 }
261 clearPreviousObject();
262 }
263
264 if (iterator == null) {
265 return false;
266 }
267 while (iterator.hasPrevious()) {
268 final E object = iterator.previous();
269 if (predicate.test(object)) {
270 previousObject = object;
271 previousObjectSet = true;
272 return true;
273 }
274 }
275 return false;
276 }
277
278 }