From 4eceb95409e844fdc33c9c706e1dc307bfd40303 Mon Sep 17 00:00:00 2001 From: Yohann Roussel Date: Wed, 19 Mar 2014 16:25:37 +0100 Subject: Initial Jack import. Change-Id: I953cf0a520195a7187d791b2885848ad0d5a9b43 --- .../google/common/collect/ImmutableMultimap.java | 682 +++++++++++++++++++++ 1 file changed, 682 insertions(+) create mode 100644 guava/src/com/google/common/collect/ImmutableMultimap.java (limited to 'guava/src/com/google/common/collect/ImmutableMultimap.java') diff --git a/guava/src/com/google/common/collect/ImmutableMultimap.java b/guava/src/com/google/common/collect/ImmutableMultimap.java new file mode 100644 index 0000000..8b6aa29 --- /dev/null +++ b/guava/src/com/google/common/collect/ImmutableMultimap.java @@ -0,0 +1,682 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * An immutable {@link Multimap}. Does not permit null keys or values. + * + *

Unlike {@link Multimaps#unmodifiableMultimap(Multimap)}, which is + * a view of a separate multimap which can still change, an instance of + * {@code ImmutableMultimap} contains its own data and will never + * change. {@code ImmutableMultimap} is convenient for + * {@code public static final} multimaps ("constant multimaps") and also lets + * you easily make a "defensive copy" of a multimap provided to your class by + * a caller. + * + *

Note: Although this class is not final, it cannot be subclassed as + * it has no public or protected constructors. Thus, instances of this class + * are guaranteed to be immutable. + * + *

In addition to methods defined by {@link Multimap}, an {@link #inverse} + * method is also supported. + * + *

See the Guava User Guide article on + * immutable collections. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(emulated = true) +public abstract class ImmutableMultimap + implements Multimap, Serializable { + + /** Returns an empty multimap. */ + public static ImmutableMultimap of() { + return ImmutableListMultimap.of(); + } + + /** + * Returns an immutable multimap containing a single entry. + */ + public static ImmutableMultimap of(K k1, V v1) { + return ImmutableListMultimap.of(k1, v1); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableMultimap of(K k1, V v1, K k2, V v2) { + return ImmutableListMultimap.of(k1, v1, k2, v2); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableMultimap of( + K k1, V v1, K k2, V v2, K k3, V v3) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableMultimap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableMultimap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); + } + + // looking for of() with > 5 entries? Use the builder instead. + + /** + * Returns a new builder. The generated builder is equivalent to the builder + * created by the {@link Builder} constructor. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Multimap for {@link ImmutableMultimap.Builder} that maintains key and + * value orderings, allows duplicate values, and performs better than + * {@link LinkedListMultimap}. + */ + private static class BuilderMultimap extends AbstractMultimap { + BuilderMultimap() { + super(new LinkedHashMap>()); + } + @Override Collection createCollection() { + return Lists.newArrayList(); + } + private static final long serialVersionUID = 0; + } + + /** + * A builder for creating immutable multimap instances, especially + * {@code public static final} multimaps ("constant multimaps"). Example: + *

   {@code
+   *
+   *   static final Multimap STRING_TO_INTEGER_MULTIMAP =
+   *       new ImmutableMultimap.Builder()
+   *           .put("one", 1)
+   *           .putAll("several", 1, 2, 3)
+   *           .putAll("many", 1, 2, 3, 4, 5)
+   *           .build();}
+ * + * Builder instances can be reused; it is safe to call {@link #build} multiple + * times to build multiple multimaps in series. Each multimap contains the + * key-value mappings in the previously created multimaps. + * + * @since 2.0 (imported from Google Collections Library) + */ + public static class Builder { + Multimap builderMultimap = new BuilderMultimap(); + Comparator keyComparator; + Comparator valueComparator; + + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableMultimap#builder}. + */ + public Builder() {} + + /** + * Adds a key-value mapping to the built multimap. + */ + public Builder put(K key, V value) { + builderMultimap.put(checkNotNull(key), checkNotNull(value)); + return this; + } + + /** + * Adds an entry to the built multimap. + * + * @since 11.0 + */ + public Builder put(Entry entry) { + builderMultimap.put( + checkNotNull(entry.getKey()), checkNotNull(entry.getValue())); + return this; + } + + /** + * Stores a collection of values with the same key in the built multimap. + * + * @throws NullPointerException if {@code key}, {@code values}, or any + * element in {@code values} is null. The builder is left in an invalid + * state. + */ + public Builder putAll(K key, Iterable values) { + Collection valueList = builderMultimap.get(checkNotNull(key)); + for (V value : values) { + valueList.add(checkNotNull(value)); + } + return this; + } + + /** + * Stores an array of values with the same key in the built multimap. + * + * @throws NullPointerException if the key or any value is null. The builder + * is left in an invalid state. + */ + public Builder putAll(K key, V... values) { + return putAll(key, Arrays.asList(values)); + } + + /** + * Stores another multimap's entries in the built multimap. The generated + * multimap's key and value orderings correspond to the iteration ordering + * of the {@code multimap.asMap()} view, with new keys and values following + * any existing keys and values. + * + * @throws NullPointerException if any key or value in {@code multimap} is + * null. The builder is left in an invalid state. + */ + public Builder putAll(Multimap multimap) { + for (Entry> entry + : multimap.asMap().entrySet()) { + putAll(entry.getKey(), entry.getValue()); + } + return this; + } + + /** + * Specifies the ordering of the generated multimap's keys. + * + * @since 8.0 + */ + @Beta + public Builder orderKeysBy(Comparator keyComparator) { + this.keyComparator = checkNotNull(keyComparator); + return this; + } + + /** + * Specifies the ordering of the generated multimap's values for each key. + * + * @since 8.0 + */ + @Beta + public Builder orderValuesBy(Comparator valueComparator) { + this.valueComparator = checkNotNull(valueComparator); + return this; + } + + /** + * Returns a newly-created immutable multimap. + */ + public ImmutableMultimap build() { + if (valueComparator != null) { + for (Collection values : builderMultimap.asMap().values()) { + List list = (List ) values; + Collections.sort(list, valueComparator); + } + } + if (keyComparator != null) { + Multimap sortedCopy = new BuilderMultimap(); + List>> entries = Lists.newArrayList( + builderMultimap.asMap().entrySet()); + Collections.sort( + entries, + Ordering.from(keyComparator).onResultOf(new Function>, K>() { + @Override + public K apply(Entry> entry) { + return entry.getKey(); + } + })); + for (Map.Entry> entry : entries) { + sortedCopy.putAll(entry.getKey(), entry.getValue()); + } + builderMultimap = sortedCopy; + } + return copyOf(builderMultimap); + } + } + + /** + * Returns an immutable multimap containing the same mappings as {@code + * multimap}. The generated multimap's key and value orderings correspond to + * the iteration ordering of the {@code multimap.asMap()} view. + * + *

Despite the method name, this method attempts to avoid actually copying + * the data when it is safe to do so. The exact circumstances under which a + * copy will or will not be performed are undocumented and subject to change. + * + * @throws NullPointerException if any key or value in {@code multimap} is + * null + */ + public static ImmutableMultimap copyOf( + Multimap multimap) { + if (multimap instanceof ImmutableMultimap) { + @SuppressWarnings("unchecked") // safe since multimap is not writable + ImmutableMultimap kvMultimap + = (ImmutableMultimap) multimap; + if (!kvMultimap.isPartialView()) { + return kvMultimap; + } + } + return ImmutableListMultimap.copyOf(multimap); + } + + final transient ImmutableMap> map; + final transient int size; + + // These constants allow the deserialization code to set final fields. This + // holder class makes sure they are not initialized unless an instance is + // deserialized. + @GwtIncompatible("java serialization is not supported") + static class FieldSettersHolder { + static final Serialization.FieldSetter + MAP_FIELD_SETTER = Serialization.getFieldSetter( + ImmutableMultimap.class, "map"); + static final Serialization.FieldSetter + SIZE_FIELD_SETTER = Serialization.getFieldSetter( + ImmutableMultimap.class, "size"); + } + + ImmutableMultimap(ImmutableMap> map, + int size) { + this.map = map; + this.size = size; + } + + // mutators (not supported) + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public ImmutableCollection removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public ImmutableCollection replaceValues(K key, + Iterable values) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Returns an immutable collection of the values for the given key. If no + * mappings in the multimap have the provided key, an empty immutable + * collection is returned. The values are in the same order as the parameters + * used to build this multimap. + */ + @Override + public abstract ImmutableCollection get(K key); + + /** + * Returns an immutable multimap which is the inverse of this one. For every + * key-value mapping in the original, the result will have a mapping with + * key and value reversed. + * + * @since 11.0 + */ + @Beta + public abstract ImmutableMultimap inverse(); + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public boolean put(K key, V value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public boolean putAll(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + boolean isPartialView() { + return map.isPartialView(); + } + + // accessors + + @Override + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { + Collection values = map.get(key); + return values != null && values.contains(value); + } + + @Override + public boolean containsKey(@Nullable Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(@Nullable Object value) { + for (Collection valueCollection : map.values()) { + if (valueCollection.contains(value)) { + return true; + } + } + return false; + } + + @Override + public boolean isEmpty() { + return size == 0; + } + + @Override + public int size() { + return size; + } + + @Override public boolean equals(@Nullable Object object) { + if (object instanceof Multimap) { + Multimap that = (Multimap) object; + return this.map.equals(that.asMap()); + } + return false; + } + + @Override public int hashCode() { + return map.hashCode(); + } + + @Override public String toString() { + return map.toString(); + } + + // views + + /** + * Returns an immutable set of the distinct keys in this multimap. These keys + * are ordered according to when they first appeared during the construction + * of this multimap. + */ + @Override + public ImmutableSet keySet() { + return map.keySet(); + } + + /** + * Returns an immutable map that associates each key with its corresponding + * values in the multimap. + */ + @Override + @SuppressWarnings("unchecked") // a widening cast + public ImmutableMap> asMap() { + return (ImmutableMap) map; + } + + private transient ImmutableCollection> entries; + + /** + * Returns an immutable collection of all key-value pairs in the multimap. Its + * iterator traverses the values for the first key, the values for the second + * key, and so on. + */ + @Override + public ImmutableCollection> entries() { + ImmutableCollection> result = entries; + return (result == null) + ? (entries = new EntryCollection(this)) : result; + } + + private static class EntryCollection + extends ImmutableCollection> { + final ImmutableMultimap multimap; + + EntryCollection(ImmutableMultimap multimap) { + this.multimap = multimap; + } + + @Override public UnmodifiableIterator> iterator() { + final Iterator>> + mapIterator = this.multimap.map.entrySet().iterator(); + + return new UnmodifiableIterator>() { + K key; + Iterator valueIterator; + + @Override + public boolean hasNext() { + return (key != null && valueIterator.hasNext()) + || mapIterator.hasNext(); + } + + @Override + public Entry next() { + if (key == null || !valueIterator.hasNext()) { + Entry> entry + = mapIterator.next(); + key = entry.getKey(); + valueIterator = entry.getValue().iterator(); + } + return Maps.immutableEntry(key, valueIterator.next()); + } + }; + } + + @Override boolean isPartialView() { + return multimap.isPartialView(); + } + + @Override + public int size() { + return multimap.size(); + } + + @Override public boolean contains(Object object) { + if (object instanceof Entry) { + Entry entry = (Entry) object; + return multimap.containsEntry(entry.getKey(), entry.getValue()); + } + return false; + } + + private static final long serialVersionUID = 0; + } + + private transient ImmutableMultiset keys; + + /** + * Returns a collection, which may contain duplicates, of all keys. The number + * of times a key appears in the returned multiset equals the number of + * mappings the key has in the multimap. Duplicate keys appear consecutively + * in the multiset's iteration order. + */ + @Override + public ImmutableMultiset keys() { + ImmutableMultiset result = keys; + return (result == null) ? (keys = createKeys()) : result; + } + + private ImmutableMultiset createKeys() { + return new Keys(); + } + + @SuppressWarnings("serial") // Uses writeReplace, not default serialization + class Keys extends ImmutableMultiset { + @Override + public boolean contains(@Nullable Object object) { + return containsKey(object); + } + + @Override + public int count(@Nullable Object element) { + Collection values = map.get(element); + return (values == null) ? 0 : values.size(); + } + + @Override + public Set elementSet() { + return keySet(); + } + + @Override + public int size() { + return ImmutableMultimap.this.size(); + } + + @Override + ImmutableSet> createEntrySet() { + return new KeysEntrySet(); + } + + private class KeysEntrySet extends ImmutableMultiset.EntrySet { + @Override + public int size() { + return keySet().size(); + } + + @Override + public UnmodifiableIterator> iterator() { + return asList().iterator(); + } + + @Override + ImmutableList> createAsList() { + final ImmutableList>> mapEntries = + map.entrySet().asList(); + return new ImmutableAsList>() { + @Override + public Entry get(int index) { + Map.Entry> entry = mapEntries.get(index); + return Multisets.immutableEntry(entry.getKey(), entry.getValue().size()); + } + + @Override + ImmutableCollection> delegateCollection() { + return KeysEntrySet.this; + } + }; + } + } + + @Override + boolean isPartialView() { + return true; + } + } + + private transient ImmutableCollection values; + + /** + * Returns an immutable collection of the values in this multimap. Its + * iterator traverses the values for the first key, the values for the second + * key, and so on. + */ + @Override + public ImmutableCollection values() { + ImmutableCollection result = values; + return (result == null) ? (values = new Values(this)) : result; + } + + private static class Values extends ImmutableCollection { + final ImmutableMultimap multimap; + + Values(ImmutableMultimap multimap) { + this.multimap = multimap; + } + + @Override public UnmodifiableIterator iterator() { + return Maps.valueIterator(multimap.entries().iterator()); + } + + @Override + public int size() { + return multimap.size(); + } + + @Override boolean isPartialView() { + return true; + } + + private static final long serialVersionUID = 0; + } + + private static final long serialVersionUID = 0; +} -- cgit v1.1