View Javadoc
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 }