View Javadoc
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  
15  package com.google.common.collect;
16  
17  import static com.google.common.base.Preconditions.checkNotNull;
18  import static com.google.common.base.Preconditions.checkPositionIndex;
19  
20  import com.google.common.annotations.GwtCompatible;
21  import com.google.common.base.Predicate;
22  import com.google.errorprone.annotations.CanIgnoreReturnValue;
23  import com.google.j2objc.annotations.WeakOuter;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Map.Entry;
30  import java.util.Set;
31  import javax.annotation.Nullable;
32  
33  /**
34   * Implementation of {@link Multimaps#filterKeys(Multimap, Predicate)}.
35   *
36   * @author Louis Wasserman
37   */
38  @GwtCompatible
39  class 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<>(key);
106     } else {
107       return new AddRejectingList<>(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     @CanIgnoreReturnValue
161     @Override
162     public boolean addAll(int index, Collection<? extends V> elements) {
163       checkNotNull(elements);
164       checkPositionIndex(index, 0);
165       throw new IllegalArgumentException("Key does not satisfy predicate: " + key);
166     }
167 
168     @Override
169     protected List<V> delegate() {
170       return Collections.emptyList();
171     }
172   }
173 
174   @Override
175   Iterator<Entry<K, V>> entryIterator() {
176     throw new AssertionError("should never be called");
177   }
178 
179   @Override
180   Collection<Entry<K, V>> createEntries() {
181     return new Entries();
182   }
183 
184   @WeakOuter
185   class Entries extends ForwardingCollection<Entry<K, V>> {
186     @Override
187     protected Collection<Entry<K, V>> delegate() {
188       return Collections2.filter(unfiltered.entries(), entryPredicate());
189     }
190 
191     @Override
192     @SuppressWarnings("unchecked")
193     public boolean remove(@Nullable Object o) {
194       if (o instanceof Entry) {
195         Entry<?, ?> entry = (Entry<?, ?>) o;
196         if (unfiltered.containsKey(entry.getKey())
197             // if this holds, then we know entry.getKey() is a K
198             && keyPredicate.apply((K) entry.getKey())) {
199           return unfiltered.remove(entry.getKey(), entry.getValue());
200         }
201       }
202       return false;
203     }
204   }
205 
206   @Override
207   Collection<V> createValues() {
208     return new FilteredMultimapValues<>(this);
209   }
210 
211   @Override
212   Map<K, Collection<V>> createAsMap() {
213     return Maps.filterKeys(unfiltered.asMap(), keyPredicate);
214   }
215 
216   @Override
217   Multiset<K> createKeys() {
218     return Multisets.filter(unfiltered.keys(), keyPredicate);
219   }
220 }