    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.Iterator;
    020import java.util.NoSuchElementException;
    021
    022import org.apache.commons.collections4.Predicate;
    023import org.apache.commons.collections4.functors.TruePredicate;
    024
    025/**
    026 * Decorates an {@link Iterator} using an optional predicate to filter elements.
    027 * <p>
    028 * This iterator decorates the underlying iterator, only allowing through
    029 * those elements that match the specified {@link Predicate Predicate}.
    030 * </p>
    031 *
    032 * @param <E> The type of elements returned by this iterator.
    033 * @since 1.0
    034 */
    035public class FilterIterator<E> implements IteratorOperations<E> {
    036
    037    /** The iterator to be filtered. */
    038    private Iterator<? extends E> iterator;
    039
    040    /** The predicate to filter elements. */
    041    private Predicate<? super E> predicate = TruePredicate.truePredicate();
    042
    043    /** The next object in the iteration. */
    044    private E nextObject;
    045
    046    /** Whether the next object has been calculated yet. */
    047    private boolean nextObjectSet;
    048
    049    /**
    050     * Constructs a new {@code FilterIterator} that will not function
    051     * until {@link #setIterator(Iterator) setIterator} is invoked.
    052     */
    053    public FilterIterator() {
    054    }
    055
    056    /**
    057     * Constructs a new {@code FilterIterator} that will not function
    058     * until {@link #setPredicate(Predicate) setPredicate} is invoked.
    059     *
    060     * @param iterator  The iterator to use
    061     */
    062    public FilterIterator(final Iterator<? extends E> iterator) {
    063        this.iterator = iterator;
    064    }
    065
    066    /**
    067     * Constructs a new {@code FilterIterator} that will use the
    068     * given iterator and predicate.
    069     *
    070     * @param iterator  The iterator to use
    071     * @param predicate  The predicate to use, null accepts all values.
    072     */
    073    public FilterIterator(final Iterator<? extends E> iterator, final Predicate<? super E> predicate) {
    074        this.iterator = iterator;
    075        this.predicate = safePredicate(predicate);
    076    }
    077
    078    /**
    079     * Gets the iterator this iterator is using.
    080     *
    081     * @return The underlying iterator.
    082     */
    083    public Iterator<? extends E> getIterator() {
    084        return iterator;
    085    }
    086
    087    /**
    088     * Gets the predicate this iterator is using.
    089     *
    090     * @return The filtering predicate.
    091     */
    092    public Predicate<? super E> getPredicate() {
    093        return predicate;
    094    }
    095
    096    /**
    097     * Returns true if the underlying iterator contains an object that
    098     * matches the predicate.
    099     *
    100     * @return true if there is another object that matches the predicate
    101     * @throws NullPointerException if either the iterator or predicate are null
    102     */
    103    @Override
    104    public boolean hasNext() {
    105        return nextObjectSet || setNextObject();
    106    }
    107
    108    /**
    109     * Returns the next object that matches the predicate.
    110     *
    111     * @return The next object which matches the given predicate
    112     * @throws NullPointerException if either the iterator or predicate are null
    113     * @throws NoSuchElementException if there are no more elements that
    114     *  match the predicate
    115     */
    116    @Override
    117    public E next() {
    118        if (!nextObjectSet && !setNextObject()) {
    119            throw new NoSuchElementException();
    120        }
    121        nextObjectSet = false;
    122        return nextObject;
    123    }
    124
    125    /**
    126     * Removes from the underlying collection of the base iterator the last
    127     * element returned by this iterator.
    128     * This method can only be called
    129     * if {@code next()} was called, but not after
    130     * {@code hasNext()}, because the {@code hasNext()} call
    131     * changes the base iterator.
    132     *
    133     * @throws IllegalStateException if {@code hasNext()} has already
    134     *  been called.
    135     */
    136    @Override
    137    public void remove() {
    138        if (nextObjectSet) {
    139            throw new IllegalStateException("remove() cannot be called");
    140        }
    141        iterator.remove();
    142    }
    143
    144    private Predicate<? super E> safePredicate(final Predicate<? super E> predicate) {
    145        return predicate != null ? predicate : TruePredicate.truePredicate();
    146    }
    147
    148    /**
    149     * Sets the iterator for this iterator to use.
    150     * If iteration has started, this effectively resets the iterator.
    151     *
    152     * @param iterator  The iterator to use
    153     */
    154    public void setIterator(final Iterator<? extends E> iterator) {
    155        this.iterator = iterator;
    156        nextObject = null;
    157        nextObjectSet = false;
    158    }
    159
    160    /**
    161     * Sets nextObject to the next object. If there are no more
    162     * objects, then return false. Otherwise, return true.
    163     */
    164    private boolean setNextObject() {
    165        while (iterator.hasNext()) {
    166            final E object = iterator.next();
    167            if (predicate.test(object)) {
    168                nextObject = object;
    169                nextObjectSet = true;
    170                return true;
    171            }
    172        }
    173        return false;
    174    }
    175
    176    /**
    177     * Sets the predicate this the iterator to use where null accepts all values.
    178     *
    179     * @param predicate  The predicate to use, null accepts all values.
    180     */
    181    public void setPredicate(final Predicate<? super E> predicate) {
    182        this.predicate = safePredicate(predicate);
    183        nextObject = null;
    184        nextObjectSet = false;
    185    }
    186
    187}