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.Iterator;
20 import java.util.NoSuchElementException;
21
22 import org.apache.commons.collections4.Predicate;
23
24 /**
25 * Decorates another {@link Iterator} 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 *
30 * @param <E> the type of elements returned by this iterator.
31 * @since 1.0
32 */
33 public class FilterIterator<E> implements Iterator<E> {
34
35 /** The iterator being used */
36 private Iterator<? extends E> iterator;
37 /** The predicate being used */
38 private Predicate<? super E> predicate;
39 /** The next object in the iteration */
40 private E nextObject;
41 /** Whether the next object has been calculated yet */
42 private boolean nextObjectSet;
43
44 /**
45 * Constructs a new {@code FilterIterator} that will not function
46 * until {@link #setIterator(Iterator) setIterator} is invoked.
47 */
48 public FilterIterator() {
49 }
50
51 /**
52 * Constructs a new {@code FilterIterator} that will not function
53 * until {@link #setPredicate(Predicate) setPredicate} is invoked.
54 *
55 * @param iterator the iterator to use
56 */
57 public FilterIterator(final Iterator<? extends E> iterator) {
58 this.iterator = iterator;
59 }
60
61 /**
62 * Constructs a new {@code FilterIterator} that will use the
63 * given iterator and predicate.
64 *
65 * @param iterator the iterator to use
66 * @param predicate the predicate to use
67 */
68 public FilterIterator(final Iterator<? extends E> iterator, final Predicate<? super E> predicate) {
69 this.iterator = iterator;
70 this.predicate = predicate;
71 }
72
73 /**
74 * Gets the iterator this iterator is using.
75 *
76 * @return the iterator
77 */
78 public Iterator<? extends E> getIterator() {
79 return iterator;
80 }
81
82 /**
83 * Gets the predicate this iterator is using.
84 *
85 * @return the predicate
86 */
87 public Predicate<? super E> getPredicate() {
88 return predicate;
89 }
90
91 /**
92 * Returns true if the underlying iterator contains an object that
93 * matches the predicate.
94 *
95 * @return true if there is another object that matches the predicate
96 * @throws NullPointerException if either the iterator or predicate are null
97 */
98 @Override
99 public boolean hasNext() {
100 return nextObjectSet || setNextObject();
101 }
102
103 /**
104 * Returns the next object that matches the predicate.
105 *
106 * @return the next object which matches the given predicate
107 * @throws NullPointerException if either the iterator or predicate are null
108 * @throws NoSuchElementException if there are no more elements that
109 * match the predicate
110 */
111 @Override
112 public E next() {
113 if (!nextObjectSet && !setNextObject()) {
114 throw new NoSuchElementException();
115 }
116 nextObjectSet = false;
117 return nextObject;
118 }
119
120 /**
121 * Removes from the underlying collection of the base iterator the last
122 * element returned by this iterator.
123 * This method can only be called
124 * if {@code next()} was called, but not after
125 * {@code hasNext()}, because the {@code hasNext()} call
126 * changes the base iterator.
127 *
128 * @throws IllegalStateException if {@code hasNext()} has already
129 * been called.
130 */
131 @Override
132 public void remove() {
133 if (nextObjectSet) {
134 throw new IllegalStateException("remove() cannot be called");
135 }
136 iterator.remove();
137 }
138
139 /**
140 * Sets the iterator for this iterator to use.
141 * If iteration has started, this effectively resets the iterator.
142 *
143 * @param iterator the iterator to use
144 */
145 public void setIterator(final Iterator<? extends E> iterator) {
146 this.iterator = iterator;
147 nextObject = null;
148 nextObjectSet = false;
149 }
150
151 /**
152 * Sets nextObject to the next object. If there are no more
153 * objects, then return false. Otherwise, return true.
154 */
155 private boolean setNextObject() {
156 while (iterator.hasNext()) {
157 final E object = iterator.next();
158 if (predicate.evaluate(object)) {
159 nextObject = object;
160 nextObjectSet = true;
161 return true;
162 }
163 }
164 return false;
165 }
166
167 /**
168 * Sets the predicate this the iterator to use.
169 *
170 * @param predicate the predicate to use
171 */
172 public void setPredicate(final Predicate<? super E> predicate) {
173 this.predicate = predicate;
174 nextObject = null;
175 nextObjectSet = false;
176 }
177
178 }