001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.tools;
003    
004    import java.util.AbstractCollection;
005    import java.util.Collection;
006    import java.util.Iterator;
007    
008    /**
009     * Filtered view of a collection.
010     * (read-only collection, but elements can be changed, of course)
011     * Lets you iterate through those elements of a given collection that satisfy a
012     * certain condition (imposed by a predicate).
013     * @param <S> element type of the underlying collection
014     * @param <T> element type of filtered collection (and subclass of S). The predicate
015     *      must accept only objects of type T.
016     */
017    public class SubclassFilteredCollection<S, T extends S> extends AbstractCollection<T> {
018    
019        private final Collection<? extends S> collection;
020        private final Predicate<? super S> predicate;
021        int size = -1;
022    
023        private class FilterIterator implements Iterator<T> {
024    
025            private final Iterator<? extends S> iterator;
026            private S current;
027    
028            public FilterIterator(Iterator<? extends S> iterator) {
029                this.iterator = iterator;
030            }
031    
032            private void findNext() {
033                if (current == null) {
034                    while (iterator.hasNext()) {
035                        current = iterator.next();
036                        if (predicate.evaluate(current))
037                            return;
038                    }
039                    current = null;
040                }
041            }
042    
043            public boolean hasNext() {
044                findNext();
045                return current != null;
046            }
047    
048            public T next() {
049                findNext();
050                S old = current;
051                current = null;
052                // we are save because predicate only accepts objects of type T
053                @SuppressWarnings("unchecked") T res = (T) old;
054                return res;
055            }
056    
057            public void remove() {
058                throw new UnsupportedOperationException();
059            }
060        }
061    
062        public SubclassFilteredCollection(Collection<? extends S> collection, Predicate<? super S> predicate) {
063            this.collection = collection;
064            this.predicate = predicate;
065        }
066    
067        @Override
068        public Iterator<T> iterator() {
069            return new FilterIterator(collection.iterator());
070        }
071    
072        @Override
073        public int size() {
074            if (size == -1) {
075                size = 0;
076                Iterator<T> it = iterator();
077                while (it.hasNext()) {
078                    size++;
079                    it.next();
080                }
081            }
082            return size;
083        }
084    
085        @Override
086        public boolean isEmpty() {
087            return !iterator().hasNext();
088        }
089    
090    }