001/*
002 * Copyright (C) 2008 The Guava Authors
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
017package com.google.common.collect;
018
019import com.google.common.annotations.Beta;
020import com.google.common.annotations.GwtCompatible;
021import com.google.common.annotations.GwtIncompatible;
022import com.google.errorprone.annotations.CanIgnoreReturnValue;
023import com.google.errorprone.annotations.DoNotCall;
024import com.google.errorprone.annotations.concurrent.LazyInit;
025import com.google.j2objc.annotations.RetainedWith;
026import java.io.IOException;
027import java.io.InvalidObjectException;
028import java.io.ObjectInputStream;
029import java.io.ObjectOutputStream;
030import java.util.Collection;
031import java.util.Comparator;
032import java.util.Map;
033import java.util.Map.Entry;
034import org.checkerframework.checker.nullness.compatqual.NullableDecl;
035
036/**
037 * A {@link ListMultimap} whose contents will never change, with many other important properties
038 * detailed at {@link ImmutableCollection}.
039 *
040 * <p>See the Guava User Guide article on <a href=
041 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained"> immutable collections</a>.
042 *
043 * @author Jared Levy
044 * @since 2.0
045 */
046@GwtCompatible(serializable = true, emulated = true)
047public class ImmutableListMultimap<K, V> extends ImmutableMultimap<K, V>
048    implements ListMultimap<K, V> {
049
050  /** Returns the empty multimap. */
051  // Casting is safe because the multimap will never hold any elements.
052  @SuppressWarnings("unchecked")
053  public static <K, V> ImmutableListMultimap<K, V> of() {
054    return (ImmutableListMultimap<K, V>) EmptyImmutableListMultimap.INSTANCE;
055  }
056
057  /** Returns an immutable multimap containing a single entry. */
058  public static <K, V> ImmutableListMultimap<K, V> of(K k1, V v1) {
059    ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder();
060    builder.put(k1, v1);
061    return builder.build();
062  }
063
064  /** Returns an immutable multimap containing the given entries, in order. */
065  public static <K, V> ImmutableListMultimap<K, V> of(K k1, V v1, K k2, V v2) {
066    ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder();
067    builder.put(k1, v1);
068    builder.put(k2, v2);
069    return builder.build();
070  }
071
072  /** Returns an immutable multimap containing the given entries, in order. */
073  public static <K, V> ImmutableListMultimap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
074    ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder();
075    builder.put(k1, v1);
076    builder.put(k2, v2);
077    builder.put(k3, v3);
078    return builder.build();
079  }
080
081  /** Returns an immutable multimap containing the given entries, in order. */
082  public static <K, V> ImmutableListMultimap<K, V> of(
083      K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
084    ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder();
085    builder.put(k1, v1);
086    builder.put(k2, v2);
087    builder.put(k3, v3);
088    builder.put(k4, v4);
089    return builder.build();
090  }
091
092  /** Returns an immutable multimap containing the given entries, in order. */
093  public static <K, V> ImmutableListMultimap<K, V> of(
094      K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
095    ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder();
096    builder.put(k1, v1);
097    builder.put(k2, v2);
098    builder.put(k3, v3);
099    builder.put(k4, v4);
100    builder.put(k5, v5);
101    return builder.build();
102  }
103
104  // looking for of() with > 5 entries? Use the builder instead.
105
106  /**
107   * Returns a new builder. The generated builder is equivalent to the builder created by the {@link
108   * Builder} constructor.
109   */
110  public static <K, V> Builder<K, V> builder() {
111    return new Builder<>();
112  }
113
114  /**
115   * A builder for creating immutable {@code ListMultimap} instances, especially {@code public
116   * static final} multimaps ("constant multimaps"). Example:
117   *
118   * <pre>{@code
119   * static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP =
120   *     new ImmutableListMultimap.Builder<String, Integer>()
121   *         .put("one", 1)
122   *         .putAll("several", 1, 2, 3)
123   *         .putAll("many", 1, 2, 3, 4, 5)
124   *         .build();
125   * }</pre>
126   *
127   * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build
128   * multiple multimaps in series. Each multimap contains the key-value mappings in the previously
129   * created multimaps.
130   *
131   * @since 2.0
132   */
133  public static final class Builder<K, V> extends ImmutableMultimap.Builder<K, V> {
134    /**
135     * Creates a new builder. The returned builder is equivalent to the builder generated by {@link
136     * ImmutableListMultimap#builder}.
137     */
138    public Builder() {}
139
140    @CanIgnoreReturnValue
141    @Override
142    public Builder<K, V> put(K key, V value) {
143      super.put(key, value);
144      return this;
145    }
146
147    /**
148     * {@inheritDoc}
149     *
150     * @since 11.0
151     */
152    @CanIgnoreReturnValue
153    @Override
154    public Builder<K, V> put(Entry<? extends K, ? extends V> entry) {
155      super.put(entry);
156      return this;
157    }
158
159    /**
160     * {@inheritDoc}
161     *
162     * @since 19.0
163     */
164    @CanIgnoreReturnValue
165    @Beta
166    @Override
167    public Builder<K, V> putAll(Iterable<? extends Entry<? extends K, ? extends V>> entries) {
168      super.putAll(entries);
169      return this;
170    }
171
172    @CanIgnoreReturnValue
173    @Override
174    public Builder<K, V> putAll(K key, Iterable<? extends V> values) {
175      super.putAll(key, values);
176      return this;
177    }
178
179    @CanIgnoreReturnValue
180    @Override
181    public Builder<K, V> putAll(K key, V... values) {
182      super.putAll(key, values);
183      return this;
184    }
185
186    @CanIgnoreReturnValue
187    @Override
188    public Builder<K, V> putAll(Multimap<? extends K, ? extends V> multimap) {
189      super.putAll(multimap);
190      return this;
191    }
192
193    /**
194     * {@inheritDoc}
195     *
196     * @since 8.0
197     */
198    @CanIgnoreReturnValue
199    @Override
200    public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) {
201      super.orderKeysBy(keyComparator);
202      return this;
203    }
204
205    /**
206     * {@inheritDoc}
207     *
208     * @since 8.0
209     */
210    @CanIgnoreReturnValue
211    @Override
212    public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) {
213      super.orderValuesBy(valueComparator);
214      return this;
215    }
216
217    @CanIgnoreReturnValue
218    @Override
219    Builder<K, V> combine(ImmutableMultimap.Builder<K, V> other) {
220      super.combine(other);
221      return this;
222    }
223
224    /** Returns a newly-created immutable list multimap. */
225    @Override
226    public ImmutableListMultimap<K, V> build() {
227      return (ImmutableListMultimap<K, V>) super.build();
228    }
229  }
230
231  /**
232   * Returns an immutable multimap containing the same mappings as {@code multimap}. The generated
233   * multimap's key and value orderings correspond to the iteration ordering of the {@code
234   * multimap.asMap()} view.
235   *
236   * <p>Despite the method name, this method attempts to avoid actually copying the data when it is
237   * safe to do so. The exact circumstances under which a copy will or will not be performed are
238   * undocumented and subject to change.
239   *
240   * @throws NullPointerException if any key or value in {@code multimap} is null
241   */
242  public static <K, V> ImmutableListMultimap<K, V> copyOf(
243      Multimap<? extends K, ? extends V> multimap) {
244    if (multimap.isEmpty()) {
245      return of();
246    }
247
248    // TODO(lowasser): copy ImmutableSetMultimap by using asList() on the sets
249    if (multimap instanceof ImmutableListMultimap) {
250      @SuppressWarnings("unchecked") // safe since multimap is not writable
251      ImmutableListMultimap<K, V> kvMultimap = (ImmutableListMultimap<K, V>) multimap;
252      if (!kvMultimap.isPartialView()) {
253        return kvMultimap;
254      }
255    }
256
257    return fromMapEntries(multimap.asMap().entrySet(), null);
258  }
259
260  /**
261   * Returns an immutable multimap containing the specified entries. The returned multimap iterates
262   * over keys in the order they were first encountered in the input, and the values for each key
263   * are iterated in the order they were encountered.
264   *
265   * @throws NullPointerException if any key, value, or entry is null
266   * @since 19.0
267   */
268  @Beta
269  public static <K, V> ImmutableListMultimap<K, V> copyOf(
270      Iterable<? extends Entry<? extends K, ? extends V>> entries) {
271    return new Builder<K, V>().putAll(entries).build();
272  }
273
274  /** Creates an ImmutableListMultimap from an asMap.entrySet. */
275  static <K, V> ImmutableListMultimap<K, V> fromMapEntries(
276      Collection<? extends Map.Entry<? extends K, ? extends Collection<? extends V>>> mapEntries,
277      @NullableDecl Comparator<? super V> valueComparator) {
278    if (mapEntries.isEmpty()) {
279      return of();
280    }
281    ImmutableMap.Builder<K, ImmutableList<V>> builder =
282        new ImmutableMap.Builder<>(mapEntries.size());
283    int size = 0;
284
285    for (Entry<? extends K, ? extends Collection<? extends V>> entry : mapEntries) {
286      K key = entry.getKey();
287      Collection<? extends V> values = entry.getValue();
288      ImmutableList<V> list =
289          (valueComparator == null)
290              ? ImmutableList.copyOf(values)
291              : ImmutableList.sortedCopyOf(valueComparator, values);
292      if (!list.isEmpty()) {
293        builder.put(key, list);
294        size += list.size();
295      }
296    }
297
298    return new ImmutableListMultimap<>(builder.build(), size);
299  }
300
301  ImmutableListMultimap(ImmutableMap<K, ImmutableList<V>> map, int size) {
302    super(map, size);
303  }
304
305  // views
306
307  /**
308   * Returns an immutable list of the values for the given key. If no mappings in the multimap have
309   * the provided key, an empty immutable list is returned. The values are in the same order as the
310   * parameters used to build this multimap.
311   */
312  @Override
313  public ImmutableList<V> get(@NullableDecl K key) {
314    // This cast is safe as its type is known in constructor.
315    ImmutableList<V> list = (ImmutableList<V>) map.get(key);
316    return (list == null) ? ImmutableList.<V>of() : list;
317  }
318
319  @LazyInit @RetainedWith private transient ImmutableListMultimap<V, K> inverse;
320
321  /**
322   * {@inheritDoc}
323   *
324   * <p>Because an inverse of a list multimap can contain multiple pairs with the same key and
325   * value, this method returns an {@code ImmutableListMultimap} rather than the {@code
326   * ImmutableMultimap} specified in the {@code ImmutableMultimap} class.
327   *
328   * @since 11.0
329   */
330  @Override
331  public ImmutableListMultimap<V, K> inverse() {
332    ImmutableListMultimap<V, K> result = inverse;
333    return (result == null) ? (inverse = invert()) : result;
334  }
335
336  private ImmutableListMultimap<V, K> invert() {
337    Builder<V, K> builder = builder();
338    for (Entry<K, V> entry : entries()) {
339      builder.put(entry.getValue(), entry.getKey());
340    }
341    ImmutableListMultimap<V, K> invertedMultimap = builder.build();
342    invertedMultimap.inverse = this;
343    return invertedMultimap;
344  }
345
346  /**
347   * Guaranteed to throw an exception and leave the multimap unmodified.
348   *
349   * @throws UnsupportedOperationException always
350   * @deprecated Unsupported operation.
351   */
352  @CanIgnoreReturnValue
353  @Deprecated
354  @Override
355  @DoNotCall("Always throws UnsupportedOperationException")
356  public final ImmutableList<V> removeAll(Object key) {
357    throw new UnsupportedOperationException();
358  }
359
360  /**
361   * Guaranteed to throw an exception and leave the multimap unmodified.
362   *
363   * @throws UnsupportedOperationException always
364   * @deprecated Unsupported operation.
365   */
366  @CanIgnoreReturnValue
367  @Deprecated
368  @Override
369  @DoNotCall("Always throws UnsupportedOperationException")
370  public final ImmutableList<V> replaceValues(K key, Iterable<? extends V> values) {
371    throw new UnsupportedOperationException();
372  }
373
374  /**
375   * @serialData number of distinct keys, and then for each distinct key: the key, the number of
376   *     values for that key, and the key's values
377   */
378  @GwtIncompatible // java.io.ObjectOutputStream
379  private void writeObject(ObjectOutputStream stream) throws IOException {
380    stream.defaultWriteObject();
381    Serialization.writeMultimap(this, stream);
382  }
383
384  @GwtIncompatible // java.io.ObjectInputStream
385  private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
386    stream.defaultReadObject();
387    int keyCount = stream.readInt();
388    if (keyCount < 0) {
389      throw new InvalidObjectException("Invalid key count " + keyCount);
390    }
391    ImmutableMap.Builder<Object, ImmutableList<Object>> builder = ImmutableMap.builder();
392    int tmpSize = 0;
393
394    for (int i = 0; i < keyCount; i++) {
395      Object key = stream.readObject();
396      int valueCount = stream.readInt();
397      if (valueCount <= 0) {
398        throw new InvalidObjectException("Invalid value count " + valueCount);
399      }
400
401      ImmutableList.Builder<Object> valuesBuilder = ImmutableList.builder();
402      for (int j = 0; j < valueCount; j++) {
403        valuesBuilder.add(stream.readObject());
404      }
405      builder.put(key, valuesBuilder.build());
406      tmpSize += valueCount;
407    }
408
409    ImmutableMap<Object, ImmutableList<Object>> tmpMap;
410    try {
411      tmpMap = builder.build();
412    } catch (IllegalArgumentException e) {
413      throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e);
414    }
415
416    FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap);
417    FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize);
418  }
419
420  @GwtIncompatible // Not needed in emulated source
421  private static final long serialVersionUID = 0;
422}