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