    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}