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 * @since 3147
017 */
018public class SubclassFilteredCollection<S, T extends S> extends AbstractCollection<T> {
019
020    private final Collection<? extends S> collection;
021    private final Predicate<? super S> predicate;
022    int size = -1;
023
024    private class FilterIterator implements Iterator<T> {
025
026        private final Iterator<? extends S> iterator;
027        private S current;
028
029        public FilterIterator(Iterator<? extends S> iterator) {
030            this.iterator = iterator;
031        }
032
033        private void findNext() {
034            if (current == null) {
035                while (iterator.hasNext()) {
036                    current = iterator.next();
037                    if (predicate.evaluate(current))
038                        return;
039                }
040                current = null;
041            }
042        }
043
044        @Override
045        public boolean hasNext() {
046            findNext();
047            return current != null;
048        }
049
050        @SuppressWarnings("unchecked")
051        @Override
052        public T next() {
053            findNext();
054            S old = current;
055            current = null;
056            // we are save because predicate only accepts objects of type T
057            return (T) old;
058        }
059
060        @Override
061        public void remove() {
062            throw new UnsupportedOperationException();
063        }
064    }
065
066    /**
067     * Constructs a new {@code SubclassFilteredCollection}.
068     * @param collection The base collection to filter
069     * @param predicate The predicate to use as filter
070     */
071    public SubclassFilteredCollection(Collection<? extends S> collection, Predicate<? super S> predicate) {
072        this.collection = collection;
073        this.predicate = predicate;
074    }
075
076    @Override
077    public Iterator<T> iterator() {
078        return new FilterIterator(collection.iterator());
079    }
080
081    @Override
082    public int size() {
083        if (size == -1) {
084            size = 0;
085            Iterator<T> it = iterator();
086            while (it.hasNext()) {
087                size++;
088                it.next();
089            }
090        }
091        return size;
092    }
093
094    @Override
095    public boolean isEmpty() {
096        return !iterator().hasNext();
097    }
098}