package com.google.inject.internal;
import static com.google.inject.internal.Element.Type.MAPBINDER;
import static com.google.inject.internal.Errors.checkConfiguration;
import static com.google.inject.internal.Errors.checkNotNull;
import static com.google.inject.internal.RealMultibinder.setOf;
import static com.google.inject.util.Types.newParameterizedType;
import static com.google.inject.util.Types.newParameterizedTypeWithOwner;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.inject.Binder;
import com.google.inject.Binding;
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.InternalProviderInstanceBindingImpl.InitializationTiming;
import com.google.inject.multibindings.MapBinderBinding;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.multibindings.MultibindingsTargetVisitor;
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.util.Types;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 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.
*
* <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.
*/
public final class RealMapBinder<K, V> implements Module {
/**
* 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> RealMapBinder<K, V> newMapRealBinder(
Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
binder = binder.skipSources(RealMapBinder.class);
return newRealMapBinder(
binder,
keyType,
valueType,
Key.get(mapOf(keyType, valueType)),
RealMultibinder.newRealSetBinder(binder, Key.get(entryOfProviderOf(keyType, 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> RealMapBinder<K, V> newRealMapBinder(
Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Annotation annotation) {
binder = binder.skipSources(RealMapBinder.class);
return newRealMapBinder(
binder,
keyType,
valueType,
Key.get(mapOf(keyType, valueType), annotation),
RealMultibinder.newRealSetBinder(
binder, Key.get(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 annotationType}.
*/
public static <K, V> RealMapBinder<K, V> newRealMapBinder(
Binder binder,
TypeLiteral<K> keyType,
TypeLiteral<V> valueType,
Class<? extends Annotation> annotationType) {
binder = binder.skipSources(RealMapBinder.class);
return newRealMapBinder(
binder,
keyType,
valueType,
Key.get(mapOf(keyType, valueType), annotationType),
RealMultibinder.newRealSetBinder(
binder, Key.get(entryOfProviderOf(keyType, valueType), annotationType)));
}
@SuppressWarnings("unchecked") // a map of <K, V> is safely a Map<K, V>
static <K, V> TypeLiteral<Map<K, V>> mapOf(TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
return (TypeLiteral<Map<K, V>>)
TypeLiteral.get(Types.mapOf(keyType.getType(), valueType.getType()));
}
@SuppressWarnings("unchecked") // a provider map <K, V> is safely a Map<K, Provider<V>>
static <K, V> TypeLiteral<Map<K, Provider<V>>> mapOfProviderOf(
TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
return (TypeLiteral<Map<K, Provider<V>>>)
TypeLiteral.get(Types.mapOf(keyType.getType(), Types.providerOf(valueType.getType())));
}
// provider map <K, V> is safely a Map<K, javax.inject.Provider<V>>>
@SuppressWarnings("unchecked")
static <K, V> TypeLiteral<Map<K, javax.inject.Provider<V>>> mapOfJavaxProviderOf(
TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
return (TypeLiteral<Map<K, javax.inject.Provider<V>>>)
TypeLiteral.get(
Types.mapOf(
keyType.getType(),
newParameterizedType(javax.inject.Provider.class, valueType.getType())));
}
@SuppressWarnings("unchecked") // a provider map <K, Set<V>> is safely a Map<K, Set<Provider<V>>>
static <K, V> TypeLiteral<Map<K, Set<Provider<V>>>> mapOfSetOfProviderOf(
TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
return (TypeLiteral<Map<K, Set<Provider<V>>>>)
TypeLiteral.get(
Types.mapOf(keyType.getType(), Types.setOf(Types.providerOf(valueType.getType()))));
}
@SuppressWarnings("unchecked") // a provider map <K, Set<V>> is safely a Map<K, Set<Provider<V>>>
static <K, V> TypeLiteral<Map<K, Set<javax.inject.Provider<V>>>> mapOfSetOfJavaxProviderOf(
TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
return (TypeLiteral<Map<K, Set<javax.inject.Provider<V>>>>)
TypeLiteral.get(
Types.mapOf(
keyType.getType(), Types.setOf(Types.javaxProviderOf(valueType.getType()))));
}
@SuppressWarnings("unchecked") // a provider map <K, Set<V>> is safely a Map<K, Set<Provider<V>>>
static <K, V> TypeLiteral<Map<K, Collection<Provider<V>>>> mapOfCollectionOfProviderOf(
TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
return (TypeLiteral<Map<K, Collection<Provider<V>>>>)
TypeLiteral.get(
Types.mapOf(
keyType.getType(), Types.collectionOf(Types.providerOf(valueType.getType()))));
}
@SuppressWarnings("unchecked") // a provider map <K, Set<V>> is safely a Map<K, Set<Provider<V>>>
static <K, V>
TypeLiteral<Map<K, Collection<javax.inject.Provider<V>>>> mapOfCollectionOfJavaxProviderOf(
TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
return (TypeLiteral<Map<K, Collection<javax.inject.Provider<V>>>>)
TypeLiteral.get(
Types.mapOf(
keyType.getType(), Types.collectionOf(Types.javaxProviderOf(valueType.getType()))));
}
@SuppressWarnings("unchecked") // a provider entry <K, V> is safely a Map.Entry<K, Provider<V>>
static <K, V> TypeLiteral<Map.Entry<K, Provider<V>>> entryOfProviderOf(
TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
return (TypeLiteral<Map.Entry<K, Provider<V>>>)
TypeLiteral.get(
newParameterizedTypeWithOwner(
Map.class,
Map.Entry.class,
keyType.getType(),
Types.providerOf(valueType.getType())));
}
@SuppressWarnings("unchecked") // a provider entry <K, V> is safely a Map.Entry<K, Provider<V>>
static <K, V> TypeLiteral<Map.Entry<K, Provider<V>>> entryOfJavaxProviderOf(
TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
return (TypeLiteral<Map.Entry<K, Provider<V>>>)
TypeLiteral.get(
newParameterizedTypeWithOwner(
Map.class,
Map.Entry.class,
keyType.getType(),
Types.javaxProviderOf(valueType.getType())));
}
@SuppressWarnings("unchecked") // a provider entry <K, V> is safely a Map.Entry<K, Provider<V>>
static <K, V>
TypeLiteral<Set<Map.Entry<K, javax.inject.Provider<V>>>> setOfEntryOfJavaxProviderOf(
TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
return (TypeLiteral<Set<Map.Entry<K, javax.inject.Provider<V>>>>)
TypeLiteral.get(Types.setOf(entryOfJavaxProviderOf(keyType, valueType).getType()));
}
/** Given a Key<T> will return a Key<Provider<T>> */
@SuppressWarnings("unchecked")
private static <T> Key<Provider<T>> getKeyOfProvider(Key<T> valueKey) {
return (Key<Provider<T>>)
valueKey.ofType(Types.providerOf(valueKey.getTypeLiteral().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<K, V> newRealMapBinder(
Binder binder, TypeLiteral<K> keyType, Key<V> valueTypeAndAnnotation) {
binder = binder.skipSources(RealMapBinder.class);
TypeLiteral<V> valueType = valueTypeAndAnnotation.getTypeLiteral();
return newRealMapBinder(
binder,
keyType,
valueType,
valueTypeAndAnnotation.ofType(mapOf(keyType, valueType)),
RealMultibinder.newRealSetBinder(
binder, valueTypeAndAnnotation.ofType(entryOfProviderOf(keyType, valueType))));
}
private static <K, V> RealMapBinder<K, V> newRealMapBinder(
Binder binder,
TypeLiteral<K> keyType,
TypeLiteral<V> valueType,
Key<Map<K, V>> mapKey,
RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder) {
RealMapBinder<K, V> mapBinder =
new RealMapBinder<K, V>(binder, keyType, valueType, mapKey, entrySetBinder);
binder.install(mapBinder);
return mapBinder;
}
// Until the injector initializes us, we don't know what our dependencies are,
// so initialize to the whole Injector.
private static final ImmutableSet<Dependency<?>> MODULE_DEPENDENCIES =
ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
private final BindingSelection<K, V> bindingSelection;
private final Binder binder;
private final RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder;
private RealMapBinder(
Binder binder,
TypeLiteral<K> keyType,
TypeLiteral<V> valueType,
Key<Map<K, V>> mapKey,
RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder) {
this.bindingSelection = new BindingSelection<K, V>(keyType, valueType, mapKey, entrySetBinder);
this.binder = binder;
this.entrySetBinder = entrySetBinder;
}
public void permitDuplicates() {
checkConfiguration(!bindingSelection.isInitialized(), "MapBinder was already initialized");
entrySetBinder.permitDuplicates();
binder.install(new MultimapBinder<K, V>(bindingSelection));
}
/** Adds a binding to the map for the given key. */
Key<V> getKeyForNewValue(K key) {
checkNotNull(key, "key");
checkConfiguration(!bindingSelection.isInitialized(), "MapBinder was already initialized");
RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder =
bindingSelection.getEntrySetBinder();
Key<V> valueKey =
Key.get(
bindingSelection.getValueType(),
new RealElement(
entrySetBinder.getSetName(), MAPBINDER, bindingSelection.getKeyType().toString()));
// TODO(user): Convert ProviderMapEntry to an InternalFactory
// when this happens, we can remove the ProviderLookup check in
// BindingSelection.containsElement()
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<V>>} and another for {@code
* V}.
*/
public LinkedBindingBuilder<V> addBinding(K key) {
return binder.bind(getKeyForNewValue(key));
}
@Override
public void configure(Binder binder) {
checkConfiguration(!bindingSelection.isInitialized(), "MapBinder was already initialized");
// Binds a Map<K, Provider<V>>
RealProviderMapProvider<K, V> providerMapProvider =
new RealProviderMapProvider<K, V>(bindingSelection);
binder.bind(bindingSelection.getProviderMapKey()).toProvider(providerMapProvider);
// 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", "rawtypes"})
Provider<Map<K, javax.inject.Provider<V>>> javaxProviderMapProvider =
(Provider) providerMapProvider;
binder.bind(bindingSelection.getJavaxProviderMapKey()).toProvider(javaxProviderMapProvider);
RealMapProvider<K, V> mapProvider = new RealMapProvider<K, V>(bindingSelection);
binder.bind(bindingSelection.getMapKey()).toProvider(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", "rawtypes"})
Key<Set<Map.Entry<K, javax.inject.Provider<V>>>> massagedEntrySetProviderKey =
(Key) bindingSelection.getEntrySetBinder().getSetKey();
binder.bind(bindingSelection.getEntrySetJavaxProviderKey()).to(massagedEntrySetProviderKey);
}
@Override
public boolean equals(Object o) {
return o instanceof RealMapBinder
&& ((RealMapBinder<?, ?>) o).bindingSelection.equals(bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
/**
* The BindingSelection contains some of the core state and logic for the MapBinder.
*
* <p>It lazily computes the value for keys for various permutations of Maps that are provided by
* this module. It also builds up maps from {@code K} to {@code Binding<V>}, which is used by all
* of the internal factories to actually provide the desired maps.
*
* <p>During initialization time there is only one BindingSelection. It is possible that multiple
* different BindingSelections are constructed. Specifically, in the case of two different modules
* each adding bindings to the same MapBinder. If that happens, we define the BindingSelection
* held by the {@link RealMapProvider} to be the authoritative one. The logic for this exists in
* {@link RealMultimapBinderProviderWithDependencies}. This is done to avoid confusion because the
* BindingSelection contains mutable state.
*/
private static final class BindingSelection<K, V> {
private enum InitializationState {
UNINITIALIZED,
INITIALIZED,
HAS_ERRORS;
}
private final TypeLiteral<K> keyType;
private final TypeLiteral<V> valueType;
private final Key<Map<K, V>> mapKey;
// Lazily computed
private Key<Map<K, javax.inject.Provider<V>>> javaxProviderMapKey;
private Key<Map<K, Provider<V>>> providerMapKey;
private Key<Map<K, Set<V>>> multimapKey;
private Key<Map<K, Set<Provider<V>>>> providerSetMultimapKey;
private Key<Map<K, Set<javax.inject.Provider<V>>>> javaxProviderSetMultimapKey;
private Key<Map<K, Collection<Provider<V>>>> providerCollectionMultimapKey;
private Key<Map<K, Collection<javax.inject.Provider<V>>>> javaxProviderCollectionMultimapKey;
private Key<Set<Map.Entry<K, javax.inject.Provider<V>>>> entrySetJavaxProviderKey;
private final RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder;
private InitializationState initializationState;
/**
* These are built during initialization and used by all factories to actually provide the
* relevant maps. These contain all of the necessary information about the map binder.
*/
private ImmutableMap<K, Binding<V>> mapBindings;
private ImmutableMap<K, Set<Binding<V>>> multimapBindings;
private ImmutableList<Map.Entry<K, Binding<V>>> entries;
/**
* Indicates if this Map permits duplicates. It is initialized during initialization by querying
* the injector. This is done because multiple different modules can contribute to a MapBinder,
* and any one could set permitDuplicates.
*/
private boolean permitsDuplicates;
private BindingSelection(
TypeLiteral<K> keyType,
TypeLiteral<V> valueType,
Key<Map<K, V>> mapKey,
RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder) {
this.keyType = keyType;
this.valueType = valueType;
this.mapKey = mapKey;
this.entrySetBinder = entrySetBinder;
this.initializationState = InitializationState.UNINITIALIZED;
}
/**
* Will initialize internal data structures.
*
* @return {@code true} if initialization was successful, {@code false} if there were errors
*/
private boolean tryInitialize(InjectorImpl injector, Errors errors) {
// Every one of our providers will call this method, so only execute the logic once.
if (initializationState != InitializationState.UNINITIALIZED) {
return initializationState != InitializationState.HAS_ERRORS;
}
// Multiple different modules can all contribute to the same MapBinder, and if any
// one of them permits duplicates, then the map binder as a whole will permit duplicates.
// Since permitDuplicates() may not have been called on this instance, we need to go
// to the injector to see if permitDuplicates was set.
permitsDuplicates = entrySetBinder.permitsDuplicates(injector);
// We now build the Map<K, Set<Binding<V>>> from the entrySetBinder.
// The entrySetBinder contains all of the ProviderMapEntrys, and once
// we have those, it's easy to iterate through them to organize them by K.
Map<K, ImmutableSet.Builder<Binding<V>>> bindingMultimapMutable =
new LinkedHashMap<K, ImmutableSet.Builder<Binding<V>>>();
Map<K, Binding<V>> bindingMapMutable = new LinkedHashMap<K, Binding<V>>();
Multimap<K, Indexer.IndexedBinding> index = HashMultimap.create();
Indexer indexer = new Indexer(injector);
Multimap<K, Binding<V>> duplicates = null;
ImmutableList.Builder<Map.Entry<K, Binding<V>>> entriesBuilder = ImmutableList.builder();
// We get all of the Bindings that were put into the entrySetBinder
for (Binding<Map.Entry<K, Provider<V>>> binding :
injector.findBindingsByType(entrySetBinder.getElementTypeLiteral())) {
if (entrySetBinder.containsElement(binding)) {
// Protected by findBindingByType() and the fact that all providers are added by us
// in addBinding(). It would theoretically be possible for someone to directly
// add their own binding to the entrySetBinder, but they shouldn't do that.
@SuppressWarnings({"unchecked", "rawtypes"})
ProviderInstanceBinding<ProviderMapEntry<K, V>> entryBinding =
(ProviderInstanceBinding) binding;
// We added all these bindings initially, so we know they are ProviderMapEntrys
@SuppressWarnings({"unchecked", "rawtypes"})
ProviderMapEntry<K, V> entry = (ProviderMapEntry) entryBinding.getUserSuppliedProvider();
K key = entry.getKey();
Key<V> valueKey = entry.getValueKey();
Binding<V> valueBinding = injector.getExistingBinding(valueKey);
// Use the indexer to de-dupe user bindings. This is needed because of the
// uniqueId in RealElement. The uniqueId intentionally circumvents the regular
// Guice deduplication, so we need to re-implement our own here, ignoring
// uniqueId.
if (index.put(key, valueBinding.acceptTargetVisitor(indexer))) {
entriesBuilder.add(Maps.immutableEntry(key, valueBinding));
Binding<V> previous = bindingMapMutable.put(key, valueBinding);
// Check if this is a duplicate binding
if (previous != null && !permitsDuplicates) {
if (duplicates == null) {
// This is linked for both keys and values to maintain order
duplicates = LinkedHashMultimap.create();
}
// We add both the previous and the current value to the duplicates map.
// This is because if there are three duplicates, we will only execute this code
// for the second and third, but we want all three values to display a helpful
// error message. We rely on the multimap to dedupe repeated values.
duplicates.put(key, previous);
duplicates.put(key, valueBinding);
}
// Don't do extra work unless we need to
if (permitsDuplicates) {
// Create a set builder for this key if it's the first time we've seen it
if (!bindingMultimapMutable.containsKey(key)) {
bindingMultimapMutable.put(key, ImmutableSet.<Binding<V>>builder());
}
// Add the Binding<V>
bindingMultimapMutable.get(key).add(valueBinding);
}
}
}
}
// It is safe to check if duplicates is non-null because if duplicates are allowed,
// we don't build up this data structure
if (duplicates != null) {
initializationState = InitializationState.HAS_ERRORS;
reportDuplicateKeysError(duplicates, errors);
return false;
}
// Build all of the ImmutableSet.Builders,
// transforming from Map<K, ImmutableSet.Builder<Binding<V>>> to
// ImmutableMap<K, Set<Binding<V>>>
ImmutableMap.Builder<K, Set<Binding<V>>> bindingsMultimapBuilder = ImmutableMap.builder();
for (Map.Entry<K, ImmutableSet.Builder<Binding<V>>> entry :
bindingMultimapMutable.entrySet()) {
bindingsMultimapBuilder.put(entry.getKey(), entry.getValue().build());
}
mapBindings = ImmutableMap.copyOf(bindingMapMutable);
multimapBindings = bindingsMultimapBuilder.build();
entries = entriesBuilder.build();
initializationState = InitializationState.INITIALIZED;
return true;
}
private static <K, V> void reportDuplicateKeysError(
Multimap<K, Binding<V>> duplicates, Errors errors) {
StringBuilder sb = new StringBuilder("Map injection failed due to duplicated key ");
boolean first = true;
for (Map.Entry<K, Collection<Binding<V>>> entry : duplicates.asMap().entrySet()) {
K dupKey = entry.getKey();
if (first) {
first = false;
sb.append("\"" + dupKey + "\", from bindings:\n");
} else {
sb.append("\n and key: \"" + dupKey + "\", from bindings:\n");
}
for (Binding<V> dup : entry.getValue()) {
sb.append("\t at " + Errors.convert(dup.getSource()) + "\n");
}
}
// TODO(user): Add a different error for every duplicated key
errors.addMessage(sb.toString());
}
private boolean containsElement(Element element) {
if (entrySetBinder.containsElement(element)) {
return true;
}
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(getMapKey())
|| key.equals(getProviderMapKey())
|| key.equals(getJavaxProviderMapKey())
|| key.equals(getMultimapKey())
|| key.equals(getProviderSetMultimapKey())
|| key.equals(getJavaxProviderSetMultimapKey())
|| key.equals(getProviderCollectionMultimapKey())
|| key.equals(getJavaxProviderCollectionMultimapKey())
|| key.equals(entrySetBinder.getSetKey())
|| key.equals(getEntrySetJavaxProviderKey())
|| 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 Key<Map<K, Provider<V>>> getProviderMapKey() {
Key<Map<K, Provider<V>>> local = providerMapKey;
if (local == null) {
local = providerMapKey = mapKey.ofType(mapOfProviderOf(keyType, valueType));
}
return local;
}
private Key<Map<K, javax.inject.Provider<V>>> getJavaxProviderMapKey() {
Key<Map<K, javax.inject.Provider<V>>> local = javaxProviderMapKey;
if (local == null) {
local = javaxProviderMapKey = mapKey.ofType(mapOfJavaxProviderOf(keyType, valueType));
}
return local;
}
private Key<Map<K, Set<V>>> getMultimapKey() {
Key<Map<K, Set<V>>> local = multimapKey;
if (local == null) {
local = multimapKey = mapKey.ofType(mapOf(keyType, setOf(valueType)));
}
return local;
}
private Key<Map<K, Set<Provider<V>>>> getProviderSetMultimapKey() {
Key<Map<K, Set<Provider<V>>>> local = providerSetMultimapKey;
if (local == null) {
local = providerSetMultimapKey = mapKey.ofType(mapOfSetOfProviderOf(keyType, valueType));
}
return local;
}
private Key<Map<K, Set<javax.inject.Provider<V>>>> getJavaxProviderSetMultimapKey() {
Key<Map<K, Set<javax.inject.Provider<V>>>> local = javaxProviderSetMultimapKey;
if (local == null) {
local =
javaxProviderSetMultimapKey =
mapKey.ofType(mapOfSetOfJavaxProviderOf(keyType, valueType));
}
return local;
}
private Key<Map<K, Collection<Provider<V>>>> getProviderCollectionMultimapKey() {
Key<Map<K, Collection<Provider<V>>>> local = providerCollectionMultimapKey;
if (local == null) {
local =
providerCollectionMultimapKey =
mapKey.ofType(mapOfCollectionOfProviderOf(keyType, valueType));
}
return local;
}
private Key<Map<K, Collection<javax.inject.Provider<V>>>>
getJavaxProviderCollectionMultimapKey() {
Key<Map<K, Collection<javax.inject.Provider<V>>>> local = javaxProviderCollectionMultimapKey;
if (local == null) {
local =
javaxProviderCollectionMultimapKey =
mapKey.ofType(mapOfCollectionOfJavaxProviderOf(keyType, valueType));
}
return local;
}
private Key<Set<Map.Entry<K, javax.inject.Provider<V>>>> getEntrySetJavaxProviderKey() {
Key<Set<Map.Entry<K, javax.inject.Provider<V>>>> local = entrySetJavaxProviderKey;
if (local == null) {
local =
entrySetJavaxProviderKey =
mapKey.ofType(setOfEntryOfJavaxProviderOf(keyType, valueType));
}
return local;
}
private ImmutableMap<K, Binding<V>> getMapBindings() {
checkConfiguration(isInitialized(), "MapBinder has not yet been initialized");
return mapBindings;
}
private ImmutableMap<K, Set<Binding<V>>> getMultimapBindings() {
checkConfiguration(isInitialized(), "MapBinder has not yet been initialized");
return multimapBindings;
}
private ImmutableList<Map.Entry<K, Binding<V>>> getEntries() {
checkConfiguration(isInitialized(), "MapBinder has not yet been initialized");
return entries;
}
private boolean isInitialized() {
return initializationState == InitializationState.INITIALIZED;
}
private TypeLiteral<K> getKeyType() {
return keyType;
}
private TypeLiteral<V> getValueType() {
return valueType;
}
private Key<Map<K, V>> getMapKey() {
return mapKey;
}
private RealMultibinder<Map.Entry<K, Provider<V>>> getEntrySetBinder() {
return entrySetBinder;
}
private boolean permitsDuplicates() {
if (isInitialized()) {
return permitsDuplicates;
} else {
throw new UnsupportedOperationException(
"permitsDuplicates() not supported for module bindings");
}
}
@Override
public boolean equals(Object o) {
return o instanceof BindingSelection && ((BindingSelection<?, ?>) o).mapKey.equals(mapKey);
}
@Override
public int hashCode() {
return mapKey.hashCode();
}
}
private static final class RealProviderMapProvider<K, V>
extends RealMapBinderProviderWithDependencies<K, V, Map<K, Provider<V>>> {
private Map<K, Provider<V>> mapOfProviders;
private Set<Dependency<?>> dependencies = RealMapBinder.MODULE_DEPENDENCIES;
private RealProviderMapProvider(BindingSelection<K, V> bindingSelection) {
super(bindingSelection);
}
@Override
public Set<Dependency<?>> getDependencies() {
return dependencies;
}
@Override
protected void doInitialize(InjectorImpl injector, Errors errors) {
ImmutableMap.Builder<K, Provider<V>> mapOfProvidersBuilder = ImmutableMap.builder();
ImmutableSet.Builder<Dependency<?>> dependenciesBuilder = ImmutableSet.builder();
for (Map.Entry<K, Binding<V>> entry : bindingSelection.getMapBindings().entrySet()) {
mapOfProvidersBuilder.put(entry.getKey(), entry.getValue().getProvider());
dependenciesBuilder.add(Dependency.get(getKeyOfProvider(entry.getValue().getKey())));
}
mapOfProviders = mapOfProvidersBuilder.build();
dependencies = dependenciesBuilder.build();
}
@Override
protected Map<K, Provider<V>> doProvision(
Errors errors, InternalContext context, Dependency<?> dependency) {
return mapOfProviders;
}
}
private static final class RealMapProvider<K, V>
extends RealMapBinderProviderWithDependencies<K, V, Map<K, V>>
implements ProviderWithExtensionVisitor<Map<K, V>>, MapBinderBinding<Map<K, V>> {
private Set<Dependency<?>> dependencies = RealMapBinder.MODULE_DEPENDENCIES;
/**
* An array of all the injectors.
*
* <p>This is parallel to array of keys below
*/
private SingleParameterInjector<V>[] injectors;
private K[] keys;
private RealMapProvider(BindingSelection<K, V> bindingSelection) {
super(bindingSelection);
}
private BindingSelection<K, V> getBindingSelection() {
return bindingSelection;
}
@Override
protected void doInitialize(InjectorImpl injector, Errors errors) throws ErrorsException {
@SuppressWarnings("unchecked")
K[] keysArray = (K[]) new Object[bindingSelection.getMapBindings().size()];
keys = keysArray;
ImmutableSet.Builder<Dependency<?>> dependenciesBuilder = ImmutableSet.builder();
int i = 0;
for (Map.Entry<K, Binding<V>> entry : bindingSelection.getMapBindings().entrySet()) {
dependenciesBuilder.add(Dependency.get(entry.getValue().getKey()));
keys[i] = entry.getKey();
i++;
}
ImmutableSet<Dependency<?>> localDependencies = dependenciesBuilder.build();
dependencies = localDependencies;
List<Dependency<?>> dependenciesList = localDependencies.asList();
// We know the type because we built up our own sets of dependencies, it's just
// that the interface uses a "?" generic
@SuppressWarnings("unchecked")
SingleParameterInjector<V>[] typedInjectors =
(SingleParameterInjector<V>[]) injector.getParametersInjectors(dependenciesList, errors);
injectors = typedInjectors;
}
@Override
protected Map<K, V> doProvision(
Errors errors, InternalContext context, Dependency<?> dependency) throws ErrorsException {
SingleParameterInjector<V>[] localInjectors = injectors;
if (localInjectors == null) {
// if injectors == null, then we have no bindings so return the empty map.
return ImmutableMap.of();
}
ImmutableMap.Builder<K, V> resultBuilder = ImmutableMap.builder();
K[] localKeys = keys;
for (int i = 0; i < localInjectors.length; i++) {
SingleParameterInjector<V> injector = localInjectors[i];
K key = localKeys[i];
V value = injector.inject(errors, context);
if (value == null) {
throw createNullValueException(errors, key, bindingSelection.getMapBindings().get(key));
}
resultBuilder.put(key, value);
}
return resultBuilder.build();
}
@Override
public Set<Dependency<?>> getDependencies() {
return dependencies;
}
@Override
@SuppressWarnings("unchecked")
public <B, W> W acceptExtensionVisitor(
BindingTargetVisitor<B, W> visitor, ProviderInstanceBinding<? extends B> binding) {
if (visitor instanceof MultibindingsTargetVisitor) {
return ((MultibindingsTargetVisitor<Map<K, V>, W>) visitor).visit(this);
} else {
return visitor.visit(binding);
}
}
@Override
public Key<Map<K, V>> getMapKey() {
return bindingSelection.getMapKey();
}
@Override
public TypeLiteral<K> getKeyTypeLiteral() {
return bindingSelection.getKeyType();
}
@Override
public TypeLiteral<V> getValueTypeLiteral() {
return bindingSelection.getValueType();
}
@Override
@SuppressWarnings("unchecked")
public List<Map.Entry<?, Binding<?>>> getEntries() {
if (bindingSelection.isInitialized()) {
return (List<Map.Entry<?, Binding<?>>>) (List<?>) bindingSelection.getEntries();
} else {
throw new UnsupportedOperationException("getEntries() not supported for module bindings");
}
}
@Override
public List<Map.Entry<?, Binding<?>>> getEntries(Iterable<? extends Element> elements) {
// Iterate over the elements, building up the below maps
// This is a preprocessing step allowing us to only iterate over elements
// once and have O(n) runtime
ImmutableMultimap.Builder<K, Key<V>> keyToValueKeyBuilder = ImmutableMultimap.builder();
ImmutableMap.Builder<Key<V>, Binding<V>> valueKeyToBindingBuilder = ImmutableMap.builder();
ImmutableMap.Builder<Key<V>, K> valueKeyToKeyBuilder = ImmutableMap.builder();
ImmutableMap.Builder<Key<V>, Binding<Map.Entry<K, Provider<V>>>>
valueKeyToEntryBindingBuilder = ImmutableMap.builder();
for (Element element : elements) {
if (element instanceof Binding) {
Binding<?> binding = (Binding<?>) element;
if (bindingSelection.matchesValueKey(binding.getKey())
&& binding.getKey().getTypeLiteral().equals(bindingSelection.valueType)) {
// Safe because of the check on the type literal above
@SuppressWarnings("unchecked")
Binding<V> typedBinding = (Binding<V>) binding;
Key<V> typedKey = typedBinding.getKey();
valueKeyToBindingBuilder.put(typedKey, typedBinding);
}
}
if (element instanceof ProviderInstanceBinding
&& bindingSelection.getEntrySetBinder().containsElement(element)) {
// Safe because of the instanceof check, and containsElement() check
@SuppressWarnings({"unchecked", "rawtypes"})
ProviderInstanceBinding<Map.Entry<K, Provider<V>>> entryBinding =
(ProviderInstanceBinding) element;
// Safe because of the check for containsElement() above
@SuppressWarnings("unchecked")
Provider<Map.Entry<K, Provider<V>>> typedProvider =
(Provider<Map.Entry<K, Provider<V>>>) entryBinding.getUserSuppliedProvider();
Provider<Map.Entry<K, Provider<V>>> userSuppliedProvider = typedProvider;
if (userSuppliedProvider instanceof ProviderMapEntry) {
// Safe because of the instanceof check
@SuppressWarnings("unchecked")
ProviderMapEntry<K, V> typedUserSuppliedProvider =
(ProviderMapEntry<K, V>) userSuppliedProvider;
ProviderMapEntry<K, V> entry = typedUserSuppliedProvider;
keyToValueKeyBuilder.put(entry.getKey(), entry.getValueKey());
valueKeyToEntryBindingBuilder.put(entry.getValueKey(), entryBinding);
valueKeyToKeyBuilder.put(entry.getValueKey(), entry.getKey());
}
}
}
ImmutableMultimap<K, Key<V>> keyToValueKey = keyToValueKeyBuilder.build();
ImmutableMap<Key<V>, K> valueKeyToKey = valueKeyToKeyBuilder.build();
ImmutableMap<Key<V>, Binding<V>> valueKeyToBinding = valueKeyToBindingBuilder.build();
ImmutableMap<Key<V>, Binding<Map.Entry<K, Provider<V>>>> valueKeyToEntryBinding =
valueKeyToEntryBindingBuilder.build();
// Check that there is a 1:1 mapping from keys from the ProviderMapEntrys to the
// keys from the Bindings.
Set<Key<V>> keysFromProviderMapEntrys = Sets.newHashSet(keyToValueKey.values());
Set<Key<V>> keysFromBindings = valueKeyToBinding.keySet();
if (!keysFromProviderMapEntrys.equals(keysFromBindings)) {
Set<Key<V>> keysOnlyFromProviderMapEntrys =
Sets.difference(keysFromProviderMapEntrys, keysFromBindings);
Set<Key<V>> keysOnlyFromBindings =
Sets.difference(keysFromBindings, keysFromProviderMapEntrys);
StringBuilder sb = new StringBuilder("Expected a 1:1 mapping from map keys to values.");
if (!keysOnlyFromBindings.isEmpty()) {
sb.append(
Errors.format("%nFound these Bindings that were missing an associated entry:%n"));
for (Key<V> key : keysOnlyFromBindings) {
sb.append(
Errors.format(" %s bound at: %s%n", key, valueKeyToBinding.get(key).getSource()));
}
}
if (!keysOnlyFromProviderMapEntrys.isEmpty()) {
sb.append(Errors.format("%nFound these map keys without a corresponding value:%n"));
for (Key<V> key : keysOnlyFromProviderMapEntrys) {
sb.append(
Errors.format(
" '%s' bound at: %s%n",
valueKeyToKey.get(key), valueKeyToEntryBinding.get(key).getSource()));
}
}
throw new IllegalArgumentException(sb.toString());
}
// Now that we have the two maps, generate the result map
ImmutableList.Builder<Map.Entry<?, Binding<?>>> resultBuilder = ImmutableList.builder();
for (Map.Entry<K, Key<V>> entry : keyToValueKey.entries()) {
Binding<?> binding = valueKeyToBinding.get(entry.getValue());
// No null check for binding needed because of the above check to make sure all the
// values in keyToValueKey are present as keys in valueKeyToBinding
@SuppressWarnings({"unchecked", "rawtypes"})
Map.Entry<?, Binding<?>> newEntry =
(Map.Entry) Maps.immutableEntry(entry.getKey(), binding);
resultBuilder.add(newEntry);
}
return resultBuilder.build();
}
@Override
public boolean permitsDuplicates() {
if (bindingSelection.isInitialized()) {
return bindingSelection.permitsDuplicates();
} else {
throw new UnsupportedOperationException(
"permitsDuplicates() not supported for module bindings");
}
}
@Override
public boolean containsElement(Element element) {
return bindingSelection.containsElement(element);
}
}
/**
* Binds {@code Map<K, Set<V>>} and {{@code Map<K, Set<Provider<V>>>}.
*
* <p>This will only exist if permitDuplicates() is called.
*/
private static final class MultimapBinder<K, V> implements Module {
private final BindingSelection<K, V> bindingSelection;
private MultimapBinder(BindingSelection<K, V> bindingSelection) {
this.bindingSelection = bindingSelection;
}
@Override
public void configure(Binder binder) {
// Binds a Map<K, Set<Provider<V>>>
Provider<Map<K, Set<Provider<V>>>> multimapProvider =
new RealProviderMultimapProvider<K, V>(bindingSelection.getMapKey());
binder.bind(bindingSelection.getProviderSetMultimapKey()).toProvider(multimapProvider);
// Provide links from a few different public keys to the providerMultimapKey.
// The collection this exposes is internally an ImmutableMap, so it's OK to massage
// the guice Provider to javax Provider in the value (since the guice Provider implements
// javax Provider).
@SuppressWarnings({"unchecked", "rawtypes"})
Provider<Map<K, Set<javax.inject.Provider<V>>>> javaxProvider = (Provider) multimapProvider;
binder.bind(bindingSelection.getJavaxProviderSetMultimapKey()).toProvider(javaxProvider);
@SuppressWarnings({"unchecked", "rawtypes"})
Provider<Map<K, Collection<Provider<V>>>> collectionProvider = (Provider) multimapProvider;
binder
.bind(bindingSelection.getProviderCollectionMultimapKey())
.toProvider(collectionProvider);
@SuppressWarnings({"unchecked", "rawtypes"})
Provider<Map<K, Collection<javax.inject.Provider<V>>>> collectionJavaxProvider =
(Provider) multimapProvider;
binder
.bind(bindingSelection.getJavaxProviderCollectionMultimapKey())
.toProvider(collectionJavaxProvider);
// Binds a Map<K, Set<V>>
@SuppressWarnings({"unchecked", "rawtypes"})
Provider<Map<K, Set<V>>> realMultimapProvider =
new RealMultimapProvider(bindingSelection.getMapKey());
binder.bind(bindingSelection.getMultimapKey()).toProvider(realMultimapProvider);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof MultimapBinder
&& ((MultimapBinder<?, ?>) o).bindingSelection.equals(bindingSelection);
}
private static final class RealProviderMultimapProvider<K, V>
extends RealMultimapBinderProviderWithDependencies<K, V, Map<K, Set<Provider<V>>>> {
private Map<K, Set<Provider<V>>> multimapOfProviders;
private Set<Dependency<?>> dependencies = RealMapBinder.MODULE_DEPENDENCIES;
private RealProviderMultimapProvider(Key<Map<K, V>> mapKey) {
super(mapKey);
}
@Override
public Set<Dependency<?>> getDependencies() {
return dependencies;
}
@Override
protected void doInitialize(InjectorImpl injector, Errors errors) {
ImmutableMap.Builder<K, Set<Provider<V>>> multimapOfProvidersBuilder =
ImmutableMap.builder();
ImmutableSet.Builder<Dependency<?>> dependenciesBuilder = ImmutableSet.builder();
for (Map.Entry<K, Set<Binding<V>>> entry :
bindingSelection.getMultimapBindings().entrySet()) {
ImmutableSet.Builder<Provider<V>> providersBuilder = ImmutableSet.builder();
for (Binding<V> binding : entry.getValue()) {
providersBuilder.add(binding.getProvider());
dependenciesBuilder.add(Dependency.get(getKeyOfProvider(binding.getKey())));
}
multimapOfProvidersBuilder.put(entry.getKey(), providersBuilder.build());
}
multimapOfProviders = multimapOfProvidersBuilder.build();
dependencies = dependenciesBuilder.build();
}
@Override
protected Map<K, Set<Provider<V>>> doProvision(
Errors errors, InternalContext context, Dependency<?> dependency) {
return multimapOfProviders;
}
}
private static final class RealMultimapProvider<K, V>
extends RealMultimapBinderProviderWithDependencies<K, V, Map<K, Set<V>>> {
/**
* A simple class to hold a key and the associated bindings as an array.
*
* <p>Arrays are used for performance.
*/
private static final class PerKeyData<K, V> {
private final K key;
private final Binding<V>[] bindings;
private final SingleParameterInjector<V>[] injectors;
private PerKeyData(K key, Binding<V>[] bindings, SingleParameterInjector<V>[] injectors) {
Preconditions.checkArgument(bindings.length == injectors.length);
this.key = key;
this.bindings = bindings;
this.injectors = injectors;
}
}
private Set<Dependency<?>> dependencies = RealMapBinder.MODULE_DEPENDENCIES;
private PerKeyData<K, V>[] perKeyDatas;
private RealMultimapProvider(Key<Map<K, V>> mapKey) {
super(mapKey);
}
@Override
public Set<Dependency<?>> getDependencies() {
return dependencies;
}
@Override
protected void doInitialize(InjectorImpl injector, Errors errors) throws ErrorsException {
@SuppressWarnings({"unchecked", "rawtypes"})
PerKeyData<K, V>[] typedPerKeyData =
new PerKeyData[bindingSelection.getMapBindings().size()];
perKeyDatas = typedPerKeyData;
ImmutableSet.Builder<Dependency<?>> dependenciesBuilder = ImmutableSet.builder();
List<Dependency<?>> dependenciesForKey = Lists.newArrayList();
int i = 0;
for (Map.Entry<K, Set<Binding<V>>> entry :
bindingSelection.getMultimapBindings().entrySet()) {
// Clear the list of dependencies because we're reusing it for each different key
dependenciesForKey.clear();
Set<Binding<V>> bindings = entry.getValue();
@SuppressWarnings({"unchecked", "rawtypes"})
Binding<V>[] typedBindings = new Binding[bindings.size()];
Binding<V>[] bindingsArray = typedBindings;
int j = 0;
for (Binding<V> binding : bindings) {
Dependency<V> dependency = Dependency.get(binding.getKey());
dependenciesBuilder.add(dependency);
dependenciesForKey.add(dependency);
bindingsArray[j] = binding;
j++;
}
@SuppressWarnings("unchecked")
SingleParameterInjector<V>[] injectors =
(SingleParameterInjector<V>[])
injector.getParametersInjectors(dependenciesForKey, errors);
perKeyDatas[i] = new PerKeyData<K, V>(entry.getKey(), bindingsArray, injectors);
i++;
}
dependencies = dependenciesBuilder.build();
}
@Override
protected Map<K, Set<V>> doProvision(
Errors errors, InternalContext context, Dependency<?> dependency) throws ErrorsException {
ImmutableMap.Builder<K, Set<V>> resultBuilder = ImmutableMap.builder();
for (PerKeyData<K, V> perKeyData : perKeyDatas) {
ImmutableSet.Builder<V> bindingsBuilder = ImmutableSet.builder();
SingleParameterInjector<V>[] injectors = perKeyData.injectors;
for (int i = 0; i < injectors.length; i++) {
SingleParameterInjector<V> injector = injectors[i];
V value = injector.inject(errors, context);
if (value == null) {
throw createNullValueException(errors, perKeyData.key, perKeyData.bindings[i]);
}
bindingsBuilder.add(value);
}
resultBuilder.put(perKeyData.key, bindingsBuilder.build());
}
return resultBuilder.build();
}
}
}
/**
* 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<K, Provider<V>>>, Map.Entry<K, Provider<V>> {
private final K key;
private final Provider<V> provider;
private final Key<V> valueKey;
ProviderMapEntry(K key, Provider<V> provider, Key<V> valueKey) {
this.key = key;
this.provider = provider;
this.valueKey = valueKey;
}
@Override
public Map.Entry<K, Provider<V>> 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<V> value) {
throw new UnsupportedOperationException();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Map.Entry) {
@SuppressWarnings("rawtypes")
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 + ")";
}
}
/** A base class for ProviderWithDependencies that need equality based on a specific object. */
private abstract static class RealMapBinderProviderWithDependencies<K, V, P>
extends InternalProviderInstanceBindingImpl.Factory<P> {
final BindingSelection<K, V> bindingSelection;
private RealMapBinderProviderWithDependencies(BindingSelection<K, V> bindingSelection) {
// While MapBinders only depend on bindings created in modules so we could theoretically
// initialize eagerly, they also depend on
// 1. findBindingsByType returning results
// 2. being able to call BindingImpl.acceptTargetVisitor
// neither of those is available during eager initialization, so we use DELAYED
super(InitializationTiming.DELAYED);
this.bindingSelection = bindingSelection;
}
@Override
final void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
if (bindingSelection.tryInitialize(injector, errors)) {
doInitialize(injector, errors);
}
}
/**
* Initialize the factory. BindingSelection is guaranteed to be initialized at this point and
* this will be called prior to any provisioning.
*/
protected abstract void doInitialize(InjectorImpl injector, Errors errors)
throws ErrorsException;
@Override
public boolean equals(Object obj) {
return obj != null
&& this.getClass() == obj.getClass()
&& bindingSelection.equals(
((RealMapBinderProviderWithDependencies<?, ?, ?>) obj).bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
}
/**
* A base class for ProviderWithDependencies that need equality based on a specific object.
*
* <p>This differs from {@link RealMapBinderProviderWithDependencies} in that it gets the {@code
* bindingSelection} from the injector at initialization time, rather than in the constructor.
* This is done to allow all the providers to operate on the same instance of the {@link
* BindingSelection}.
*/
private abstract static class RealMultimapBinderProviderWithDependencies<K, V, P>
extends InternalProviderInstanceBindingImpl.Factory<P> {
final Key<Map<K, V>> mapKey;
BindingSelection<K, V> bindingSelection;
private RealMultimapBinderProviderWithDependencies(Key<Map<K, V>> mapKey) {
// While MapBinders only depend on bindings created in modules so we could theoretically
// initialize eagerly, they also depend on
// 1. findBindingsByType returning results
// 2. being able to call BindingImpl.acceptTargetVisitor
// neither of those is available during eager initialization, so we use DELAYED
super(InitializationTiming.DELAYED);
this.mapKey = mapKey;
}
/**
* This will get the authoritative {@link BindingSelection} from the map provider. This
* guarantees that everyone has the same instance of the bindingSelection and sees consistent
* state.
*/
@Override
final void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
Binding<Map<K, V>> mapBinding = injector.getExistingBinding(mapKey);
ProviderInstanceBinding<Map<K, V>> providerInstanceBinding =
(ProviderInstanceBinding<Map<K, V>>) mapBinding;
@SuppressWarnings("unchecked")
RealMapProvider<K, V> mapProvider =
(RealMapProvider<K, V>) providerInstanceBinding.getUserSuppliedProvider();
this.bindingSelection = mapProvider.getBindingSelection();
if (bindingSelection.tryInitialize(injector, errors)) {
doInitialize(injector, errors);
}
}
/**
* Initialize the factory. BindingSelection is guaranteed to be initialized at this point and
* this will be called prior to any provisioning.
*/
abstract void doInitialize(InjectorImpl injector, Errors errors) throws ErrorsException;
@Override
public boolean equals(Object obj) {
return obj != null
&& this.getClass() == obj.getClass()
&& mapKey.equals(((RealMultimapBinderProviderWithDependencies<?, ?, ?>) obj).mapKey);
}
@Override
public int hashCode() {
return mapKey.hashCode();
}
}
private static <K, V> ErrorsException createNullValueException(
Errors errors, K key, Binding<V> binding) {
errors.addMessage(
"Map injection failed due to null value for key \"%s\", bound at: %s",
key, binding.getSource());
return errors.toException();
}
}