Add ImmutableSet support to SetBindingRequestFulfillment
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=160912106
diff --git a/java/dagger/internal/SetBuilder.java b/java/dagger/internal/SetBuilder.java
index 2ca8462..16bc589 100644
--- a/java/dagger/internal/SetBuilder.java
+++ b/java/dagger/internal/SetBuilder.java
@@ -37,7 +37,7 @@
/**
* {@code estimatedSize} is the number of bindings which contribute to the set. They may each
* provide {@code [0..n)} instances to the set. Because the final size is unknown, {@code
- * contributions} are collected in a list and only hashed in {@link #create()}.
+ * contributions} are collected in a list and only hashed in {@link #build()}.
*/
public static <T> SetBuilder<T> newSetBuilder(int estimatedSize) {
return new SetBuilder<T>(estimatedSize);
@@ -53,7 +53,7 @@
return this;
}
- public Set<T> create() {
+ public Set<T> build() {
switch (contributions.size()) {
case 0:
return Collections.emptySet();
diff --git a/java/dagger/internal/codegen/AbstractComponentWriter.java b/java/dagger/internal/codegen/AbstractComponentWriter.java
index 84b999d..193022a 100644
--- a/java/dagger/internal/codegen/AbstractComponentWriter.java
+++ b/java/dagger/internal/codegen/AbstractComponentWriter.java
@@ -174,7 +174,7 @@
this.optionalFactories = optionalFactories;
this.bindingExpressionFactory =
new BindingExpression.Factory(
- name, this, childComponentNames(keyFactory, subcomponentNames), graph);
+ name, this, childComponentNames(keyFactory, subcomponentNames), graph, elements);
}
private static ImmutableMap<BindingKey, String> childComponentNames(
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
index c271b2f..fd43c8f 100644
--- a/java/dagger/internal/codegen/BindingExpression.java
+++ b/java/dagger/internal/codegen/BindingExpression.java
@@ -30,6 +30,7 @@
import dagger.internal.DelegateFactory;
import java.util.Optional;
import java.util.function.BiConsumer;
+import javax.lang.model.util.Elements;
/** The code expressions to declare, initialize, and/or access a binding in a component. */
final class BindingExpression extends RequestFulfillment {
@@ -57,16 +58,19 @@
private final HasBindingExpressions hasBindingExpressions;
private final ImmutableMap<BindingKey, String> subcomponentNames;
private final BindingGraph graph;
+ private final Elements elements;
Factory(
ClassName componentName,
HasBindingExpressions hasBindingExpressions,
ImmutableMap<BindingKey, String> subcomponentNames,
- BindingGraph graph) {
+ BindingGraph graph,
+ Elements elements) {
this.componentName = checkNotNull(componentName);
this.hasBindingExpressions = checkNotNull(hasBindingExpressions);
this.subcomponentNames = checkNotNull(subcomponentNames);
this.graph = checkNotNull(graph);
+ this.elements = elements;
}
/** Creates a binding expression for a field. */
@@ -112,7 +116,8 @@
provisionBinding,
graph,
hasBindingExpressions,
- providerFieldRequestFulfillment);
+ providerFieldRequestFulfillment,
+ elements);
case INJECTION:
case PROVISION:
if (provisionBinding.implicitDependencies().isEmpty()
diff --git a/java/dagger/internal/codegen/SetBindingRequestFulfillment.java b/java/dagger/internal/codegen/SetBindingRequestFulfillment.java
index f5368e5..502a15b 100644
--- a/java/dagger/internal/codegen/SetBindingRequestFulfillment.java
+++ b/java/dagger/internal/codegen/SetBindingRequestFulfillment.java
@@ -17,14 +17,19 @@
package dagger.internal.codegen;
import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.squareup.javapoet.CodeBlock.of;
import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
+import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeName;
import dagger.internal.SetBuilder;
import java.util.Collections;
+import java.util.Set;
+import java.util.function.Function;
import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
/**
* A {@link RequestFulfillment} for {@link
@@ -34,24 +39,46 @@
private final ProvisionBinding binding;
private final BindingGraph graph;
private final HasBindingExpressions hasBindingExpressions;
+ private final Elements elements;
SetBindingRequestFulfillment(
BindingKey bindingKey,
ProvisionBinding binding,
BindingGraph graph,
HasBindingExpressions hasBindingExpressions,
- RequestFulfillment delegate) {
+ RequestFulfillment delegate,
+ Elements elements) {
super(bindingKey, delegate);
this.binding = binding;
this.graph = graph;
this.hasBindingExpressions = hasBindingExpressions;
+ this.elements = elements;
}
@Override
CodeBlock getSimpleInvocation(DependencyRequest request, ClassName requestingClass) {
- // TODO(ronshapiro): if you have ImmutableSet on your classpath, use ImmutableSet.Builder
- // otherwise, we can consider providing our own static factories for multibinding cases where
- // all of the dependencies are @IntoSet
+ Function<DependencyRequest, CodeBlock> getRequestFulfillmentForDependency =
+ dependency ->
+ hasBindingExpressions
+ .getBindingExpression(dependency.bindingKey())
+ .getSnippetForDependencyRequest(dependency, requestingClass);
+
+ // TODO(ronshapiro): We should also make an ImmutableSet version of SetFactory
+ boolean isImmutableSetAvailable = isImmutableSetAvailable();
+ // TODO(ronshapiro, gak): Use Sets.immutableEnumSet() if it's available?
+ if (isImmutableSetAvailable && binding.dependencies().stream().allMatch(this::isSingleValue)) {
+ return CodeBlock.builder()
+ .add("$T.", ImmutableSet.class)
+ .add(maybeTypeParameter(request, requestingClass))
+ .add(
+ "of($L)",
+ binding
+ .dependencies()
+ .stream()
+ .map(getRequestFulfillmentForDependency)
+ .collect(toParametersCodeBlock()))
+ .build();
+ }
switch (binding.dependencies().size()) {
case 0:
return collectionsStaticFactoryInvocation(
@@ -59,22 +86,30 @@
case 1:
{
DependencyRequest dependency = getOnlyElement(binding.dependencies());
+ CodeBlock dependencySnippet =
+ getRequestFulfillmentForDependency(dependency, requestingClass);
if (isSingleValue(dependency)) {
return collectionsStaticFactoryInvocation(
- request,
- requestingClass,
- CodeBlock.of(
- "singleton($L)",
- getRequestFulfillmentForDependency(dependency, requestingClass)));
+ request, requestingClass, CodeBlock.of("singleton($L)", dependencySnippet));
+ } else if (isImmutableSetAvailable) {
+ return CodeBlock.builder()
+ .add("$T.", ImmutableSet.class)
+ .add(maybeTypeParameter(request, requestingClass))
+ .add("copyOf($L)", dependencySnippet)
+ .build();
}
}
// fall through
default:
CodeBlock.Builder instantiation = CodeBlock.builder();
instantiation
- .add("$T.", SetBuilder.class)
- .add(maybeTypeParameter(request, requestingClass))
- .add("newSetBuilder($L)", binding.dependencies().size());
+ .add("$T.", isImmutableSetAvailable ? ImmutableSet.class : SetBuilder.class)
+ .add(maybeTypeParameter(request, requestingClass));
+ if (isImmutableSetAvailable) {
+ instantiation.add("builder()");
+ } else {
+ instantiation.add("newSetBuilder($L)", binding.dependencies().size());
+ }
for (DependencyRequest dependency : binding.dependencies()) {
String builderMethod = isSingleValue(dependency) ? "add" : "addAll";
instantiation.add(
@@ -82,7 +117,7 @@
builderMethod,
getRequestFulfillmentForDependency(dependency, requestingClass));
}
- return instantiation.add(".create()").build();
+ return instantiation.add(".build()").build();
}
}
@@ -106,8 +141,8 @@
DependencyRequest request, ClassName requestingClass) {
TypeMirror elementType = SetType.from(request.key()).elementType();
return isTypeAccessibleFrom(elementType, requestingClass.packageName())
- ? of("<$T>", elementType)
- : of("");
+ ? CodeBlock.of("<$T>", elementType)
+ : CodeBlock.of("");
}
private boolean isSingleValue(DependencyRequest dependency) {
@@ -118,4 +153,21 @@
.contributionType()
.equals(ContributionType.SET);
}
+
+ private boolean isImmutableSetAvailable() {
+ return elements.getTypeElement(ImmutableSet.class.getCanonicalName()) != null;
+ }
+
+ @Override
+ protected CodeBlock explicitTypeParameter(ClassName requestingClass) {
+ if (isImmutableSetAvailable()) {
+ TypeMirror keyType = binding.key().type();
+ return CodeBlock.of(
+ "<$T>",
+ isTypeAccessibleFrom(keyType, requestingClass.packageName())
+ ? TypeName.get(keyType)
+ : ClassName.get(Set.class));
+ }
+ return CodeBlock.of("");
+ }
}
diff --git a/java/dagger/internal/codegen/SimpleInvocationRequestFulfillment.java b/java/dagger/internal/codegen/SimpleInvocationRequestFulfillment.java
index 0e43011..94e19bd 100644
--- a/java/dagger/internal/codegen/SimpleInvocationRequestFulfillment.java
+++ b/java/dagger/internal/codegen/SimpleInvocationRequestFulfillment.java
@@ -34,6 +34,17 @@
abstract CodeBlock getSimpleInvocation(DependencyRequest request, ClassName requestingClass);
+ /**
+ * Java 7 type inference is not as strong as in Java 8, and therefore some generated code must
+ * make type parameters for {@link Futures#immediateFuture(Object)} explicit.
+ *
+ * <p>For example, {@code javac7} cannot detect that Futures.immediateFuture(ImmutableSet.of(T))}
+ * can safely be assigned to {@code ListenableFuture<Set<T>>}.
+ */
+ protected CodeBlock explicitTypeParameter(ClassName requestingClass) {
+ return CodeBlock.of("");
+ }
+
@Override
final CodeBlock getSnippetForDependencyRequest(
DependencyRequest request, ClassName requestingClass) {
@@ -41,8 +52,11 @@
case INSTANCE:
return getSimpleInvocation(request, requestingClass);
case FUTURE:
- return CodeBlock.of(
- "$T.immediateFuture($L)", Futures.class, getSimpleInvocation(request, requestingClass));
+ return CodeBlock.builder()
+ .add("$T.", Futures.class)
+ .add(explicitTypeParameter(requestingClass))
+ .add("immediateFuture($L)", getSimpleInvocation(request, requestingClass))
+ .build();
default:
return delegate.getSnippetForDependencyRequest(request, requestingClass);
}