1 /* 2 * Copyright (C) 2015 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.writing; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 import static com.google.common.collect.Iterables.getOnlyElement; 21 import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType; 22 import static dagger.internal.codegen.javapoet.TypeNames.INSTANCE_FACTORY; 23 import static dagger.internal.codegen.javapoet.TypeNames.MEMBERS_INJECTORS; 24 25 import androidx.room.compiler.processing.XType; 26 import androidx.room.compiler.processing.XTypeElement; 27 import com.squareup.javapoet.CodeBlock; 28 import dagger.assisted.Assisted; 29 import dagger.assisted.AssistedFactory; 30 import dagger.assisted.AssistedInject; 31 import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite; 32 import dagger.internal.codegen.binding.ProvisionBinding; 33 import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation; 34 import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression; 35 36 /** A {@code Provider<MembersInjector<Foo>>} creation expression. */ 37 final class MembersInjectorProviderCreationExpression 38 implements FrameworkInstanceCreationExpression { 39 40 private final ShardImplementation shardImplementation; 41 private final ComponentRequestRepresentations componentRequestRepresentations; 42 private final ProvisionBinding binding; 43 44 @AssistedInject MembersInjectorProviderCreationExpression( @ssisted ProvisionBinding binding, ComponentImplementation componentImplementation, ComponentRequestRepresentations componentRequestRepresentations)45 MembersInjectorProviderCreationExpression( 46 @Assisted ProvisionBinding binding, 47 ComponentImplementation componentImplementation, 48 ComponentRequestRepresentations componentRequestRepresentations) { 49 this.binding = checkNotNull(binding); 50 this.shardImplementation = componentImplementation.shardImplementation(binding); 51 this.componentRequestRepresentations = checkNotNull(componentRequestRepresentations); 52 } 53 54 @Override creationExpression()55 public CodeBlock creationExpression() { 56 XType membersInjectedType = 57 getOnlyElement(binding.key().type().xprocessing().getTypeArguments()); 58 59 boolean castThroughRawType = false; 60 CodeBlock membersInjector; 61 if (binding.injectionSites().isEmpty()) { 62 membersInjector = 63 CodeBlock.of("$T.<$T>noOp()", MEMBERS_INJECTORS, membersInjectedType.getTypeName()); 64 } else { 65 XTypeElement injectedTypeElement = membersInjectedType.getTypeElement(); 66 while (!hasLocalInjectionSites(injectedTypeElement)) { 67 // Cast through a raw type since we're going to be using the MembersInjector for the 68 // parent type. 69 castThroughRawType = true; 70 injectedTypeElement = injectedTypeElement.getSuperType().getTypeElement(); 71 } 72 73 membersInjector = 74 CodeBlock.of( 75 "$T.create($L)", 76 membersInjectorNameForType(injectedTypeElement), 77 componentRequestRepresentations.getCreateMethodArgumentsCodeBlock( 78 binding, shardImplementation.name())); 79 } 80 81 // TODO(ronshapiro): consider adding a MembersInjectorRequestRepresentation to return this 82 // directly 83 // (as it's rarely requested as a Provider). 84 CodeBlock providerExpression = CodeBlock.of("$T.create($L)", INSTANCE_FACTORY, membersInjector); 85 // If needed we cast through raw type around the InstanceFactory type as opposed to the 86 // MembersInjector since we end up with an InstanceFactory<MembersInjector> as opposed to a 87 // InstanceFactory<MembersInjector<Foo>> and that becomes unassignable. To fix it would require 88 // a second cast. If we just cast to the raw type InstanceFactory though, that becomes 89 // assignable. 90 return castThroughRawType 91 ? CodeBlock.of("($T) $L", INSTANCE_FACTORY, providerExpression) 92 : providerExpression; 93 } 94 hasLocalInjectionSites(XTypeElement injectedTypeElement)95 private boolean hasLocalInjectionSites(XTypeElement injectedTypeElement) { 96 return binding.injectionSites().stream() 97 .map(InjectionSite::enclosingTypeElement) 98 .anyMatch(injectedTypeElement::equals); 99 } 100 101 @AssistedFactory 102 static interface Factory { create(ProvisionBinding binding)103 MembersInjectorProviderCreationExpression create(ProvisionBinding binding); 104 } 105 } 106