001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.tools;
003
004import java.util.AbstractCollection;
005import java.util.Collection;
006import 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 */
017public 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        @Override
044        public boolean hasNext() {
045            findNext();
046            return current != null;
047        }
048
049        @SuppressWarnings("unchecked")
050        @Override
051        public T next() {
052            findNext();
053            S old = current;
054            current = null;
055            // we are save because predicate only accepts objects of type T
056            return (T) old;
057        }
058
059        @Override
060        public void remove() {
061            throw new UnsupportedOperationException();
062        }
063    }
064
065    public SubclassFilteredCollection(Collection<? extends S> collection, Predicate<? super S> predicate) {
066        this.collection = collection;
067        this.predicate = predicate;
068    }
069
070    @Override
071    public Iterator<T> iterator() {
072        return new FilterIterator(collection.iterator());
073    }
074
075    @Override
076    public int size() {
077        if (size == -1) {
078            size = 0;
079            Iterator<T> it = iterator();
080            while (it.hasNext()) {
081                size++;
082                it.next();
083            }
084        }
085        return size;
086    }
087
088    @Override
089    public boolean isEmpty() {
090        return !iterator().hasNext();
091    }
092
093}