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