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.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   *
30   * @param <E> the type of elements returned by this iterator.
31   * @since 2.0
32   */
33  public class FilterListIterator<E> implements ListIterator<E> {
34  
35      /** The iterator being used */
36      private ListIterator<? extends E> iterator;
37  
38      /** The predicate being used */
39      private Predicate<? super E> predicate;
40  
41      /**
42       * The value of the next (matching) object, when
43       * {@link #nextObjectSet} is true.
44       */
45      private E nextObject;
46  
47      /**
48       * Whether or not the {@link #nextObject} has been set
49       * (possibly to {@code null}).
50       */
51      private boolean nextObjectSet;
52  
53      /**
54       * The value of the previous (matching) object, when
55       * {@link #previousObjectSet} is true.
56       */
57      private E previousObject;
58  
59      /**
60       * Whether or not the {@link #previousObject} has been set
61       * (possibly to {@code null}).
62       */
63      private boolean previousObjectSet;
64  
65      /**
66       * The index of the element that would be returned by {@link #next}.
67       */
68      private int nextIndex;
69  
70      /**
71       * Constructs a new {@code FilterListIterator} that will not function
72       * until {@link #setListIterator(ListIterator) setListIterator}
73       * and {@link #setPredicate(Predicate) setPredicate} are invoked.
74       */
75      public FilterListIterator() {
76      }
77  
78      /**
79       * Constructs a new {@code FilterListIterator} that will not
80       * function until {@link #setPredicate(Predicate) setPredicate} is invoked.
81       *
82       * @param iterator  the iterator to use
83       */
84      public FilterListIterator(final ListIterator<? extends E> iterator ) {
85          this.iterator = iterator;
86      }
87  
88      /**
89       * Constructs a new {@code FilterListIterator}.
90       *
91       * @param iterator  the iterator to use
92       * @param predicate  the predicate to use
93       */
94      public FilterListIterator(final ListIterator<? extends E> iterator, final Predicate<? super E> predicate) {
95          this.iterator = iterator;
96          this.predicate = predicate;
97      }
98  
99      /**
100      * Constructs a new {@code FilterListIterator} that will not function
101      * until {@link #setListIterator(ListIterator) setListIterator} is invoked.
102      *
103      * @param predicate  the predicate to use.
104      */
105     public FilterListIterator(final Predicate<? super E> predicate) {
106         this.predicate = predicate;
107     }
108 
109     /**
110      * Not supported.
111      * @param o the element to insert
112      */
113     @Override
114     public void add(final E o) {
115         throw new UnsupportedOperationException("FilterListIterator.add(Object) is not supported.");
116     }
117 
118     private void clearNextObject() {
119         nextObject = null;
120         nextObjectSet = false;
121     }
122 
123     private void clearPreviousObject() {
124         previousObject = null;
125         previousObjectSet = false;
126     }
127 
128     /**
129      * Gets the iterator this iterator is using.
130      *
131      * @return the iterator.
132      */
133     public ListIterator<? extends E> getListIterator() {
134         return iterator;
135     }
136 
137     /**
138      * Gets the predicate this iterator is using.
139      *
140      * @return the predicate.
141      */
142     public Predicate<? super E> getPredicate() {
143         return predicate;
144     }
145 
146     @Override
147     public boolean hasNext() {
148         return nextObjectSet || setNextObject();
149     }
150 
151     @Override
152     public boolean hasPrevious() {
153         return previousObjectSet || setPreviousObject();
154     }
155 
156     @Override
157     public E next() {
158         if (!nextObjectSet && !setNextObject()) {
159             throw new NoSuchElementException();
160         }
161         nextIndex++;
162         final E temp = nextObject;
163         clearNextObject();
164         return temp;
165     }
166 
167     @Override
168     public int nextIndex() {
169         return nextIndex;
170     }
171 
172     @Override
173     public E previous() {
174         if (!previousObjectSet && !setPreviousObject()) {
175             throw new NoSuchElementException();
176         }
177         nextIndex--;
178         final E temp = previousObject;
179         clearPreviousObject();
180         return temp;
181     }
182 
183     @Override
184     public int previousIndex() {
185         return nextIndex-1;
186     }
187 
188     /** Not supported. */
189     @Override
190     public void remove() {
191         throw new UnsupportedOperationException("FilterListIterator.remove() is not supported.");
192     }
193 
194     /**
195      * Not supported.
196      * @param o the element with which to replace the last element returned by
197      *          {@code next} or {@code previous}
198      */
199     @Override
200     public void set(final E o) {
201         throw new UnsupportedOperationException("FilterListIterator.set(Object) is not supported.");
202     }
203 
204     /**
205      * Sets the iterator for this iterator to use.
206      * If iteration has started, this effectively resets the iterator.
207      *
208      * @param iterator  the iterator to use
209      */
210     public void setListIterator(final ListIterator<? extends E> iterator) {
211         this.iterator = iterator;
212     }
213 
214     private boolean setNextObject() {
215         // if previousObjectSet,
216         // then we've walked back one step in the
217         // underlying list (due to a hasPrevious() call)
218         // so skip ahead one matching object
219         if (previousObjectSet) {
220             clearPreviousObject();
221             if (!setNextObject()) {
222                 return false;
223             }
224             clearNextObject();
225         }
226 
227         if (iterator == null) {
228             return false;
229         }
230         while (iterator.hasNext()) {
231             final E object = iterator.next();
232             if (predicate.evaluate(object)) {
233                 nextObject = object;
234                 nextObjectSet = true;
235                 return true;
236             }
237         }
238         return false;
239     }
240 
241     /**
242      * Sets the predicate this the iterator to use.
243      *
244      * @param predicate  the transformer to use
245      */
246     public void setPredicate(final Predicate<? super E> predicate) {
247         this.predicate = predicate;
248     }
249 
250     private boolean setPreviousObject() {
251         // if nextObjectSet,
252         // then we've walked back one step in the
253         // underlying list (due to a hasNext() call)
254         // so skip ahead one matching object
255         if (nextObjectSet) {
256             clearNextObject();
257             if (!setPreviousObject()) {
258                 return false;
259             }
260             clearPreviousObject();
261         }
262 
263         if (iterator == null) {
264             return false;
265         }
266         while (iterator.hasPrevious()) {
267             final E object = iterator.previous();
268             if (predicate.evaluate(object)) {
269                 previousObject = object;
270                 previousObjectSet = true;
271                 return true;
272             }
273         }
274         return false;
275     }
276 
277 }