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.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}