Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 117 additions & 29 deletions core/src/com/google/inject/internal/RealMapBinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.ExposedBinding;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.util.Types;
Expand Down Expand Up @@ -119,6 +120,62 @@ public static <K, V> RealMapBinder<K, V> newRealMapBinder(
binder, Key.get(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> RealMapBinder<K, V> newRealMapBinder(
Binder binder,
TypeLiteral<K> keyType,
TypeLiteral<V> valueType,
Class<? extends Annotation> annotationType,
boolean bindMap) {
return newRealMapBinder(
binder,
keyType,
valueType,
Key.get(mapOf(keyType, valueType), annotationType),
RealMultibinder.newRealSetBinder(
binder, Key.get(entryOfProviderOf(keyType, valueType), annotationType), bindMap),
bindMap);
}

/**
* 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,
boolean bindMap) {
return newRealMapBinder(
binder,
keyType,
valueType,
Key.get(mapOf(keyType, valueType), annotation),
RealMultibinder.newRealSetBinder(
binder, Key.get(entryOfProviderOf(keyType, valueType), annotation), bindMap),
bindMap);
}

/**
* 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> newRealMapBinder(
Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, boolean bindMap) {
return newRealMapBinder(
binder,
keyType,
valueType,
Key.get(mapOf(keyType, valueType)),
RealMultibinder.newRealSetBinder(
binder, Key.get(entryOfProviderOf(keyType, valueType)), bindMap),
bindMap);
}

@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>>)
Expand Down Expand Up @@ -240,8 +297,18 @@ private static <K, V> RealMapBinder<K, V> newRealMapBinder(
TypeLiteral<V> valueType,
Key<Map<K, V>> mapKey,
RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder) {
return newRealMapBinder(binder, keyType, valueType, mapKey, entrySetBinder, true);
}

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,
boolean bindMap) {
RealMapBinder<K, V> mapBinder =
new RealMapBinder<>(binder, keyType, valueType, mapKey, entrySetBinder);
new RealMapBinder<>(binder, keyType, valueType, mapKey, entrySetBinder, bindMap);
binder.install(mapBinder);
return mapBinder;
}
Expand All @@ -253,6 +320,8 @@ private static <K, V> RealMapBinder<K, V> newRealMapBinder(

private final BindingSelection<K, V> bindingSelection;
private final Binder binder;
private final boolean bindMap;
private boolean permitSpanInjectors;

private final RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder;

Expand All @@ -261,10 +330,12 @@ private RealMapBinder(
TypeLiteral<K> keyType,
TypeLiteral<V> valueType,
Key<Map<K, V>> mapKey,
RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder) {
RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder,
boolean bindMap) {
this.bindingSelection = new BindingSelection<>(keyType, valueType, mapKey, entrySetBinder);
this.binder = binder;
this.entrySetBinder = entrySetBinder;
this.bindMap = bindMap;
}

public void permitDuplicates() {
Expand All @@ -273,6 +344,11 @@ public void permitDuplicates() {
binder.install(new MultimapBinder<K, V>(bindingSelection));
}

public void permitSpanInjectors() {
this.permitSpanInjectors = true;
entrySetBinder.permitSpanInjectors();
}

/** Adds a binding to the map for the given key. */
Key<V> getKeyForNewValue(K key) {
checkNotNull(key, "key");
Expand All @@ -294,40 +370,47 @@ Key<V> getKeyForNewValue(K key) {
* V}.
*/
public LinkedBindingBuilder<V> addBinding(K key) {
return binder.bind(getKeyForNewValue(key));
Key<V> valueKey = getKeyForNewValue(key);
LinkedBindingBuilder<V> builder = binder.bind(valueKey);
if (permitSpanInjectors && binder instanceof com.google.inject.PrivateBinder) {
((com.google.inject.PrivateBinder) binder).expose(valueKey);
}
return builder;
}

@SuppressWarnings({"unchecked", "rawtypes"}) // we use raw Key to link bindings.
@Override
public void configure(Binder binder) {
checkConfiguration(!bindingSelection.isInitialized(), "MapBinder was already initialized");

// Binds a Map<K, Provider<V>>
binder
.bind(bindingSelection.getProviderMapKey())
.toProvider(new RealProviderMapProvider<>(bindingSelection));

// The map this exposes is internally an ImmutableMap, so it's OK to massage
// the guice Provider to jakarta Provider in the value (since Guice provider
// implements jakarta Provider).
binder
.bind(bindingSelection.getJakartaProviderMapKey())
.to((Key) bindingSelection.getProviderMapKey());

// Bind Map<K, V> to the provider w/ extension support.
binder
.bind(bindingSelection.getMapKey())
.toProvider(new ExtensionRealMapProvider<>(bindingSelection));
// Bind Map<K, ? extends V> to the provider w/o the extension support.
binder
.bind(bindingSelection.getMapOfKeyExtendsValueKey())
.to((Key) bindingSelection.getMapKey());

// The Map.Entries are all ProviderMapEntry instances which do not allow setValue, so it is
// safe to massage the return type like this
binder
.bind(bindingSelection.getEntrySetJakartaProviderKey())
.to((Key) bindingSelection.getEntrySetBinder().getSetKey());
if (bindMap) {
// Binds a Map<K, Provider<V>>
binder
.bind(bindingSelection.getProviderMapKey())
.toProvider(new RealProviderMapProvider<>(bindingSelection));

// The map this exposes is internally an ImmutableMap, so it's OK to massage
// the guice Provider to jakarta Provider in the value (since Guice provider
// implements jakarta Provider).
binder
.bind(bindingSelection.getJakartaProviderMapKey())
.to((Key) bindingSelection.getProviderMapKey());

// Bind Map<K, V> to the provider w/ extension support.
binder
.bind(bindingSelection.getMapKey())
.toProvider(new ExtensionRealMapProvider<>(bindingSelection));
// Bind Map<K, ? extends V> to the provider w/o the extension support.
binder
.bind(bindingSelection.getMapOfKeyExtendsValueKey())
.to((Key) bindingSelection.getMapKey());

// The Map.Entries are all ProviderMapEntry instances which do not allow setValue, so it is
// safe to massage the return type like this
binder
.bind(bindingSelection.getEntrySetJakartaProviderKey())
.to((Key) bindingSelection.getEntrySetBinder().getSetKey());
}
}

@Override
Expand Down Expand Up @@ -443,6 +526,11 @@ private boolean tryInitialize(InjectorImpl injector, Errors errors) {
injector.findBindingsByType(entrySetBinder.getElementTypeLiteral())) {
if (entrySetBinder.containsElement(binding)) {

if (binding instanceof ExposedBinding) {
ExposedBinding<Map.Entry<K, Provider<V>>> exposed = (ExposedBinding) binding;
binding = exposed.getPrivateElements().getInjector().getBinding(binding.getKey());
}

// 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.
Expand Down
61 changes: 44 additions & 17 deletions core/src/com/google/inject/internal/RealMultibinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ public static <T> RealMultibinder<T> newRealSetBinder(Binder binder, Key<T> key)
return result;
}

/**
* Implementation of newSetBinder that avoids binding the set itself.
*/
public static <T> RealMultibinder<T> newRealSetBinder(Binder binder, Key<T> key, boolean bindSet) {
RealMultibinder<T> result = new RealMultibinder<>(binder, key, bindSet);
binder.install(result);
return result;
}

@SuppressWarnings("unchecked") // wrapping a T in a Set safely returns a Set<T>
static <T> TypeLiteral<Set<T>> setOf(TypeLiteral<T> elementType) {
Type type = Types.setOf(elementType.getType());
Expand Down Expand Up @@ -96,39 +105,52 @@ static <T> TypeLiteral<Set<? extends T>> setOfExtendsOf(TypeLiteral<T> elementTy

private final BindingSelection<T> bindingSelection;
private final Binder binder;
private final boolean bindSet;
private boolean permitSpanInjectors;

RealMultibinder(Binder binder, Key<T> key) {
this(binder, key, true);
}

RealMultibinder(Binder binder, Key<T> key, boolean bindSet) {
this.binder = checkNotNull(binder, "binder");
this.bindingSelection = new BindingSelection<>(key);
this.bindSet = bindSet;
}

@SuppressWarnings({"unchecked", "rawtypes"}) // we use raw Key to link bindings together.
@Override
public void configure(Binder binder) {
checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");

// Bind the setKey to the provider wrapped w/ extension support.
binder
.bind(bindingSelection.getSetKey())
.toProvider(new RealMultibinderProvider<>(bindingSelection));
binder.bind(bindingSelection.getSetOfExtendsKey()).to(bindingSelection.getSetKey());

binder
.bind(bindingSelection.getCollectionOfProvidersKey())
.toProvider(new RealMultibinderCollectionOfProvidersProvider<T>(bindingSelection));

// The collection this exposes is internally an ImmutableList, so it's OK to massage
// the guice Provider to jakarta Provider in the value (since the guice Provider implements
// jakarta Provider).
binder
.bind(bindingSelection.getCollectionOfJakartaProvidersKey())
.to((Key) bindingSelection.getCollectionOfProvidersKey());
if (bindSet) {
// Bind the setKey to the provider wrapped w/ extension support.
binder
.bind(bindingSelection.getSetKey())
.toProvider(new RealMultibinderProvider<>(bindingSelection));
binder.bind(bindingSelection.getSetOfExtendsKey()).to(bindingSelection.getSetKey());

binder
.bind(bindingSelection.getCollectionOfProvidersKey())
.toProvider(new RealMultibinderCollectionOfProvidersProvider<T>(bindingSelection));

// The collection this exposes is internally an ImmutableList, so it's OK to massage
// the guice Provider to jakarta Provider in the value (since the guice Provider implements
// jakarta Provider).
binder
.bind(bindingSelection.getCollectionOfJakartaProvidersKey())
.to((Key) bindingSelection.getCollectionOfProvidersKey());
}
}

public void permitDuplicates() {
binder.install(new PermitDuplicatesModule(bindingSelection.getPermitDuplicatesKey()));
}

public void permitSpanInjectors() {
this.permitSpanInjectors = true;
}

/** Adds a new entry to the set and returns the key for it. */
Key<T> getKeyForNewItem() {
checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");
Expand All @@ -138,7 +160,12 @@ Key<T> getKeyForNewItem() {
}

public LinkedBindingBuilder<T> addBinding() {
return binder.bind(getKeyForNewItem());
Key<T> key = getKeyForNewItem();
LinkedBindingBuilder<T> builder = binder.bind(key);
if (permitSpanInjectors && binder instanceof com.google.inject.PrivateBinder) {
((com.google.inject.PrivateBinder) binder).expose(key);
}
return builder;
}

// These methods are used by RealMapBinder
Expand Down
Loading