Home | History | Annotate | Download | only in collect
      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 
     23 import java.util.Collection;
     24 import java.util.Collections;
     25 import java.util.Iterator;
     26 import java.util.List;
     27 import java.util.Map;
     28 import java.util.Map.Entry;
     29 import java.util.Set;
     30 
     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<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