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 * @param <E> the type of elements returned by this iterator.
031 * @since 2.0
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}).
050     */
051    private boolean nextObjectSet;
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}).
062     */
063    private boolean previousObjectSet;
064
065    /**
066     * The index of the element that would be returned by {@link #next}.
067     */
068    private int nextIndex;
069
070    /**
071     * Constructs a new {@code FilterListIterator} that will not function
072     * until {@link #setListIterator(ListIterator) setListIterator}
073     * and {@link #setPredicate(Predicate) setPredicate} are invoked.
074     */
075    public FilterListIterator() {
076    }
077
078    /**
079     * Constructs a new {@code FilterListIterator} that will not
080     * function until {@link #setPredicate(Predicate) setPredicate} is invoked.
081     *
082     * @param iterator  the iterator to use
083     */
084    public FilterListIterator(final ListIterator<? extends E> iterator ) {
085        this.iterator = iterator;
086    }
087
088    /**
089     * Constructs a new {@code FilterListIterator}.
090     *
091     * @param iterator  the iterator to use
092     * @param predicate  the predicate to use
093     */
094    public FilterListIterator(final ListIterator<? extends E> iterator, final Predicate<? super E> predicate) {
095        this.iterator = iterator;
096        this.predicate = predicate;
097    }
098
099    /**
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}