Skip to content

Commit

Permalink
Use linked bindings for MapBinder/Multibinder/OptionalBinder aliases,…
Browse files Browse the repository at this point in the history
… instead of linking to the same internal factory implementation. (Reusing the internal factory results in some quirks that we want to avoid.)

(cherry-picked into the 6.0 release)
  • Loading branch information
sameb committed May 12, 2023
1 parent 9abff25 commit e897e89
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 179 deletions.
131 changes: 45 additions & 86 deletions core/src/com/google/inject/internal/RealMapBinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -340,52 +340,50 @@ public LinkedBindingBuilder<V> addBinding(K key) {
return binder.bind(getKeyForNewValue(key));
}

@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>>
RealProviderMapProvider<K, V> providerMapProvider =
new RealProviderMapProvider<>(bindingSelection);
binder.bind(bindingSelection.getProviderMapKey()).toProvider(providerMapProvider);
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).
@SuppressWarnings({"unchecked", "rawtypes"})
Provider<Map<K, jakarta.inject.Provider<V>>> jakartaProviderMapProvider =
(Provider) providerMapProvider;
binder.bind(bindingSelection.getJakartaProviderMapKey()).toProvider(jakartaProviderMapProvider);
binder
.bind(bindingSelection.getJakartaProviderMapKey())
.to((Key) bindingSelection.getProviderMapKey());

// 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);
binder
.bind(bindingSelection.getJavaxProviderMapKey())
.to((Key) bindingSelection.getProviderMapKey());

RealMapProvider<K, V> mapProvider = new RealMapProvider<>(bindingSelection);
// Bind Map<K, V> to the provider w/ extension support.
binder
.bind(bindingSelection.getMapKey())
.toProvider(new ExtensionRealMapProvider<>(mapProvider));
.toProvider(new ExtensionRealMapProvider<>(bindingSelection));
// Bind Map<K, ? extends V> to the provider w/o the extension support.
binder.bind(bindingSelection.getMapOfKeyExtendsValueKey()).toProvider(mapProvider);
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
@SuppressWarnings({"unchecked", "rawtypes"})
Key<Set<Map.Entry<K, jakarta.inject.Provider<V>>>> jakartaEntrySetProviderKey =
(Key) bindingSelection.getEntrySetBinder().getSetKey();
binder.bind(bindingSelection.getEntrySetJakartaProviderKey()).to(jakartaEntrySetProviderKey);
binder
.bind(bindingSelection.getEntrySetJakartaProviderKey())
.to((Key) bindingSelection.getEntrySetBinder().getSetKey());

// 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);
binder
.bind(bindingSelection.getEntrySetJavaxProviderKey())
.to((Key) bindingSelection.getEntrySetBinder().getSetKey());
}

@Override
Expand Down Expand Up @@ -847,8 +845,14 @@ protected Map<K, Provider<V>> doProvision(InternalContext context, Dependency<?>
}
}

