1 /* 2 * Copyright (C) 2016 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 17 package dagger.internal.codegen; 18 19 import static com.google.common.base.CaseFormat.UPPER_CAMEL; 20 import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE; 21 import static com.google.common.base.Verify.verify; 22 import static com.google.common.collect.Iterables.getOnlyElement; 23 import static com.squareup.javapoet.MethodSpec.constructorBuilder; 24 import static com.squareup.javapoet.MethodSpec.methodBuilder; 25 import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder; 26 import static com.squareup.javapoet.TypeSpec.classBuilder; 27 import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.ABSENT_OPTIONAL_FIELD; 28 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.ABSENT_OPTIONAL_METHOD; 29 import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.PRESENT_FACTORY; 30 import static dagger.internal.codegen.RequestKinds.requestTypeName; 31 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES; 32 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED; 33 import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER; 34 import static dagger.internal.codegen.javapoet.TypeNames.abstractProducerOf; 35 import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf; 36 import static dagger.internal.codegen.javapoet.TypeNames.providerOf; 37 import static javax.lang.model.element.Modifier.FINAL; 38 import static javax.lang.model.element.Modifier.PRIVATE; 39 import static javax.lang.model.element.Modifier.PUBLIC; 40 import static javax.lang.model.element.Modifier.STATIC; 41 42 import com.google.auto.value.AutoValue; 43 import com.google.common.base.Function; 44 import com.google.common.util.concurrent.Futures; 45 import com.google.common.util.concurrent.ListenableFuture; 46 import com.google.common.util.concurrent.MoreExecutors; 47 import com.squareup.javapoet.ClassName; 48 import com.squareup.javapoet.CodeBlock; 49 import com.squareup.javapoet.FieldSpec; 50 import com.squareup.javapoet.MethodSpec; 51 import com.squareup.javapoet.ParameterSpec; 52 import com.squareup.javapoet.ParameterizedTypeName; 53 import com.squareup.javapoet.TypeName; 54 import com.squareup.javapoet.TypeSpec; 55 import com.squareup.javapoet.TypeVariableName; 56 import dagger.internal.InstanceFactory; 57 import dagger.internal.Preconditions; 58 import dagger.internal.codegen.OptionalType.OptionalKind; 59 import dagger.internal.codegen.javapoet.AnnotationSpecs; 60 import dagger.model.RequestKind; 61 import dagger.producers.Producer; 62 import dagger.producers.internal.Producers; 63 import java.util.Comparator; 64 import java.util.Map; 65 import java.util.Optional; 66 import java.util.TreeMap; 67 import java.util.concurrent.Executor; 68 import javax.inject.Inject; 69 import javax.inject.Provider; 70 71 /** The nested class and static methods required by the component to implement optional bindings. */ 72 // TODO(dpb): Name members simply if a component uses only one of Guava or JDK Optional. 73 @PerGeneratedFile 74 final class OptionalFactories { 75 private final ComponentImplementation componentImplementation; 76 OptionalFactories(@opLevel ComponentImplementation componentImplementation)77 @Inject OptionalFactories(@TopLevel ComponentImplementation componentImplementation) { 78 this.componentImplementation = componentImplementation; 79 } 80 81 /** 82 * The factory classes that implement {@code Provider<Optional<T>>} or {@code 83 * Producer<Optional<T>>} for present optional bindings for a given kind of dependency request 84 * within the component. 85 * 86 * <p>The key is the {@code Provider<Optional<T>>} type. 87 */ 88 private final Map<PresentFactorySpec, TypeSpec> presentFactoryClasses = 89 new TreeMap<>( 90 Comparator.comparing(PresentFactorySpec::valueKind) 91 .thenComparing(PresentFactorySpec::frameworkType) 92 .thenComparing(PresentFactorySpec::optionalKind)); 93 94 /** 95 * The static methods that return a {@code Provider<Optional<T>>} that always returns an absent 96 * value. 97 */ 98 private final Map<OptionalKind, MethodSpec> absentOptionalProviderMethods = new TreeMap<>(); 99 100 /** 101 * The static fields for {@code Provider<Optional<T>>} objects that always return an absent value. 102 */ 103 private final Map<OptionalKind, FieldSpec> absentOptionalProviderFields = new TreeMap<>(); 104 105 /** 106 * Returns an expression that calls a static method that returns a {@code Provider<Optional<T>>} 107 * for absent optional bindings. 108 */ absentOptionalProvider(ContributionBinding binding)109 CodeBlock absentOptionalProvider(ContributionBinding binding) { 110 verify( 111 binding.bindingType().equals(BindingType.PROVISION), 112 "Absent optional bindings should be provisions: %s", 113 binding); 114 OptionalKind optionalKind = OptionalType.from(binding.key()).kind(); 115 return CodeBlock.of( 116 "$N()", 117 absentOptionalProviderMethods.computeIfAbsent( 118 optionalKind, 119 kind -> { 120 MethodSpec method = absentOptionalProviderMethod(kind); 121 componentImplementation.addMethod(ABSENT_OPTIONAL_METHOD, method); 122 return method; 123 })); 124 } 125 126 /** 127 * Creates a method specification for a {@code Provider<Optional<T>>} that always returns an 128 * absent value. 129 */ absentOptionalProviderMethod(OptionalKind optionalKind)130 private MethodSpec absentOptionalProviderMethod(OptionalKind optionalKind) { 131 TypeVariableName typeVariable = TypeVariableName.get("T"); 132 return methodBuilder( 133 String.format( 134 "absent%sProvider", UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind.name()))) 135 .addModifiers(PRIVATE, STATIC) 136 .addTypeVariable(typeVariable) 137 .returns(providerOf(optionalKind.of(typeVariable))) 138 .addJavadoc( 139 "Returns a {@link $T} that returns {@code $L}.", 140 Provider.class, 141 optionalKind.absentValueExpression()) 142 .addCode("$L // safe covariant cast\n", AnnotationSpecs.suppressWarnings(UNCHECKED)) 143 .addCode( 144 "$1T provider = ($1T) $2N;", 145 providerOf(optionalKind.of(typeVariable)), 146 absentOptionalProviderFields.computeIfAbsent( 147 optionalKind, 148 kind -> { 149 FieldSpec field = absentOptionalProviderField(kind); 150 componentImplementation.addField(ABSENT_OPTIONAL_FIELD, field); 151 return field; 152 })) 153 .addCode("return provider;") 154 .build(); 155 } 156 157 /** 158 * Creates a field specification for a {@code Provider<Optional<T>>} that always returns an absent 159 * value. 160 */ 161 private FieldSpec absentOptionalProviderField(OptionalKind optionalKind) { 162 return FieldSpec.builder( 163 PROVIDER, 164 String.format("ABSENT_%s_PROVIDER", optionalKind.name()), 165 PRIVATE, 166 STATIC, 167 FINAL) 168 .addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES)) 169 .initializer("$T.create($L)", InstanceFactory.class, optionalKind.absentValueExpression()) 170 .addJavadoc( 171 "A {@link $T} that returns {@code $L}.", 172 Provider.class, 173 optionalKind.absentValueExpression()) 174 .build(); 175 } 176 177 /** Information about the type of a factory for present bindings. */ 178 @AutoValue 179 abstract static class PresentFactorySpec { 180 /** Whether the factory is a {@link Provider} or a {@link Producer}. */ 181 abstract FrameworkType frameworkType(); 182 183 /** What kind of {@code Optional} is returned. */ 184 abstract OptionalKind optionalKind(); 185 186 /** The kind of request satisfied by the value of the {@code Optional}. */ 187 abstract RequestKind valueKind(); 188 189 /** The type variable for the factory class. */ 190 TypeVariableName typeVariable() { 191 return TypeVariableName.get("T"); 192 } 193 194 /** The type contained by the {@code Optional}. */ 195 TypeName valueType() { 196 return requestTypeName(valueKind(), typeVariable()); 197 } 198 199 /** The type provided or produced by the factory. */ 200 ParameterizedTypeName optionalType() { 201 return optionalKind().of(valueType()); 202 } 203 204 /** The type of the factory. */ 205 ParameterizedTypeName factoryType() { 206 return frameworkType().frameworkClassOf(optionalType()); 207 } 208 209 /** The type of the delegate provider or producer. */ 210 ParameterizedTypeName delegateType() { 211 return frameworkType().frameworkClassOf(typeVariable()); 212 } 213 214 /** Returns the superclass the generated factory should have, if any. */ 215 Optional<ParameterizedTypeName> superclass() { 216 switch (frameworkType()) { 217 case PRODUCER_NODE: 218 // TODO(cgdecker): This probably isn't a big issue for now, but it's possible this 219 // shouldn't be an AbstractProducer: 220 // - As AbstractProducer, it'll only call the delegate's get() method once and then cache 221 // that result (essentially) rather than calling the delegate's get() method each time 222 // its get() method is called (which was what it did before the cancellation change). 223 // - It's not 100% clear to me whether the view-creation methods should return a view of 224 // the same view created by the delegate or if they should just return their own views. 225 return Optional.of(abstractProducerOf(optionalType())); 226 default: 227 return Optional.empty(); 228 } 229 } 230 231 /** Returns the superinterface the generated factory should have, if any. */ 232 Optional<ParameterizedTypeName> superinterface() { 233 switch (frameworkType()) { 234 case PROVIDER: 235 return Optional.of(factoryType()); 236 default: 237 return Optional.empty(); 238 } 239 } 240 241 /** Returns the name of the factory method to generate. */ 242 String factoryMethodName() { 243 switch (frameworkType()) { 244 case PROVIDER: 245 return "get"; 246 case PRODUCER_NODE: 247 return "compute"; 248 } 249 throw new AssertionError(frameworkType()); 250 } 251 252 /** The name of the factory class. */ 253 String factoryClassName() { 254 return new StringBuilder("Present") 255 .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind().name())) 256 .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, valueKind().toString())) 257 .append(frameworkType().frameworkClass().getSimpleName()) 258 .toString(); 259 } 260 261 private static PresentFactorySpec of(ContributionBinding binding) { 262 return new AutoValue_OptionalFactories_PresentFactorySpec( 263 FrameworkType.forBindingType(binding.bindingType()), 264 OptionalType.from(binding.key()).kind(), 265 getOnlyElement(binding.dependencies()).kind()); 266 } 267 } 268 269 /** 270 * Returns an expression for an instance of a nested class that implements {@code 271 * Provider<Optional<T>>} or {@code Producer<Optional<T>>} for a present optional binding, where 272 * {@code T} represents dependency requests of that kind. 273 * 274 * <ul> 275 * <li>If {@code optionalRequestKind} is {@link RequestKind#INSTANCE}, the class implements 276 * {@code ProviderOrProducer<Optional<T>>}. 277 * <li>If {@code optionalRequestKind} is {@link RequestKind#PROVIDER}, the class implements 278 * {@code Provider<Optional<Provider<T>>>}. 279 * <li>If {@code optionalRequestKind} is {@link RequestKind#LAZY}, the class implements {@code 280 * Provider<Optional<Lazy<T>>>}. 281 * <li>If {@code optionalRequestKind} is {@link RequestKind#PROVIDER_OF_LAZY}, the class 282 * implements {@code Provider<Optional<Provider<Lazy<T>>>>}. 283 * <li>If {@code optionalRequestKind} is {@link RequestKind#PRODUCER}, the class implements 284 * {@code Producer<Optional<Producer<T>>>}. 285 * <li>If {@code optionalRequestKind} is {@link RequestKind#PRODUCED}, the class implements 286 * {@code Producer<Optional<Produced<T>>>}. 287 * </ul> 288 * 289 * @param delegateFactory an expression for a {@link Provider} or {@link Producer} of the 290 * underlying type 291 */ 292 CodeBlock presentOptionalFactory(ContributionBinding binding, CodeBlock delegateFactory) { 293 return CodeBlock.of( 294 "$N.of($L)", 295 presentFactoryClasses.computeIfAbsent( 296 PresentFactorySpec.of(binding), 297 spec -> { 298 TypeSpec type = presentOptionalFactoryClass(spec); 299 componentImplementation.addType(PRESENT_FACTORY, type); 300 return type; 301 }), 302 delegateFactory); 303 } 304 305 private TypeSpec presentOptionalFactoryClass(PresentFactorySpec spec) { 306 FieldSpec delegateField = 307 FieldSpec.builder(spec.delegateType(), "delegate", PRIVATE, FINAL).build(); 308 ParameterSpec delegateParameter = ParameterSpec.builder(delegateField.type, "delegate").build(); 309 TypeSpec.Builder factoryClassBuilder = 310 classBuilder(spec.factoryClassName()) 311 .addTypeVariable(spec.typeVariable()) 312 .addModifiers(PRIVATE, STATIC, FINAL) 313 .addJavadoc( 314 "A {@code $T} that uses a delegate {@code $T}.", 315 spec.factoryType(), 316 delegateField.type); 317 318 spec.superclass().ifPresent(factoryClassBuilder::superclass); 319 spec.superinterface().ifPresent(factoryClassBuilder::addSuperinterface); 320 321 return factoryClassBuilder 322 .addField(delegateField) 323 .addMethod( 324 constructorBuilder() 325 .addModifiers(PRIVATE) 326 .addParameter(delegateParameter) 327 .addCode( 328 "this.$N = $T.checkNotNull($N);", 329 delegateField, 330 Preconditions.class, 331 delegateParameter) 332 .build()) 333 .addMethod(presentOptionalFactoryGetMethod(spec, delegateField)) 334 .addMethod( 335 methodBuilder("of") 336 .addModifiers(PRIVATE, STATIC) 337 .addTypeVariable(spec.typeVariable()) 338 .returns(spec.factoryType()) 339 .addParameter(delegateParameter) 340 .addCode( 341 "return new $L<$T>($N);", 342 spec.factoryClassName(), 343 spec.typeVariable(), 344 delegateParameter) 345 .build()) 346 .build(); 347 } 348 349 private MethodSpec presentOptionalFactoryGetMethod( 350 PresentFactorySpec spec, FieldSpec delegateField) { 351 MethodSpec.Builder getMethodBuilder = 352 methodBuilder(spec.factoryMethodName()).addAnnotation(Override.class).addModifiers(PUBLIC); 353 354 switch (spec.frameworkType()) { 355 case PROVIDER: 356 return getMethodBuilder 357 .returns(spec.optionalType()) 358 .addCode( 359 "return $L;", 360 spec.optionalKind() 361 .presentExpression( 362 FrameworkType.PROVIDER.to( 363 spec.valueKind(), CodeBlock.of("$N", delegateField)))) 364 .build(); 365 366 case PRODUCER_NODE: 367 getMethodBuilder.returns(listenableFutureOf(spec.optionalType())); 368 369 switch (spec.valueKind()) { 370 case FUTURE: // return a ListenableFuture<Optional<ListenableFuture<T>>> 371 case PRODUCER: // return a ListenableFuture<Optional<Producer<T>>> 372 return getMethodBuilder 373 .addCode( 374 "return $T.immediateFuture($L);", 375 Futures.class, 376 spec.optionalKind() 377 .presentExpression( 378 FrameworkType.PRODUCER_NODE.to( 379 spec.valueKind(), CodeBlock.of("$N", delegateField)))) 380 .build(); 381 382 case INSTANCE: // return a ListenableFuture<Optional<T>> 383 return getMethodBuilder 384 .addCode( 385 "return $L;", 386 transformFutureToOptional( 387 spec.optionalKind(), 388 spec.typeVariable(), 389 CodeBlock.of("$N.get()", delegateField))) 390 .build(); 391 392 case PRODUCED: // return a ListenableFuture<Optional<Produced<T>>> 393 return getMethodBuilder 394 .addCode( 395 "return $L;", 396 transformFutureToOptional( 397 spec.optionalKind(), 398 spec.valueType(), 399 CodeBlock.of( 400 "$T.createFutureProduced($N.get())", Producers.class, delegateField))) 401 .build(); 402 403 default: 404 throw new UnsupportedOperationException( 405 spec.factoryType() + " objects are not supported"); 406 } 407 } 408 throw new AssertionError(spec.frameworkType()); 409 } 410 411 /** 412 * An expression that uses {@link Futures#transform(ListenableFuture, Function, Executor)} to 413 * transform a {@code ListenableFuture<inputType>} into a {@code 414 * ListenableFuture<Optional<inputType>>}. 415 * 416 * @param inputFuture an expression of type {@code ListenableFuture<inputType>} 417 */ 418 private static CodeBlock transformFutureToOptional( 419 OptionalKind optionalKind, TypeName inputType, CodeBlock inputFuture) { 420 return CodeBlock.of( 421 "$T.transform($L, $L, $T.directExecutor())", 422 Futures.class, 423 inputFuture, 424 anonymousClassBuilder("") 425 .addSuperinterface( 426 ParameterizedTypeName.get( 427 ClassName.get(Function.class), inputType, optionalKind.of(inputType))) 428 .addMethod( 429 methodBuilder("apply") 430 .addAnnotation(Override.class) 431 .addModifiers(PUBLIC) 432 .returns(optionalKind.of(inputType)) 433 .addParameter(inputType, "input") 434 .addCode("return $L;", optionalKind.presentExpression(CodeBlock.of("input"))) 435 .build()) 436 .build(), 437 MoreExecutors.class); 438 } 439 } 440