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.functor.core.collection;
018
019import java.util.Iterator;
020import java.util.NoSuchElementException;
021
022import org.apache.commons.functor.Predicate;
023import org.apache.commons.lang3.Validate;
024
025/**
026 * Iterator that filters another Iterator by only passing through those elements
027 * that are matched by a specified Predicate.
028 *
029 * @param <T> the {@link Iterator} generic type
030 * @version $Revision: 1508677 $ $Date: 2013-07-31 00:48:02 +0200 (Mi, 31 Jul 2013) $
031 */
032public final class FilteredIterator<T> implements Iterator<T> {
033    // attributes
034    // ------------------------------------------------------------------------
035
036    /**
037     * The predicate used to test this Iterator elements.
038     */
039    private final Predicate<? super T> predicate;
040    /**
041     * The wrapped iterator.
042     */
043    private final Iterator<? extends T> iterator;
044
045    /**
046     * Reference to next element has to be returned by this iterator.
047     */
048    private T next = null;
049    /**
050     * Flag to mark this iterator has more elements or not.
051     */
052    private boolean nextSet = false;
053    /**
054     * Flag to mark current iterator element can be removed.
055     */
056    private boolean canRemove = false;
057
058    // constructor
059    // ------------------------------------------------------------------------
060    /**
061     * Create a new FilteredIterator.
062     * @param iterator to filter
063     * @param predicate to apply
064     */
065    public FilteredIterator(Iterator<? extends T> iterator, Predicate<? super T> predicate) {
066        this.iterator = Validate.notNull(iterator, "Iterator argument was null");
067        this.predicate = Validate.notNull(predicate, "filtering Predicate argument was null");
068    }
069
070    // iterator methods
071    // ------------------------------------------------------------------------
072
073    /**
074     * {@inheritDoc}
075     * @see java.util.Iterator#hasNext()
076     */
077    public boolean hasNext() {
078        return nextSet || setNext();
079    }
080
081    /**
082     * {@inheritDoc}
083     * @see java.util.Iterator#next()
084     */
085    public T next() {
086        if (hasNext()) {
087            return returnNext();
088        }
089        throw new NoSuchElementException();
090    }
091
092    /**
093     * {@inheritDoc}
094     * @see java.util.Iterator#remove()
095     */
096    public void remove() {
097        if (canRemove) {
098            canRemove = false;
099            iterator.remove();
100        } else {
101            throw new IllegalStateException();
102        }
103    }
104
105    /**
106     * {@inheritDoc}
107     */
108    @Override
109    public boolean equals(Object obj) {
110        if (obj == this) {
111            return true;
112        }
113        if (!(obj instanceof FilteredIterator<?>)) {
114            return false;
115        }
116        FilteredIterator<?> that = (FilteredIterator<?>) obj;
117        return predicate.equals(that.predicate) && iterator.equals(that.iterator);
118    }
119
120    /**
121     * {@inheritDoc}
122     */
123    @Override
124    public int hashCode() {
125        int hash = "FilteredIterator".hashCode();
126        hash <<= 2;
127        hash ^= predicate.hashCode();
128        hash <<= 2;
129        hash ^= iterator.hashCode();
130        return hash;
131    }
132
133    /**
134     * {@inheritDoc}
135     */
136    @Override
137    public String toString() {
138        return "FilteredIterator<" + iterator + "," + predicate + ">";
139    }
140
141    // static methods
142    // ------------------------------------------------------------------------
143    /**
144     * Get a filtered Iterator instance applying <code>pred</code> to <code>iter</code>.
145     * @param <T> the input iterator generic type
146     * @param iter to filter
147     * @param pred to apply
148     * @return Iterator
149     */
150    @SuppressWarnings("unchecked")
151    public static <T> Iterator<T> filter(Iterator<? extends T> iter, Predicate<? super T> pred) {
152        return null == pred ? (Iterator<T>) iter : (null == iter ? null : new FilteredIterator<T>(iter, pred));
153    }
154
155    // private
156    // ------------------------------------------------------------------------
157    /**
158     * Set next element.
159     * @return whether the current iterator position is valid
160     */
161    private boolean setNext() {
162        while (iterator.hasNext()) {
163            canRemove = false;
164            T obj = iterator.next();
165            if (predicate.test(obj)) {
166                nextSet = true;
167                next = obj;
168                return true;
169            }
170        }
171        next = null;
172        nextSet = false;
173        return false;
174    }
175
176    /**
177     * Get the next element.
178     * @return next element.
179     */
180    private T returnNext() {
181        T temp = next;
182        canRemove = true;
183        next = null;
184        nextSet = false;
185        return temp;
186    }
187
188}