private static final class RealMapProvider<K, V>
extends RealMapBinderProviderWithDependencies<K, V, Map<K, V>> {
/**
* Implementation of a provider instance for the map that also exposes details about the MapBinder
* using the extension SPI, delegating to another provider instance for non-extension (e.g, the
* actual provider instance info) data.
*/
private static final class ExtensionRealMapProvider<K, V>
extends RealMapBinderProviderWithDependencies<K, V, Map<K, V>>
implements ProviderWithExtensionVisitor<Map<K, V>>, MapBinderBinding<Map<K, V>> {
Set<Dependency<?>> dependencies = RealMapBinder.MODULE_DEPENDENCIES;

/**
Expand All @@ -862,14 +866,10 @@ private static final class RealMapProvider<K, V>

private boolean initialized = false;

RealMapProvider(BindingSelection<K, V> bindingSelection) {
ExtensionRealMapProvider(BindingSelection<K, V> bindingSelection) {
super(bindingSelection);
}

BindingSelection<K, V> getBindingSelection() {
return bindingSelection;
}

@Override
protected void doInitialize(InjectorImpl injector, Errors errors) throws ErrorsException {
if (initialized) {
Expand Down Expand Up @@ -931,43 +931,11 @@ protected Map<K, V> doProvision(InternalContext context, Dependency<?> dependenc
public Set<Dependency<?>> getDependencies() {
return dependencies;
}
}

/**
* Implementation of a provider instance for the map that also exposes details about the MapBinder
* using the extension SPI, delegating to another provider instance for non-extension (e.g, the
* actual provider instance info) data.
*/
private static final class ExtensionRealMapProvider<K, V>
extends RealMapBinderProviderWithDependencies<K, V, Map<K, V>>
implements ProviderWithExtensionVisitor<Map<K, V>>, MapBinderBinding<Map<K, V>> {
final RealMapProvider<K, V> delegate;

ExtensionRealMapProvider(RealMapProvider<K, V> delegate) {
super(delegate.bindingSelection);
this.delegate = delegate;
}

BindingSelection<K, V> getBindingSelection() {
return bindingSelection;
}

@Override
protected void doInitialize(InjectorImpl injector, Errors errors) throws ErrorsException {
delegate.doInitialize(injector, errors);
}

@Override
protected Map<K, V> doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException {
return delegate.doProvision(context, dependency);
}

@Override
public Set<Dependency<?>> getDependencies() {
return delegate.getDependencies();
}

@Override
@SuppressWarnings("unchecked")
public <B, W> W acceptExtensionVisitor(
Expand Down Expand Up @@ -1154,55 +1122,46 @@ private MultimapBinder(BindingSelection<K, V> bindingSelection) {
this.bindingSelection = bindingSelection;
}

@SuppressWarnings({"unchecked", "rawtypes"}) // we use raw Key to link bindings together.
@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);
binder
.bind(bindingSelection.getProviderSetMultimapKey())
.toProvider(new RealProviderMultimapProvider<K, V>(bindingSelection.getMapKey()));

// 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);
binder
.bind(bindingSelection.getJavaxProviderSetMultimapKey())
.to((Key) bindingSelection.getProviderSetMultimapKey());

// 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 jakarta Provider in the value (since the guice Provider implements
// jakarta Provider).
@SuppressWarnings({"unchecked", "rawtypes"})
Provider<Map<K, Set<jakarta.inject.Provider<V>>>> jakartaProvider =
(Provider) multimapProvider;
binder.bind(bindingSelection.getJakartaProviderSetMultimapKey()).toProvider(jakartaProvider);
binder
.bind(bindingSelection.getJakartaProviderSetMultimapKey())
.to((Key) bindingSelection.getProviderSetMultimapKey());

@SuppressWarnings({"unchecked", "rawtypes"})
Provider<Map<K, Collection<Provider<V>>>> collectionProvider = (Provider) multimapProvider;
binder
.bind(bindingSelection.getProviderCollectionMultimapKey())
.toProvider(collectionProvider);
.to((Key) bindingSelection.getProviderSetMultimapKey());

@SuppressWarnings({"unchecked", "rawtypes"})
Provider<Map<K, Collection<jakarta.inject.Provider<V>>>> collectionJakartaProvider =
(Provider) multimapProvider;
binder
.bind(bindingSelection.getJakartaProviderCollectionMultimapKey())
.toProvider(collectionJakartaProvider);
.to((Key) bindingSelection.getProviderSetMultimapKey());

@SuppressWarnings({"unchecked", "rawtypes"})
Provider<Map<K, Collection<javax.inject.Provider<V>>>> collectionJavaxProvider =
(Provider) multimapProvider;
binder
.bind(bindingSelection.getJavaxProviderCollectionMultimapKey())
.toProvider(collectionJavaxProvider);
.to((Key) bindingSelection.getProviderSetMultimapKey());

// 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);
binder
.bind(bindingSelection.getMultimapKey())
.toProvider(new RealMultimapProvider(bindingSelection.getMapKey()));
}

@Override
Expand Down
59 changes: 13 additions & 46 deletions core/src/com/google/inject/internal/RealMultibinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,41 +105,34 @@ static <T> TypeLiteral<Set<? extends T>> setOfExtendsOf(TypeLiteral<T> elementTy
this.bindingSelection = new BindingSelection<>(key);
}

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

RealMultibinderProvider<T> setProvider = new RealMultibinderProvider<T>(bindingSelection);
// Bind the setKey to the provider wrapped w/ extension support.
binder
.bind(bindingSelection.getSetKey())
.toProvider(new ExtensionRealMultibinderProvider<>(setProvider));
// Bind the <? extends T> to the provider w/o extension support.
// It's important the exactly one binding implement the extension support and show
// the other keys as aliases, to adhere to the extension contract.
binder.bind(bindingSelection.getSetOfExtendsKey()).toProvider(setProvider);

Provider<Collection<Provider<T>>> collectionOfProvidersProvider =
new RealMultibinderCollectionOfProvidersProvider<T>(bindingSelection);
.toProvider(new RealMultibinderProvider<>(bindingSelection));
binder.bind(bindingSelection.getSetOfExtendsKey()).to(bindingSelection.getSetKey());

binder
.bind(bindingSelection.getCollectionOfProvidersKey())
.toProvider(collectionOfProvidersProvider);
.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).
@SuppressWarnings("unchecked")
Provider<Collection<jakarta.inject.Provider<T>>> jakartaProvider =
(Provider) collectionOfProvidersProvider;
binder.bind(bindingSelection.getCollectionOfJakartaProvidersKey()).toProvider(jakartaProvider);
binder
.bind(bindingSelection.getCollectionOfJakartaProvidersKey())
.to((Key) bindingSelection.getCollectionOfProvidersKey());

// The collection this exposes is internally an ImmutableList, so it's OK to massage
// the guice Provider to javax Provider in the value (since the guice Provider implements
// javax Provider).
@SuppressWarnings("unchecked")
Provider<Collection<javax.inject.Provider<T>>> javaxProvider =
(Provider) collectionOfProvidersProvider;
binder.bind(bindingSelection.getCollectionOfJavaxProvidersKey()).toProvider(javaxProvider);
binder
.bind(bindingSelection.getCollectionOfJavaxProvidersKey())
.to((Key) bindingSelection.getCollectionOfProvidersKey());
}

