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   * </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 }