Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2009 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.collect;
     18 
     19 import com.google.common.annotations.GwtCompatible;
     20 import static com.google.common.base.Preconditions.checkNotNull;
     21 
     22 import java.io.IOException;
     23 import java.io.InvalidObjectException;
     24 import java.io.ObjectInputStream;
     25 import java.io.ObjectOutputStream;
     26 import java.util.Arrays;
     27 import java.util.Collection;
     28 import java.util.LinkedHashMap;
     29 import java.util.Map;
     30 
     31 import javax.annotation.Nullable;
     32 
     33 /**
     34  * An immutable {@link SetMultimap} with reliable user-specified key and value
     35  * iteration order. Does not permit null keys or values.
     36  *
     37  * <p>Unlike {@link Multimaps#unmodifiableSetMultimap(SetMultimap)}, which is
     38  * a <i>view</i> of a separate multimap which can still change, an instance of
     39  * {@code ImmutableSetMultimap} contains its own data and will <i>never</i>
     40  * change. {@code ImmutableSetMultimap} is convenient for
     41  * {@code public static final} multimaps ("constant multimaps") and also lets
     42  * you easily make a "defensive copy" of a multimap provided to your class by
     43  * a caller.
     44  *
     45  * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as
     46  * it has no public or protected constructors. Thus, instances of this class
     47  * are guaranteed to be immutable.
     48  *
     49  * @author Mike Ward
     50  * @since 2010.01.04 <b>stable</b> (imported from Google Collections Library)
     51  */
     52 @GwtCompatible(serializable = true)
     53 public class ImmutableSetMultimap<K, V>
     54     extends ImmutableMultimap<K, V>
     55     implements SetMultimap<K, V> {
     56 
     57   /** Returns the empty multimap. */
     58   // Casting is safe because the multimap will never hold any elements.
     59   @SuppressWarnings("unchecked")
     60   public static <K, V> ImmutableSetMultimap<K, V> of() {
     61     // BEGIN android-changed
     62     return (ImmutableSetMultimap) EmptyImmutableSetMultimap.INSTANCE;
     63     // END android-changed
     64   }
     65 
     66   /**
     67    * Returns an immutable multimap containing a single entry.
     68    */
     69   public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1) {
     70     ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
     71     builder.put(k1, v1);
     72     return builder.build();
     73   }
     74 
     75   /**
     76    * Returns an immutable multimap containing the given entries, in order.
     77    * Repeated occurrences of an entry (according to {@link Object#equals}) after
     78    * the first are ignored.
     79    */
     80   public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1, K k2, V v2) {
     81     ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
     82     builder.put(k1, v1);
     83     builder.put(k2, v2);
     84     return builder.build();
     85   }
     86 
     87   /**
     88    * Returns an immutable multimap containing the given entries, in order.
     89    * Repeated occurrences of an entry (according to {@link Object#equals}) after
     90    * the first are ignored.
     91    */
     92   public static <K, V> ImmutableSetMultimap<K, V> of(
     93       K k1, V v1, K k2, V v2, K k3, V v3) {
     94     ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
     95     builder.put(k1, v1);
     96     builder.put(k2, v2);
     97     builder.put(k3, v3);
     98     return builder.build();
     99   }
    100 
    101   /**
    102    * Returns an immutable multimap containing the given entries, in order.
    103    * Repeated occurrences of an entry (according to {@link Object#equals}) after
    104    * the first are ignored.
    105    */
    106   public static <K, V> ImmutableSetMultimap<K, V> of(
    107       K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
    108     ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
    109     builder.put(k1, v1);
    110     builder.put(k2, v2);
    111     builder.put(k3, v3);
    112     builder.put(k4, v4);
    113     return builder.build();
    114   }
    115 
    116   /**
    117    * Returns an immutable multimap containing the given entries, in order.
    118    * Repeated occurrences of an entry (according to {@link Object#equals}) after
    119    * the first are ignored.
    120    */
    121   public static <K, V> ImmutableSetMultimap<K, V> of(
    122       K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
    123     ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
    124     builder.put(k1, v1);
    125     builder.put(k2, v2);
    126     builder.put(k3, v3);
    127     builder.put(k4, v4);
    128     builder.put(k5, v5);
    129     return builder.build();
    130   }
    131 
    132   // looking for of() with > 5 entries? Use the builder instead.
    133 
    134   /**
    135    * Returns a new {@link Builder}.
    136    */
    137   public static <K, V> Builder<K, V> builder() {
    138     return new Builder<K, V>();
    139   }
    140 
    141   /**
    142    * Multimap for {@link ImmutableSetMultimap.Builder} that maintains key
    143    * and value orderings and performs better than {@link LinkedHashMultimap}.
    144    */
    145   private static class BuilderMultimap<K, V> extends AbstractMultimap<K, V> {
    146     BuilderMultimap() {
    147       super(new LinkedHashMap<K, Collection<V>>());
    148     }
    149     @Override Collection<V> createCollection() {
    150       return Sets.newLinkedHashSet();
    151     }
    152     private static final long serialVersionUID = 0;
    153   }
    154 
    155   /**
    156    * A builder for creating immutable {@code SetMultimap} instances, especially
    157    * {@code public static final} multimaps ("constant multimaps"). Example:
    158    * <pre>   {@code
    159    *
    160    *   static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP =
    161    *       new ImmutableSetMultimap.Builder<String, Integer>()
    162    *           .put("one", 1)
    163    *           .putAll("several", 1, 2, 3)
    164    *           .putAll("many", 1, 2, 3, 4, 5)
    165    *           .build();}</pre>
    166    *
    167    * <p>Builder instances can be reused - it is safe to call {@link #build}
    168    * multiple times to build multiple multimaps in series. Each multimap
    169    * contains the key-value mappings in the previously created multimaps.
    170    */
    171   public static final class Builder<K, V>
    172       extends ImmutableMultimap.Builder<K, V> {
    173     private final Multimap<K, V> builderMultimap = new BuilderMultimap<K, V>();
    174 
    175     /**
    176      * Creates a new builder. The returned builder is equivalent to the builder
    177      * generated by {@link ImmutableSetMultimap#builder}.
    178      */
    179     public Builder() {}
    180 
    181     /**
    182      * Adds a key-value mapping to the built multimap if it is not already
    183      * present.
    184      */
    185     @Override public Builder<K, V> put(K key, V value) {
    186       builderMultimap.put(checkNotNull(key), checkNotNull(value));
    187       return this;
    188     }
    189 
    190     /**
    191      * Stores a collection of values with the same key in the built multimap.
    192      *
    193      * @throws NullPointerException if {@code key}, {@code values}, or any
    194      *     element in {@code values} is null. The builder is left in an invalid
    195      *     state.
    196      */
    197     @Override public Builder<K, V> putAll(K key, Iterable<? extends V> values) {
    198       Collection<V> collection = builderMultimap.get(checkNotNull(key));
    199       for (V value : values) {
    200         collection.add(checkNotNull(value));
    201       }
    202       return this;
    203     }
    204 
    205     /**
    206      * Stores an array of values with the same key in the built multimap.
    207      *
    208      * @throws NullPointerException if the key or any value is null. The
    209      *     builder is left in an invalid state.
    210      */
    211     @Override public Builder<K, V> putAll(K key, V... values) {
    212       return putAll(key, Arrays.asList(values));
    213     }
    214 
    215     /**
    216      * Stores another multimap's entries in the built multimap. The generated
    217      * multimap's key and value orderings correspond to the iteration ordering
    218      * of the {@code multimap.asMap()} view, with new keys and values following
    219      * any existing keys and values.
    220      *
    221      * @throws NullPointerException if any key or value in {@code multimap} is
    222      *     null. The builder is left in an invalid state.
    223      */
    224     @Override public Builder<K, V> putAll(
    225         Multimap<? extends K, ? extends V> multimap) {
    226       for (Map.Entry<? extends K, ? extends Collection<? extends V>> entry
    227           : multimap.asMap().entrySet()) {
    228         putAll(entry.getKey(), entry.getValue());
    229       }
    230       return this;
    231     }
    232 
    233     /**
    234      * Returns a newly-created immutable set multimap.
    235      */
    236     @Override public ImmutableSetMultimap<K, V> build() {
    237       return copyOf(builderMultimap);
    238     }
    239   }
    240 
    241   /**
    242    * Returns an immutable set multimap containing the same mappings as
    243    * {@code multimap}. The generated multimap's key and value orderings
    244    * correspond to the iteration ordering of the {@code multimap.asMap()} view.
    245    * Repeated occurrences of an entry in the multimap after the first are
    246    * ignored.
    247    *
    248    * <p><b>Note:</b> Despite what the method name suggests, if
    249    * {@code multimap} is an {@code ImmutableSetMultimap}, no copy will actually
    250    * be performed, and the given multimap itself will be returned.
    251    *
    252    * @throws NullPointerException if any key or value in {@code multimap} is
    253    *     null
    254    */
    255   public static <K, V> ImmutableSetMultimap<K, V> copyOf(
    256       Multimap<? extends K, ? extends V> multimap) {
    257     if (multimap.isEmpty()) {
    258       return of();
    259     }
    260 
    261     if (multimap instanceof ImmutableSetMultimap) {
    262       @SuppressWarnings("unchecked") // safe since multimap is not writable
    263       ImmutableSetMultimap<K, V> kvMultimap
    264           = (ImmutableSetMultimap<K, V>) multimap;
    265       return kvMultimap;
    266     }
    267 
    268     ImmutableMap.Builder<K, ImmutableSet<V>> builder = ImmutableMap.builder();
    269     int size = 0;
    270 
    271     for (Map.Entry<? extends K, ? extends Collection<? extends V>> entry
    272         : multimap.asMap().entrySet()) {
    273       K key = entry.getKey();
    274       Collection<? extends V> values = entry.getValue();
    275       ImmutableSet<V> set = ImmutableSet.copyOf(values);
    276       if (!set.isEmpty()) {
    277         builder.put(key, set);
    278         size += set.size();
    279       }
    280     }
    281 
    282     return new ImmutableSetMultimap<K, V>(builder.build(), size);
    283   }
    284 
    285   ImmutableSetMultimap(ImmutableMap<K, ImmutableSet<V>> map, int size) {
    286     super(map, size);
    287   }
    288 
    289   // views
    290 
    291   /**
    292    * Returns an immutable set of the values for the given key.  If no mappings
    293    * in the multimap have the provided key, an empty immutable set is returned.
    294    * The values are in the same order as the parameters used to build this
    295    * multimap.
    296    */
    297   @Override public ImmutableSet<V> get(@Nullable K key) {
    298     // This cast is safe as its type is known in constructor.
    299     ImmutableSet<V> set = (ImmutableSet<V>) map.get(key);
    300     return (set == null) ? ImmutableSet.<V>of() : set;
    301   }
    302 
    303   /**
    304    * Guaranteed to throw an exception and leave the multimap unmodified.
    305    *
    306    * @throws UnsupportedOperationException always
    307    */
    308   @Override public ImmutableSet<V> removeAll(Object key) {
    309     throw new UnsupportedOperationException();
    310   }
    311 
    312   /**
    313    * Guaranteed to throw an exception and leave the multimap unmodified.
    314    *
    315    * @throws UnsupportedOperationException always
    316    */
    317   @Override public ImmutableSet<V> replaceValues(
    318       K key, Iterable<? extends V> values) {
    319     throw new UnsupportedOperationException();
    320   }
    321 
    322   private transient ImmutableSet<Map.Entry<K, V>> entries;
    323 
    324   /**
    325    * Returns an immutable collection of all key-value pairs in the multimap.
    326    * Its iterator traverses the values for the first key, the values for the
    327    * second key, and so on.
    328    */
    329   // TODO: Fix this so that two copies of the entries are not created.
    330   @Override public ImmutableSet<Map.Entry<K, V>> entries() {
    331     ImmutableSet<Map.Entry<K, V>> result = entries;
    332     return (result == null)
    333         ? (entries = ImmutableSet.copyOf(super.entries()))
    334         : result;
    335   }
    336 
    337   /**
    338    * @serialData number of distinct keys, and then for each distinct key: the
    339    *     key, the number of values for that key, and the key's values
    340    */
    341   private void writeObject(ObjectOutputStream stream) throws IOException {
    342     stream.defaultWriteObject();
    343     Serialization.writeMultimap(this, stream);
    344   }
    345 
    346   private void readObject(ObjectInputStream stream)
    347       throws IOException, ClassNotFoundException {
    348     stream.defaultReadObject();
    349     int keyCount = stream.readInt();
    350     if (keyCount < 0) {
    351       throw new InvalidObjectException("Invalid key count " + keyCount);
    352     }
    353     ImmutableMap.Builder<Object, ImmutableSet<Object>> builder
    354         = ImmutableMap.builder();
    355     int tmpSize = 0;
    356 
    357     for (int i = 0; i < keyCount; i++) {
    358       Object key = stream.readObject();
    359       int valueCount = stream.readInt();
    360       if (valueCount <= 0) {
    361         throw new InvalidObjectException("Invalid value count " + valueCount);
    362       }
    363 
    364       Object[] array = new Object[valueCount];
    365       for (int j = 0; j < valueCount; j++) {
    366         array[j] = stream.readObject();
    367       }
    368       ImmutableSet<Object> valueSet = ImmutableSet.of(array);
    369       if (valueSet.size() != array.length) {
    370         throw new InvalidObjectException(
    371             "Duplicate key-value pairs exist for key " + key);
    372       }
    373       builder.put(key, valueSet);
    374       tmpSize += valueCount;
    375     }
    376 
    377     ImmutableMap<Object, ImmutableSet<Object>> tmpMap;
    378     try {
    379       tmpMap = builder.build();
    380     } catch (IllegalArgumentException e) {
    381       throw (InvalidObjectException)
    382           new InvalidObjectException(e.getMessage()).initCause(e);
    383     }
    384 
    385     FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap);
    386     FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize);
    387   }
    388 
    389   private static final long serialVersionUID = 0;
    390 }
    391