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