1/* 2 * Copyright (C) 2012 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15package com.google.common.collect; 16 17import static com.google.common.base.Preconditions.checkNotNull; 18import static com.google.common.base.Preconditions.checkPositionIndex; 19 20import com.google.common.annotations.GwtCompatible; 21import com.google.common.base.Predicate; 22 23import java.util.Collection; 24import java.util.Collections; 25import java.util.Iterator; 26import java.util.List; 27import java.util.Map; 28import java.util.Map.Entry; 29import java.util.Set; 30 31import javax.annotation.Nullable; 32 33/** 34 * Implementation of {@link Multimaps#filterKeys(Multimap, Predicate)}. 35 * 36 * @author Louis Wasserman 37 */ 38@GwtCompatible 39class FilteredKeyMultimap<K, V> extends AbstractMultimap<K, V> implements FilteredMultimap<K, V> { 40 final Multimap<K, V> unfiltered; 41 final Predicate<? super K> keyPredicate; 42 43 FilteredKeyMultimap(Multimap<K, V> unfiltered, Predicate<? super K> keyPredicate) { 44 this.unfiltered = checkNotNull(unfiltered); 45 this.keyPredicate = checkNotNull(keyPredicate); 46 } 47 48 @Override 49 public Multimap<K, V> unfiltered() { 50 return unfiltered; 51 } 52 53 @Override 54 public Predicate<? super Entry<K, V>> entryPredicate() { 55 return Maps.keyPredicateOnEntries(keyPredicate); 56 } 57 58 @Override 59 public int size() { 60 int size = 0; 61 for (Collection<V> collection : asMap().values()) { 62 size += collection.size(); 63 } 64 return size; 65 } 66 67 @Override 68 public boolean containsKey(@Nullable Object key) { 69 if (unfiltered.containsKey(key)) { 70 @SuppressWarnings("unchecked") // k is equal to a K, if not one itself 71 K k = (K) key; 72 return keyPredicate.apply(k); 73 } 74 return false; 75 } 76 77 @Override 78 public Collection<V> removeAll(Object key) { 79 return containsKey(key) ? unfiltered.removeAll(key) : unmodifiableEmptyCollection(); 80 } 81 82 Collection<V> unmodifiableEmptyCollection() { 83 if (unfiltered instanceof SetMultimap) { 84 return ImmutableSet.of(); 85 } else { 86 return ImmutableList.of(); 87 } 88 } 89 90 @Override 91 public void clear() { 92 keySet().clear(); 93 } 94 95 @Override 96 Set<K> createKeySet() { 97 return Sets.filter(unfiltered.keySet(), keyPredicate); 98 } 99 100 @Override 101 public Collection<V> get(K key) { 102 if (keyPredicate.apply(key)) { 103 return unfiltered.get(key); 104 } else if (unfiltered instanceof SetMultimap) { 105 return new AddRejectingSet<K, V>(key); 106 } else { 107 return new AddRejectingList<K, V>(key); 108 } 109 } 110 111 static class AddRejectingSet<K, V> extends ForwardingSet<V> { 112 final K key; 113 114 AddRejectingSet(K key) { 115 this.key = key; 116 } 117 118 @Override 119 public boolean add(V element) { 120 throw new IllegalArgumentException("Key does not satisfy predicate: " + key); 121 } 122 123 @Override 124 public boolean addAll(Collection<? extends V> collection) { 125 checkNotNull(collection); 126 throw new IllegalArgumentException("Key does not satisfy predicate: " + key); 127 } 128 129 @Override 130 protected Set<V> delegate() { 131 return Collections.emptySet(); 132 } 133 } 134 135 static class AddRejectingList<K, V> extends ForwardingList<V> { 136 final K key; 137 138 AddRejectingList(K key) { 139 this.key = key; 140 } 141 142 @Override 143 public boolean add(V v) { 144 add(0, v); 145 return true; 146 } 147 148 @Override 149 public boolean addAll(Collection<? extends V> collection) { 150 addAll(0, collection); 151 return true; 152 } 153 154 @Override 155 public void add(int index, V element) { 156 checkPositionIndex(index, 0); 157 throw new IllegalArgumentException("Key does not satisfy predicate: " + key); 158 } 159 160 @Override 161 public boolean addAll(int index, Collection<? extends V> elements) { 162 checkNotNull(elements); 163 checkPositionIndex(index, 0); 164 throw new IllegalArgumentException("Key does not satisfy predicate: " + key); 165 } 166 167 @Override 168 protected List<V> delegate() { 169 return Collections.emptyList(); 170 } 171 } 172 173 @Override 174 Iterator<Entry<K, V>> entryIterator() { 175 throw new AssertionError("should never be called"); 176 } 177 178 @Override 179 Collection<Entry<K, V>> createEntries() { 180 return new Entries(); 181 } 182 183 class Entries extends ForwardingCollection<Entry<K, V>> { 184 @Override 185 protected Collection<Entry<K, V>> delegate() { 186 return Collections2.filter(unfiltered.entries(), entryPredicate()); 187 } 188 189 @Override 190 @SuppressWarnings("unchecked") 191 public boolean remove(@Nullable Object o) { 192 if (o instanceof Entry) { 193 Entry<?, ?> entry = (Entry<?, ?>) o; 194 if (unfiltered.containsKey(entry.getKey()) 195 // if this holds, then we know entry.getKey() is a K 196 && keyPredicate.apply((K) entry.getKey())) { 197 return unfiltered.remove(entry.getKey(), entry.getValue()); 198 } 199 } 200 return false; 201 } 202 } 203 204 @Override 205 Collection<V> createValues() { 206 return new FilteredMultimapValues<K, V>(this); 207 } 208 209 @Override 210 Map<K, Collection<V>> createAsMap() { 211 return Maps.filterKeys(unfiltered.asMap(), keyPredicate); 212 } 213 214 @Override 215 Multiset<K> createKeys() { 216 return Multisets.filter(unfiltered.keys(), keyPredicate); 217 } 218} 219