Inline requests for map bindings
Under the hood, remove ContributionBinding.Kind.SYNTHETIC_MAP. Map<K, V> and Map<K, Produced<V>> bindings now act just like Map<K, FrameworkType<V>>, and have dependencies on their contributions directly instead of wrapping a another FrameworkType<Map<K, FrameworkType<V>>>
This is a rollforward of 1a5123d0. The original commit changed the semantics of dependency requests for Map<K, V> and Set<T> bindings.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=167024142
diff --git a/java/dagger/internal/MapBuilder.java b/java/dagger/internal/MapBuilder.java
new file mode 100644
index 0000000..1560491
--- /dev/null
+++ b/java/dagger/internal/MapBuilder.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * 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 dagger.internal;
+
+import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * A fluent builder class that returns a {@link Map}. Used in component implementations where a map
+ * must be created in one fluent statement for inlined request fulfillments.
+ */
+public final class MapBuilder<K, V> {
+ private final Map<K, V> contributions;
+
+ private MapBuilder(int size) {
+ contributions = newLinkedHashMapWithExpectedSize(size);
+ }
+
+ /**
+ * Creates a new {@link MapBuilder} with {@code size} elements.
+ */
+ public static <K, V> MapBuilder<K, V> newMapBuilder(int size) {
+ return new MapBuilder<>(size);
+ }
+
+ public MapBuilder<K, V> put(K key, V value) {
+ contributions.put(key, value);
+ return this;
+ }
+
+ public Map<K, V> build() {
+ switch (contributions.size()) {
+ case 0:
+ return Collections.emptyMap();
+ default:
+ return Collections.unmodifiableMap(contributions);
+ }
+ }
+}
diff --git a/java/dagger/internal/MapFactory.java b/java/dagger/internal/MapFactory.java
index d4b9189..b6f925a 100644
--- a/java/dagger/internal/MapFactory.java
+++ b/java/dagger/internal/MapFactory.java
@@ -17,8 +17,11 @@
package dagger.internal;
import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
+import static dagger.internal.Preconditions.checkNotNull;
import static java.util.Collections.unmodifiableMap;
+import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.inject.Provider;
@@ -32,18 +35,28 @@
*
*/
public final class MapFactory<K, V> implements Factory<Map<K, V>> {
+ private static final Provider<Map<Object, Object>> EMPTY =
+ InstanceFactory.create(Collections.emptyMap());
+
private final Map<K, Provider<V>> contributingMap;
- private MapFactory(Map<K, Provider<V>> map) {
- this.contributingMap = unmodifiableMap(map);
+ /**
+ * Returns a new {@link Builder}
+ */
+ public static <K, V> Builder<K, V> builder(int size) {
+ return new Builder<>(size);
}
/**
- * Returns a new MapFactory.
+ * Returns a factory of an empty map.
*/
- public static <K, V> MapFactory<K, V> create(Provider<Map<K, Provider<V>>> mapProviderFactory) {
- Map<K, Provider<V>> map = mapProviderFactory.get();
- return new MapFactory<K, V>(map);
+ @SuppressWarnings("unchecked") // safe contravariant cast
+ public static <K, V> Provider<Map<K, V>> emptyMapProvider() {
+ return (Provider<Map<K, V>>) (Provider) EMPTY;
+ }
+
+ private MapFactory(Map<K, Provider<V>> map) {
+ this.contributingMap = unmodifiableMap(map);
}
/**
@@ -58,4 +71,25 @@
}
return unmodifiableMap(result);
}
+
+ // TODO(ronshapiro): can we merge the builders? Or maybe just use a (Immutable)MapBuilder?
+ /** A builder for {@link MapFactory}. */
+ public static final class Builder<K, V> {
+ private final LinkedHashMap<K, Provider<V>> map;
+
+ private Builder(int size) {
+ this.map = newLinkedHashMapWithExpectedSize(size);
+ }
+
+ /** Associates {@code key} with {@code providerOfValue}. */
+ public Builder<K, V> put(K key, Provider<V> providerOfValue) {
+ map.put(checkNotNull(key, "key"), checkNotNull(providerOfValue, "provider"));
+ return this;
+ }
+
+ /** Returns a new {@link MapProviderFactory}. */
+ public MapFactory<K, V> build() {
+ return new MapFactory<>(map);
+ }
+ }
}
diff --git a/java/dagger/internal/MapProviderFactory.java b/java/dagger/internal/MapProviderFactory.java
index c27beb2..2458a3c 100644
--- a/java/dagger/internal/MapProviderFactory.java
+++ b/java/dagger/internal/MapProviderFactory.java
@@ -17,10 +17,10 @@
package dagger.internal;
import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
+import static dagger.internal.Preconditions.checkNotNull;
import static java.util.Collections.unmodifiableMap;
import dagger.Lazy;
-import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.inject.Provider;
@@ -34,24 +34,13 @@
*/
public final class MapProviderFactory<K, V>
implements Factory<Map<K, Provider<V>>>, Lazy<Map<K, Provider<V>>> {
- private static final MapProviderFactory<Object, Object> EMPTY =
- new MapProviderFactory<Object, Object>(Collections.<Object, Provider<Object>>emptyMap());
-
private final Map<K, Provider<V>> contributingMap;
/**
* Returns a new {@link Builder}
*/
public static <K, V> Builder<K, V> builder(int size) {
- return new Builder<K, V>(size);
- }
-
- /**
- * Returns a factory of an empty map.
- */
- @SuppressWarnings("unchecked") // safe contravariant cast
- public static <K, V> MapProviderFactory<K, V> empty() {
- return (MapProviderFactory<K, V>) EMPTY;
+ return new Builder<>(size);
}
private MapProviderFactory(Map<K, Provider<V>> contributingMap) {
@@ -68,37 +57,23 @@
return this.contributingMap;
}
- /**
- * A builder to help build the {@link MapProviderFactory}
- */
+ /** A builder for {@link MapProviderFactory}. */
public static final class Builder<K, V> {
- private final LinkedHashMap<K, Provider<V>> mapBuilder;
+ private final LinkedHashMap<K, Provider<V>> map;
private Builder(int size) {
- // TODO(user): consider which way to initialize mapBuilder is better
- this.mapBuilder = newLinkedHashMapWithExpectedSize(size);
+ this.map = newLinkedHashMapWithExpectedSize(size);
}
- /**
- * Returns a new {@link MapProviderFactory}
- */
- public MapProviderFactory<K, V> build() {
- return new MapProviderFactory<K, V>(this.mapBuilder);
- }
-
- /**
- * Associate k with providerOfValue in {@code Builder}
- */
+ /** Associates {@code key} with {@code providerOfValue}. */
public Builder<K, V> put(K key, Provider<V> providerOfValue) {
- if (key == null) {
- throw new NullPointerException("The key is null");
- }
- if (providerOfValue == null) {
- throw new NullPointerException("The provider of the value is null");
- }
-
- this.mapBuilder.put(key, providerOfValue);
+ map.put(checkNotNull(key, "key"), checkNotNull(providerOfValue, "provider"));
return this;
}
+
+ /** Returns a new {@link MapProviderFactory}. */
+ public MapProviderFactory<K, V> build() {
+ return new MapProviderFactory<>(map);
+ }
}
}
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
index 093e5c5..06c8e64 100644
--- a/java/dagger/internal/codegen/BindingExpression.java
+++ b/java/dagger/internal/codegen/BindingExpression.java
@@ -198,6 +198,9 @@
case SYNTHETIC_MULTIBOUND_SET:
return new SetBindingExpression(
provisionBinding, graph, componentBindingExpressions, bindingExpression, elements);
+ case SYNTHETIC_MULTIBOUND_MAP:
+ return new MapBindingExpression(
+ provisionBinding, graph, componentBindingExpressions, bindingExpression, elements);
case SYNTHETIC_OPTIONAL_BINDING:
return new OptionalBindingExpression(
provisionBinding, bindingExpression, componentBindingExpressions);
diff --git a/java/dagger/internal/codegen/BindingGraph.java b/java/dagger/internal/codegen/BindingGraph.java
index 110d08e..14b9c7d 100644
--- a/java/dagger/internal/codegen/BindingGraph.java
+++ b/java/dagger/internal/codegen/BindingGraph.java
@@ -35,7 +35,6 @@
import com.google.auto.common.MoreTypes;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.base.VerifyException;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -519,7 +518,6 @@
ImmutableSet.Builder<Optional<ContributionBinding>> maybeContributionBindings =
ImmutableSet.builder();
- maybeContributionBindings.add(syntheticMapOfValuesBinding(requestKey));
maybeContributionBindings.add(
syntheticMultibinding(
requestKey, multibindingContributions, multibindingDeclarations));
@@ -585,71 +583,17 @@
owningResolver.componentDescriptor.subcomponentsByBuilderType().get(builderType));
}
- private Iterable<Key> keysMatchingRequest(Key requestKey) {
+ private ImmutableSet<Key> keysMatchingRequest(Key requestKey) {
ImmutableSet.Builder<Key> keys = ImmutableSet.builder();
keys.add(requestKey);
keyFactory.unwrapSetKey(requestKey, Produced.class).ifPresent(keys::add);
keyFactory.rewrapMapKey(requestKey, Producer.class, Provider.class).ifPresent(keys::add);
keyFactory.rewrapMapKey(requestKey, Provider.class, Producer.class).ifPresent(keys::add);
+ keys.addAll(keyFactory.implicitFrameworkMapKeys(requestKey));
return keys.build();
}
/**
- * If {@code key} is a {@code Map<K, V>} or {@code Map<K, Produced<V>>}, and there are any
- * multibinding contributions or declarations that apply to that map, returns a synthetic
- * binding for the {@code key} that depends on an {@linkplain #syntheticMultibinding(Key,
- * Iterable, Iterable) underlying synthetic multibinding}.
- *
- * <p>The returned binding has the same {@link BindingType} as the underlying synthetic
- * multibinding.
- */
- private Optional<ContributionBinding> syntheticMapOfValuesBinding(final Key key) {
- return syntheticMultibinding(
- key,
- multibindingContributionsForValueMap(key),
- multibindingDeclarationsForValueMap(key))
- .map(
- syntheticMultibinding -> {
- switch (syntheticMultibinding.bindingType()) {
- case PROVISION:
- return provisionBindingFactory.syntheticMapOfValuesBinding(key);
-
- case PRODUCTION:
- return productionBindingFactory.syntheticMapOfValuesOrProducedBinding(key);
-
- default:
- throw new VerifyException(syntheticMultibinding.toString());
- }
- });
- }
-
- /**
- * If {@code key} is for {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns all
- * multibinding contributions whose key is for {@code Map<K, Provider<V>>} or {@code Map<K,
- * Producer<V>>} with the same qualifier and {@code K} and {@code V}.
- */
- private ImmutableSet<ContributionBinding> multibindingContributionsForValueMap(Key key) {
- return keyFactory
- .implicitFrameworkMapKeys(key)
- .stream()
- .flatMap(mapKey -> getExplicitMultibindings(mapKey).stream())
- .collect(toImmutableSet());
- }
-
- /**
- * If {@code key} is for {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns all
- * multibinding declarations whose key is for {@code Map<K, Provider<V>>} or {@code Map<K,
- * Producer<V>>} with the same qualifier and {@code K} and {@code V}.
- */
- private ImmutableSet<MultibindingDeclaration> multibindingDeclarationsForValueMap(Key key) {
- return keyFactory
- .implicitFrameworkMapKeys(key)
- .stream()
- .flatMap(mapKey -> getMultibindingDeclarations(mapKey).stream())
- .collect(toImmutableSet());
- }
-
- /**
* Returns a synthetic binding that depends on individual multibinding contributions.
*
* <p>If there are no {@code multibindingContributions} or {@code multibindingDeclarations},
@@ -1165,7 +1109,9 @@
.stream()
.map(ContributionBinding::bindingKind)
.anyMatch(SYNTHETIC_MULTIBOUND_KINDS::contains)
- && !getLocalExplicitMultibindings(resolvedBindings.key()).isEmpty();
+ && keysMatchingRequest(resolvedBindings.key())
+ .stream()
+ .anyMatch(key -> !getLocalExplicitMultibindings(key).isEmpty());
}
/**
diff --git a/java/dagger/internal/codegen/BindingGraphValidator.java b/java/dagger/internal/codegen/BindingGraphValidator.java
index 1bbcbdc..32143d0 100644
--- a/java/dagger/internal/codegen/BindingGraphValidator.java
+++ b/java/dagger/internal/codegen/BindingGraphValidator.java
@@ -30,7 +30,6 @@
import static dagger.internal.codegen.ConfigurationAnnotations.getComponentAnnotation;
import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
-import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MAP;
import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_KINDS;
import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_MAP;
import static dagger.internal.codegen.ContributionBinding.indexMapBindingsByAnnotationType;
@@ -994,8 +993,7 @@
.stream()
.map(ContributionBinding::bindingKind)
// TODO(dpb): Kill with fire.
- .anyMatch(
- kind -> SYNTHETIC_MULTIBOUND_KINDS.contains(kind) || SYNTHETIC_MAP.equals(kind))) {
+ .anyMatch(SYNTHETIC_MULTIBOUND_KINDS::contains)) {
reportMultipleContributionTypes();
return;
}
diff --git a/java/dagger/internal/codegen/ContributionBinding.java b/java/dagger/internal/codegen/ContributionBinding.java
index 8891e67..6706757 100644
--- a/java/dagger/internal/codegen/ContributionBinding.java
+++ b/java/dagger/internal/codegen/ContributionBinding.java
@@ -73,12 +73,6 @@
*/
enum Kind {
/**
- * The synthetic binding for {@code Map<K, V>} that depends on either
- * {@code Map<K, Provider<V>>} or {@code Map<K, Producer<V>>}.
- */
- SYNTHETIC_MAP,
-
- /**
* A synthetic binding for a multibound set that depends on the individual multibinding
* {@link Provides @Provides} or {@link Produces @Produces} methods.
*/
diff --git a/java/dagger/internal/codegen/DependencyRequest.java b/java/dagger/internal/codegen/DependencyRequest.java
index bbd899a..06ab062 100644
--- a/java/dagger/internal/codegen/DependencyRequest.java
+++ b/java/dagger/internal/codegen/DependencyRequest.java
@@ -341,26 +341,48 @@
}
/**
+ * Creates synthetic dependency requests for each individual multibinding contribution in {@code
+ * multibindingContributions}.
+ */
+ ImmutableSet<DependencyRequest> forMultibindingContributions(
+ Key multibindingKey, Iterable<ContributionBinding> multibindingContributions) {
+ ImmutableSet.Builder<DependencyRequest> requests = ImmutableSet.builder();
+ for (ContributionBinding multibindingContribution : multibindingContributions) {
+ requests.add(forMultibindingContribution(multibindingKey, multibindingContribution));
+ }
+ return requests.build();
+ }
+
+ /**
* Creates a synthetic dependency request for one individual {@code multibindingContribution}.
*/
private DependencyRequest forMultibindingContribution(
- ContributionBinding multibindingContribution) {
+ Key multibindingKey, ContributionBinding multibindingContribution) {
checkArgument(
multibindingContribution.key().multibindingContributionIdentifier().isPresent(),
"multibindingContribution's key must have a multibinding contribution identifier: %s",
multibindingContribution);
return DependencyRequest.builder()
- .kind(multibindingContributionRequestKind(multibindingContribution))
+ .kind(multibindingContributionRequestKind(multibindingKey, multibindingContribution))
.key(multibindingContribution.key())
.build();
}
- private Kind multibindingContributionRequestKind(ContributionBinding multibindingContribution) {
+ // TODO(b/28555349): support PROVIDER_OF_LAZY here too
+ private static final ImmutableSet<Kind> WRAPPING_MAP_VALUE_FRAMEWORK_TYPES =
+ ImmutableSet.of(Kind.PROVIDER, Kind.PRODUCER);
+
+ private Kind multibindingContributionRequestKind(
+ Key multibindingKey, ContributionBinding multibindingContribution) {
switch (multibindingContribution.contributionType()) {
case MAP:
- return multibindingContribution.bindingType().equals(BindingType.PRODUCTION)
- ? Kind.PRODUCER
- : Kind.PROVIDER;
+ MapType mapType = MapType.from(multibindingKey);
+ for (Kind kind : WRAPPING_MAP_VALUE_FRAMEWORK_TYPES) {
+ if (mapType.valuesAreTypeOf(kind.frameworkClass.get())) {
+ return kind;
+ }
+ }
+ // fall through
case SET:
case SET_VALUES:
return Kind.INSTANCE;
@@ -372,19 +394,6 @@
}
}
- /**
- * Creates synthetic dependency requests for each individual multibinding contribution in {@code
- * multibindingContributions}.
- */
- ImmutableSet<DependencyRequest> forMultibindingContributions(
- Iterable<ContributionBinding> multibindingContributions) {
- ImmutableSet.Builder<DependencyRequest> requests = ImmutableSet.builder();
- for (ContributionBinding multibindingContribution : multibindingContributions) {
- requests.add(forMultibindingContribution(multibindingContribution));
- }
- return requests.build();
- }
-
DependencyRequest forRequiredVariable(VariableElement variableElement) {
return forRequiredVariable(variableElement, Optional.empty());
}
diff --git a/java/dagger/internal/codegen/FrameworkFieldInitializer.java b/java/dagger/internal/codegen/FrameworkFieldInitializer.java
index a8b5b16..0b22230 100644
--- a/java/dagger/internal/codegen/FrameworkFieldInitializer.java
+++ b/java/dagger/internal/codegen/FrameworkFieldInitializer.java
@@ -29,7 +29,6 @@
import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
import static dagger.internal.codegen.MoreAnnotationMirrors.getTypeValue;
-import static dagger.internal.codegen.SourceFiles.frameworkMapFactoryClassName;
import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
import static dagger.internal.codegen.SourceFiles.mapFactoryClassName;
import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
@@ -48,6 +47,7 @@
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
@@ -328,14 +328,6 @@
makeParametersCodeBlock(arguments));
}
- case SYNTHETIC_MAP:
- FrameworkDependency frameworkDependency = getOnlyElement(binding.frameworkDependencies());
- return CodeBlock.of(
- "$T.create($L)",
- mapFactoryClassName(binding),
- componentBindingExpressions.getDependencyExpression(
- frameworkDependency, componentName));
-
case SYNTHETIC_MULTIBOUND_SET:
return factoryForSetMultibindingInitialization(binding);
@@ -410,16 +402,27 @@
ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
MapType mapType = MapType.from(binding.key().type());
- CodeBlock.Builder builderCall =
- CodeBlock.builder().add("$T.", frameworkMapFactoryClassName(binding.bindingType()));
+ CodeBlock.Builder builderCall = CodeBlock.builder().add("$T.", mapFactoryClassName(binding));
boolean useRawTypes = useRawType();
if (!useRawTypes) {
- builderCall.add(
- "<$T, $T>",
- mapType.keyType(),
- mapType.unwrappedValueType(binding.bindingType().frameworkClass()));
+ // TODO(ronshapiro): either inline this into mapFactoryClassName, or add a
+ // mapType.unwrappedValueType() method that doesn't require a framework type
+ TypeMirror valueType = mapType.valueType();
+ for (Class<?> frameworkClass :
+ ImmutableSet.of(Provider.class, Producer.class, Produced.class)) {
+ if (mapType.valuesAreTypeOf(frameworkClass)) {
+ valueType = mapType.unwrappedValueType(frameworkClass);
+ break;
+ }
+ }
+ builderCall.add("<$T, $T>", mapType.keyType(), valueType);
}
- builderCall.add("builder($L)", frameworkDependencies.size());
+
+ if (binding.bindingType().equals(BindingType.PROVISION)) {
+ builderCall.add("builder($L)", frameworkDependencies.size());
+ } else {
+ builderCall.add("builder()");
+ }
codeBlocks.add(builderCall.build());
for (FrameworkDependency frameworkDependency : frameworkDependencies) {
diff --git a/java/dagger/internal/codegen/Key.java b/java/dagger/internal/codegen/Key.java
index 3c4cecc..4871e4b 100644
--- a/java/dagger/internal/codegen/Key.java
+++ b/java/dagger/internal/codegen/Key.java
@@ -546,12 +546,15 @@
}
/**
- * Optionally extract a {@link Key} for the underlying provision binding(s) if such a
- * valid key can be inferred from the given key. Specifically, if the key represents a
- * {@link Map}{@code <K, V>}, a key of {@code Map<K, Provider<V>>} will be returned.
+ * Optionally extract a {@link Key} for the underlying provision binding(s) if such a valid key
+ * can be inferred from the given key. Specifically, if the key represents a {@link Map}{@code
+ * <K, V>} or {@code Map<K, Producer<V>>}, a key of {@code Map<K, Provider<V>>} will be
+ * returned.
*/
Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) {
- return wrapMapKey(possibleMapKey, Provider.class);
+ return firstPresent(
+ rewrapMapKey(possibleMapKey, Produced.class, Provider.class),
+ wrapMapKey(possibleMapKey, Provider.class));
}
/**
diff --git a/java/dagger/internal/codegen/MapBindingExpression.java b/java/dagger/internal/codegen/MapBindingExpression.java
new file mode 100644
index 0000000..8e5746c
--- /dev/null
+++ b/java/dagger/internal/codegen/MapBindingExpression.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * 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 dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_MAP;
+import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.MapBuilder;
+import java.util.Collections;
+import java.util.Map;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+
+/** A {@link BindingExpression} for multibound maps. */
+final class MapBindingExpression extends SimpleInvocationBindingExpression {
+ /** Maximum number of key-value pairs that can be passed to ImmutableMap.of(K, V, K, V, ...). */
+ private static final int MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS = 5;
+
+ private final ProvisionBinding binding;
+ private final ImmutableMap<DependencyRequest, ContributionBinding> dependencies;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final Elements elements;
+
+ MapBindingExpression(
+ ProvisionBinding binding,
+ BindingGraph graph,
+ ComponentBindingExpressions componentBindingExpressions,
+ BindingExpression delegate,
+ Elements elements) {
+ super(delegate);
+ ContributionBinding.Kind bindingKind = binding.bindingKind();
+ checkArgument(bindingKind.equals(SYNTHETIC_MULTIBOUND_MAP), bindingKind);
+ this.binding = binding;
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.elements = elements;
+ this.dependencies =
+ Maps.toMap(
+ binding.dependencies(),
+ dep -> graph.resolvedBindings().get(dep.bindingKey()).contributionBinding());
+ }
+
+ @Override
+ CodeBlock getInstanceDependencyExpression(
+ DependencyRequest.Kind requestKind, ClassName requestingClass) {
+ // TODO(ronshapiro): We should also make an ImmutableMap version of MapFactory
+ boolean isImmutableMapAvailable = isImmutableMapAvailable();
+ // TODO(ronshapiro, gak): Use Maps.immutableEnumMap() if it's available?
+ if (isImmutableMapAvailable && dependencies.size() <= MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS) {
+ return CodeBlock.builder()
+ .add("$T.", ImmutableMap.class)
+ .add(maybeTypeParameters(requestingClass))
+ .add(
+ "of($L)",
+ dependencies
+ .keySet()
+ .stream()
+ .map(dependency -> keyAndValueExpression(dependency, requestingClass))
+ .collect(toParametersCodeBlock()))
+ .build();
+ }
+ switch (dependencies.size()) {
+ case 0:
+ return collectionsStaticFactoryInvocation(requestingClass, CodeBlock.of("emptyMap()"));
+ case 1:
+ return collectionsStaticFactoryInvocation(
+ requestingClass,
+ CodeBlock.of(
+ "singletonMap($L)",
+ keyAndValueExpression(getOnlyElement(dependencies.keySet()), requestingClass)));
+ default:
+ CodeBlock.Builder instantiation = CodeBlock.builder();
+ instantiation
+ .add("$T.", isImmutableMapAvailable ? ImmutableMap.class : MapBuilder.class)
+ .add(maybeTypeParameters(requestingClass));
+ if (isImmutableMapAvailable) {
+ // TODO(ronshapiro): builderWithExpectedSize
+ instantiation.add("builder()");
+ } else {
+ instantiation.add("newMapBuilder($L)", dependencies.size());
+ }
+ for (DependencyRequest dependency : dependencies.keySet()) {
+ instantiation.add(".put($L)", keyAndValueExpression(dependency, requestingClass));
+ }
+ return instantiation.add(".build()").build();
+ }
+ }
+
+ private CodeBlock keyAndValueExpression(DependencyRequest dependency, ClassName requestingClass) {
+ return CodeBlock.of(
+ "$L, $L",
+ getMapKeyExpression(dependencies.get(dependency).mapKey().get()),
+ componentBindingExpressions.getDependencyExpression(dependency, requestingClass));
+ }
+
+ private CodeBlock collectionsStaticFactoryInvocation(
+ ClassName requestingClass, CodeBlock methodInvocation) {
+ return CodeBlock.builder()
+ .add("$T.", Collections.class)
+ .add(maybeTypeParameters(requestingClass))
+ .add(methodInvocation)
+ .build();
+ }
+
+ private CodeBlock maybeTypeParameters(ClassName requestingClass) {
+ TypeMirror bindingKeyType = binding.key().type();
+ MapType mapType = MapType.from(binding.key());
+ return isTypeAccessibleFrom(bindingKeyType, requestingClass.packageName())
+ ? CodeBlock.of("<$T, $T>", mapType.keyType(), mapType.valueType())
+ : CodeBlock.of("");
+ }
+
+ private boolean isImmutableMapAvailable() {
+ return elements.getTypeElement(ImmutableMap.class.getCanonicalName()) != null;
+ }
+
+ @Override
+ protected CodeBlock explicitTypeParameter(ClassName requestingClass) {
+ if (isImmutableMapAvailable()) {
+ TypeMirror keyType = binding.key().type();
+ return CodeBlock.of(
+ "<$T>",
+ isTypeAccessibleFrom(keyType, requestingClass.packageName()) ? keyType : Map.class);
+ }
+ return CodeBlock.of("");
+ }
+}
diff --git a/java/dagger/internal/codegen/MemberSelect.java b/java/dagger/internal/codegen/MemberSelect.java
index 5466292..fc0217a 100644
--- a/java/dagger/internal/codegen/MemberSelect.java
+++ b/java/dagger/internal/codegen/MemberSelect.java
@@ -20,17 +20,19 @@
import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
import static dagger.internal.codegen.CodeBlocks.toTypeNamesCodeBlock;
import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
-import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
-import static dagger.internal.codegen.ContributionBinding.Kind.PROVISION;
import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
-import static dagger.internal.codegen.SourceFiles.frameworkMapFactoryClassName;
import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
import static dagger.internal.codegen.SourceFiles.setFactoryClassName;
import static dagger.internal.codegen.TypeNames.FACTORY;
+import static dagger.internal.codegen.TypeNames.MAP_FACTORY;
import static dagger.internal.codegen.TypeNames.MEMBERS_INJECTOR;
import static dagger.internal.codegen.TypeNames.MEMBERS_INJECTORS;
+import static dagger.internal.codegen.TypeNames.PRODUCER;
+import static dagger.internal.codegen.TypeNames.PRODUCERS;
+import static dagger.internal.codegen.TypeNames.PROVIDER;
import static javax.lang.model.type.TypeKind.DECLARED;
+import com.google.auto.common.MoreTypes;
import com.google.common.collect.ImmutableList;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
@@ -88,7 +90,7 @@
&& !contributionBinding.scope().isPresent()) {
switch (contributionBinding.bindingKind()) {
case SYNTHETIC_MULTIBOUND_MAP:
- return Optional.of(emptyFrameworkMapFactory(contributionBinding));
+ return Optional.of(emptyMapFactory(contributionBinding));
case SYNTHETIC_MULTIBOUND_SET:
return Optional.of(emptySetFactory(contributionBinding));
@@ -166,20 +168,19 @@
MEMBERS_INJECTOR);
}
- /**
- * A {@link MemberSelect} for a factory of an empty map of factory types, where a factory can be
- * either a {@link javax.inject.Provider} or {@link dagger.producers.Producer}.
- */
- private static MemberSelect emptyFrameworkMapFactory(ContributionBinding contributionBinding) {
+ /** A {@link MemberSelect} for a factory of an empty map. */
+ private static MemberSelect emptyMapFactory(ContributionBinding contributionBinding) {
BindingType bindingType = contributionBinding.bindingType();
- MapType mapType = MapType.from(contributionBinding.key());
-
- return new ParameterizedStaticMethod(
- frameworkMapFactoryClassName(bindingType),
- ImmutableList.of(
- mapType.keyType(), mapType.unwrappedValueType(bindingType.frameworkClass())),
- CodeBlock.of("empty()"),
- ClassName.get(bindingType.frameworkClass()));
+ ImmutableList<TypeMirror> typeParameters =
+ ImmutableList.copyOf(
+ MoreTypes.asDeclared(contributionBinding.key().type()).getTypeArguments());
+ if (bindingType.equals(BindingType.PRODUCTION)) {
+ return new ParameterizedStaticMethod(
+ PRODUCERS, typeParameters, CodeBlock.of("emptyMapProducer()"), PRODUCER);
+ } else {
+ return new ParameterizedStaticMethod(
+ MAP_FACTORY, typeParameters, CodeBlock.of("emptyMapProvider()"), PROVIDER);
+ }
}
/**
diff --git a/java/dagger/internal/codegen/ProductionBinding.java b/java/dagger/internal/codegen/ProductionBinding.java
index 4a71dea..441fb5c 100644
--- a/java/dagger/internal/codegen/ProductionBinding.java
+++ b/java/dagger/internal/codegen/ProductionBinding.java
@@ -179,28 +179,6 @@
}
/**
- * A synthetic binding of {@code Map<K, V>} or {@code Map<K, Produced<V>>} that depends on
- * {@code Map<K, Producer<V>>}.
- */
- ProductionBinding syntheticMapOfValuesOrProducedBinding(Key mapOfValuesOrProducedKey) {
- checkNotNull(mapOfValuesOrProducedKey);
- Optional<Key> mapOfProducersKey =
- keyFactory.implicitMapProducerKeyFrom(mapOfValuesOrProducedKey);
- checkArgument(
- mapOfProducersKey.isPresent(),
- "%s is not a key for of Map<K, V> or Map<K, Produced<V>>",
- mapOfValuesOrProducedKey);
- DependencyRequest requestForMapOfProducers =
- dependencyRequestFactory.producerForImplicitMapBinding(mapOfProducersKey.get());
- return ProductionBinding.builder()
- .contributionType(ContributionType.UNIQUE)
- .key(mapOfValuesOrProducedKey)
- .explicitDependencies(requestForMapOfProducers)
- .bindingKind(Kind.SYNTHETIC_MAP)
- .build();
- }
-
- /**
* A synthetic binding that depends explicitly on a set of individual provision or production
* multibinding contribution methods.
*
@@ -212,7 +190,7 @@
.contributionType(ContributionType.UNIQUE)
.key(key)
.explicitDependencies(
- dependencyRequestFactory.forMultibindingContributions(multibindingContributions))
+ dependencyRequestFactory.forMultibindingContributions(key, multibindingContributions))
.bindingKind(Kind.forMultibindingKey(key))
.build();
}
diff --git a/java/dagger/internal/codegen/ProvisionBinding.java b/java/dagger/internal/codegen/ProvisionBinding.java
index 6d40e97..67168ce 100644
--- a/java/dagger/internal/codegen/ProvisionBinding.java
+++ b/java/dagger/internal/codegen/ProvisionBinding.java
@@ -235,21 +235,6 @@
.build();
}
- /** A synthetic binding of {@code Map<K, V>} that depends on {@code Map<K, Provider<V>>}. */
- ProvisionBinding syntheticMapOfValuesBinding(Key mapOfValuesKey) {
- checkNotNull(mapOfValuesKey);
- Optional<Key> mapOfProvidersKey = keyFactory.implicitMapProviderKeyFrom(mapOfValuesKey);
- checkArgument(mapOfProvidersKey.isPresent(), "%s is not a key for Map<K, V>", mapOfValuesKey);
- DependencyRequest requestForMapOfProviders =
- dependencyRequestFactory.providerForImplicitMapBinding(mapOfProvidersKey.get());
- return ProvisionBinding.builder()
- .contributionType(ContributionType.UNIQUE)
- .key(mapOfValuesKey)
- .provisionDependencies(requestForMapOfProviders)
- .bindingKind(Kind.SYNTHETIC_MAP)
- .build();
- }
-
/**
* A synthetic binding that depends explicitly on a set of individual provision multibinding
* contribution methods.
@@ -262,7 +247,7 @@
.contributionType(ContributionType.UNIQUE)
.key(key)
.provisionDependencies(
- dependencyRequestFactory.forMultibindingContributions(multibindingContributions))
+ dependencyRequestFactory.forMultibindingContributions(key, multibindingContributions))
.bindingKind(Kind.forMultibindingKey(key))
.build();
}
diff --git a/java/dagger/internal/codegen/SourceFiles.java b/java/dagger/internal/codegen/SourceFiles.java
index ded5f6f..858f184 100644
--- a/java/dagger/internal/codegen/SourceFiles.java
+++ b/java/dagger/internal/codegen/SourceFiles.java
@@ -19,8 +19,10 @@
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
+import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_MAP;
import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_SET;
import static dagger.internal.codegen.Optionals.optionalComparator;
import static dagger.internal.codegen.TypeNames.DOUBLE_CHECK;
@@ -51,17 +53,15 @@
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeVariableName;
-import dagger.internal.MapFactory;
-import dagger.internal.MapProviderFactory;
import dagger.internal.SetFactory;
import dagger.producers.Produced;
-import dagger.producers.internal.MapOfProducerProducer;
-import dagger.producers.internal.MapProducer;
+import dagger.producers.Producer;
import dagger.producers.internal.SetOfProducedProducer;
import dagger.producers.internal.SetProducer;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
+import javax.inject.Provider;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
@@ -264,41 +264,24 @@
}
}
- /**
- * The {@link java.util.Map}-of-value factory class name appropriate for map bindings.
- *
- * <ul>
- * <li>{@link MapFactory} for provision bindings.
- * <li>{@link MapProducer} for production bindings.
- * </ul>
- */
+ /** The {@link java.util.Map} factory class name appropriate for map bindings. */
static ClassName mapFactoryClassName(ContributionBinding binding) {
+ checkState(binding.bindingKind().equals(SYNTHETIC_MULTIBOUND_MAP), binding.bindingKind());
+ MapType mapType = MapType.from(binding.key());
switch (binding.bindingType()) {
- case PRODUCTION:
- return MapType.from(binding.key()).valuesAreTypeOf(Produced.class)
- ? MAP_OF_PRODUCED_PRODUCER : MAP_PRODUCER;
-
case PROVISION:
- return MAP_FACTORY;
-
+ return mapType.valuesAreTypeOf(Provider.class) ? MAP_PROVIDER_FACTORY : MAP_FACTORY;
+ case PRODUCTION:
+ return mapType.valuesAreFrameworkType()
+ ? mapType.valuesAreTypeOf(Producer.class)
+ ? MAP_OF_PRODUCER_PRODUCER
+ : MAP_OF_PRODUCED_PRODUCER
+ : MAP_PRODUCER;
default:
- throw new AssertionError(binding.toString());
+ throw new IllegalArgumentException(binding.bindingType().toString());
}
}
- /**
- * The {@link java.util.Map}-of-framework factory class name appropriate for map bindings.
- *
- * <ul>
- * <li>{@link MapProviderFactory} for provision bindings.
- * <li>{@link MapOfProducerProducer} for production bindings.
- * </ul>
- */
- static ClassName frameworkMapFactoryClassName(BindingType bindingType) {
- return bindingType.equals(BindingType.PRODUCTION)
- ? MAP_OF_PRODUCER_PRODUCER : MAP_PROVIDER_FACTORY;
- }
-
private static String factoryPrefix(ContributionBinding binding) {
switch (binding.bindingKind()) {
case INJECTION: