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     */
017    package org.apache.commons.collections.iterators;
018    
019    import java.util.ListIterator;
020    import java.util.NoSuchElementException;
021    
022    import org.apache.commons.collections.Predicate;
023    
024    /** 
025     * Decorates another {@link ListIterator} 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 Commons Collections 2.0
031     * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
032     * 
033     * @author Rodney Waldhoff
034     */
035    public class FilterListIterator implements ListIterator {
036    
037        /** The iterator being used */
038        private ListIterator iterator;
039        
040        /** The predicate being used */
041        private Predicate predicate;
042    
043        /** 
044         * The value of the next (matching) object, when 
045         * {@link #nextObjectSet} is true. 
046         */
047        private Object nextObject;
048    
049        /** 
050         * Whether or not the {@link #nextObject} has been set
051         * (possibly to <code>null</code>). 
052         */
053        private boolean nextObjectSet = false;   
054    
055        /** 
056         * The value of the previous (matching) object, when 
057         * {@link #previousObjectSet} is true. 
058         */
059        private Object previousObject;
060    
061        /** 
062         * Whether or not the {@link #previousObject} has been set
063         * (possibly to <code>null</code>). 
064         */
065        private boolean previousObjectSet = false;   
066    
067        /** 
068         * The index of the element that would be returned by {@link #next}.
069         */
070        private int nextIndex = 0;
071        
072        //-----------------------------------------------------------------------
073        /**
074         * Constructs a new <code>FilterListIterator</code> that will not function
075         * until {@link #setListIterator(ListIterator) setListIterator}
076         * and {@link #setPredicate(Predicate) setPredicate} are invoked.
077         */
078        public FilterListIterator() {
079            super();
080        }
081    
082        /**
083         * Constructs a new <code>FilterListIterator</code> that will not 
084         * function until {@link #setPredicate(Predicate) setPredicate} is invoked.
085         *
086         * @param iterator  the iterator to use
087         */
088        public FilterListIterator(ListIterator iterator ) {
089            super();
090            this.iterator = iterator;
091        }
092    
093        /**
094         * Constructs a new <code>FilterListIterator</code>.
095         *
096         * @param iterator  the iterator to use
097         * @param predicate  the predicate to use
098         */
099        public FilterListIterator(ListIterator iterator, Predicate predicate) {
100            super();
101            this.iterator = iterator;
102            this.predicate = predicate;
103        }
104    
105        /**
106         * Constructs a new <code>FilterListIterator</code> that will not function
107         * until {@link #setListIterator(ListIterator) setListIterator} is invoked.
108         *
109         * @param predicate  the predicate to use.
110         */
111        public FilterListIterator(Predicate predicate) {
112            super();
113            this.predicate = predicate;
114        }
115    
116        //-----------------------------------------------------------------------
117        /** Not supported. */
118        public void add(Object o) {
119            throw new UnsupportedOperationException("FilterListIterator.add(Object) is not supported.");
120        }
121    
122        public boolean hasNext() {
123            if(nextObjectSet) {
124                return true;
125            } else {
126                return setNextObject();
127            }
128        }
129    
130        public boolean hasPrevious() {
131            if(previousObjectSet) {
132                return true;
133            } else {
134                return setPreviousObject();
135            }
136        }
137    
138        public Object next() {
139            if(!nextObjectSet) {
140                if(!setNextObject()) {
141                    throw new NoSuchElementException();
142                }
143            }
144            nextIndex++;
145            Object temp = nextObject;
146            clearNextObject();
147            return temp;
148        }
149    
150        public int nextIndex() {
151            return nextIndex;
152        }
153    
154        public Object previous() {
155            if(!previousObjectSet) {
156                if(!setPreviousObject()) {
157                    throw new NoSuchElementException();
158                }
159            }
160            nextIndex--;
161            Object temp = previousObject;
162            clearPreviousObject();
163            return temp;
164        }
165    
166        public int previousIndex() {
167            return (nextIndex-1);
168        }
169    
170        /** Not supported. */
171        public void remove() {
172            throw new UnsupportedOperationException("FilterListIterator.remove() is not supported.");
173        }
174    
175        /** Not supported. */
176        public void set(Object o) {
177            throw new UnsupportedOperationException("FilterListIterator.set(Object) is not supported.");
178        }
179    
180        //-----------------------------------------------------------------------
181        /** 
182         * Gets the iterator this iterator is using.
183         * 
184         * @return the iterator.
185         */
186        public ListIterator getListIterator() {
187            return iterator;
188        }
189    
190        /** 
191         * Sets the iterator for this iterator to use.
192         * If iteration has started, this effectively resets the iterator.
193         * 
194         * @param iterator  the iterator to use
195         */
196        public void setListIterator(ListIterator iterator) {
197            this.iterator = iterator;
198        }
199    
200        //-----------------------------------------------------------------------
201        /** 
202         * Gets the predicate this iterator is using.
203         * 
204         * @return the predicate.
205         */
206        public Predicate getPredicate() {
207            return predicate;
208        }
209    
210        /** 
211         * Sets the predicate this the iterator to use.
212         * 
213         * @param predicate  the transformer to use
214         */
215        public void setPredicate(Predicate predicate) {
216            this.predicate = predicate;
217        }
218    
219        //-----------------------------------------------------------------------
220        private void clearNextObject() {
221            nextObject = null;
222            nextObjectSet = false;
223        }
224    
225        private boolean setNextObject() {
226            // if previousObjectSet,
227            // then we've walked back one step in the 
228            // underlying list (due to a hasPrevious() call)
229            // so skip ahead one matching object
230            if(previousObjectSet) {
231                clearPreviousObject();
232                if(!setNextObject()) {
233                    return false;
234                } else {
235                    clearNextObject();
236                }
237            }
238    
239            while(iterator.hasNext()) {
240                Object object = iterator.next();
241                if(predicate.evaluate(object)) {
242                    nextObject = object;
243                    nextObjectSet = true;
244                    return true;
245                }
246            }
247            return false;
248        }
249    
250        private void clearPreviousObject() {
251            previousObject = null;
252            previousObjectSet = false;
253        }
254    
255        private boolean setPreviousObject() {
256            // if nextObjectSet,
257            // then we've walked back one step in the 
258            // underlying list (due to a hasNext() call)
259            // so skip ahead one matching object
260            if(nextObjectSet) {
261                clearNextObject();
262                if(!setPreviousObject()) {
263                    return false;
264                } else {
265                    clearPreviousObject();
266                }
267            }
268    
269            while(iterator.hasPrevious()) {
270                Object object = iterator.previous();
271                if(predicate.evaluate(object)) {
272                    previousObject = object;
273                    previousObjectSet = true;
274                    return true;
275                }
276            }
277            return false;
278        }
279    
280    }