public void permitDuplicates() {
Expand Down Expand Up @@ -233,7 +226,8 @@ public int hashCode() {
* so it can be used to supply a {@code Set<T>} and {@code Set<? extends T>}, the latter being
* useful for Kotlin support.
*/
private static final class RealMultibinderProvider<T> extends BaseFactory<T, Set<T>> {
private static final class RealMultibinderProvider<T> extends BaseFactory<T, Set<T>>
implements ProviderWithExtensionVisitor<Set<T>>, MultibinderBinding<Set<T>> {
List<Binding<T>> bindings;
SingleParameterInjector<T>[] injectors;
boolean permitDuplicates;
Expand Down Expand Up @@ -296,33 +290,6 @@ private InternalProvisionException newDuplicateValuesException(T[] values) {
bindingSelection.getSetKey(), bindings, values, ImmutableList.of(getSource())));
return new InternalProvisionException(message);
}
}

/**
* Implementation of BaseFactory that exposes details about the multibinder through the extension
* SPI.
*/
private static final class ExtensionRealMultibinderProvider<T> extends BaseFactory<T, Set<T>>
implements ProviderWithExtensionVisitor<Set<T>>, MultibinderBinding<Set<T>> {
final RealMultibinderProvider<T> delegate;

ExtensionRealMultibinderProvider(RealMultibinderProvider<T> delegate) {
// Note: method reference doesn't work for the 2nd arg for some reason when compiling on java8
super(delegate.bindingSelection, bs -> bs.getDependencies());
this.delegate = delegate;
}

@Override
protected void doInitialize() {
delegate.doInitialize();
}

@Override
protected ImmutableSet<T> doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException {
return delegate.doProvision(context, dependency);
}

@SuppressWarnings("unchecked")
@Override
public <B, V> V acceptExtensionVisitor(
Expand Down
Loading

0 comments on commit e897e89

Please sign in to comment.