/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * 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.jboss.solder.util.collections; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Collection; import java.util.Map; import java.util.Set; import static org.jboss.solder.util.collections.Preconditions.checkNotNull; public class Multimaps { private Multimaps() { } private static class CustomSetMultimap<K, V> extends AbstractSetMultimap<K, V> { transient Supplier<? extends Set<V>> factory; CustomSetMultimap(Map<K, Collection<V>> map, Supplier<? extends Set<V>> factory) { super(map); this.factory = checkNotNull(factory); } @Override protected Set<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 Set<V>>) stream.readObject(); Map<K, Collection<V>> map = (Map<K, Collection<V>>) stream.readObject(); setMap(map); } private static final long serialVersionUID = 0; } /** * Creates a new {@code SetMultimap} that uses the provided map and factory. * It can generate a multimap based on arbitrary {@link Map} and {@link Set} * 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. However, the multimap's {@code get} method returns * instances of a different class than {@code factory.get()} does. * <p/> * <p/> * The multimap is serializable if {@code map}, {@code factory}, the sets * 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 #synchronizedSetMultimap}. * <p/> * <p/> * Call this method only when the simpler methods HashMultimap#create(), * LinkedHashMultimap#create(), TreeMultimap#create(), and * TreeMultimap#create(Comparator, Comparator) won't suffice. * <p/> * <p/> * Note: the multimap assumes complete ownership over of {@code map} and the * sets returned by {@code factory}. Those objects should not be manually * updated 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 sets that will each hold all values * for a given key * @throws IllegalArgumentException if {@code map} is not empty */ public static <K, V> SetMultimap<K, V> newSetMultimap(Map<K, Collection<V>> map, final Supplier<? extends Set<V>> factory) { return new CustomSetMultimap<K, V>(map, factory); } }