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 }