001    /*
002     * Copyright (C) 2008 Google Inc.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package com.google.common.collect;
018    
019    import com.google.common.annotations.GwtCompatible;
020    import com.google.common.annotations.GwtIncompatible;
021    
022    import java.io.IOException;
023    import java.io.InvalidObjectException;
024    import java.io.ObjectInputStream;
025    import java.io.ObjectOutputStream;
026    import java.util.Collection;
027    import java.util.Map;
028    
029    import javax.annotation.Nullable;
030    
031    /**
032     * An immutable {@link ListMultimap} with reliable user-specified key and value
033     * iteration order. Does not permit null keys or values.
034     *
035     * <p>Unlike {@link Multimaps#unmodifiableListMultimap(ListMultimap)}, which is
036     * a <i>view</i> of a separate multimap which can still change, an instance of
037     * {@code ImmutableListMultimap} contains its own data and will <i>never</i>
038     * change. {@code ImmutableListMultimap} is convenient for
039     * {@code public static final} multimaps ("constant multimaps") and also lets
040     * you easily make a "defensive copy" of a multimap provided to your class by
041     * a caller.
042     *
043     * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as
044     * it has no public or protected constructors. Thus, instances of this class
045     * are guaranteed to be immutable.
046     *
047     * @author Jared Levy
048     * @since 2 (imported from Google Collections Library)
049     */
050    @GwtCompatible(serializable = true, emulated = true)
051    public class ImmutableListMultimap<K, V>
052        extends ImmutableMultimap<K, V>
053        implements ListMultimap<K, V> {
054    
055      /** Returns the empty multimap. */
056      // Casting is safe because the multimap will never hold any elements.
057      @SuppressWarnings("unchecked")
058      public static <K, V> ImmutableListMultimap<K, V> of() {
059        return (ImmutableListMultimap<K, V>) EmptyImmutableListMultimap.INSTANCE;
060      }
061    
062      /**
063       * Returns an immutable multimap containing a single entry.
064       */
065      public static <K, V> ImmutableListMultimap<K, V> of(K k1, V v1) {
066        ImmutableListMultimap.Builder<K, V> builder
067            = ImmutableListMultimap.builder();
068        builder.put(k1, v1);
069        return builder.build();
070      }
071    
072      /**
073       * Returns an immutable multimap containing the given entries, in order.
074       */
075      public static <K, V> ImmutableListMultimap<K, V> of(K k1, V v1, K k2, V v2) {
076        ImmutableListMultimap.Builder<K, V> builder
077            = ImmutableListMultimap.builder();
078        builder.put(k1, v1);
079        builder.put(k2, v2);
080        return builder.build();
081      }
082    
083      /**
084       * Returns an immutable multimap containing the given entries, in order.
085       */
086      public static <K, V> ImmutableListMultimap<K, V> of(
087          K k1, V v1, K k2, V v2, K k3, V v3) {
088        ImmutableListMultimap.Builder<K, V> builder
089            = ImmutableListMultimap.builder();
090        builder.put(k1, v1);
091        builder.put(k2, v2);
092        builder.put(k3, v3);
093        return builder.build();
094      }
095    
096      /**
097       * Returns an immutable multimap containing the given entries, in order.
098       */
099      public static <K, V> ImmutableListMultimap<K, V> of(
100          K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
101        ImmutableListMultimap.Builder<K, V> builder
102            = ImmutableListMultimap.builder();
103        builder.put(k1, v1);
104        builder.put(k2, v2);
105        builder.put(k3, v3);
106        builder.put(k4, v4);
107        return builder.build();
108      }
109    
110      /**
111       * Returns an immutable multimap containing the given entries, in order.
112       */
113      public static <K, V> ImmutableListMultimap<K, V> of(
114          K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
115        ImmutableListMultimap.Builder<K, V> builder
116            = ImmutableListMultimap.builder();
117        builder.put(k1, v1);
118        builder.put(k2, v2);
119        builder.put(k3, v3);
120        builder.put(k4, v4);
121        builder.put(k5, v5);
122        return builder.build();
123      }
124    
125      // looking for of() with > 5 entries? Use the builder instead.
126    
127      /**
128       * Returns a new builder. The generated builder is equivalent to the builder
129       * created by the {@link Builder} constructor.
130       */
131      public static <K, V> Builder<K, V> builder() {
132        return new Builder<K, V>();
133      }
134    
135      /**
136       * A builder for creating immutable {@code ListMultimap} instances, especially
137       * {@code public static final} multimaps ("constant multimaps"). Example:
138       * <pre>   {@code
139       *
140       *   static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP =
141       *       new ImmutableListMultimap.Builder<String, Integer>()
142       *           .put("one", 1)
143       *           .putAll("several", 1, 2, 3)
144       *           .putAll("many", 1, 2, 3, 4, 5)
145       *           .build();}</pre>
146       *
147       * <p>Builder instances can be reused - it is safe to call {@link #build}
148       * multiple times to build multiple multimaps in series. Each multimap
149       * contains the key-value mappings in the previously created multimaps.
150       */
151      public static final class Builder<K, V>
152          extends ImmutableMultimap.Builder<K, V> {
153        /**
154         * Creates a new builder. The returned builder is equivalent to the builder
155         * generated by {@link ImmutableListMultimap#builder}.
156         */
157        public Builder() {}
158    
159        /**
160         * Adds a key-value mapping to the built multimap.
161         */
162        @Override public Builder<K, V> put(K key, V value) {
163          super.put(key, value);
164          return this;
165        }
166    
167        /**
168         * Stores a collection of values with the same key in the built multimap.
169         *
170         * @throws NullPointerException if {@code key}, {@code values}, or any
171         *     element in {@code values} is null. The builder is left in an invalid
172         *     state.
173         */
174        @Override public Builder<K, V> putAll(K key, Iterable<? extends V> values) {
175          super.putAll(key, values);
176          return this;
177        }
178    
179        /**
180         * Stores an array of values with the same key in the built multimap.
181         *
182         * @throws NullPointerException if the key or any value is null. The builder
183         *     is left in an invalid state.
184         */
185        @Override public Builder<K, V> putAll(K key, V... values) {
186          super.putAll(key, values);
187          return this;
188        }
189    
190        /**
191         * Stores another multimap's entries in the built multimap. The generated
192         * multimap's key and value orderings correspond to the iteration ordering
193         * of the {@code multimap.asMap()} view, with new keys and values following
194         * any existing keys and values.
195         *
196         * @throws NullPointerException if any key or value in {@code multimap} is
197         *     null. The builder is left in an invalid state.
198         */
199        @Override public Builder<K, V> putAll(
200            Multimap<? extends K, ? extends V> multimap) {
201          super.putAll(multimap);
202          return this;
203        }
204    
205        /**
206         * Returns a newly-created immutable multimap.
207         */
208        @Override public ImmutableListMultimap<K, V> build() {
209          return (ImmutableListMultimap<K, V>) super.build();
210        }
211      }
212    
213      /**
214       * Returns an immutable multimap containing the same mappings as
215       * {@code multimap}. The generated multimap's key and value orderings
216       * correspond to the iteration ordering of the {@code multimap.asMap()} view.
217       *
218       * <p><b>Note:</b> Despite what the method name suggests, if
219       * {@code multimap} is an {@code ImmutableListMultimap}, no copy will actually
220       * be performed, and the given multimap itself will be returned.
221       *
222       * @throws NullPointerException if any key or value in {@code multimap} is
223       *     null
224       */
225      public static <K, V> ImmutableListMultimap<K, V> copyOf(
226          Multimap<? extends K, ? extends V> multimap) {
227        if (multimap.isEmpty()) {
228          return of();
229        }
230    
231        if (multimap instanceof ImmutableListMultimap) {
232          @SuppressWarnings("unchecked") // safe since multimap is not writable
233          ImmutableListMultimap<K, V> kvMultimap
234              = (ImmutableListMultimap<K, V>) multimap;
235          return kvMultimap;
236        }
237    
238        ImmutableMap.Builder<K, ImmutableList<V>> builder = ImmutableMap.builder();
239        int size = 0;
240    
241        for (Map.Entry<? extends K, ? extends Collection<? extends V>> entry
242            : multimap.asMap().entrySet()) {
243          ImmutableList<V> list = ImmutableList.copyOf(entry.getValue());
244          if (!list.isEmpty()) {
245            builder.put(entry.getKey(), list);
246            size += list.size();
247          }
248        }
249    
250        return new ImmutableListMultimap<K, V>(builder.build(), size);
251      }
252    
253      ImmutableListMultimap(ImmutableMap<K, ImmutableList<V>> map, int size) {
254        super(map, size);
255      }
256    
257      // views
258    
259      /**
260       * Returns an immutable list of the values for the given key.  If no mappings
261       * in the multimap have the provided key, an empty immutable list is
262       * returned. The values are in the same order as the parameters used to build
263       * this multimap.
264       */
265      @Override public ImmutableList<V> get(@Nullable K key) {
266        // This cast is safe as its type is known in constructor.
267        ImmutableList<V> list = (ImmutableList<V>) map.get(key);
268        return (list == null) ? ImmutableList.<V>of() : list;
269      }
270    
271      /**
272       * Guaranteed to throw an exception and leave the multimap unmodified.
273       *
274       * @throws UnsupportedOperationException always
275       */
276      @Override public ImmutableList<V> removeAll(Object key) {
277        throw new UnsupportedOperationException();
278      }
279    
280      /**
281       * Guaranteed to throw an exception and leave the multimap unmodified.
282       *
283       * @throws UnsupportedOperationException always
284       */
285      @Override public ImmutableList<V> replaceValues(
286          K key, Iterable<? extends V> values) {
287        throw new UnsupportedOperationException();
288      }
289    
290      /**
291       * @serialData number of distinct keys, and then for each distinct key: the
292       *     key, the number of values for that key, and the key's values
293       */
294      @GwtIncompatible("java.io.ObjectOutputStream")
295      private void writeObject(ObjectOutputStream stream) throws IOException {
296        stream.defaultWriteObject();
297        Serialization.writeMultimap(this, stream);
298      }
299    
300      @GwtIncompatible("java.io.ObjectInputStream")
301      private void readObject(ObjectInputStream stream)
302          throws IOException, ClassNotFoundException {
303        stream.defaultReadObject();
304        int keyCount = stream.readInt();
305        if (keyCount < 0) {
306          throw new InvalidObjectException("Invalid key count " + keyCount);
307        }
308        ImmutableMap.Builder<Object, ImmutableList<Object>> builder
309            = ImmutableMap.builder();
310        int tmpSize = 0;
311    
312        for (int i = 0; i < keyCount; i++) {
313          Object key = stream.readObject();
314          int valueCount = stream.readInt();
315          if (valueCount <= 0) {
316            throw new InvalidObjectException("Invalid value count " + valueCount);
317          }
318    
319          Object[] array = new Object[valueCount];
320          for (int j = 0; j < valueCount; j++) {
321            array[j] = stream.readObject();
322          }
323          builder.put(key, ImmutableList.copyOf(array));
324          tmpSize += valueCount;
325        }
326    
327        ImmutableMap<Object, ImmutableList<Object>> tmpMap;
328        try {
329          tmpMap = builder.build();
330        } catch (IllegalArgumentException e) {
331          throw (InvalidObjectException)
332              new InvalidObjectException(e.getMessage()).initCause(e);
333        }
334    
335        FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap);
336        FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize);
337      }
338    
339      @GwtIncompatible("Not needed in emulated source")
340      private static final long serialVersionUID = 0;
341    }