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 *      https://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 * </p>
030 *
031 * @param <E> The type of elements returned by this iterator.
032 * @since 2.0
033 */
034public class FilterListIterator<E> implements ListIterator<E> {
035
036    /** The iterator being used */
037    private ListIterator<? extends E> iterator;
038
039    /** The predicate being used */
040    private Predicate<? super E> predicate;
041
042    /**
043     * The value of the next (matching) object, when
044     * {@link #nextObjectSet} is true.
045     */
046    private E nextObject;
047
048    /**
049     * Whether or not the {@link #nextObject} has been set
050     * (possibly to {@code null}).
051     */
052    private boolean nextObjectSet;
053
054    /**
055     * The value of the previous (matching) object, when
056     * {@link #previousObjectSet} is true.
057     */
058    private E previousObject;
059
060    /**
061     * Whether or not the {@link #previousObject} has been set
062     * (possibly to {@code null}).
063     */
064    private boolean previousObjectSet;
065
066    /**
067     * The index of the element that would be returned by {@link #next}.
068     */
069    private int nextIndex;
070
071    /**
072     * Constructs a new {@code FilterListIterator} that will not function
073     * until {@link #setListIterator(ListIterator) setListIterator}
074     * and {@link #setPredicate(Predicate) setPredicate} are invoked.
075     */
076    public FilterListIterator() {
077    }
078
079    /**
080     * Constructs a new {@code FilterListIterator} that will not
081     * function until {@link #setPredicate(Predicate) setPredicate} is invoked.
082     *
083     * @param iterator  The iterator to use
084     */
085    public FilterListIterator(final ListIterator<? extends E> iterator) {
086        this.iterator = iterator;
087    }
088
089    /**
090     * Constructs a new {@code FilterListIterator}.
091     *
092     * @param iterator  The iterator to use
093     * @param predicate  The predicate to use
094     */
095    public FilterListIterator(final ListIterator<? extends E> iterator, final Predicate<? super E> predicate) {
096        this.iterator = iterator;
097        this.predicate = predicate;
098    }
099
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     * Always throws {@link UnsupportedOperationException}.
112     *
113     * @param o ignored.
114     * @throws UnsupportedOperationException Always thrown.
115     */
116    @Override
117    public void add(final E o) {
118        throw new UnsupportedOperationException("FilterListIterator.add(Object) is not supported.");
119    }
120
121    private void clearNextObject() {
122        nextObject = null;
123        nextObjectSet = false;
124    }
125
126    private void clearPreviousObject() {
127        previousObject = null;
128        previousObjectSet = false;
129    }
130
131    /**
132     * Gets the iterator this iterator is using.
133     *
134     * @return The iterator.
135     */
136    public ListIterator<? extends E> getListIterator() {
137        return iterator;
138    }
139
140    /**
141     * Gets the predicate this iterator is using.
142     *
143     * @return The predicate.
144     */
145    public Predicate<? super E> getPredicate() {
146        return predicate;
147    }
148
149    @Override
150    public boolean hasNext() {
151        return nextObjectSet || setNextObject();
152    }
153
154    @Override
155    public boolean hasPrevious() {
156        return previousObjectSet || setPreviousObject();
157    }
158
159    @Override
160    public E next() {
161        if (!nextObjectSet && !setNextObject()) {
162            throw new NoSuchElementException();
163        }
164        nextIndex++;
165        final E temp = nextObject;
166        clearNextObject();
167        return temp;
168    }
169
170    @Override
171    public int nextIndex() {
172        return nextIndex;
173    }
174
175    @Override
176    public E previous() {
177        if (!previousObjectSet && !setPreviousObject()) {
178            throw new NoSuchElementException();
179        }
180        nextIndex--;
181        final E temp = previousObject;
182        clearPreviousObject();
183        return temp;
184    }
185
186    @Override
187    public int previousIndex() {
188        return nextIndex - 1;
189    }
190
191    /**
192     * Always throws {@link UnsupportedOperationException}.
193     *
194     * @throws UnsupportedOperationException Always thrown.
195     */
196    @Override
197    public void remove() {
198        throw new UnsupportedOperationException("FilterListIterator.remove() is not supported.");
199    }
200
201    /**
202     * Always throws {@link UnsupportedOperationException}.
203     *
204     * @param e ignored.
205     * @throws UnsupportedOperationException Always thrown.
206     */
207    @Override
208    public void set(final E e) {
209        throw new UnsupportedOperationException("FilterListIterator.set(Object) is not supported.");
210    }
211
212    /**
213     * Sets the iterator for this iterator to use.
214     * If iteration has started, this effectively resets the iterator.
215     *
216     * @param iterator  The iterator to use
217     */
218    public void setListIterator(final ListIterator<? extends E> iterator) {
219        this.iterator = iterator;
220    }
221
222    private boolean setNextObject() {
223        // if previousObjectSet,
224        // then we've walked back one step in the
225        // underlying list (due to a hasPrevious() call)
226        // so skip ahead one matching object
227        if (previousObjectSet) {
228            clearPreviousObject();
229            if (!setNextObject()) {
230                return false;
231            }
232            clearNextObject();
233        }
234
235        if (iterator == null) {
236            return false;
237        }
238        while (iterator.hasNext()) {
239            final E object = iterator.next();
240            if (predicate.test(object)) {
241                nextObject = object;
242                nextObjectSet = true;
243                return true;
244            }
245        }
246        return false;
247    }
248
249    /**
250     * Sets the predicate this the iterator to use.
251     *
252     * @param predicate  The transformer to use
253     */
254    public void setPredicate(final Predicate<? super E> predicate) {
255        this.predicate = predicate;
256    }
257
258    private boolean setPreviousObject() {
259        // if nextObjectSet,
260        // then we've walked back one step in the
261        // underlying list (due to a hasNext() call)
262        // so skip ahead one matching object
263        if (nextObjectSet) {
264            clearNextObject();
265            if (!setPreviousObject()) {
266                return false;
267            }
268            clearPreviousObject();
269        }
270
271        if (iterator == null) {
272            return false;
273        }
274        while (iterator.hasPrevious()) {
275            final E object = iterator.previous();
276            if (predicate.test(object)) {
277                previousObject = object;
278                previousObjectSet = true;
279                return true;
280            }
281        }
282        return false;
283    }
284
285}