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