blob: e6a3a6805b0f2160813093daaf86a309bbb14d1f [file] [log] [blame]
ronshapiro7bd173e2017-06-14 11:00:36 -07001/*
2 * Copyright (C) 2017 The Dagger Authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package dagger.internal.codegen;
18
19import static com.google.common.collect.Iterables.getOnlyElement;
ronshapiro7bd173e2017-06-14 11:00:36 -070020import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
dpb3379b952018-09-24 13:39:04 -070021import static dagger.internal.codegen.BindingRequest.bindingRequest;
ronshapirocb725da2017-07-04 15:14:48 -070022import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
sunxin84ec3c62018-03-16 12:31:45 -070023import static javax.lang.model.util.ElementFilter.methodsIn;
ronshapiro7bd173e2017-06-14 11:00:36 -070024
ronshapirocb725da2017-07-04 15:14:48 -070025import com.google.common.collect.ImmutableSet;
ronshapiro7bd173e2017-06-14 11:00:36 -070026import com.squareup.javapoet.ClassName;
27import com.squareup.javapoet.CodeBlock;
28import dagger.internal.SetBuilder;
ronshapiroa5023ca2018-01-02 12:55:01 -080029import dagger.model.DependencyRequest;
ronshapiro7bd173e2017-06-14 11:00:36 -070030import java.util.Collections;
dstrasburg09adeae2018-09-07 12:11:06 -070031import java.util.Optional;
dpb356a6842018-02-07 07:45:50 -080032import javax.lang.model.type.DeclaredType;
ronshapiro7bd173e2017-06-14 11:00:36 -070033import javax.lang.model.type.TypeMirror;
34
dpb68c77d82017-08-15 15:23:35 -070035/** A binding expression for multibound sets. */
dstrasburg09adeae2018-09-07 12:11:06 -070036final class SetBindingExpression extends MultibindingExpression {
ronshapiro7bd173e2017-06-14 11:00:36 -070037 private final ProvisionBinding binding;
erichang9dd6aed2017-06-15 15:54:00 -070038 private final BindingGraph graph;
dpb68c77d82017-08-15 15:23:35 -070039 private final ComponentBindingExpressions componentBindingExpressions;
dpb356a6842018-02-07 07:45:50 -080040 private final DaggerTypes types;
dpbe7b89582018-02-19 08:42:19 -080041 private final DaggerElements elements;
ronshapiro7bd173e2017-06-14 11:00:36 -070042
ronshapiro186d17a2017-08-11 08:18:07 -070043 SetBindingExpression(
dpb09fb2cb2018-01-29 08:39:33 -080044 ResolvedBindings resolvedBindings,
dpb159d81b2018-11-01 14:06:17 -070045 ComponentImplementation componentImplementation,
erichang9dd6aed2017-06-15 15:54:00 -070046 BindingGraph graph,
dpb68c77d82017-08-15 15:23:35 -070047 ComponentBindingExpressions componentBindingExpressions,
dpba409c6f2017-10-04 09:38:54 -070048 DaggerTypes types,
dpbe7b89582018-02-19 08:42:19 -080049 DaggerElements elements) {
dpb159d81b2018-11-01 14:06:17 -070050 super(resolvedBindings, componentImplementation);
dpb09fb2cb2018-01-29 08:39:33 -080051 this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
erichang9dd6aed2017-06-15 15:54:00 -070052 this.graph = graph;
dpb68c77d82017-08-15 15:23:35 -070053 this.componentBindingExpressions = componentBindingExpressions;
dpb356a6842018-02-07 07:45:50 -080054 this.types = types;
ronshapirocb725da2017-07-04 15:14:48 -070055 this.elements = elements;
ronshapiro7bd173e2017-06-14 11:00:36 -070056 }
57
58 @Override
dstrasburg09adeae2018-09-07 12:11:06 -070059 protected Expression buildDependencyExpression(ClassName requestingClass) {
60 Optional<CodeBlock> superMethodCall = superMethodCall();
ronshapirocb725da2017-07-04 15:14:48 -070061 // TODO(ronshapiro): We should also make an ImmutableSet version of SetFactory
62 boolean isImmutableSetAvailable = isImmutableSetAvailable();
63 // TODO(ronshapiro, gak): Use Sets.immutableEnumSet() if it's available?
dstrasburg09adeae2018-09-07 12:11:06 -070064 if (isImmutableSetAvailable
65 && binding.dependencies().stream().allMatch(this::isSingleValue)
66 && !superMethodCall.isPresent()) {
dpb356a6842018-02-07 07:45:50 -080067 return Expression.create(
68 immutableSetType(),
69 CodeBlock.builder()
70 .add("$T.", ImmutableSet.class)
71 .add(maybeTypeParameter(requestingClass))
72 .add(
73 "of($L)",
74 binding
75 .dependencies()
76 .stream()
77 .map(dependency -> getContributionExpression(dependency, requestingClass))
78 .collect(toParametersCodeBlock()))
79 .build());
ronshapirocb725da2017-07-04 15:14:48 -070080 }
ronshapiro7bd173e2017-06-14 11:00:36 -070081 switch (binding.dependencies().size()) {
82 case 0:
dpb68c77d82017-08-15 15:23:35 -070083 return collectionsStaticFactoryInvocation(requestingClass, CodeBlock.of("emptySet()"));
ronshapiro7bd173e2017-06-14 11:00:36 -070084 case 1:
85 {
86 DependencyRequest dependency = getOnlyElement(binding.dependencies());
dpba6ff6c12017-08-14 08:47:36 -070087 CodeBlock contributionExpression = getContributionExpression(dependency, requestingClass);
ronshapiro7bd173e2017-06-14 11:00:36 -070088 if (isSingleValue(dependency)) {
89 return collectionsStaticFactoryInvocation(
dpb68c77d82017-08-15 15:23:35 -070090 requestingClass, CodeBlock.of("singleton($L)", contributionExpression));
ronshapirocb725da2017-07-04 15:14:48 -070091 } else if (isImmutableSetAvailable) {
dpb356a6842018-02-07 07:45:50 -080092 return Expression.create(
93 immutableSetType(),
94 CodeBlock.builder()
95 .add("$T.", ImmutableSet.class)
96 .add(maybeTypeParameter(requestingClass))
97 .add("copyOf($L)", contributionExpression)
98 .build());
ronshapiro7bd173e2017-06-14 11:00:36 -070099 }
100 }
101 // fall through
102 default:
103 CodeBlock.Builder instantiation = CodeBlock.builder();
104 instantiation
ronshapirocb725da2017-07-04 15:14:48 -0700105 .add("$T.", isImmutableSetAvailable ? ImmutableSet.class : SetBuilder.class)
dpb68c77d82017-08-15 15:23:35 -0700106 .add(maybeTypeParameter(requestingClass));
sunxin84ec3c62018-03-16 12:31:45 -0700107 if (isImmutableSetBuilderWithExpectedSizeAvailable()) {
108 instantiation.add("builderWithExpectedSize($L)", binding.dependencies().size());
109 } else if (isImmutableSetAvailable) {
ronshapirocb725da2017-07-04 15:14:48 -0700110 instantiation.add("builder()");
111 } else {
112 instantiation.add("newSetBuilder($L)", binding.dependencies().size());
113 }
dstrasburg09adeae2018-09-07 12:11:06 -0700114 for (DependencyRequest dependency : getNewContributions(binding.dependencies())) {
ronshapiro7bd173e2017-06-14 11:00:36 -0700115 String builderMethod = isSingleValue(dependency) ? "add" : "addAll";
116 instantiation.add(
dpba6ff6c12017-08-14 08:47:36 -0700117 ".$L($L)", builderMethod, getContributionExpression(dependency, requestingClass));
ronshapiro7bd173e2017-06-14 11:00:36 -0700118 }
dstrasburg09adeae2018-09-07 12:11:06 -0700119 if (superMethodCall.isPresent()) {
120 instantiation.add(CodeBlock.of(".addAll($L)", superMethodCall.get()));
121 }
dpb356a6842018-02-07 07:45:50 -0800122 instantiation.add(".build()");
123 return Expression.create(
124 isImmutableSetAvailable ? immutableSetType() : binding.key().type(),
125 instantiation.build());
ronshapiro7bd173e2017-06-14 11:00:36 -0700126 }
127 }
128
dpb356a6842018-02-07 07:45:50 -0800129 private DeclaredType immutableSetType() {
130 return types.getDeclaredType(
dpbe7b89582018-02-19 08:42:19 -0800131 elements.getTypeElement(ImmutableSet.class), SetType.from(binding.key()).elementType());
dpb356a6842018-02-07 07:45:50 -0800132 }
133
dpba6ff6c12017-08-14 08:47:36 -0700134 private CodeBlock getContributionExpression(
ronshapiro7bd173e2017-06-14 11:00:36 -0700135 DependencyRequest dependency, ClassName requestingClass) {
ronshapiroba794862017-09-28 11:39:34 -0700136 return componentBindingExpressions
dpb3379b952018-09-24 13:39:04 -0700137 .getDependencyExpression(bindingRequest(dependency), requestingClass)
ronshapiroba794862017-09-28 11:39:34 -0700138 .codeBlock();
ronshapiro7bd173e2017-06-14 11:00:36 -0700139 }
140
dpb356a6842018-02-07 07:45:50 -0800141 private Expression collectionsStaticFactoryInvocation(
dpb68c77d82017-08-15 15:23:35 -0700142 ClassName requestingClass, CodeBlock methodInvocation) {
dpb356a6842018-02-07 07:45:50 -0800143 return Expression.create(
144 binding.key().type(),
145 CodeBlock.builder()
146 .add("$T.", Collections.class)
147 .add(maybeTypeParameter(requestingClass))
148 .add(methodInvocation)
149 .build());
ronshapiro7bd173e2017-06-14 11:00:36 -0700150 }
151
dpb68c77d82017-08-15 15:23:35 -0700152 private CodeBlock maybeTypeParameter(ClassName requestingClass) {
153 TypeMirror elementType = SetType.from(binding.key()).elementType();
ronshapiro7bd173e2017-06-14 11:00:36 -0700154 return isTypeAccessibleFrom(elementType, requestingClass.packageName())
ronshapirocb725da2017-07-04 15:14:48 -0700155 ? CodeBlock.of("<$T>", elementType)
156 : CodeBlock.of("");
ronshapiro7bd173e2017-06-14 11:00:36 -0700157 }
158
159 private boolean isSingleValue(DependencyRequest dependency) {
erichang9dd6aed2017-06-15 15:54:00 -0700160 return graph
ronshapiro0a277fd2017-12-22 08:30:49 -0800161 .contributionBindings()
162 .get(dependency.key())
ronshapiro7bd173e2017-06-14 11:00:36 -0700163 .contributionBinding()
164 .contributionType()
165 .equals(ContributionType.SET);
166 }
ronshapirocb725da2017-07-04 15:14:48 -0700167
sunxin84ec3c62018-03-16 12:31:45 -0700168 private boolean isImmutableSetBuilderWithExpectedSizeAvailable() {
169 if (isImmutableSetAvailable()) {
170 return methodsIn(elements.getTypeElement(ImmutableSet.class).getEnclosedElements())
171 .stream()
172 .anyMatch(method -> method.getSimpleName().contentEquals("builderWithExpectedSize"));
173 }
174 return false;
175 }
176
ronshapirocb725da2017-07-04 15:14:48 -0700177 private boolean isImmutableSetAvailable() {
dpbe7b89582018-02-19 08:42:19 -0800178 return elements.getTypeElement(ImmutableSet.class) != null;
ronshapirocb725da2017-07-04 15:14:48 -0700179 }
ronshapiro7bd173e2017-06-14 11:00:36 -0700180}