home | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (MapBinder.java)

This example Java source code file (MapBinder.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

annotation, binding, class, immutableset, key, map, mapbinder, override, provider, providermapentry, realmapbinder, set, suppresswarnings, typeliteral, util

The MapBinder.java Java example source code

/**
 * Copyright (C) 2008 Google Inc.
 *
 * 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.inject.multibindings;

import static com.google.inject.multibindings.Element.Type.MAPBINDER;
import static com.google.inject.multibindings.Multibinder.checkConfiguration;
import static com.google.inject.multibindings.Multibinder.checkNotNull;
import static com.google.inject.multibindings.Multibinder.setOf;
import static com.google.inject.util.Types.newParameterizedType;
import static com.google.inject.util.Types.newParameterizedTypeWithOwner;

import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Supplier;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.Errors;
import com.google.inject.multibindings.Indexer.IndexedBinding;
import com.google.inject.multibindings.Multibinder.RealMultibinder;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderLookup;
import com.google.inject.spi.ProviderWithDependencies;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.Toolable;
import com.google.inject.util.Types;

import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * An API to bind multiple map entries separately, only to later inject them as
 * a complete map. MapBinder is intended for use in your application's module:
 * <pre>
 * public class SnacksModule extends AbstractModule {
 *   protected void configure() {
 *     MapBinder<String, Snack> mapbinder
 *         = MapBinder.newMapBinder(binder(), String.class, Snack.class);
 *     mapbinder.addBinding("twix").toInstance(new Twix());
 *     mapbinder.addBinding("snickers").toProvider(SnickersProvider.class);
 *     mapbinder.addBinding("skittles").to(Skittles.class);
 *   }
 * }</code>
* * <p>With this binding, a {@link Map}{@code } can now be * injected: * <pre> * class SnackMachine { * {@literal @}Inject * public SnackMachine(Map<String, Snack> snacks) { ... } * }</code> * * <p>In addition to binding {@code Map}, a mapbinder will also bind * {@code Map<K, Provider} for lazy value provision: * <pre> * class SnackMachine { * {@literal @}Inject * public SnackMachine(Map<String, Provider<Snack>> snackProviders) { ... } * }</code> * * <p>Contributing mapbindings from different modules is supported. For example, * it is okay to have both {@code CandyModule} and {@code ChipsModule} both * create their own {@code MapBinder<String, Snack>}, and to each contribute * bindings to the snacks map. When that map is injected, it will contain * entries from both modules. * * <p>The map's iteration order is consistent with the binding order. This is * convenient when multiple elements are contributed by the same module because * that module can order its bindings appropriately. Avoid relying on the * iteration order of elements contributed by different modules, since there is * no equivalent mechanism to order modules. * * <p>The map is unmodifiable. Elements can only be added to the map by * configuring the MapBinder. Elements can never be removed from the map. * * <p>Values are resolved at map injection time. If a value is bound to a * provider, that provider's get method will be called each time the map is * injected (unless the binding is also scoped, or a map of providers is injected). * * <p>Annotations are used to create different maps of the same key/value * type. Each distinct annotation gets its own independent map. * * <p>Keys must be distinct. If the same key is bound more than * once, map injection will fail. However, use {@link #permitDuplicates()} in * order to allow duplicate keys; extra bindings to {@code Map<K, Set} and * {@code Map<K, Set} will be added. * * <p>Keys must be non-null. {@code addBinding(null)} will * throw an unchecked exception. * * <p>Values must be non-null to use map injection. If any * value is null, map injection will fail (although injecting a map of providers * will not). * * @author dpb@google.com (David P. Baker) */ public abstract class MapBinder<K, V> { private MapBinder() {} /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with no binding annotation. */ public static <K, V> MapBinder newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral valueType) { binder = binder.skipSources(MapBinder.class, RealMapBinder.class); return newRealMapBinder(binder, keyType, valueType, Key.get(mapOf(keyType, valueType)), Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType))); } /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with no binding annotation. */ public static <K, V> MapBinder newMapBinder(Binder binder, Class<K> keyType, Class valueType) { return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType)); } /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with {@code annotation}. */ public static <K, V> MapBinder newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral valueType, Annotation annotation) { binder = binder.skipSources(MapBinder.class, RealMapBinder.class); return newRealMapBinder(binder, keyType, valueType, Key.get(mapOf(keyType, valueType), annotation), Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType), annotation)); } /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with {@code annotation}. */ public static <K, V> MapBinder newMapBinder(Binder binder, Class<K> keyType, Class valueType, Annotation annotation) { return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotation); } /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with {@code annotationType}. */ public static <K, V> MapBinder newMapBinder(Binder binder, TypeLiteral keyType, TypeLiteral<V> valueType, Class annotationType) { binder = binder.skipSources(MapBinder.class, RealMapBinder.class); return newRealMapBinder(binder, keyType, valueType, Key.get(mapOf(keyType, valueType), annotationType), Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType), annotationType)); } /** * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a * {@link Map} that is itself bound with {@code annotationType}. */ public static <K, V> MapBinder newMapBinder(Binder binder, Class keyType, Class<V> valueType, Class annotationType) { return newMapBinder( binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotationType); } @SuppressWarnings("unchecked") // a map of <K, V> is safely a Map static <K, V> TypeLiteral> mapOf( TypeLiteral<K> keyType, TypeLiteral valueType) { return (TypeLiteral<Map) TypeLiteral.get( Types.mapOf(keyType.getType(), valueType.getType())); } @SuppressWarnings("unchecked") // a provider map <K, V> is safely a Map> static <K, V> TypeLiteral>> mapOfProviderOf( TypeLiteral<K> keyType, TypeLiteral valueType) { return (TypeLiteral<Map>) TypeLiteral.get( Types.mapOf(keyType.getType(), Types.providerOf(valueType.getType()))); } // provider map <K, V> is safely a Map>> @SuppressWarnings("unchecked") static <K, V> TypeLiteral>> mapOfJavaxProviderOf( TypeLiteral<K> keyType, TypeLiteral valueType) { return (TypeLiteral<Map>) TypeLiteral.get( Types.mapOf(keyType.getType(), newParameterizedType(javax.inject.Provider.class, valueType.getType()))); } @SuppressWarnings("unchecked") // a provider map <K, Set is safely a Map>> static <K, V> TypeLiteral>>> mapOfSetOfProviderOf( TypeLiteral<K> keyType, TypeLiteral valueType) { return (TypeLiteral<Map>>) TypeLiteral.get( Types.mapOf(keyType.getType(), Types.setOf(Types.providerOf(valueType.getType())))); } @SuppressWarnings("unchecked") // a provider map <K, Set is safely a Map>> static <K, V> TypeLiteral>>> mapOfSetOfJavaxProviderOf( TypeLiteral<K> keyType, TypeLiteral valueType) { return (TypeLiteral<Map>>) TypeLiteral.get( Types.mapOf(keyType.getType(), Types.setOf(Types.javaxProviderOf(valueType.getType())))); } @SuppressWarnings("unchecked") // a provider map <K, Set is safely a Map>> static <K, V> TypeLiteral>>> mapOfCollectionOfProviderOf( TypeLiteral<K> keyType, TypeLiteral valueType) { return (TypeLiteral<Map>>) TypeLiteral.get( Types.mapOf(keyType.getType(), Types.collectionOf(Types.providerOf(valueType.getType())))); } @SuppressWarnings("unchecked") // a provider map <K, Set is safely a Map>> static <K, V> TypeLiteral>>> mapOfCollectionOfJavaxProviderOf( TypeLiteral<K> keyType, TypeLiteral valueType) { return (TypeLiteral<Map>>) TypeLiteral.get( Types.mapOf(keyType.getType(), Types.collectionOf(Types.javaxProviderOf(valueType.getType())))); } @SuppressWarnings("unchecked") // a provider entry <K, V> is safely a Map.Entry> static <K, V> TypeLiteral>> entryOfProviderOf( TypeLiteral<K> keyType, TypeLiteral valueType) { return (TypeLiteral<Entry>) TypeLiteral.get(newParameterizedTypeWithOwner( Map.class, Entry.class, keyType.getType(), Types.providerOf(valueType.getType()))); } @SuppressWarnings("unchecked") // a provider entry <K, V> is safely a Map.Entry> static <K, V> TypeLiteral>> entryOfJavaxProviderOf( TypeLiteral<K> keyType, TypeLiteral valueType) { return (TypeLiteral<Entry>) TypeLiteral.get(newParameterizedTypeWithOwner( Map.class, Entry.class, keyType.getType(), Types.javaxProviderOf(valueType.getType()))); } @SuppressWarnings("unchecked") // a provider entry <K, V> is safely a Map.Entry> static <K, V> TypeLiteral>>> setOfEntryOfJavaxProviderOf( TypeLiteral<K> keyType, TypeLiteral valueType) { return (TypeLiteral<Set>>) TypeLiteral.get(Types.setOf(entryOfJavaxProviderOf(keyType, valueType).getType())); } // Note: We use valueTypeAndAnnotation effectively as a Pair<TypeLiteral, Annotation|Class> // since it's an easy way to group a type and an optional annotation type or instance. static <K, V> RealMapBinder newRealMapBinder(Binder binder, TypeLiteral keyType, Key<V> valueTypeAndAnnotation) { binder = binder.skipSources(MapBinder.class, RealMapBinder.class); TypeLiteral<V> valueType = valueTypeAndAnnotation.getTypeLiteral(); return newRealMapBinder(binder, keyType, valueType, valueTypeAndAnnotation.ofType(mapOf(keyType, valueType)), Multibinder.newSetBinder(binder, valueTypeAndAnnotation.ofType(entryOfProviderOf(keyType, valueType)))); } private static <K, V> RealMapBinder newRealMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral valueType, Key> mapKey, Multibinder<Entry> entrySetBinder) { RealMapBinder<K, V> mapBinder = new RealMapBinder<K, V>(binder, keyType, valueType, mapKey, entrySetBinder); binder.install(mapBinder); return mapBinder; } /** * Configures the {@code MapBinder} to handle duplicate entries. * <p>When multiple equal keys are bound, the value that gets included in the map is * arbitrary. * <p>In addition to the {@code Map} and {@code Map>} * maps that are normally bound, a {@code Map<K, Set} and * {@code Map<K, Set>} are also bound, which contain * all values bound to each key. * <p> * When multiple modules contribute elements to the map, this configuration * option impacts all of them. * * @return this map binder * @since 3.0 */ public abstract MapBinder<K, V> permitDuplicates(); /** * Returns a binding builder used to add a new entry in the map. Each * key must be distinct (and non-null). Bound providers will be evaluated each * time the map is injected. * * <p>It is an error to call this method without also calling one of the * {@code to} methods on the returned binding builder. * * <p>Scoping elements independently is supported. Use the {@code in} method * to specify a binding scope. */ public abstract LinkedBindingBuilder<V> addBinding(K key); /** * The actual mapbinder plays several roles: * * <p>As a MapBinder, it acts as a factory for LinkedBindingBuilders for * each of the map's values. It delegates to a {@link Multibinder} of * entries (keys to value providers). * * <p>As a Module, it installs the binding to the map itself, as well as to * a corresponding map whose values are providers. It uses the entry set * multibinder to construct the map and the provider map. * * <p>As a module, this implements equals() and hashcode() in order to trick * Guice into executing its configure() method only once. That makes it so * that multiple mapbinders can be created for the same target map, but * only one is bound. Since the list of bindings is retrieved from the * injector itself (and not the mapbinder), each mapbinder has access to * all contributions from all equivalent mapbinders. * * <p>Rather than binding a single Map.Entry<K, V>, the map binder * binds keys and values independently. This allows the values to be properly * scoped. * * <p>We use a subclass to hide 'implements Module' from the public API. */ static final class RealMapBinder<K, V> extends MapBinder implements Module { private final TypeLiteral<K> keyType; private final TypeLiteral<V> valueType; private final Key<Map mapKey; private final Key<Map> javaxProviderMapKey; private final Key<Map> providerMapKey; private final Key<Map> multimapKey; private final Key<Map>> providerSetMultimapKey; private final Key<Map>> javaxProviderSetMultimapKey; private final Key<Map>> providerCollectionMultimapKey; private final Key<Map>> javaxProviderCollectionMultimapKey; private final Key<Set>> entrySetJavaxProviderKey; private final RealMultibinder<Map.Entry> entrySetBinder; private final Map<K, String> duplicateKeyErrorMessages; /* the target injector's binder. non-null until initialization, null afterwards */ private Binder binder; private boolean permitDuplicates; private ImmutableList<Map.Entry> mapBindings; private RealMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral valueType, Key<Map mapKey, Multibinder>> entrySetBinder) { this.keyType = keyType; this.valueType = valueType; this.mapKey = mapKey; this.providerMapKey = mapKey.ofType(mapOfProviderOf(keyType, valueType)); this.javaxProviderMapKey = mapKey.ofType(mapOfJavaxProviderOf(keyType, valueType)); this.multimapKey = mapKey.ofType(mapOf(keyType, setOf(valueType))); this.providerSetMultimapKey = mapKey.ofType(mapOfSetOfProviderOf(keyType, valueType)); this.javaxProviderSetMultimapKey = mapKey.ofType(mapOfSetOfJavaxProviderOf(keyType, valueType)); this.providerCollectionMultimapKey = mapKey.ofType(mapOfCollectionOfProviderOf(keyType, valueType)); this.javaxProviderCollectionMultimapKey = mapKey.ofType(mapOfCollectionOfJavaxProviderOf(keyType, valueType)); this.entrySetJavaxProviderKey = mapKey.ofType(setOfEntryOfJavaxProviderOf(keyType, valueType)); this.entrySetBinder = (RealMultibinder<Entry>) entrySetBinder; this.binder = binder; this.duplicateKeyErrorMessages = Maps.newHashMap(); } /** Sets the error message to be shown if the key had duplicate non-equal bindings. */ void updateDuplicateKeyMessage(K k, String errMsg) { duplicateKeyErrorMessages.put(k, errMsg); } @Override public MapBinder<K, V> permitDuplicates() { entrySetBinder.permitDuplicates(); binder.install(new MultimapBinder<K, V>( multimapKey, providerSetMultimapKey, javaxProviderSetMultimapKey, providerCollectionMultimapKey, javaxProviderCollectionMultimapKey, entrySetBinder.getSetKey())); return this; } Key<V> getKeyForNewValue(K key) { checkNotNull(key, "key"); checkConfiguration(!isInitialized(), "MapBinder was already initialized"); Key<V> valueKey = Key.get(valueType, new RealElement(entrySetBinder.getSetName(), MAPBINDER, keyType.toString())); entrySetBinder.addBinding().toProvider(new ProviderMapEntry<K, V>( key, binder.getProvider(valueKey), valueKey)); return valueKey; } /** * This creates two bindings. One for the {@code Map.Entry<K, Provider} * and another for {@code V}. */ @Override public LinkedBindingBuilder<V> addBinding(K key) { return binder.bind(getKeyForNewValue(key)); } @Override public void configure(Binder binder) { checkConfiguration(!isInitialized(), "MapBinder was already initialized"); ImmutableSet<Dependency dependencies = ImmutableSet.<Dependencyof(Dependency.get(entrySetBinder.getSetKey())); // Binds a Map<K, Provider from a collection of Set>. Provider<Set>> entrySetProvider = binder .getProvider(entrySetBinder.getSetKey()); binder.bind(providerMapKey).toProvider( new RealProviderMapProvider(dependencies, entrySetProvider)); // The map this exposes is internally an ImmutableMap, so it's OK to massage // the guice Provider to javax Provider in the value (since Guice provider // implements javax Provider). @SuppressWarnings("unchecked") Key massagedProviderMapKey = (Key)providerMapKey; binder.bind(javaxProviderMapKey).to(massagedProviderMapKey); Provider<Map> mapProvider = binder.getProvider(providerMapKey); binder.bind(mapKey).toProvider(new RealMapProvider(dependencies, mapProvider)); // The Map.Entries are all ProviderMapEntry instances which do not allow setValue, so it is // safe to massage the return type like this @SuppressWarnings("unchecked") Key<Set>> massagedEntrySetProviderKey = (Key) entrySetBinder.getSetKey(); binder.bind(entrySetJavaxProviderKey).to(massagedEntrySetProviderKey); } boolean containsElement(Element element) { if (entrySetBinder.containsElement(element)) { return true; } else { Key<?> key; if (element instanceof Binding) { key = ((Binding<?>)element).getKey(); } else if (element instanceof ProviderLookup) { key = ((ProviderLookup<?>)element).getKey(); } else { return false; // cannot match; } return key.equals(mapKey) || key.equals(providerMapKey) || key.equals(javaxProviderMapKey) || key.equals(multimapKey) || key.equals(providerSetMultimapKey) || key.equals(javaxProviderSetMultimapKey) || key.equals(providerCollectionMultimapKey) || key.equals(javaxProviderCollectionMultimapKey) || key.equals(entrySetBinder.getSetKey()) || key.equals(entrySetJavaxProviderKey) || matchesValueKey(key); } } /** Returns true if the key indicates this is a value in the map. */ private boolean matchesValueKey(Key<?> key) { return key.getAnnotation() instanceof RealElement && ((RealElement) key.getAnnotation()).setName().equals(entrySetBinder.getSetName()) && ((RealElement) key.getAnnotation()).type() == MAPBINDER && ((RealElement) key.getAnnotation()).keyType().equals(keyType.toString()) && key.getTypeLiteral().equals(valueType); } private boolean isInitialized() { return binder == null; } @Override public boolean equals(Object o) { return o instanceof RealMapBinder && ((RealMapBinder<?, ?>) o).mapKey.equals(mapKey); } @Override public int hashCode() { return mapKey.hashCode(); } final class RealProviderMapProvider extends RealMapBinderProviderWithDependencies<Map> { private final ImmutableSet<Dependency dependencies; private final Provider<Set>> entrySetProvider; private Map<K, Provider providerMap; private RealProviderMapProvider( ImmutableSet<Dependency dependencies, Provider<Set>> entrySetProvider) { super(mapKey); this.dependencies = dependencies; this.entrySetProvider = entrySetProvider; } @Toolable @Inject void initialize(Injector injector) { RealMapBinder.this.binder = null; permitDuplicates = entrySetBinder.permitsDuplicates(injector); Map<K, Provider providerMapMutable = new LinkedHashMap>(); List<Map.Entry> bindingsMutable = Lists.newArrayList(); Indexer indexer = new Indexer(injector); Multimap<K, IndexedBinding> index = HashMultimap.create(); Set<K> duplicateKeys = null; for (Entry<K, Provider entry : entrySetProvider.get()) { ProviderMapEntry<K, V> providerEntry = (ProviderMapEntry) entry; Key<V> valueKey = providerEntry.getValueKey(); Binding<V> valueBinding = injector.getBinding(valueKey); // If this isn't a dup due to an exact same binding, add it. if (index.put(providerEntry.getKey(), valueBinding.acceptTargetVisitor(indexer))) { Provider<V> previous = providerMapMutable.put(providerEntry.getKey(), new ValueProvider<V>(providerEntry.getValue(), valueBinding)); if (previous != null && !permitDuplicates) { if (duplicateKeys == null) { duplicateKeys = Sets.newHashSet(); } duplicateKeys.add(providerEntry.getKey()); } bindingsMutable.add(Maps.immutableEntry(providerEntry.getKey(), valueBinding)); } } if (duplicateKeys != null) { // Must use a ListMultimap in case more than one binding has the same source // and is listed multiple times. Multimap<K, String> dups = newLinkedKeyArrayValueMultimap(); for (Map.Entry<K, Binding entry : bindingsMutable) { if (duplicateKeys.contains(entry.getKey())) { dups.put(entry.getKey(), "\t at " + Errors.convert(entry.getValue().getSource())); } } StringBuilder sb = new StringBuilder("Map injection failed due to duplicated key "); boolean first = true; for (K key : dups.keySet()) { if (first) { first = false; if (duplicateKeyErrorMessages.containsKey(key)) { sb.setLength(0); sb.append(duplicateKeyErrorMessages.get(key)); } else { sb.append("\"" + key + "\", from bindings:\n"); } } else { if (duplicateKeyErrorMessages.containsKey(key)) { sb.append("\n and " + duplicateKeyErrorMessages.get(key)); } else { sb.append("\n and key: \"" + key + "\", from bindings:\n"); } } Joiner.on('\n').appendTo(sb, dups.get(key)).append("\n"); } checkConfiguration(false, sb.toString()); } providerMap = ImmutableMap.copyOf(providerMapMutable); mapBindings = ImmutableList.copyOf(bindingsMutable); } @Override public Map<K, Provider get() { return providerMap; } @Override public Set<Dependency getDependencies() { return dependencies; } } final class RealMapProvider extends RealMapWithExtensionProvider<Map { private final ImmutableSet<Dependency dependencies; private final Provider<Map> mapProvider; private RealMapProvider( ImmutableSet<Dependency dependencies, Provider<Map> mapProvider) { super(mapKey); this.dependencies = dependencies; this.mapProvider = mapProvider; } @Override public Map<K, V> get() { // We can initialize the internal table efficiently this way and then swap the values // one by one. Map<K, Object> map = new LinkedHashMap(mapProvider.get()); for (Entry<K, Object> entry : map.entrySet()) { @SuppressWarnings("unchecked") // we initialized the entries with providers ValueProvider<V> provider = (ValueProvider)entry.getValue(); V value = provider.get(); checkConfiguration(value != null, "Map injection failed due to null value for key \"%s\", bound at: %s", entry.getKey(), provider.getValueBinding().getSource()); entry.setValue(value); } @SuppressWarnings("unchecked") // if we exited the loop then we replaced all Providers Map<K, V> typedMap = (Map) map; return Collections.unmodifiableMap(typedMap); } @Override public Set<Dependency getDependencies() { return dependencies; } @SuppressWarnings("unchecked") @Override public <B, R> R acceptExtensionVisitor(BindingTargetVisitor visitor, ProviderInstanceBinding<? extends B> binding) { if (visitor instanceof MultibindingsTargetVisitor) { return ((MultibindingsTargetVisitor<Map)visitor).visit(this); } else { return visitor.visit(binding); } } @Override public Key<Map getMapKey() { return mapKey; } @Override public TypeLiteral<?> getKeyTypeLiteral() { return keyType; } @Override public TypeLiteral<?> getValueTypeLiteral() { return valueType; } @SuppressWarnings("unchecked") @Override public List<Entry> getEntries() { if (isInitialized()) { return (List)mapBindings; // safe because mapBindings is immutable } else { throw new UnsupportedOperationException( "getElements() not supported for module bindings"); } } @Override public boolean permitsDuplicates() { if (isInitialized()) { return permitDuplicates; } else { throw new UnsupportedOperationException( "permitsDuplicates() not supported for module bindings"); } } @Override public boolean containsElement(Element element) { return RealMapBinder.this.containsElement(element); } } /** * Binds {@code Map<K, Set} and {{@code Map>>}. */ static final class MultimapBinder<K, V> implements Module { private final Key<Map> multimapKey; private final Key<Map>> providerMultimapKey; private final Key<Map>> javaxProviderMultimapKey; private final Key<Set>> entrySetKey; private final Key<Map>> javaxProviderCollectionMultimapKey; private final Key<Map>> providerCollectionMultimapKey; public MultimapBinder( Key<Map> multimapKey, Key<Map>> providerSetMultimapKey, Key<Map>> javaxProviderSetMultimapKey, Key<Map>> providerCollectionMultimapKey, Key<Map>> javaxProviderCollectionMultimapKey, Key<Set>> entrySetKey) { this.multimapKey = multimapKey; this.providerMultimapKey = providerSetMultimapKey; this.javaxProviderMultimapKey = javaxProviderSetMultimapKey; this.providerCollectionMultimapKey = providerCollectionMultimapKey; this.javaxProviderCollectionMultimapKey = javaxProviderCollectionMultimapKey; this.entrySetKey = entrySetKey; } @Override public void configure(Binder binder) { ImmutableSet<Dependency dependencies = ImmutableSet.<Dependencyof(Dependency.get(entrySetKey)); Provider<Set>> entrySetProvider = binder.getProvider(entrySetKey); // Binds a Map<K, Set> from a collection of Map> if // permitDuplicates was called. binder.bind(providerMultimapKey).toProvider( new RealProviderMultimapProvider(dependencies, entrySetProvider)); Provider<Map>> multimapProvider = binder.getProvider(providerMultimapKey); binder.bind(multimapKey).toProvider( new RealMultimapProvider(dependencies, multimapProvider)); linkKeys(binder); } // Provide links from a few different public keys to the providerMultimapKey. In each case // this is safe because the Map and Set instances are unmodifiable by consumers. @SuppressWarnings("unchecked") private void linkKeys(Binder binder) { binder.bind(javaxProviderMultimapKey).to((Key) providerMultimapKey); binder.bind(javaxProviderCollectionMultimapKey).to((Key) providerMultimapKey); binder.bind(providerCollectionMultimapKey).to((Key) providerMultimapKey); } @Override public int hashCode() { return multimapKey.hashCode(); } @Override public boolean equals(Object o) { return o instanceof MultimapBinder && ((MultimapBinder<?, ?>) o).multimapKey.equals(multimapKey); } final class RealProviderMultimapProvider extends RealMapBinderProviderWithDependencies<Map>> { private final ImmutableSet<Dependency dependencies; private final Provider<Set>> entrySetProvider; private Map<K, Set> providerMultimap; private RealProviderMultimapProvider(ImmutableSet<Dependency dependencies, Provider<Set>> entrySetProvider) { super(multimapKey); this.dependencies = dependencies; this.entrySetProvider = entrySetProvider; } @SuppressWarnings("unused") @Inject void initialize(Injector injector) { Map<K, ImmutableSet.Builder> providerMultimapMutable = new LinkedHashMap<K, ImmutableSet.Builder>(); for (Entry<K, Provider entry : entrySetProvider.get()) { if (!providerMultimapMutable.containsKey(entry.getKey())) { providerMultimapMutable.put( entry.getKey(), ImmutableSet.<Providerbuilder()); } providerMultimapMutable.get(entry.getKey()).add(entry.getValue()); } ImmutableMap.Builder<K, Set> providerMultimapBuilder = ImmutableMap.builder(); for (Entry<K, ImmutableSet.Builder> entry : providerMultimapMutable.entrySet()) { providerMultimapBuilder.put(entry.getKey(), entry.getValue().build()); } providerMultimap = providerMultimapBuilder.build(); } @Override public Map<K, Set> get() { return providerMultimap; } @Override public Set<Dependency getDependencies() { return dependencies; } } final class RealMultimapProvider extends RealMapBinderProviderWithDependencies<Map> { private final ImmutableSet<Dependency dependencies; private final Provider<Map>> multimapProvider; RealMultimapProvider( ImmutableSet<Dependency dependencies, Provider<Map>> multimapProvider) { super(multimapKey); this.dependencies = dependencies; this.multimapProvider = multimapProvider; } @Override public Map<K, Set get() { ImmutableMap.Builder<K, Set multimapBuilder = ImmutableMap.builder(); for (Entry<K, Set> entry : multimapProvider.get().entrySet()) { K key = entry.getKey(); ImmutableSet.Builder<V> valuesBuilder = ImmutableSet.builder(); for (Provider<V> valueProvider : entry.getValue()) { V value = valueProvider.get(); checkConfiguration(value != null, "Multimap injection failed due to null value for key \"%s\"", key); valuesBuilder.add(value); } multimapBuilder.put(key, valuesBuilder.build()); } return multimapBuilder.build(); } @Override public Set<Dependency getDependencies() { return dependencies; } } } static final class ValueProvider<V> implements Provider { private final Provider<V> delegate; private final Binding<V> binding; ValueProvider(Provider<V> delegate, Binding binding) { this.delegate = delegate; this.binding = binding; } @Override public V get() { return delegate.get(); } public Binding<V> getValueBinding() { return binding; } } /** * A Provider that Map.Entry that is also a Provider. The key is the entry in the * map this corresponds to and the value is the provider of the user's binding. * This returns itself as the Provider.get value. */ static final class ProviderMapEntry<K, V> implements ProviderWithDependencies<Map.Entry>, Map.Entry> { private final K key; private final Provider<V> provider; private final Key<V> valueKey; private ProviderMapEntry(K key, Provider<V> provider, Key valueKey) { this.key = key; this.provider = provider; this.valueKey = valueKey; } @Override public Entry<K, Provider get() { return this; } @Override public Set<Dependency getDependencies() { return ((HasDependencies) provider).getDependencies(); } public Key<V> getValueKey() { return valueKey; } @Override public K getKey() { return key; } @Override public Provider<V> getValue() { return provider; } @Override public Provider<V> setValue(Provider value) { throw new UnsupportedOperationException(); } @Override public boolean equals(Object obj) { if (obj instanceof Map.Entry) { Map.Entry o = (Map.Entry)obj; return Objects.equal(key, o.getKey()) && Objects.equal(provider, o.getValue()); } return false; } @Override public int hashCode() { return key.hashCode() ^ provider.hashCode(); } @Override public String toString() { return "ProviderMapEntry(" + key + ", " + provider + ")"; } } private static abstract class RealMapWithExtensionProvider<T> extends RealMapBinderProviderWithDependencies<T> implements ProviderWithExtensionVisitor<T>, MapBinderBinding { public RealMapWithExtensionProvider(Object equality) { super(equality); } } /** * A base class for ProviderWithDependencies that need equality * based on a specific object. */ private static abstract class RealMapBinderProviderWithDependencies<T> implements ProviderWithDependencies { private final Object equality; public RealMapBinderProviderWithDependencies(Object equality) { this.equality = equality; } @Override public boolean equals(Object obj) { return obj != null && this.getClass() == obj.getClass() && equality.equals(((RealMapBinderProviderWithDependencies<?>)obj).equality); } @Override public int hashCode() { return equality.hashCode(); } } private Multimap<K, String> newLinkedKeyArrayValueMultimap() { return Multimaps.newListMultimap( new LinkedHashMap<K, Collection(), new Supplier<List() { @Override public List<String> get() { return Lists.newArrayList(); } }); } } }

Other Java examples (source code examples)

Here is a short list of links related to this Java MapBinder.java source code file:



my book on functional programming

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.