    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}