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; 007import java.util.NoSuchElementException; 008import java.util.Objects; 009import java.util.function.Predicate; 010 011/** 012 * Filtered view of a collection. 013 * (read-only collection, but elements can be changed, of course) 014 * Lets you iterate through those elements of a given collection that satisfy a 015 * certain condition (imposed by a predicate). 016 * <p> 017 * The behaviour of this class is undefined if the underlying collection is changed. 018 * @param <S> element type of the underlying collection 019 * @param <T> element type of filtered collection (and subclass of S). The predicate 020 * must accept only objects of type T. 021 * @since 3147 022 */ 023public class SubclassFilteredCollection<S, T extends S> extends AbstractCollection<T> { 024 025 private final Collection<? extends S> collection; 026 private final Predicate<? super S> predicate; 027 private int size = -1; 028 029 private class FilterIterator implements Iterator<T> { 030 031 private final Iterator<? extends S> iterator; 032 private S current; 033 034 FilterIterator(Iterator<? extends S> iterator) { 035 this.iterator = iterator; 036 } 037 038 private void findNext() { 039 if (current == null) { 040 while (iterator.hasNext()) { 041 current = iterator.next(); 042 if (predicate.test(current)) 043 return; 044 } 045 current = null; 046 } 047 } 048 049 @Override 050 public boolean hasNext() { 051 findNext(); 052 return current != null; 053 } 054 055 @SuppressWarnings("unchecked") 056 @Override 057 public T next() { 058 if (!hasNext()) 059 throw new NoSuchElementException(); 060 S old = current; 061 current = null; 062 // we are save because predicate only accepts objects of type T 063 return (T) old; 064 } 065 066 @Override 067 public void remove() { 068 throw new UnsupportedOperationException(); 069 } 070 } 071 072 /** 073 * Constructs a new {@code SubclassFilteredCollection}. 074 * @param collection The base collection to filter 075 * @param predicate The predicate to use as filter 076 * @see #filter(Collection, Predicate) for an alternative way to construct this. 077 */ 078 public SubclassFilteredCollection(Collection<? extends S> collection, Predicate<? super S> predicate) { 079 this.collection = Objects.requireNonNull(collection); 080 this.predicate = Objects.requireNonNull(predicate); 081 } 082 083 @Override 084 public Iterator<T> iterator() { 085 return new FilterIterator(collection.iterator()); 086 } 087 088 @Override 089 public int size() { 090 if (size == -1) { 091 size = 0; 092 Iterator<T> it = iterator(); 093 while (it.hasNext()) { 094 size++; 095 it.next(); 096 } 097 } 098 return size; 099 } 100 101 @Override 102 public boolean isEmpty() { 103 return !iterator().hasNext(); 104 } 105 106 /** 107 * Create a new filtered collection without any constraints on the predicate type. 108 * @param <T> The collection type. 109 * @param collection The collection to filter. 110 * @param predicate The predicate to filter for. 111 * @return The filtered collection. It is a {@code Collection<T>}. 112 */ 113 public static <T> SubclassFilteredCollection<T, T> filter(Collection<? extends T> collection, Predicate<T> predicate) { 114 return new SubclassFilteredCollection<>(collection, predicate); 115 } 116}