001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.iterators;
018
019import java.util.ListIterator;
020import java.util.NoSuchElementException;
021
022import org.apache.commons.collections4.Predicate;
023
024/**
025 * Decorates another {@link ListIterator} using a predicate to filter elements.
026 * <p>
027 * This iterator decorates the underlying iterator, only allowing through
028 * those elements that match the specified {@link Predicate Predicate}.
029 *
030 * @since 2.0
031 * @version $Id: FilterListIterator.html 972421 2015-11-14 20:00:04Z tn $
032 */
033public class FilterListIterator<E> implements ListIterator<E> {
034
035    /** The iterator being used */
036    private ListIterator<? extends E> iterator;
037
038    /** The predicate being used */
039    private Predicate<? super E> predicate;
040
041    /**
042     * The value of the next (matching) object, when
043     * {@link #nextObjectSet} is true.
044     */
045    private E nextObject;
046
047    /**
048     * Whether or not the {@link #nextObject} has been set
049     * (possibly to <code>null</code>).
050     */
051    private boolean nextObjectSet = false;
052
053    /**
054     * The value of the previous (matching) object, when
055     * {@link #previousObjectSet} is true.
056     */
057    private E previousObject;
058
059    /**
060     * Whether or not the {@link #previousObject} has been set
061     * (possibly to <code>null</code>).
062     */
063    private boolean previousObjectSet = false;
064
065    /**
066     * The index of the element that would be returned by {@link #next}.
067     */
068    private int nextIndex = 0;
069
070    //-----------------------------------------------------------------------
071    /**
072     * Constructs a new <code>FilterListIterator</code> that will not function
073     * until {@link #setListIterator(ListIterator) setListIterator}
074     * and {@link #setPredicate(Predicate) setPredicate} are invoked.
075     */
076    public FilterListIterator() {
077        super();
078    }
079
080    /**
081     * Constructs a new <code>FilterListIterator</code> that will not
082     * function until {@link #setPredicate(Predicate) setPredicate} is invoked.
083     *
084     * @param iterator  the iterator to use
085     */
086    public FilterListIterator(final ListIterator<? extends E> iterator ) {
087        super();
088        this.iterator = iterator;
089    }
090
091    /**
092     * Constructs a new <code>FilterListIterator</code>.
093     *
094     * @param iterator  the iterator to use
095     * @param predicate  the predicate to use
096     */
097    public FilterListIterator(final ListIterator<? extends E> iterator, final Predicate<? super E> predicate) {
098        super();
099        this.iterator = iterator;
100        this.predicate = predicate;
101    }
102
103    /**
104     * Constructs a new <code>FilterListIterator</code> that will not function
105     * until {@link #setListIterator(ListIterator) setListIterator} is invoked.
106     *
107     * @param predicate  the predicate to use.
108     */
109    public FilterListIterator(final Predicate<? super E> predicate) {
110        super();
111        this.predicate = predicate;
112    }
113
114    //-----------------------------------------------------------------------
115    /** Not supported. */
116    public void add(final E o) {
117        throw new UnsupportedOperationException("FilterListIterator.add(Object) is not supported.");
118    }
119
120    public boolean hasNext() {
121        return nextObjectSet || setNextObject();
122    }
123
124    public boolean hasPrevious() {
125        return previousObjectSet || setPreviousObject();
126    }
127
128    public E next() {
129        if (!nextObjectSet) {
130            if (!setNextObject()) {
131                throw new NoSuchElementException();
132            }
133        }
134        nextIndex++;
135        final E temp = nextObject;
136        clearNextObject();
137        return temp;
138    }
139
140    public int nextIndex() {
141        return nextIndex;
142    }
143
144    public E previous() {
145        if (!previousObjectSet) {
146            if (!setPreviousObject()) {
147                throw new NoSuchElementException();
148            }
149        }
150        nextIndex--;
151        final E temp = previousObject;
152        clearPreviousObject();
153        return temp;
154    }
155
156    public int previousIndex() {
157        return nextIndex-1;
158    }
159
160    /** Not supported. */
161    public void remove() {
162        throw new UnsupportedOperationException("FilterListIterator.remove() is not supported.");
163    }
164
165    /** Not supported. */
166    public void set(final E o) {
167        throw new UnsupportedOperationException("FilterListIterator.set(Object) is not supported.");
168    }
169
170    //-----------------------------------------------------------------------
171    /**
172     * Gets the iterator this iterator is using.
173     *
174     * @return the iterator.
175     */
176    public ListIterator<? extends E> getListIterator() {
177        return iterator;
178    }
179
180    /**
181     * Sets the iterator for this iterator to use.
182     * If iteration has started, this effectively resets the iterator.
183     *
184     * @param iterator  the iterator to use
185     */
186    public void setListIterator(final ListIterator<? extends E> iterator) {
187        this.iterator = iterator;
188    }
189
190    //-----------------------------------------------------------------------
191    /**
192     * Gets the predicate this iterator is using.
193     *
194     * @return the predicate.
195     */
196    public Predicate<? super E> getPredicate() {
197        return predicate;
198    }
199
200    /**
201     * Sets the predicate this the iterator to use.
202     *
203     * @param predicate  the transformer to use
204     */
205    public void setPredicate(final Predicate<? super E> predicate) {
206        this.predicate = predicate;
207    }
208
209    //-----------------------------------------------------------------------
210    private void clearNextObject() {
211        nextObject = null;
212        nextObjectSet = false;
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.evaluate(object)) {
234                nextObject = object;
235                nextObjectSet = true;
236                return true;
237            }
238        }
239        return false;
240    }
241
242    private void clearPreviousObject() {
243        previousObject = null;
244        previousObjectSet = false;
245    }
246
247    private boolean setPreviousObject() {
248        // if nextObjectSet,
249        // then we've walked back one step in the
250        // underlying list (due to a hasNext() call)
251        // so skip ahead one matching object
252        if (nextObjectSet) {
253            clearNextObject();
254            if (!setPreviousObject()) {
255                return false;
256            }
257            clearPreviousObject();
258        }
259
260        if (iterator == null) {
261            return false;
262        }
263        while (iterator.hasPrevious()) {
264            final E object = iterator.previous();
265            if (predicate.evaluate(object)) {
266                previousObject = object;
267                previousObjectSet = true;
268                return true;
269            }
270        }
271        return false;
272    }
273
274}