/* * Copyright (C) 2007 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 org.glassfish.jersey.internal.guava; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.AbstractCollection; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.function.Supplier; import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; /** * Provides static methods acting on or generating a {@code Multimap}. * <p> * <p>See the Guava User Guide article on <a href= * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Multimaps"> * {@code Multimaps}</a>. * * @author Jared Levy * @author Robert Konigsberg * @author Mike Bostock * @author Louis Wasserman * @since 2.0 (imported from Google Collections Library) */ public final class Multimaps { private Multimaps() { } /** * Creates a new {@code ListMultimap} that uses the provided map and factory. * It can generate a multimap based on arbitrary {@link Map} and {@link List} * classes. * <p> * <p>The {@code factory}-generated and {@code map} classes determine the * multimap iteration order. They also specify the behavior of the * {@code equals}, {@code hashCode}, and {@code toString} methods for the * multimap and its returned views. The multimap's {@code get}, {@code * removeAll}, and {@code replaceValues} methods return {@code RandomAccess} * lists if the factory does. However, the multimap's {@code get} method * returns instances of a different class than does {@code factory.get()}. * <p> * <p>The multimap is serializable if {@code map}, {@code factory}, the * lists generated by {@code factory}, and the multimap contents are all * serializable. * <p> * <p>The multimap is not threadsafe when any concurrent operations update the * multimap, even if {@code map} and the instances generated by * {@code factory} are. Concurrent read operations will work correctly. To * allow concurrent update operations, wrap the multimap with a call to * {@link #synchronizedListMultimap}. * <p> * <p>Call this method only when the simpler methods * {@link ArrayListMultimap#create()} and {@link LinkedListMultimap#create()} * won't suffice. * <p> * <p>Note: the multimap assumes complete ownership over of {@code map} and * the lists returned by {@code factory}. Those objects should not be manually * updated, they should be empty when provided, and they should not use soft, * weak, or phantom references. * * @param map place to store the mapping from each key to its corresponding * values * @param factory supplier of new, empty lists that will each hold all values * for a given key * @throws IllegalArgumentException if {@code map} is not empty */ public static <K, V> ListMultimap<K, V> newListMultimap( Map<K, Collection<V>> map, final Supplier<? extends List<V>> factory) { return new CustomListMultimap<K, V>(map, factory); } static boolean equalsImpl(Multimap<?, ?> multimap, Object object) { if (object == multimap) { return true; } if (object instanceof Multimap) { Multimap<?, ?> that = (Multimap<?, ?>) object; return multimap.asMap().equals(that.asMap()); } return false; } private static class CustomListMultimap<K, V> extends AbstractListMultimap<K, V> { private static final long serialVersionUID = 0; transient Supplier<? extends List<V>> factory; CustomListMultimap(Map<K, Collection<V>> map, Supplier<? extends List<V>> factory) { super(map); this.factory = checkNotNull(factory); } @Override protected List<V> createCollection() { return factory.get(); } /** * @serialData the factory and the backing map */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(factory); stream.writeObject(backingMap()); } @SuppressWarnings("unchecked") // reading data stored by writeObject private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); factory = (Supplier<? extends List<V>>) stream.readObject(); Map<K, Collection<V>> map = (Map<K, Collection<V>>) stream.readObject(); setMap(map); } } /** * A skeleton implementation of {@link Multimap#entries()}. */ abstract static class Entries<K, V> extends AbstractCollection<Entry<K, V>> { abstract Multimap<K, V> multimap(); @Override public int size() { return multimap().size(); } @Override public boolean contains(Object o) { if (o instanceof Map.Entry) { Entry<?, ?> entry = (Entry<?, ?>) o; return multimap().containsEntry(entry.getKey(), entry.getValue()); } return false; } @Override public boolean remove(Object o) { if (o instanceof Map.Entry) { Entry<?, ?> entry = (Entry<?, ?>) o; return multimap().remove(entry.getKey(), entry.getValue()); } return false; } @Override public void clear() { multimap().clear(); } } // TODO(jlevy): Create methods that filter a SortedSetMultimap. }