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