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